2014年6月1日日曜日

Golang Cafe #31を開催しました。

Golang Cafe #31を開催しました。

最近、次の回とBlogを書く回が何回目だったかよくわからなくなる今日このごろです。
それぐらい長い長い運営が続いているようです。(Blogを書くのは31回目で、次回(今日)が32回目です。)
本来は、「私が適当にコードを書くために時間を強制的に確保するためのネタ」だったのですが、今では、常に4人集まる「巨大イベント」になってしまいました。

さて、今回は、+Takanobu Haginoさんがいらっしゃらなかったので、Blog記事を読み進めるというのはやめて、世界の+Ryuji Iwataさんがお書きになった「プログラムでシダを描画する」をGoで描画するをGoroutineを使って高速化しようということにしました。
流れでそうなってしまいました。

今回のソースコードはgithubにあげていますのでそちらもごらんください。
shida.goがとりあえず、関数f()の呼び出しをgoroutineに入れておくパターンで作り、
shida2.goはif文が付くものと、必ず呼び出されるものの大きく2つにわける形でgoroutineに登録したものです。

結果として、shida.goを実行すると、N=11あたりで、panicが発生して描画することができなくなりました。で、改善(?)したものもやっぱりN=11あたりでpanicが発生してしまいました。もう少し、検証を進めてみたいと思ったのですが、時間があまり取れなかったので、進めることができませんでしたが、恐らく、登録数が多すぎてオーバーフローしているのではないか?と当日は考えていました。

が、今、ちゃんとログを見てみると、panic: runtime error: index out of range
ということなので、何か違う問題がありそうです。

で、原因が、math/rand/rng.goで発生しているので、じっくりと見てみると、
rng.feed--とか、計算している場所がありました。ほぼ確実に、マルチスレッドのブロックがないので、GOMAXPROCS(4)とかで動かしていると配列エラーになりそうな予感がします。ということで、goroutineの登録が多すぎたのではなくて、randで乱数を生成するときの配列の添字がマイナスになってしまうのが原因のように感じられました。

ということで、今回のポイントは「randとgoroutineを組み合わせる時は、しっかり排他処理をしましょう」ということでした。

2014/06/02追記:
Golang Cafe #32でも並列化についての話題が議論となり、私のshida3.goでは、goroutineの終了を取りこぼしている可能性があることに気が付きました。
結局、sync.WaitGroupを使って、カウントを取ることと、バッファ付きChannelを使って、終了を待ち受けるような構成にしなければいけないだろうという話になりましたが、
そもそも、goroutineの切り替わりが発生しない処理ばかりなので、実際にどこで切り替わるの?ということも考えないといけないことになりました。その点については、runtime.GOMAXPROCS()で同時実行数を変更することで、並列処理されるから、問題はないのでは?という話になったり…。+Ryuji Iwataさんのお題は実は難しかった。と再認識させられる結果になりました。

次回(今日)は、5/31のGo Conferenceでの資料を読み進める予定です。