Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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の数、Iterationsb.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の出力ファイルを引数として受け取り、それぞれのベンチマークの結果を相関させ、パフォーマンスの差分(デルタ)を表示します。これにより、コード変更がパフォーマンスに与える影響(改善、悪化、変化なし)を容易に把握できます。

使用例:

  1. 変更前のベンチマークを実行し、結果をold.txtに保存:
    go test -run=NONE -bench=. ./... > old.txt
    
  2. コードを変更。
  3. 変更後のベンチマークを実行し、結果をnew.txtに保存:
    go test -run=NONE -bench=. ./... > new.txt
    
  4. benchcmpで比較:
    benchcmp old.txt new.txt
    

出力例:

benchmark                old ns/op     new ns/op     delta
BenchmarkMyFunction-8    100           90            -10.00%

benchcmpは、ns/opallocsbytesなどの値と、そのパーセンテージ差分を表示します。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"という文字列が含まれていれば正しく認識し、処理できるようになりました。これにより、ツールの汎用性と堅牢性が向上しています。

関連リンク

参考にした情報源リンク