tsucchi’s diary(元はてなダイアリー)

はてなダイアリー(d.hatena.ne.jp/tsucchi1022)から移行したものです

prove の並列実行の話

perl でテストスクリプトを実行する prove コマンドには -j という並列実行するオプションがあります。たとえば prove -j2 とすると、2プロセス作って並列実行されます。

ただ、ウチのところのテストコードは基本的にストアドプロシージャのテストなので、DB をあれこれしている間は、他のテストを並列で流すことができません。

その辺の事情はだいぶ前にも書いてますが、まあこんな感じです。

データベースのテストってみんなどうやってるんだろ?

DB を使う Web アプリって、同時にテスト流すとおかしくなるよね?

で、DB が1個しかないから、テストデータを流すときに主キーがかぶってエラーになったり、ゴミデータが残って、それが外部キー制約とかあったりして消せなくて、次のテストデータのロードでエラーになってこけたり。とにかく同時にテストを流すとめちゃくちゃになるんですよ。

で、いろいろ考えたんだけど、幸いにして、ウチのプロジェクトではテスト用の専用モジュールを作っているから、テスト用モジュールの中で排他制御しちゃうのが一番いいのかな、とか思ったり。

とはいえ、テストコードの実行に 10 分以上かかっている現状を、手をこまねいてみてるのもなんだかなー、と top の出力とかテストコードの実行を眺めてたら、ふと思いつきました。

「ん?スクリプトコンパイルも意外と馬鹿にならないオーバーヘッドあるよな?並列化したらこの分だけでも少しは早くなるんじゃね?」

で、ためしに -j2 つけて実行してみました。

通常(並列なし)

$ time prove *.t
...
All tests successful.
Files=696, Tests=36885, 774 wallclock secs (10.36 usr  2.17 sys + 573.82 cusr 42.36 csys = 628.71 CPU)
Result: PASS

real    12m54.111s
user    9m44.332s
sys     0m44.549s

並列(-j2)

$ time prove -j2 *.t
...
Files=696, Tests=36885, 565 wallclock secs (13.94 usr  2.70 sys + 585.98 cusr 43.60 csys = 646.22 CPU)
Result: PASS

real    9m24.724s
user    10m0.077s
sys     0m46.327s

まあ相変わらず時間はかかっていますが、ずいぶん早くなりました。(30%くらい早くなった)。っていうか -j2 つける以外なにもチューニングしてないわけで、もっと早くからやっておけばよかった orz*1

「DB 使うからウチのテストは並列で流せないよ」とあきらめないで、試してみるといいんじゃないかなー、と思います。*2テストコードのファイル数が少なかったりテストが短いと、ロックを取ったりする分のオーバーヘッドのほうが大きいかもしれませんが、ある程度の規模なら効果が出るんじゃないかな。

*1:後でロックのタイミングを見直したり、少しチューニングしてます。この数字からさらに 10 秒くらい早くなったはず。(ちゃんと測ってない...)

*2:ウチは前から自作のロックモジュールを使っていますが、何らかのロックが必要です。めんどくさければ Test::Synchronized とか使ってみればいいんじゃないかなー