[インデックス 14088] ファイルの概要
このコミットは、Go言語の標準ライブラリであるtesting
パッケージにおけるベンチマーク実行時間指定フラグ-test.benchtime
の型をfloat64
からflag.Duration
に変更するものです。これにより、ベンチマーク実行時間の指定がより柔軟かつ直感的になり、秒単位だけでなくミリ秒やナノ秒といったより細かい単位での指定が可能になります。
コミット
commit f8b5838123585fc74d8463ff3b99a9780b0517b9
Author: David Symonds <dsymonds@golang.org>
Date: Tue Oct 9 08:57:29 2012 +1100
testing: change -test.benchtime to a flag.Duration.
Fixes #3902.
R=golang-dev, minux.ma, rsc, r
CC=golang-dev
https://golang.org/cl/6611059
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f8b5838123585fc74d8463ff3b99a9780b0517b9
元コミット内容
misc/zsh/go | 2 +-\
src/cmd/go/doc.go | 4 ++--
src/cmd/go/test.go | 4 ++--
src/cmd/go/testflag.go | 2 +-\
src/pkg/go/doc/testdata/benchmark.go | 4 ++--
src/pkg/go/doc/testdata/testing.1.golden | 2 +-\
src/pkg/net/http/serve_test.go | 2 +-\
src/pkg/testing/benchmark.go | 4 ++--
test/fixedbugs/bug369.go | 4 ++--
9 files changed, 14 insertions(+), 14 deletions(-)
変更の背景
この変更は、GoのIssue #3902「go test -benchtime
should accept duration strings」を解決するために行われました。
従来の-test.benchtime
フラグは、ベンチマークを実行する時間を秒単位の浮動小数点数(float64
)で指定していました。例えば、1.5
秒や0.1
秒といった指定です。しかし、この形式では以下のような問題がありました。
- 単位の不明瞭さ: 数値だけでは単位が秒であることが自明ではなく、特に小数点で指定する場合に直感的ではありませんでした。
- 柔軟性の欠如: ミリ秒やマイクロ秒といったより細かい時間単位での指定が直接できず、常に秒に換算する必要がありました。例えば、100ミリ秒を指定するには
0.1
と書く必要があり、これは可読性を損ねます。 - Goのイディオムとの不一致: Goには
time.Duration
という時間間隔を表現するための強力な型があり、これは"1s"
,"100ms"
,"1h30m"
のような人間が読みやすい文字列形式でのパースをサポートしています。ベンチマークの時間指定もこのイディオムに合わせることで、一貫性と使いやすさが向上します。
これらの問題を解決し、よりGoらしい、柔軟で直感的な時間指定を可能にするために、-test.benchtime
の型をflag.Duration
に変更する決定がなされました。
前提知識の解説
1. Goのflag
パッケージ
Go言語の標準ライブラリであるflag
パッケージは、コマンドライン引数をパースするための機能を提供します。プログラム内でflag.StringVar
, flag.IntVar
, flag.BoolVar
などの関数を使ってフラグを定義し、flag.Parse()
を呼び出すことで、コマンドラインから渡された値を対応する変数に格納します。
このパッケージには、基本的な型(文字列、整数、真偽値など)だけでなく、time.Duration
型を直接パースするためのflag.DurationVar
やflag.Duration
関数も用意されています。これにより、ユーザーは"1s"
, "500ms"
, "2h30m"
といった人間が読みやすい形式で時間間隔を指定できるようになります。
2. time.Duration
型
time.Duration
は、Go言語のtime
パッケージで定義されている型で、時間間隔を表します。これはint64
のエイリアスであり、ナノ秒単位で時間間隔を保持します。
time.Duration
の大きな特徴は、そのリテラル表現と文字列パース能力です。
- リテラル:
time.Second
,time.Millisecond
,time.Minute
などの定数と組み合わせて、1 * time.Second
(1秒),500 * time.Millisecond
(500ミリ秒) のように記述できます。 - 文字列パース:
time.ParseDuration("1h30m")
のように、"1h30m"
,"1.5s"
,"200ms"
といった文字列をtime.Duration
型に変換できます。flag
パッケージは、このパース機能を内部的に利用しています。
3. Goのベンチマークと-test.benchtime
Goのtesting
パッケージは、ユニットテストだけでなく、ベンチマークテストの機能も提供しています。ベンチマーク関数はBenchmarkXxx(*testing.B)
というシグネチャを持ち、b.N
回ループを実行して処理時間を計測します。
go test -bench .
コマンドでベンチマークを実行できます。この際、-test.benchtime
フラグは、各ベンチマーク関数が実行される「最小時間」を指定します。Goのベンチマークは、指定された時間(デフォルトは1秒)が経過するか、またはb.N
が十分に大きくなるまで反復処理を続けます。これにより、短時間で終わる処理でも統計的に意味のある結果を得られるように設計されています。
例えば、-test.benchtime=5s
と指定すると、各ベンチマークは少なくとも5秒間実行されます。
技術的詳細
このコミットの技術的な核心は、testing
パッケージ内部で-test.benchtime
フラグを処理する方法の変更と、それに伴う関連ファイルの更新です。
-
src/pkg/testing/benchmark.go
の変更:benchTime
変数の定義がflag.Float64
からflag.Duration
に変更されました。
これにより、// 変更前 var benchTime = flag.Float64("test.benchtime", 1, "approximate run time for each benchmark, in seconds") // 変更後 var benchTime = flag.Duration("test.benchtime", 1*time.Second, "approximate run time for each benchmark")
benchTime
は直接time.Duration
型の値を保持するようになります。デフォルト値も1
(秒)から1*time.Second
というtime.Duration
リテラルに変更されています。- ベンチマークの実行時間を計算するロジックが簡素化されました。
変更前は、// 変更前 d := time.Duration(*benchTime * float64(time.Second)) // 変更後 d := *benchTime
float64
型の*benchTime
(秒数)をfloat64(time.Second)
(ナノ秒単位の秒数)と乗算してtime.Duration
に変換していました。変更後は、*benchTime
が既にtime.Duration
型であるため、直接その値を使用できるようになりました。
-
src/cmd/go/testflag.go
の変更:go test
コマンドの-benchtime
フラグのデフォルト値の記述が1
から1s
に変更されました。これは、新しいflag.Duration
のパース形式に合わせたものです。// 変更前 -benchtime=1: passes -test.benchtime to test // 変更後 -benchtime=1s: passes -test.benchtime to test
-
ドキュメントとテストデータの更新:
src/cmd/go/doc.go
とsrc/cmd/go/test.go
内の-test.benchtime
の説明が、「n seconds」から「t」(duration)に変更され、より一般的な時間指定を許容する記述になりました。misc/zsh/go
のZsh補完スクリプトでも、-benchtime
のヘルプメッセージが更新されました。src/pkg/net/http/serve_test.go
のコメント内の例が15
から15s
に変更され、新しい使用法を示しています。test/fixedbugs/bug369.go
では、テスト内で-test.benchtime
に渡す値が"0.1"
から"100ms"
に変更され、新しい形式でのフラグの動作を検証しています。
これらの変更により、ユーザーはgo test -benchtime=500ms
のように、より直感的で柔軟な時間指定が可能になりました。
コアとなるコードの変更箇所
このコミットの最も重要な変更は、src/pkg/testing/benchmark.go
ファイル内のbenchTime
変数の定義と、その値を使用する箇所です。
--- a/src/pkg/testing/benchmark.go
+++ b/src/pkg/testing/benchmark.go
@@ -14,7 +14,7 @@ import (
)
var matchBenchmarks = flag.String("test.bench", "", "regular expression to select benchmarks to run")
-var benchTime = flag.Float64("test.benchtime", 1, "approximate run time for each benchmark, in seconds")
+var benchTime = flag.Duration("test.benchtime", 1*time.Second, "approximate run time for each benchmark")
var benchmarkMemory = flag.Bool("test.benchmem", false, "print memory allocations for benchmarks")
// Global lock to ensure only one benchmark runs at a time.
@@ -178,7 +178,7 @@ func (b *B) launch() {
b.runN(n)
// Run the benchmark for at least the specified amount of time.
- d := time.Duration(*benchTime * float64(time.Second))
+ d := *benchTime
for !b.failed && b.duration < d && n < 1e9 {
last := n
// Predict iterations/sec.
また、src/pkg/go/doc/testdata/benchmark.go
も同様の変更を含んでおり、これはgo doc
コマンドのテストデータとして使用されるため、ドキュメント生成の正確性を保証します。
--- a/src/pkg/go/doc/testdata/benchmark.go
+++ b/src/pkg/go/doc/testdata/benchmark.go
@@ -13,7 +13,7 @@ import (
)
var matchBenchmarks = flag.String("test.bench", "", "regular expression to select benchmarks to run")
-var benchTime = flag.Float64("test.benchtime", 1, "approximate run time for each benchmark, in seconds")
+var benchTime = flag.Duration("test.benchtime", 1*time.Second, "approximate run time for each benchmark")
// An internal type but exported because it is cross-package; part of the implementation
// of go test.
@@ -151,7 +151,7 @@ func (b *B) launch() {
b.runN(n)
// Run the benchmark for at least the specified amount of time.
- d := time.Duration(*benchTime * float64(time.Second))
+ d := *benchTime
for !b.failed && b.duration < d && n < 1e9 {
last := n
// Predict iterations/sec.
コアとなるコードの解説
src/pkg/testing/benchmark.go
-
var benchTime
の型変更:- 変更前:
var benchTime = flag.Float64("test.benchtime", 1, ...)
- これは
-test.benchtime
フラグがfloat64
型の値をパースすることを意味していました。デフォルト値は1
で、これは1秒を表します。
- これは
- 変更後:
var benchTime = flag.Duration("test.benchtime", 1*time.Second, ...)
flag.Duration
関数を使用することで、benchTime
は直接time.Duration
型のポインタを返すようになります。これにより、"1s"
,"500ms"
のような文字列が自動的にtime.Duration
に変換され、*benchTime
でその値を取得できるようになります。デフォルト値も1*time.Second
というtime.Duration
リテラルで指定され、より明確になりました。
- 変更前:
-
ベンチマーク実行時間計算の簡素化:
- 変更前:
d := time.Duration(*benchTime * float64(time.Second))
*benchTime
はfloat64
型の秒数でした。これをfloat64(time.Second)
(time.Second
はtime.Duration
型ですが、float64
にキャストすることでナノ秒単位の秒数を浮動小数点数として取得できます)と乗算し、その結果をtime.Duration
にキャストしていました。これは、秒数をナノ秒に変換する複雑な計算でした。
- 変更後:
d := *benchTime
*benchTime
は既にtime.Duration
型であるため、追加の変換や計算は不要になりました。これにより、コードがより簡潔で読みやすくなりました。
- 変更前:
この変更により、Goのベンチマークシステムは、時間指定に関してよりGoのイディオムに沿った、柔軟で堅牢なものになりました。
関連リンク
- Go Issue #3902: https://github.com/golang/go/issues/3902
- Go Code Review (CL) 6611059: https://golang.org/cl/6611059
参考にした情報源リンク
- Go言語の
flag
パッケージ公式ドキュメント: https://pkg.go.dev/flag - Go言語の
time
パッケージ公式ドキュメント: https://pkg.go.dev/time - Go言語の
testing
パッケージ公式ドキュメント: https://pkg.go.dev/testing - Go Benchmarking Basics (Go公式ブログ): https://go.dev/blog/benchmarking