[インデックス 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正規表現に関する情報: