A Day in the Life

2010-08-19

Ruby / Rails のテストが約3倍速になって超快適に!!

parallel_tests 使ったらテストが約3倍速(i7)になって Life Changing すぎる、という話です。

最近かなりちゃんとテストする環境になったんだけど、テスト時間が結構かかって、master にマージ後テストこける -> 修正 -> またテスト、というサイクルだと push まで時間かかってどうにかテスト効率よくならないかなー、と思っていたら

とタクト先生(d:id:t-wada)が面白そうなことをつぶやいており、なんか名前からして並列実行できそうな!!!と思って調べてみたらやっぱりtest/spec/cucumberを並列実行できるライブラリ parallel_tests がありました。これはやばい。

前から自分も fork してテスト走らせればユーザースレッドな Ruby でもマルチコアの恩恵うけれるよなぁ、でも DB のコネクションあるし難しいよな、と思っていたんだけど parallel_tests は hey,you. テスト用 DB 複数作っちゃいな、YP! というその発想は無かった感じでうまくテストが実行できる。

rake parallel:spec[1] # 1プロセスで実行
rake parallel:spec    # 2プロセスで実行
rake parallel:spec[4] # 4プロセスで実行

で、実際にギッハブに書かれている通りに設定して Rails アプリで実行したら 4Core の i7 で 140秒弱かかったテストが45秒ほどと4倍とは行かないまでも*1,3倍とちょっぱやに。めちゃくちゃかいてきなんですけど…。なお8プロセスで走らせたらさすがにコア数以上だし、各種テストはforkされて走る訳なのでRailsの起動コストも馬鹿にならないしでかえって遅くなった。

また Rails3 な edge rails でも一行WARNはでるけど、あらかじめテストDBにスキーマを rake:db:setup で作って置いたら普通にうごいた。

bundle install
for i in {1..4};do mysqladmin -uroot -S /tmp/mysql.sock create test$i;done # 4個dbつくる
for i in {1..4};do TEST_ENV_NUMBER=$i RAILS_ENV=test rake db:setup;done #セタップ
rake parallel:prepare
rake parallel:spec[4] #=> 4 processes

というわけでRuby/Rails で使える parallel_tests 使ってちょうかいてきになった、という話でした。タクト++

でも懸念事項も以下のようなことが発生/容易に想定できますのでちゅういです。

  • rcov でのコードカバレージ
    • テストごとなコードカバレージになってしまうため、実際すべてのテストで網羅していてもカバレージ率が下がってしまって良くないです。どうにかしたい
  • DB 以外のロック
    • IO で同一ファイル開いて書いたり、AR 以外使ったりするとロックされない/同一DBにつなぎに行っておかしくなる可能性が。ENV['TEST_ENV_NUMBER'] 使ってどうにかするしかなさそう

欲求の理解

grep や ack で絞り込んだ結果を vim で開く

grep や ack なんかでいくつかのファイル名に絞った後 PAGER で見たいとき、いちいちコピペとかめんどくさい。で、vim には起動時の引数 - で stdin から食べれる機能があるので

git grep ooooo # 良い感じに絞れたよ
git grep ooooo | vim -

すると vim が起動して、ファイル開きたいところで gf で普通に開けて便利。行番号にちゃんと飛びたいときは gF すればおk。grep -n とかつけなくても食べれます。

あとは zsh の alias として

alias -g V="| vim -"

と定義すれば

git greo ooooo V

で使えて便利です。なお vimpager.vim や less.sh のような vim そのものを PAGER としてつかう物とは用途は違うます(カーソル下のファイル名を開きたいから)。

*1:これは parallel_tests がファイル単位であらかじめ分割して実行するため、テストのグループによって終了が速かったり遅かったりするため。ある程度インテリジェンスな改善はできそう