2011年11月3日木曜日

Perlでマルチスレッドプログラミング

職場で私が開発したデータ処理用Perlプログラムにギガ単位のデータを喰わせると、場合によっては処理完了まで1日以上かかってしまう。これでは業務に差し支えが出るため処理ロジックを見直すものの、チューニングの余地は少ない。

マシンの負荷を見ると、16あるCPUコアの1つに負荷が偏っている状態でIO負荷も低いので、並列処理による高速化が期待できるのは明白なのだが、並列処理には近寄りがたい雰囲気を感じていた。「変数が破壊される」「プロセスが無限に生成されてマシンが停止してしまう」といった恐れがあるのでPerlの解説書では「黒魔術」とも呼ばれる並列処理だが、悠長なことは言ってられない。解説書やWebサイトを見ながら検証を進めることにした。

Perlによる並列処理には、fork()を使ったマルチプロセスとithreadによるマルチスレッドの2種類が考えられる。今回は並列実行している処理間で一部の変数を共有する必要があるため、マルチスレッドを選択した。

詳細についてはPerlマルチスレッドのWikにまとめたので、そっちを参照してもらいたいが、まあ一筋縄ではいかなかった。苦労した点は次の通り
  • 呼び出し元のルーチン(通常はmain)でスレッドセーフでないモジュールを利用するとsegmentaion falutが発生する。実はEncodeモジュールがそれに該当するらしく、このエラーを解決するのに手間取った。
  • Thread::Semaphoreで共有する変数の宣言の場所によっては「Scalars leaked」をいう意味不明な警告が発生する。
  • threads::sharedによって共有した変数にアクセスするコストがバカにならない(ロックなしで参照だけでも)。頻繁にアクセスさせず、サブルーチンのスコープで変数を保持して、そこに求める値がなければ共有変数にアクセスする、といった工夫が必要。
チューニングしたスクリプトのベンチマークをとったら、当該部分の処理時間が1/8になった。これからの時代はマルチコアCPUを意識したプログラミングが必要だと痛感した。

0 件のコメント:

コメントを投稿