[インデックス 10814] ファイルの概要
コミット
commit 6e8875551a0db770c5fbaaf4c126646f1709cab1
Author: Russ Cox <rsc@golang.org>
Date: Thu Dec 15 12:32:59 2011 -0500
test/bench/go1: first draft of Go 1 benchmark suite
I have included a few important microbenchmarks,
but the overall intent is to have mostly end-to-end
benchmarks timing real world operations.
The jsondata.go file is a summary of agl's
activity in various open source repositories.
It gets used as test data for many of the benchmarks.
Everything links into one binary (even the test data)
so that it is easy to run the benchmarks on many
computers: there is just one file to copy around.
R=golang-dev, r, bradfitz, adg, r
CC=golang-dev
https://golang.org/cl/5484071
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6e8875551a0db770c5fbaaf4c126646f1709cab1
元コミット内容
このコミットは、Go 1リリースに向けたベンチマークスイートの最初のドラフトを導入するものです。このスイートは、いくつかの重要なマイクロベンチマークを含みつつも、主に実際の操作の時間を測定するエンドツーエンドのベンチマークに焦点を当てています。ベンチマークデータとして、jsondata.go
ファイルが使用されており、これはagl
の様々なオープンソースリポジトリにおける活動の要約データを含んでいます。このベンチマークスイートの特筆すべき点は、テストデータを含め、すべてが単一のバイナリにリンクされるように設計されていることです。これにより、複数のコンピュータでベンチマークを容易に実行できるよう、コピーするファイルが1つだけで済むようになっています。
変更の背景
Go言語は、そのパフォーマンス特性が重要な要素の一つです。Go 1のリリースを控えるにあたり、言語の安定性と性能を客観的に評価し、将来的な改善の基準となるベンチマークスイートの確立が不可欠でした。このコミットは、Go 1の初期段階における性能測定の基盤を築くことを目的としています。特に、マイクロベンチマークだけでなく、実際のアプリケーションに近いシナリオでの性能を測定するエンドツーエンドのベンチマークを重視することで、より実用的な性能評価を可能にしようとしています。また、ベンチマークの配布と実行の容易さを考慮し、単一バイナリでの提供が選択されました。
前提知識の解説
- Go言語のベンチマーク: Go言語には、
testing
パッケージにベンチマーク機能が組み込まれています。go test -bench=.
コマンドを使用することで、BenchmarkXxx
という命名規則に従って記述された関数を実行し、その性能を測定できます。これにより、コードの変更が性能に与える影響を定量的に評価することが可能です。 - マイクロベンチマーク: 特定の小さなコード片やアルゴリズムの性能を測定するベンチマークです。例えば、特定のデータ構造の操作や、基本的な算術演算の速度などを評価します。
- エンドツーエンドベンチマーク: 実際のアプリケーションやシステム全体に近いシナリオで性能を測定するベンチマークです。複数のコンポーネントや外部サービスとの連携を含むことが多く、より現実的な性能特性を反映します。
jsondata.go
: このコミットで導入されたファイルで、ベンチマークの入力データとして使用されるJSONデータを含んでいます。このデータは、agl
(Andrew Gerrand)の実際のオープンソース活動から抽出されたもので、現実世界のデータセットを模倣しています。- 単一バイナリ: 実行に必要なすべてのコードとデータが1つの実行可能ファイルにまとめられている状態を指します。これにより、依存関係の管理が簡素化され、異なる環境へのデプロイが容易になります。
Makefile
: ビルドプロセスを自動化するためのファイルです。このコミットでは、ベンチマークスイートのビルドと実行を管理するために使用されています。src/clean.bash
/src/run.bash
: Goプロジェクトのビルドやテスト実行に関連するシェルスクリプトです。このコミットでは、新しいベンチマークスイートをこれらのスクリプトに統合し、自動テストおよびクリーンアッププロセスの一部として実行できるように変更されています。
技術的詳細
このコミットは、test/bench/go1
ディレクトリ以下に新しいベンチマークスイートを構築しています。
-
ベンチマークファイルの追加:
binarytree_test.go
: バイナリツリーの生成と破棄を通じてガベージコレクタの性能をテストします。これは、メモリ割り当てと解放が頻繁に行われるシナリオをシミュレートします。fannkuch_test.go
: 配列のインデックス操作と配列境界の最適化性能をテストするFannkuchベンチマークです。fasta_test.go
: FASTA形式のデータ生成に関連するベンチマークで、主に文字列操作やバッファリングの性能を評価します。gob_test.go
: Goのgob
エンコーディング/デコーディングの性能をテストします。gob
はGo独自のバイナリシリアライゼーション形式です。gzip_test.go
:gzip
圧縮/解凍の性能をテストします。json_test.go
: JSONエンコーディング/デコーディングの性能をテストします。jsondata_test.go
: ベンチマークで使用される実際のJSONデータを含んでいます。このデータは、圧縮されたBase64形式でソースコードに埋め込まれており、実行時に解凍・デコードされます。これにより、ベンチマークバイナリが自己完結型になります。revcomp_test.go
: DNA配列の逆相補鎖を生成するベンチマークで、文字列操作とメモリ効率をテストします。template_test.go
: Goのテンプレートエンジンの性能をテストします。dummy.go
: ベンチマークパッケージのプレースホルダーファイルで、実際のロジックは_test.go
ファイルに記述されています。
-
ビルドシステムの統合:
test/bench/go1/Makefile
: 新しいベンチマークスイートをビルドするためのMakefileが追加されました。これはGoの標準的なビルドシステム(Make.inc
,Make.pkg
)を利用しています。src/clean.bash
: ベンチマークスイートのクリーンアップ処理が追加され、ビルドされたベンチマークバイナリが適切に削除されるように変更されました。src/run.bash
: ベンチマークスイートの実行が追加され、Goの標準テスト実行プロセスの一部としてベンチマークが実行されるように変更されました。
-
データ埋め込み戦略:
jsondata_test.go
では、大きなJSONデータをbzip2
で圧縮し、さらにbase64
でエンコードしてGoのソースコード内に文字列リテラルとして埋め込んでいます。これにより、ベンチマーク実行時に外部ファイルへの依存をなくし、単一バイナリでの配布を可能にしています。json_test.go
のinit()
関数でこのデータを読み込み、解凍、デコードして使用します。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、主に以下のファイルに集中しています。
test/bench/go1/
ディレクトリ以下の新規追加された_test.go
ファイル群: これらが個々のベンチマークの実装を含んでいます。test/bench/go1/jsondata_test.go
: ベンチマークの入力データが埋め込まれているファイルです。test/bench/go1/Makefile
: ベンチマークスイートのビルド設定を定義しています。src/clean.bash
およびsrc/run.bash
: 新しいベンチマークスイートをGoプロジェクトの既存のビルドおよびテストインフラに統合するための変更です。
特に、jsondata_test.go
における大規模なJSONデータの埋め込みと、それをjson_test.go
で利用するメカニズムは、ベンチマークの自己完結性を実現するための重要な変更点です。
コアとなるコードの解説
test/bench/go1/jsondata_test.go
は、jsonbz2_base64
という大きな文字列変数を定義しています。この文字列は、agl
のオープンソース活動に関するJSONデータをbzip2
で圧縮し、さらにbase64
でエンコードしたものです。
// test/bench/go1/jsondata_test.go
var jsonbz2_base64 = "QlpoOTFBWSZTWZ0H0LkG0bxfgFH8UAf/8D////q////6YSvJveAAAAAH3ddt7gAN" +
// ... 非常に長いBase64エンコードされた文字列 ...
"Uy2ylLU2jJWUySmlNmpspWFKJrTVkE1lJmakWTZmUtpS0rKyqpVsilmsNrWWbLMqaVLFlNmazabZtNppoZJEkxlJIlplYbNUMaaVKhIUylmxaWkEslm2WoAZmYGmm02kmZ9vNcym/zYZ1m/E+UySfg39LUVVWb+0CAnu+IVnzkTYexs7ufDm0VjDZ7mmLPDCjvRkCK4NH4KYIPfTEsxAJVJTCY4MNNqKqTgrJkmjRkVkyN9MMuNskxW0mmjTSlSsRiUzxDKZmvWVXm9Y8a6VLqbbWXtSMLsZGTNoSqYkyYjI2VEskoLRRUbRRsbWkqStSau0WgopMsq6V0SZehQ82kyxbJsqq2k6xbaqqpy8chxElkKVybMPBqa1jIGLJqNaWSNnw5w5rJRVJ8pXQahwWUzyp0wnz/U0hIEiEYfzEVFZGtr8GqLFqNUUa0pVZVFVlLVsEy7VF1qILVeGEk2aS+WJki7F2pqD7InRyG/WjNbxTzY7E029eTdr/1ZJlhzxxZEk7TYxZI2Vk4yI8WLZEUcfOpyzjm2I50sicpFjzHRZKelYLMxipnrpolbK2wP3Gj5QrZ3mE7dW70dJN3aTWPvGzFnLOYscQkecNk/KdSbVattW/g8tatRJ9BbaVmTUcNkbE5NiRgCjqJjBFYYMQEpXT39BIxlLWwvLhFGpGWUqKViqxkcZkznhqQLSNrGQhBUdGYxN2kbWNpAzdTVXSs46S3TNK5yd26mxXO7tXNdm0m6bRbSTJvK6rpKW2TFtvPLvJbNaSixoxKyEmNk0kWK0SdjZs2NohYnAsejhFowruXCQlUVFqVGpE0UGZIZEc3lktTo03a6N+EiflSdFScJJ0xKRxN5+ZDlCLZB9FWySZUtkhL9OWNS5Kx7HmGk3Zm+vn/kuSquatyCwYtGGhIxpQKVBh9sws7qnJ2thu+NtlbJbljpdXFmFp6I0cN3VhW7RqTNacWNJ80k/pbIVr82CR/iVzKp1fJPyjz7u0PGnOl53LbDvYakR0ZJD8qm59mWLpEWRzhu+1n9OUKT1kn8VOh3p4lv5ZjCqf3P82pWjKmxUzVvxpOH8eDZPhDwfii4JZZPcJ67Ts94+JJ8JN6n2RXJ/iPpDEN5uehqR3Yxju2bXbGDrRv6T0R5vdxJOR2mD+4dUjVjZFQf6RFZJ8rFskqu7VMy3EjDuzE2K5wH1nnG971lZjE1GMMpMLzVo"
このデータは、test/bench/go1/json_test.go
のinit()
関数で読み込まれ、実際のJSONデータに変換されます。
// test/bench/go1/json_test.go
func init() {
var r io.Reader
r = strings.NewReader(jsonbz2_base64) // Base64文字列をリーダーとして扱う
r = base64.NewDecoder(base64.StdEncoding, r) // Base64デコーダでラップ
r = bzip2.NewReader(r) // bzip2デコーダでラップ
b, err := ioutil.ReadAll(r) // 全て読み込み
if err != nil {
panic(err)
}
jsonbytes = b // デコードされたバイト列を保存
if err := json.Unmarshal(jsonbytes, &jsondata); err != nil { // JSONを構造体にアンマーシャル
panic(err)
}
gobinit() // gobベンチマークの初期化
}
このinit()
関数は、パッケージが初期化される際に自動的に実行され、jsonbz2_base64
から元のJSONデータを復元し、jsondata
変数に格納します。これにより、他のベンチマーク関数がこのデータを利用できるようになります。
各ベンチマークファイル(例: binarytree_test.go
, json_test.go
など)には、BenchmarkXxx
という形式の関数が定義されています。これらの関数は*testing.B
型の引数を取り、b.N
回繰り返されるループ内でベンチマーク対象の処理を実行します。b.SetBytes()
は、ベンチマーク対象の処理が処理するバイト数を設定するために使用され、これによりgo test -bench
コマンドがスループット(ops/secやMB/sなど)を計算できるようになります。
例えば、BenchmarkJSONEncode
はjsonenc()
関数をb.N
回実行し、JSONエンコーディングの性能を測定します。
// test/bench/go1/json_test.go
func BenchmarkJSONEncode(b *testing.B) {
b.SetBytes(int64(len(jsonbytes))) // 処理するバイト数を設定
for i := 0; i < b.N; i++ {
jsonenc() // JSONエンコードを実行
}
}
src/run.bash
とsrc/clean.bash
の変更は、Goのビルドシステムに新しいベンチマークスイートを組み込むためのものです。src/run.bash
では、test/bench/go1
ディレクトリに移動してgomake test
を実行する行が追加されており、これによりGoの標準テスト実行時にベンチマークも実行されるようになります。
関連リンク
- Go言語の
testing
パッケージ: https://pkg.go.dev/testing - Go言語のベンチマークに関する公式ドキュメント(Go 1.2以降の
go test -bench
について記述されていますが、基本的な概念は共通です): https://go.dev/doc/articles/go_benchmarking.html - Goのコードレビューシステム(Gerrit)におけるこのコミットの変更リスト: https://golang.org/cl/5484071
参考にした情報源リンク
- コミットメッセージと変更されたファイルの内容 (
./commit_data/10814.txt
) - Go言語の公式ドキュメントおよび
testing
パッケージのドキュメント(一般的なGoベンチマークの知識) - GitHub上のGoリポジトリのコミット履歴 (コンテキスト把握のため)