2017-06-29 4 views
0

私はマンデルブロ集合を計算し、外出先プログラムを実行していますよ。 gouroutineはすべてのピクセルに対して収束を計算するために開始されます。このプログラムは、pixelLengthx = 1000pixelLengthy = 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 
} 

ここでは何が起こっていますか?なぜプログラムは何かを印刷しますか?

私は、外出先のバージョンgo1.8ウィンドウ/ AMD64を使用しています。

Memory usage during program execution.

+6

スタックトレースをクラッシュさせて印刷しようとしています。一度に1,600万のゴルーチンを開始しようとしています。それが効率的にスケジュールされていても、それを行うためのシステムリソースはありますか? goroutine' 'ため – JimB

+3

最小スタックサイズは、従って、16M、メモリの少なくとも32ギガバイトを必要とする、[2キロバイト(https://golang.org/doc/go1.4#runtime)です。 – putu

+0

16Mのゴルーチンを開始する技術的な理由はありますか?または、挑戦するだけです。いくつのゴルーチンが受け入れられるのか? – 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を終了する前にすべてのゴルーチンを先取りする必要があります。ゴルーチンは、関数呼び出し時にスケジューラによって先取りされます。あなたは多くのゴルーチンが住んで、すべてのインライン数学とタイトなループをやっている場合、これは非常に長い時間がかかることがあります。

また、@ JimBと@putuのように、ゴルーチンは非常にリソース効率が高く、生産状況では非常に大きな数値が使用されていますが、こうした状況では特別なリソースが利用できます(Googleの生産インフラストラクチャなど)。ゴルーチンは軽量ですが、16Mの羽はまだ重くなります。あなたのシステムが32GB以上のメモリを持っていないなら、あなた自身がGoではなくあなたのマシンに課税する可能性が高いでしょう。

はGOTRACEBACK =クラッシュして実行してみてくださいとGODEBUG = gctrace = 1、それは死ぬときにスタックトレースからいくつかの証拠情報を取得できるかどうか。

「GCがウェイトを支援する」ためのWeb検索は、この便利なスレッドを上げ:https://groups.google.com/forum/#!topic/golang-dev/PVwDFD7gDuk