1600万goroutines - “GC辅助等待”

时间:2017-06-29 12:46:28

标签: go garbage-collection goroutine mandelbrot

我正在运行一个计算mandelbrot集的go程序。为每个像素启动一个gouroutine来计算收敛。该程序适用于pixelLengthx = 1000pixelLengthy = 1000。 如果我为pixelLengthx = 4000pixelLengthy = 4000运行相同的代码,程序会在几十秒后开始打印:

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.

2 个答案:

答案 0 :(得分:2)

goroutine很轻但你太过于自信了。我认为你应该让工人像下面那样。

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)

这是由于垃圾收集器试图进行世界末日扫描。 1.8 GC最小化但不能消除STW收集。实际的收集很快,但首先它必须先抢占所有goroutine才能完成GC。调度程序在进行函数调用时可以抢占goroutine。如果您正在进行所有内联数学和紧凑循环,并且有很多goroutine,这可能需要很长时间。

另外,正如@JimB和@putu所指出的那样,虽然goroutines资源效率非常高,并且在生产环境中使用了非常大的数字,但这些情况已经得到了非常可用的资源(例如谷歌的生产基础设施)。 Goroutines重量轻,但16M羽毛仍然很重。如果您的系统没有32GB +内存,那么您可能会对您的机器征税过高,而不是Go自己。

尝试使用GOTRACEBACK = crash和GODEBUG = gctrace = 1运行,看看是否可以在堆栈跟踪死亡时从堆栈跟踪中获取一些证明信息。

网络搜索&#34; GC辅助等待&#34;出现了这个有用的帖子:https://groups.google.com/forum/#!topic/golang-dev/PVwDFD7gDuk