[インデックス 15705] ファイルの概要
このコミットは、Go言語の標準ライブラリのベンチマークスイートに、fmt
パッケージ(printfスタイルのフォーマット)とtime
パッケージ(時刻の解析とフォーマット)に関する新しいベンチマークテストを追加し、既存のパーサーベンチマークの名称を変更するものです。これにより、Go言語のパフォーマンス特性をより詳細に測定し、将来の最適化のための基盤を強化することを目的としています。
コミット
commit 05403fa8fbfe1c6e44e1b015a6e990af11c81dfd
Author: Rob Pike <r@golang.org>
Date: Mon Mar 11 17:17:25 2013 -0700
go/test/bench/go1: add printf and time format tests
Also rename the go parser test to GoParse so it doesn't grab the globally useful Parse name.
R=golang-dev, dave
CC=golang-dev
https://golang.org/cl/7732044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/05403fa8fbfe1c6e44e1b015a6e990af11c81dfd
元コミット内容
go/test/bench/go1: add printf and time format tests
Also rename the go parser test to GoParse so it doesn't grab the globally useful Parse name.
このコミットは、fmt
パッケージとtime
パッケージのベンチマークテストを追加し、Goパーサーのテスト名をGoParse
に変更して、グローバルに有用なParse
という名前を占有しないようにします。
変更の背景
このコミットの背景には、主に以下の2つの目的があります。
- パフォーマンス測定の拡充: Go言語の標準ライブラリ、特に
fmt
パッケージとtime
パッケージは、多くのGoアプリケーションで頻繁に使用されます。これらのパッケージのパフォーマンスは、アプリケーション全体の速度に大きな影響を与える可能性があります。既存のベンチマークスイートにこれらの機能のテストを追加することで、開発者はこれらのパッケージのパフォーマンス特性をより詳細に把握し、将来のバージョンでの最適化の機会を特定できるようになります。特に、fmt.Fprintf
のようなI/O関連の操作は、バッファリングやフォーマット処理の効率が重要であり、様々なシナリオでのベンチマークは非常に価値があります。 - 命名規則の改善と衝突回避: Go言語のテストフレームワークでは、
Benchmark
プレフィックスを持つ関数がベンチマークとして認識されます。既存のパーサーベンチマーク関数がBenchmarkParse
という汎用的な名前を持っていたため、他のパッケージやテストファイルでParse
という名前が使われている場合に、意図しない名前の衝突や混乱を招く可能性がありました。これをBenchmarkGoParse
にリネームすることで、Goパーサーに特化したベンチマークであることが明確になり、名前空間の衝突を回避し、コードの可読性と保守性を向上させます。
これらの変更は、Go言語の継続的な品質向上とパフォーマンス最適化の取り組みの一環として行われました。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念とパッケージに関する知識が必要です。
- Go言語のベンチマークテスト: Go言語には、標準ライブラリの
testing
パッケージにベンチマークテストの機能が組み込まれています。testing.B
型の引数を受け取るBenchmark
プレフィックスの関数を定義することで、コードのパフォーマンスを測定できます。b.N
はテストの繰り返し回数を示し、b.ResetTimer()
やb.StopTimer()
、b.SetBytes()
などのメソッドを使って、より正確な測定を行うことができます。ベンチマークはgo test -bench .
コマンドで実行されます。 fmt
パッケージ:fmt
パッケージは、Go言語におけるフォーマットI/Oを実装します。C言語のprintf
やscanf
に似た機能を提供し、文字列、数値、構造体などを整形して出力したり、入力から解析したりすることができます。特にfmt.Fprintf
関数は、指定されたio.Writer
(この場合はbytes.Buffer
)にフォーマットされた文字列を書き込むために使用されます。time
パッケージ:time
パッケージは、時刻の表現、測定、表示に関する機能を提供します。time.Parse
関数は、指定されたレイアウトと文字列からtime.Time
オブジェクトを解析し、time.Format
関数はtime.Time
オブジェクトを指定されたレイアウトで文字列にフォーマットします。bytes.Buffer
:bytes
パッケージのBuffer
型は、可変長のバイトバッファを実装します。これは、文字列の構築やI/O操作のバッファとして非常に効率的です。fmt.Fprintf
のような関数で出力先として使用されることが多く、実際のファイルI/Oを伴わないメモリ上での高速な操作が可能です。ベンチマークでは、実際のI/Oのオーバーヘッドを排除し、フォーマット処理自体のパフォーマンスを測定するためにbytes.Buffer
がよく利用されます。go/parser
パッケージ:go/parser
パッケージは、Go言語のソースコードを解析し、抽象構文木(AST)を構築するための機能を提供します。このパッケージは、Goのツールチェインや静的解析ツールなどで利用されます。
技術的詳細
このコミットは、Go言語のベンチマークスイートに具体的なパフォーマンス測定のシナリオを追加しています。
fmt
パッケージのベンチマーク (fmt_test.go
)
fmt_test.go
では、fmt.Fprintf
関数の様々な使用パターンにおけるパフォーマンスを測定するためのベンチマークが追加されています。fmt.Fprintf
は、フォーマットされたデータをio.Writer
に書き込む関数であり、ここではbytes.Buffer
を書き込み先として使用しています。これにより、実際のディスクI/OやネットワークI/Oのオーバーヘッドを除外し、フォーマット処理自体の効率を評価できます。
BenchmarkFmtFprintfEmpty
: 空の文字列をフォーマットする場合のベンチマーク。最も基本的なケースで、フォーマットエンジンの最小限のオーバーヘッドを測定します。BenchmarkFmtFprintfString
: 単一の文字列をフォーマットする場合のベンチマーク。%s
フォーマット指定子を使用します。BenchmarkFmtFprintfInt
: 単一の整数をフォーマットする場合のベンチマーク。%d
フォーマット指定子を使用します。BenchmarkFmtFprintfIntInt
: 2つの整数をフォーマットする場合のベンチマーク。複数の引数とフォーマット指定子を処理する能力を評価します。BenchmarkFmtFprintfPrefixedInt
: プレフィックス付きのテキストと整数をフォーマットする場合のベンチマーク。フォーマット文字列中にリテラルテキストが含まれる場合の処理効率を測定します。これは、フォーマットエンジンがフォーマット指定子をスキャンする際のコストを反映します。BenchmarkFmtFprintfFloat
: 浮動小数点数をフォーマットする場合のベンチマーク。%g
フォーマット指定子を使用します。浮動小数点数のフォーマットは、整数のそれよりも複雑な処理を伴うため、そのパフォーマンス特性を評価します。BenchmarkFmtManyArgs
: 多数の異なる型の引数(整数、文字列)を複雑なフォーマット文字列でフォーマットする場合のベンチマーク。これは、実際のアプリケーションでよく見られるような、より複雑なフォーマットシナリオをシミュレートします。
これらのベンチマークは、fmt.Fprintf
が様々な入力とフォーマット指定子に対してどれだけ効率的に動作するかを詳細に分析するための基盤を提供します。
time
パッケージのベンチマーク (time_test.go
)
time_test.go
では、time
パッケージの主要な機能である時刻の解析とフォーマットに関するベンチマークが追加されています。
BenchmarkTimeParse
:time.Parse
関数を使用して、標準的な時刻文字列(time.ANSIC
レイアウト)をtime.Time
オブジェクトに解析するパフォーマンスを測定します。時刻文字列の解析は、文字列処理と日付/時刻の変換ロジックを含むため、その効率は重要です。BenchmarkTimeFormat
:time.Format
関数を使用して、time.Time
オブジェクトを特定のレイアウト("Mon Jan 2 15:04:05 2006"
)で文字列にフォーマットするパフォーマンスを測定します。時刻のフォーマットも、数値から文字列への変換や、曜日/月名のルックアップなどを含むため、その効率が評価されます。
これらのベンチマークは、Goアプリケーションで頻繁に行われる時刻のシリアライズ/デシリアライズ操作のパフォーマンス特性を理解するのに役立ちます。
パーサーベンチマークのリネーム (parser_test.go
)
parser_test.go
では、既存のBenchmarkParse
関数がBenchmarkGoParse
にリネームされています。この変更は、機能的なパフォーマンス測定には影響を与えませんが、命名規則の観点から重要です。Goのテストフレームワークでは、Benchmark
プレフィックスを持つ関数が自動的にベンチマークとして認識されます。Parse
という名前は非常に一般的であり、他のGoパッケージ(例: strconv.ParseInt
, url.Parse
など)でも広く使用されています。BenchmarkParse
という名前は、Goパーサーに特化したベンチマークであるにもかかわらず、その汎用性から誤解を招く可能性がありました。BenchmarkGoParse
とすることで、このベンチマークがGo言語のパーサーに特化したものであることが明確になり、名前の衝突を避け、コードベース全体の整合性を高めます。
コアとなるコードの変更箇所
test/bench/go1/fmt_test.go
(新規ファイル)
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package go1
// benchmark based on fmt/fmt_test.go
import (
"bytes"
"fmt"
"testing"
)
func BenchmarkFmtFprintfEmpty(b *testing.B) {
var buf bytes.Buffer
for i := 0; i < b.N; i++ {
fmt.Fprintf(&buf, "")
}
}
func BenchmarkFmtFprintfString(b *testing.B) {
var buf bytes.Buffer
for i := 0; i < b.N; i++ {
buf.Reset()
fmt.Fprintf(&buf, "%s", "hello")
}
}
func BenchmarkFmtFprintfInt(b *testing.B) {
var buf bytes.Buffer
for i := 0; i < b.N; i++ {
buf.Reset()
fmt.Fprintf(&buf, "%d", 5)
}
}
func BenchmarkFmtFprintfIntInt(b *testing.B) {
var buf bytes.Buffer
for i := 0; i < b.N; i++ {
buf.Reset()
fmt.Fprintf(&buf, "%d %d", 5, 6)
}
}
func BenchmarkFmtFprintfPrefixedInt(b *testing.B) {
var buf bytes.Buffer
for i := 0; i < b.N; i++ {
buf.Reset()
fmt.Fprintf(&buf, "This is some meaningless prefix text that needs to be scanned %d", 6)
}
}
func BenchmarkFmtFprintfFloat(b *testing.B) {
var buf bytes.Buffer
for i := 0; i < b.N; i++ {
buf.Reset()
fmt.Fprintf(&buf, "%g", 5.23184)
}
}
func BenchmarkFmtManyArgs(b *testing.B) {
var buf bytes.Buffer
for i := 0; i < b.N; i++ {
buf.Reset()
fmt.Fprintf(&buf, "%2d/%2d/%2d %d:%d:%d %s %s\n", 3, 4, 5, 11, 12, 13, "hello", "world")
}
}
test/bench/go1/parser_test.go
(変更箇所)
--- a/test/bench/go1/parser_test.go
+++ b/test/bench/go1/parser_test.go
@@ -33,7 +33,7 @@ func makeParserBytes() []byte {
return b
}
-func BenchmarkParse(b *testing.B) {
+func BenchmarkGoParse(b *testing.B) {
b.SetBytes(int64(len(parserbytes)))
for i := 0; i < b.N; i++ {
if _, err := parser.ParseFile(token.NewFileSet(), "", parserbytes, parser.ParseComments); err != nil {
test/bench/go1/time_test.go
(新規ファイル)
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package go1
// benchmark based on time/time_test.go
import (
"testing"
"time"
)
func BenchmarkTimeParse(b *testing.B) {
for i := 0; i < b.N; i++ {
time.Parse(time.ANSIC, "Mon Jan 2 15:04:05 2006")
}
}
func BenchmarkTimeFormat(b *testing.B) {
t := time.Unix(1265346057, 0)
for i := 0; i < b.N; i++ {
t.Format("Mon Jan 2 15:04:05 2006")
}
}
コアとなるコードの解説
fmt_test.go
の新規ベンチマーク
これらのベンチマーク関数はすべて、testing.B
型の引数b
を受け取り、b.N
回ループを実行して、その間の処理時間を測定します。各ベンチマークは、bytes.Buffer
をfmt.Fprintf
の出力先として使用し、buf.Reset()
を呼び出して各イテレーションでバッファをクリアしています。これにより、前のイテレーションのデータが次のイテレーションの測定に影響を与えることを防ぎ、フォーマット処理自体の純粋なパフォーマンスを測定できます。
BenchmarkFmtFprintfEmpty
,BenchmarkFmtFprintfString
,BenchmarkFmtFprintfInt
,BenchmarkFmtFprintfIntInt
,BenchmarkFmtFprintfPrefixedInt
,BenchmarkFmtFprintfFloat
,BenchmarkFmtManyArgs
: これらは、fmt.Fprintf
が様々な引数の型、数、およびフォーマット文字列の複雑さに対してどれだけ効率的に動作するかを評価します。例えば、BenchmarkFmtManyArgs
は、複数の異なる型の引数を含む複雑なフォーマット文字列の処理コストを測定し、実際のアプリケーションでの使用パターンをよりよく反映します。
parser_test.go
の変更
BenchmarkParse
関数がBenchmarkGoParse
にリネームされました。
func BenchmarkGoParse(b *testing.B)
: この変更は、Goパーサーのベンチマーク関数名をより具体的にし、Parse
という一般的な名前が他のコンテキストで誤って参照されることを防ぎます。機能的な変更はなく、ベンチマークの測定対象や方法は以前と同じです。b.SetBytes(int64(len(parserbytes)))
は、ベンチマークの実行ごとに処理されるバイト数を設定し、結果の出力に「ops/s」(1秒あたりの操作数)だけでなく「MB/s」(1秒あたりの処理メガバイト数)も表示されるようにします。
time_test.go
の新規ベンチマーク
BenchmarkTimeParse(b *testing.B)
: このベンチマークは、time.Parse
関数が特定のフォーマットの文字列をtime.Time
オブジェクトに解析する速度を測定します。ループ内でtime.Parse
を繰り返し呼び出すことで、解析処理の平均的なパフォーマンスを評価します。BenchmarkTimeFormat(b *testing.B)
: このベンチマークは、time.Time
オブジェクトを特定のフォーマットの文字列に変換するt.Format
関数の速度を測定します。time.Unix(1265346057, 0)
で固定のtime.Time
オブジェクトを作成し、そのフォーマット処理の効率を評価します。
これらの新しいベンチマークは、Go言語の標準ライブラリの重要な部分のパフォーマンス特性を継続的に監視し、将来の最適化のためのデータを提供するために不可欠です。
関連リンク
- Go Gerrit Change: https://golang.org/cl/7732044
参考にした情報源リンク
- Go言語の
testing
パッケージドキュメント: https://pkg.go.dev/testing - Go言語の
fmt
パッケージドキュメント: https://pkg.go.dev/fmt - Go言語の
time
パッケージドキュメント: https://pkg.go.dev/time - Go言語の
bytes
パッケージドキュメント: https://pkg.go.dev/bytes - Go言語の
go/parser
パッケージドキュメント: https://pkg.go.dev/go/parser - Go言語のベンチマークの書き方 (公式ブログ記事など): (具体的なURLはコミット情報にはないため、一般的な情報源として記載)