[インデックス 10643] ファイルの概要
このコミットは、Go言語のツールセットに含まれるmisc/benchcmpツールの挙動を修正するものです。具体的には、ベンチマーク結果のパースロジックを改善し、ベンチマーク名の先頭に必ずしも"Benchmark"という文字列が来ることを要求しないように変更しています。これにより、package_name.BenchmarkXXXのような形式のベンチマーク名も正しく処理できるようになります。
コミット
commit b955bbfbdbe070d65910f76dcd09276d16978e52
Author: Robert Griesemer <gri@golang.org>
Date: Wed Dec 7 10:30:08 2011 -0800
misc/benchcmp: don't require "Benchmark" at beginning of line
Output is package_name.BenchmarkXXX.
R=rsc
CC=golang-dev
https://golang.org/cl/5440123
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b955bbfbdbe070d65910f76dcd09276d16978e52
元コミット内容
misc/benchcmp: don't require "Benchmark" at beginning of line
Output is package_name.BenchmarkXXX.
このコミットは、misc/benchcmpツールがベンチマーク結果を解析する際に、行の先頭に"Benchmark"という文字列があることを必須としないように変更します。これは、ベンチマークの出力形式がpackage_name.BenchmarkXXXのようになることを考慮したものです。
変更の背景
Go言語のベンチマーク機能は、go test -bench=.コマンドによって実行され、その結果は標準出力に出力されます。この出力には、ベンチマーク名、実行回数、1操作あたりのナノ秒(ns/op)、メモリ割り当て(B/op)、割り当て回数(allocs/op)などの情報が含まれます。
misc/benchcmpツールは、このgo test -benchの出力結果を2つ(変更前と変更後など)比較し、パフォーマンスの変化を分かりやすく表示するためのユーティリティです。このツールは、ベンチマーク結果の行を正規表現でパースして、ベンチマーク名や性能指標を抽出します。
従来のbenchcmpのパースロジックでは、ベンチマーク名が常にBenchmarkXXXのように行の先頭から始まることを前提としていました。しかし、Goのベンチマーク出力では、ベンチマーク関数が属するパッケージ名がプレフィックスとして付与されることがあります。例えば、my_package.BenchmarkMyFunctionのような形式です。
このような出力形式の場合、^Benchmark(行頭に"Benchmark"があることを意味する正規表現)というパターンでは、my_package.BenchmarkMyFunctionのようなベンチマーク名を正しく認識できませんでした。このコミットは、この認識の不一致を解消し、benchcmpがより柔軟にベンチマーク出力を処理できるようにするために行われました。
前提知識の解説
Go言語のベンチマーク
Go言語には、標準ライブラリのtestingパッケージにベンチマーク機能が組み込まれています。これにより、開発者はコードのパフォーマンスを測定し、最適化の効果を評価することができます。
- ベンチマーク関数の命名規則: ベンチマーク関数は、
Benchmarkというプレフィックスで始まり、その後に続く名前の最初の文字は大文字である必要があります(例:BenchmarkMyFunction)。 - 関数シグネチャ: ベンチマーク関数は、
func BenchmarkXxx(b *testing.B)というシグネチャを持つ必要があります。 - 実行ループ: ベンチマーク関数内では、測定対象のコードを
b.N回ループで実行します。b.Nの値は、testingパッケージが自動的に調整し、統計的に有意な結果を得るために十分な時間(デフォルトで最低1秒)ベンチマークが実行されるようにします。 - 実行方法: ベンチマークは、
go test -bench=.コマンドで実行されます。-benchフラグに正規表現を指定することで、特定のベンチマークのみを実行することも可能です。 - 出力形式: ベンチマークの出力は、通常、
BenchmarkName-CPUs Iterations ns/op B/op allocs/opのような形式になります。ここで、BenchmarkNameはベンチマーク関数の名前、CPUsはベンチマークが実行されたCPUの数、Iterationsはb.Nの値、ns/opは1操作あたりの平均実行時間(ナノ秒)、B/opは1操作あたりの平均メモリ割り当て量(バイト)、allocs/opは1操作あたりの平均メモリ割り当て回数を示します。
misc/benchcmpツール
misc/benchcmpは、Goのベンチマーク結果を比較するためのコマンドラインユーティリティです。これはgolang.org/x/toolsリポジトリの一部であり、標準のGoディストリビューションには含まれていないため、別途go get golang.org/x/tools/cmd/benchcmpでインストールする必要があります。
benchcmpは、2つのgo test -benchの出力ファイルを引数として受け取り、それぞれのベンチマークの結果を相関させ、パフォーマンスの差分(デルタ)を表示します。これにより、コード変更がパフォーマンスに与える影響(改善、悪化、変化なし)を容易に把握できます。
使用例:
- 変更前のベンチマークを実行し、結果を
old.txtに保存:go test -run=NONE -bench=. ./... > old.txt - コードを変更。
- 変更後のベンチマークを実行し、結果を
new.txtに保存:go test -run=NONE -bench=. ./... > new.txt benchcmpで比較:benchcmp old.txt new.txt
出力例:
benchmark old ns/op new ns/op delta
BenchmarkMyFunction-8 100 90 -10.00%
benchcmpは、ns/op、allocs、bytesなどの値と、そのパーセンテージ差分を表示します。ns/opの負のデルタはパフォーマンスの改善を示します。
注意点: benchcmpは現在では非推奨であり、より高機能なgolang.org/x/perf/cmd/benchstatの使用が推奨されています。
技術的詳細
このコミットの技術的詳細は、misc/benchcmpツールが内部で使用しているAWKスクリプトの正規表現の変更に集約されます。
benchcmpは、AWKというテキスト処理言語で書かれたスクリプトであり、入力されたベンチマーク結果の各行をパターンマッチングによって解析します。
変更前のコードでは、ベンチマーク結果の行を識別するために以下の正規表現を使用していました。
$1 ~ /^Benchmark/ && $4 == "ns/op"
この正規表現の意味は以下の通りです。
$1: 入力行の最初のフィールド(通常、ベンチマーク名が含まれる)。~: マッチ演算子。^Benchmark: 文字列の先頭(^)に"Benchmark"という文字列が続くパターン。&&: 論理AND演算子。$4 == "ns/op": 入力行の4番目のフィールドが厳密に"ns/op"という文字列と等しいこと。
この条件は、ベンチマーク名が常にBenchmarkで始まり、かつ行の先頭にあることを前提としていました。しかし、Goのベンチマーク出力では、package_name.BenchmarkXXXのようにパッケージ名がプレフィックスとして付与される場合があるため、この^(行頭)の制約が問題となっていました。
このコミットでは、正規表現から^(行頭)のアンカーを削除し、以下のように変更しました。
$1 ~ /Benchmark/ && $4 == "ns/op"
この変更により、正規表現は$1(最初のフィールド)のどこかに"Benchmark"という文字列が含まれていればマッチするようになります。これにより、my_package.BenchmarkMyFunctionのような形式のベンチマーク名も正しく認識され、benchcmpツールがより広範なベンチマーク出力に対応できるようになりました。
この修正は、Goのベンチマーク出力形式の多様性に対応するための、堅牢性向上のための変更と言えます。
コアとなるコードの変更箇所
--- a/misc/benchcmp
+++ b/misc/benchcmp
@@ -17,7 +17,7 @@ BEGIN {
n = 0
}
-$1 ~ /^Benchmark/ && $4 == "ns/op" {
+$1 ~ /Benchmark/ && $4 == "ns/op" {
if(old[$1]) {
if(!saw[$1]++) {
name[n++] = $1
コアとなるコードの解説
変更は、misc/benchcmpスクリプト内のAWKコードの1行にあります。
元のコード:
$1 ~ /^Benchmark/ && $4 == "ns/op" {
修正後のコード:
$1 ~ /Benchmark/ && $4 == "ns/op" {
この変更は、正規表現^Benchmarkから行頭アンカー^を削除したものです。
$1: AWKにおいて、これは現在の入力行の最初のフィールド(列)を指します。Goのベンチマーク出力では、このフィールドにベンチマーク名が含まれます。~: AWKの正規表現マッチ演算子です。左側のオペランド($1)が右側の正規表現にマッチするかどうかをテストします。/Benchmark/: これは正規表現リテラルです。この正規表現は、文字列内に"Benchmark"という部分文字列が含まれている場合にマッチします。元の/^Benchmark/は、文字列の先頭に"Benchmark"がある場合にのみマッチしました。&&: 論理AND演算子です。両側の条件が真である場合に全体が真となります。$4 == "ns/op": これは、現在の入力行の4番目のフィールドが文字列"ns/op"と完全に一致するかどうかをチェックする条件です。Goのベンチマーク出力では、ns/opは通常、ベンチマーク結果の行の4番目のフィールドに現れるため、この条件はベンチマーク結果の行を正確に識別するために使用されます。
この修正により、benchcmpは、BenchmarkMyFunctionのような単純なベンチマーク名だけでなく、my_package.BenchmarkMyFunctionのようにパッケージ名がプレフィックスとして付与されたベンチマーク名も、最初のフィールドに"Benchmark"という文字列が含まれていれば正しく認識し、処理できるようになりました。これにより、ツールの汎用性と堅牢性が向上しています。
関連リンク
- Go言語のベンチマークに関する公式ドキュメント(
testingパッケージ): https://pkg.go.dev/testing golang.org/x/tools/cmd/benchcmpのソースコード(変更が適用されたリポジトリ): https://github.com/golang/tools/tree/master/cmd/benchcmpgolang.org/x/perf/cmd/benchstat(benchcmpの推奨代替ツール): https://pkg.go.dev/golang.org/x/perf/cmd/benchstat
参考にした情報源リンク
- Go言語のベンチマークに関する記事:
benchcmpツールの説明:- https://go.dev/blog/benchmarking (Go公式ブログのベンチマークに関する記事で
benchcmpに言及) - https://alexedwards.net/blog/go-benchmarking
- https://go.dev/blog/benchmarking (Go公式ブログのベンチマークに関する記事で
- AWK正規表現に関する情報: