我正在运行一个计算mandelbrot集的go程序。每个像素都启动一个美食线来计算收敛性。该程序在pixelLengthx = 1000
,pixelLengthy = 1000
上运行良好。如果我为pixelLengthx = 4000
运行相同的代码,pixelLengthy = 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 64。
发布于 2017-06-29 15:41:08
戈鲁蒂很轻巧,但你太自信了。我想你应该把工人做得像下面这样。
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
}
发布于 2017-06-29 13:26:57
这是由于垃圾收集器试图停止世界范围的清扫。1.8GC最小化,但不消除STW收集。实际的收集是快速的,但首先它必须抢占所有的峡谷,然后才能完成GC。当调度程序进行函数调用时,可以抢占一个goroutine。如果你正在做所有的内联数学和紧密的循环,与许多猩猩生活,这可能需要很长的时间。
此外,正如@JimB和@putu所指出的那样,虽然goroutines具有极高的资源效率,而且在生产环境中使用了非常多的资源,但这些情况有着非同寻常的可用资源(例如,谷歌的生产基础设施)。戈鲁丁重量轻,但16M羽毛仍将很重。如果您的系统没有32GB+内存,那么您可能会对您的机器过度征税,而不是自行操作。
尝试使用GOTRACEBACK=crash和GODEBUG=gctrace=1运行,看看是否可以在堆栈跟踪死亡时从堆栈跟踪中获得一些证明信息。
对"GC辅助等待“的web搜索找到了以下有用的线程:https://groups.google.com/forum/#!topic/golang-dev/PVwDFD7gDuk
https://stackoverflow.com/questions/44825550
复制相似问题