最近C++のインテグレーションテストでGoogle Testを使っていますが、ターゲットコードの性質上、コード内部でsleepを入れることが多くてCPUの占有時間はそれ程でも無いですが素にgtestを走らせるとかなりの時間が掛かっていました。
そこで並列実行系のテスト出来ないか調べたところ、Google公式からgtest-parallelというツールが提供されていてこれが今回はカチッと嵌まって劇的に速度向上したので使い方を紹介しながら結果を振りかえります。
目次
gtest-parallelとは
ツールの名の通り、Google Testを並列実行してくれるツールです。仕組みは挙動を見た限りでは非常にシンプルで、gtest-parallelはgtestの--gtest_filterを用いてテストを個別に並列実行させ、最終的にテスト結果を集計する様になっています。
gtest-parallelの使い方
gtest-parallelはPython2系スクリプトとなっており、gtest-parallelを実行することで使用出来ます:
$ git clone https://github.com/google/gtest-parallel.git
$ python gtest-parallel/gtest-parallel /path/to
--help
オプションでヘルプが参照出来るので日本語訳します:
gtest-parallel [options] binary [binary ...] -- [additional args]
オプション:
-h, --help
ヘルプを表示します。
-d OUTPUT_DIR, --output_dir=OUTPUT_DIR
テストログを出力するディレクトリを指定します。ログは指定したディレクトリより
`gtest-parallel-logs/` フォルダが配置されます。例えば `--output_dir=/tmp` だと
`/tmp/gtest-parallel-logs/` フォルダに出力されます。
-r REPEAT, --repeat=REPEAT
全テストの実行回数を指定します。
--retry_failed=RETRY_FAILED
失敗したテストのリトライ回数を指定します。
--failed
失敗したテストと新しいテストケースのみ実行します。
-w WORKERS, --workers=WORKERS
同時に生成するワーカープロセス数を指定します。
--gtest_color=GTEST_COLOR
カラー出力を指定します。
--gtest_filter=GTEST_FILTER
テストケースのフィルタリングを指定します。
--gtest_also_run_disabled_tests
無効化されているテストも実行します。
--print_test_times
テスト実行後にそれぞれのテストケースの実行時間を表示します。
--shard_count=SHARD_COUNT
合計シャード数を指定します。
複数のマシンでシャーディングテストを行う為に指定します。
--shard_index=SHARD_INDEX
0から始まるシャードのインデックスを指定します。
複数のマシンでシャーディングテストを行う為に指定します。
--dump_json_test_results=DUMP_JSON_TEST_RESULTS
テスト結果をJSONフォーマットで保存します。
フォーマットは以下のURLを参照してください:
https://chromium.googlesource.com/chromium/src/+/master/docs/testing/json_test_results_format.md
--timeout=TIMEOUT
指定時間を経過するとに残りの実行中プロセスは全て中断します(秒)。
--serialize_test_cases
同じテストケースを同時に実行しないようにします。
この中で必ず指定しそうなのはワーカー数で、コア数を大きく上回る数で指定するのが良いようです。自分のPCでは64〜128辺りで一番効率よく並列実行出来ていました。
実行結果
今回gtest-parallelで速度測定したテストターゲットは342 tests from 29 test cases
で先ずは単一実行して速度実行すると、
real 5m57.741s
user 0m0.410s
sys 0m0.510s
となり、real 5m57sのわりにはuser 0.41sで殆どCPUを使っておらずターゲットコードはsleep等が入っているので通しで実行してしまうとどうしても時間が掛かっています。
これに対しgtest-parallelを試します。今回はワーカー数を128、 python gtest-parallel -w 128 /path/to/target_test
で実行すると、
real 0m17.274s
user 0m6.538s
sys 0m4.565s
となり、実行時間はなんと20倍強!今までのテスト実行時間はなんだったんだwという速度差がついて大満足の結果となりました。
gtest-parallelを使うときに対処しておきたいこと
gtestを最新にしておく
gtestの初期化がテストケース数分行われるためにgtestで例外が出ることがありました。
2018/2/14現在stableの1.8.0よりmasterブランチの最新版の方が安定していたのでそちらを利用した方が良いです
テストケースを最小単位に分割しておく
逐次実行の場合はトータルの実行時間の効率の為にいくつかのテストを一纏めにしたりすることもあるかと思いますが、gtest-parallelの場合、各テストケースが並列実行の基本単位になるので幾ら並列実行したとしてもテストケース中の最長時間より実行速度が速くなることはありません。
少々トータルのテスト時間が増えたとしても、TEST_P等を利用してテストケースをなるべく分割して1テスト辺りのテスト時間を短縮することでgtest-parallelのテスト時間も短縮することが可能になります。
gtest-parallelは使った方が良い
というわけで使わない理由が無さそうなgtest-parallel。サクサクとテスト実行を進めてストレスフリーな開発が出来ると良いですね:)