2017-06-29 41 views
0

我正在運行一個計算mandelbrot集合的go程序。每個像素都啓動一個gouroutine來計算收斂。該程序運行良好的pixelLengthx = 1000,pixelLengthy = 1000。 如果我運行相同的代碼爲pixelLengthx = 4000pixelLengthy = 4000,程序啓動幾十秒鐘後打印此:1600萬套公寓 - 「GC協助等待」

goroutine 650935 [GC assist wait]: 
main.converges(0xa2, 0xb6e, 0xc04200c680) 
.../fractals/fractals.go:41 +0x17e 
created by main.main 
.../fractals/fractals.go:52 +0x2af 

該計劃不會終止,只是繼續打印。

package main 

import (
    "image" 
    "image/color" 
    "image/draw" 
    "image/png" 
    "log" 
    "math/cmplx" 
    "os" 
    "sync" 
) 

var sidex float64 = 4.0 
var sidey float64 = 4.0 
var pixelLengthx int = 4000 
var pixelLengthy int = 4000 
var numSteps int = 100 

func converges(wg *sync.WaitGroup, i, j int, m *image.RGBA) { 
    wht := color.RGBA{255, 50, 128, 255} 

    plx := float64(pixelLengthx) 
    ply := float64(pixelLengthy) 
    fi := float64(i) 
    fj := float64(j) 

    c := complex((fi-plx/2)/plx*sidex, (fj-ply/2)/ply*sidey) 
    zn := complex(0, 0) 
    for k := 0; k < numSteps; k++ { 
     zn = cmplx.Pow(zn, 2) + c 
    } 

    if cmplx.Abs(zn) > 0.1 { 
     m.Set(i, j, wht) 
    } 

    wg.Done() 
} 

func main() { 
    err := Main() 
    if err != nil { 
     log.Fatal(err) 
    } 
} 

func Main() error { 
    m := image.NewRGBA(image.Rect(0, 0, pixelLengthx, pixelLengthy)) 
    blk := color.RGBA{0, 0, 0, 255} 
    draw.Draw(m, m.Bounds(), &image.Uniform{blk}, image.ZP, draw.Src) 

    numGoroutines := pixelLengthx * pixelLengthy 
    wg := &sync.WaitGroup{} 
    wg.Add(numGoroutines) 

    for x := 0; x < pixelLengthx; x++ { 
     for y := 0; y < pixelLengthy; y++ { 
      go converges(wg, x, y, m) 
     } 
    } 

    wg.Wait() 

    f, err := os.Create("img.png") 
    if err != nil { 
     return err 
    } 
    defer f.Close() 

    err = png.Encode(f, m) 
    if err != nil { 
     return err 
    } 

    return nil 
} 

這是怎麼回事?爲什麼程序甚至打印一些東西?

我使用go版本go1.8 windows/amd64。

Memory usage during program execution.

+6

它崩潰並嘗試打印堆棧跟蹤。你正在試圖立即啓動1600萬套goroutines。即使這可以有效地進行調度,你是否有系統資源來做到這一點? – JimB

+3

'goroutine'的最小堆棧大小爲[2KB](https://golang.org/doc/go1.4#runtime),因此16M需要至少32GB的內存。 – putu

+0

是否有任何技術理由啓動16M goroutines?或者僅僅是挑戰Go有多少goroutines可以接受? – Volker

回答

2

夠程是輕量級的,但你有太多的自信。我想,你應該讓工人像下面一樣。

package main 

import (
    "image" 
    "image/color" 
    "image/draw" 
    "image/png" 
    "log" 
    "math/cmplx" 
    "os" 
    "sync" 
) 

var sidex float64 = 4.0 
var sidey float64 = 4.0 
var pixelLengthx int = 4000 
var pixelLengthy int = 4000 
var numSteps int = 100 

func main() { 
    err := Main() 
    if err != nil { 
     log.Fatal(err) 
    } 
} 

type req struct { 
    x int 
    y int 
    m *image.RGBA 
} 

func converges(wg *sync.WaitGroup, q chan *req) { 
    defer wg.Done() 

    wht := color.RGBA{255, 50, 128, 255} 
    plx := float64(pixelLengthx) 
    ply := float64(pixelLengthy) 

    for r := range q { 

     fi := float64(r.x) 
     fj := float64(r.x) 

     c := complex((fi-plx/2)/plx*sidex, (fj-ply/2)/ply*sidey) 
     zn := complex(0, 0) 
     for k := 0; k < numSteps; k++ { 
      zn = cmplx.Pow(zn, 2) + c 
     } 

     if cmplx.Abs(zn) > 0.1 { 
      r.m.Set(r.x, r.y, wht) 
     } 
    } 
} 

const numWorker = 10 

func Main() error { 
    q := make(chan *req, numWorker) 
    var wg sync.WaitGroup 
    wg.Add(numWorker) 
    for i := 0; i < numWorker; i++ { 
     go converges(&wg, q) 
    } 

    m := image.NewRGBA(image.Rect(0, 0, pixelLengthx, pixelLengthy)) 
    blk := color.RGBA{0, 0, 0, 255} 
    draw.Draw(m, m.Bounds(), &image.Uniform{blk}, image.ZP, draw.Src) 

    for x := 0; x < pixelLengthx; x++ { 
     for y := 0; y < pixelLengthy; y++ { 
      q <- &req{x: x, y: y, m: m} 
     } 
    } 
    close(q) 

    wg.Wait() 

    f, err := os.Create("img.png") 
    if err != nil { 
     return err 
    } 
    defer f.Close() 

    err = png.Encode(f, m) 
    if err != nil { 
     return err 
    } 

    return nil 
} 
1

這是由於垃圾收集器在嘗試停止對世界的橫掃。 1.8 GC可以最大限度地減少但不會消除STW收集。實際的收集速度很快,但首先它必須在完成GC之前先搶佔所有的goroutines。調度程序在進行函數調用時可以搶佔goroutine。如果你正在做所有的內聯數學和緊密循環,並且有很多goroutine存在,這可能需要很長時間。另外,正如@JimB和@putu所指出的那樣,儘管goroutines在資源效率方面非常高效,而且在生產環境中使用了大量的數據,但這些情況都有非常多的可用資源(例如Google的生產基礎設施)。 Goroutines重量輕,但16米的羽毛仍然很重。如果您的系統沒有32GB +內存,則可能是您的機器過度使用,而不是Go本身。

嘗試使用GOTRACEBACK = crash和GODEBUG = gctrace = 1運行,並查看當它死亡時是否可以從堆棧跟蹤中獲取一些證明信息。

一個在網上搜索「GC協助等待」打開了這個有用的線索:https://groups.google.com/forum/#!topic/golang-dev/PVwDFD7gDuk