本当にただこれがやりたかっただけです。今でも既存のものがあるだろうと思っているのですが、誰も教えてくれなかったのでシュッと作りました。落ち込まないので今からでも教えてくれて良いです!
pushしたらコミットの前後でgo test -bench的なことして有意に悪化してたらテスト落としてくれるSaaSとかOSSとかありませんか
— スッキリごん! (@knqyf263) 2020年1月10日
概要
上のツイートにある通りなのですが、Goだとベンチマークを計測するツールがデフォルトでgo test
に同梱されているので、GitHubとかにコミットされたらそのコミットと一つ前のコミットでベンチマークのスコアを比較して、悪くなっていたら教えて欲しかっただけです。シェルスクリプトで数行で出来るようなレベルですし、ちょっとオプションつけたり表示をリッチにしても200行ぐらいで済みそうだったのでGoでツールを作りました。ブログ執筆時点だと260行でした。
実際、あまりにも簡易的なツール過ぎてGoのコードよりインストールスクリプトのほうが行数が長く、GitHub上でShellScriptのリポジトリとして認識されてしまいました。テストを足してかさ増ししたりもしたのですが、最終的には.gitattributes
を足してちょろまかしました。
使い方
基本的にはCI上で使うことを想定しています。内部でgit reset
相当のことをしているので、ローカルで実行する場合は注意して下さい。 もしコードが消えても責任は取れないです。というか自分が開発中に間違って自分のリポジトリ上で実行してコードが消え去りました。内部でgo-gitを使っているのですが、こいつのresetはuntrackedなやつも消されるみたいです。まだgit addすらしてないから大丈夫だろうと油断していて完全にやられました。シンプルにコードを失いました。そもそもgo-git
のこの挙動がアレだと思うので直したい気持ちもあります。
とりあえず実行するならベンチマークのあるプロジェクトのディレクトリに行って以下のように打つだけです。これで勝手にHEADとHEAD{@1}のベンチマークを比較します。
$ cob ./...
メモリとかも計測したい場合は-benchmem
つければよいです。使い方はgo test
と同じです。go test
のうちベンチマークに関係ありそうで自分が欲しかったオプションだけ取り込んでいます。-bench
、-benchmem
、-benchtime
はそのままgo test
に渡されます。
$ cob -benchmem ./...
結果は以下のようになります。上のテーブルは単純にHEADとHEAD{@1}の値が並んでいます。下のテーブルはそれがどのぐらい変化したかを表示しています。赤は悪化で青は改善を示します。
デフォルトではベンチマークが20%以上悪化するとプログラムがエラーを返します。つまりCI上で実行するとテストが落ちます。ただ元々の値が小さい場合は変化が大きく出てしまうので、同じベンチマークを実行しても20%を超える場合があります。そういう場合は-threshold
を変えたり -benchtime 10s
など大きくして値が安定するようにすると良いです。
$ cob -benchtime 10s -threshold 0.5 ./...
ベンチマークの関数ごとに閾値を決められると良いかもなとは思っているのですが、-benchの正規表現指定とパッケージ指定とか工夫でなんとか出来る気がしたのでやめました。上に書いたように簡易的なツールなので機能は殆ど無いです。
$ cob -bench BenchAppend -threshold ./foo
上のような感じですね。BenchAppend
という文字列を含むfooパッケージ内のベンチマークだけ実行されます。-bench
はgo test
に渡してるだけなので、正規表現も使えます。
あと、パッケージが違えば同じベンチマークの関数名が定義できますが、それも対応してないです。同じ名前がある場合はうまく結果が表示されないのでパッケージ名を指定して2回実行して下さい。
$ cob ./foo $ cob ./bar
オプションもあまりないです。
NAME: cob - Continuous Benchmark for Go Project USAGE: cob [global options] command [command options] [arguments...] COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --only-degression Show only benchmarks with worse score (default: false) --threshold value The program fails if the benchmark gets worse than the threshold (default: 0.1) --bench value Run only those benchmarks matching a regular expression. (default: ".") --benchmem Print memory allocation statistics for benchmarks. (default: false) --benchtime value Run enough iterations of each benchmark to take t, specified as a time.Duration (for example, -benchtime 1h30s). (default: "1s") --help, -h show help (default: false)
結果をコミットIDと保存しておいて比較すれば2回実行しなくても済むかなと思ったのですが、CI環境だと起動される度に微妙に環境が違ったりしてそっちの要因でベンチマークのスコアが変わったら嫌だなと思って今のところやっていません。その辺り、何か良い知見をお持ちの方がいらっしゃれば助けてほしいです。
CI
CIで使う場合のサンプルを置いておきます。ここではGitHub Actionsだけ置いておきますが、他の例や実際の出力を見たい場合はcob-exampleを見て下さい。
name: Bench on: [push, pull_request] jobs: test: name: Bench runs-on: ubuntu-latest steps: - name: Set up Go 1.13 uses: actions/setup-go@v1 with: go-version: 1.13 id: go - name: Check out code into the Go module directory uses: actions/checkout@v1 - name: Install GolangCI-Lint run: curl -sfL https://raw.githubusercontent.com/knqyf263/cob/master/install.sh | sudo sh -s -- -b /usr/local/bin - name: Run Benchmark run: cob -benchmem ./...
まとめ
みんな黙ってるだけでやっぱりこういうツールあるんじゃないかと疑っています。でも自分の要件はこれぐらいなので、必要最小限という意味では自作して良かったかもしれません。自分の環境以外でちゃんと動くかも分かりません。もしこういうの欲しくて困っていた人はフィードバック頂ければもう少し真面目にメンテナンスします。