Railsを開発しているとRSpecの実行時間が遅くて開発効率が悪くなることがあります。
これは開発環境にDocker For Macを使っているからでしょうか?
背景
Docker For Macは遅いと言われます。
これは現在のDocker For Macでも遅いのでしょうか?
rawフォーマットは試していません
また、最新のDocker For Macでは速くなっているようです。
Docker for Macのディスクスループットを約2倍にする - Qiita
Docker Community Edition 17.12.0-ce-mac46 2018-01-09 (Stable)の更新で、ディスクスループットが320MiB/secから600MiB/secになるらしいので適用する。
APFS, SSD, High Sierraで有効。
現在Sierraを使っているので、試していません。
実行環境
- MacBook Pro Late 2013
- macOS Sierra 10.12.6
- Docker Community Edition 17.12.0-ce-mac49 (21995)
手段
AWS EC2のAmazon Linux2インスタンスを使って試します。
Dockerの起動時間を抜いて計測するために
docker-compose run --service-ports web bash
を実行し、Docker上で
time bundle exec rspec spec/models/hoge_spec.rb
を実行します。
bash-4.3# time bundle exec rspec spec/models/hoge_spec.rb
No DRb server is running. Running in local process instead ...
Rack::File headers parameter replaces cache_control after Rack 1.5.
....................................................................................................................................................................................
Finished in 5.15 seconds
180 examples, 0 failures
real 0m23.581s
user 0m0.010s
sys 0m0.000s
real
の値がtimeコマンドの結果です。
rspec
コマンドの起動から終了までの実時間です。
これは「Rails環境の読み込み」時間を含みます。
Finished in 5.15 seconds
がRSpecのテストの実行時間です。
差の18.4秒は「Rails環境の読み込み時間」です。
結果
以下の環境で試しました。
- Docker For Mac
- t2.micro
- c4.large
いずれもDockerとdocker-composeでRailsアプリケーションを構成しています。
環境 | real | rspec | real比率 | rspec比率 | Rails環境読み込み比率 |
---|---|---|---|---|---|
Docker For Mac | 0m23.581s | 5.15 seconds | - | - | - |
t2.micro | 0m14.258s | 2.9 seconds | 60% | 56% | 62% |
c4.large | 0m11.456s | 2.52 seconds | 49% | 49% | 48% |
EC2上では40〜50%の時間短縮が計測されました。
単純にDocker for Macを使うとLinux上でDockerを使う場合の、2倍の時間が掛かると考えて良いでしょう。
Docker For Mac
bash-4.3# time bundle exec rspec spec/models/hoge_spec.rb
No DRb server is running. Running in local process instead ...
Rack::File headers parameter replaces cache_control after Rack 1.5.
....................................................................................................................................................................................
Finished in 5.15 seconds
180 examples, 0 failures
real 0m23.581s
user 0m0.010s
sys 0m0.000s
t2.micro
bash-4.3# time bundle exec rspec spec/models/hoge_spec.rb
No DRb server is running. Running in local process instead ...
Rack::File headers parameter replaces cache_control after Rack 1.5.
....................................................................................................................................................................................
Finished in 2.9 seconds
180 examples, 0 failures
real 0m14.258s
user 0m0.000s
sys 0m0.000s
c4.large
bash-4.3# time bundle exec rspec spec/models/hoge_spec.rb
No DRb server is running. Running in local process instead ...
Rack::File headers parameter replaces cache_control after Rack 1.5.
....................................................................................................................................................................................
Finished in 2.52 seconds
180 examples, 0 failures
real 0m11.456s
user 0m0.000s
sys 0m0.000s
考察
EC2の方が2倍近く速い
Docker For MacのDisk I/Oの遅さが原因に思えます。
Docker for Macのディスクスループットを約2倍にする - Qiita の効果とも一致します。
ファイルシステムのraw format化で、解消されることが期待できます。
c4.largeがt2.microより15%速い
他のインスタンスタイプ、例えば
- t2.small
- m4.large
- c3.large
を使っても、t2.microとは差がありません。
これはc4.largeが(標準で)EBS 最適化インスタンスだからです。
C5、C4、M5、M4、P3、P2、G3、および D2 の各インスタンスの場合、この機能はデフォルトで有効になっており、追加料金は発生しません。
やはり、RailsのRSpecでは、Disk I/Oがボトルネックのようです。
その他の高速化
Rails application preloader
例えば、Springを使うと「Rails環境のロード時間」をスキップできます。
今回のテストのように、Rspecの実実行時間の8割が「Rails環境のロード時間」に使われている場合は、Rails application preloaderを使うと、テストの繰り返しやすさに効果的です。
#### 参考
- Railsの開発効率をあげる - Springを使ってRailsのコンソールコマンドの実行を早くする - Rails Webook
- Spork/Zeus/Spring によるテスト実行時間の短縮 - RSpec/Capybara入門 - Ruby on Rails with OIAX
- rspecを高速化するsporkとその仕組み - ITエンジニアとして生きる
スローテストを解消する
RailsのRSpecは、Rails環境のロード時間を除いても、遅いことが多いです。
特にモデルのテストの場合は、it毎にDBにテスト用のレコードを用意するためのinsert
文を発行するためです。
必要なレコードが20件ある場合は、体感でわかるぐらい遅くなりました。
テスト数が多いと線形に遅くなります。
簡単に20秒掛かるテストセットが作れます。
Mac上で20秒掛かるテストが、Linux上で10秒になっても、テスト待ちのストレスはあまり解消されません。
ローカル環境とリモート環境を切り替える手間を考えると、テストの繰り返しやすさは上がりません。
地道にスローテストを解消した方が、テストの繰り返しやすさには効果が高いです。
before :all
で複数のテストでまとめてテストデータを用意したり、FactoryGirl.create
の代わりにFactoryGirl.build
を使ったりして、DBのレコード追加削除を減らすのが効果的です。