[インデックス 17892] ファイルの概要
このコミットは、Go言語のリポジトリにおける意図しないコミットの取り消し(revert)に関するものです。具体的には、test/bench/perf
ディレクトリ内の3つのファイル(bench1.go
, bench2.go
, driver.go
)が削除されています。コミットメッセージから、コミッターが誤って別のリポジトリで作業していると勘違いし、本来コミットすべきではないファイルをGoリポジトリにコミットしてしまったことが伺えます。
コミット
commit b3d400c35e10beb2e85c0b00d61b44792b6d8457
Author: dvyukov <dvyukov@google.com>
Date: Tue Nov 19 15:36:13 2013 +0400
test: revert unintentional commits
I thought I am in a different repo...
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b3d400c35e10beb2e85c0b00d61b44792b6d8457
元コミット内容
このコミット自体は、以前のコミットで誤って追加されたファイルを削除するものです。したがって、「元コミット内容」とは、このコミットによって削除されたファイルの内容を指します。削除されたファイルは以下の通りです。
test/bench/perf/bench1.go
test/bench/perf/bench2.go
test/bench/perf/driver.go
これらのファイルは、Goのパフォーマンスベンチマークに関連するコードを含んでいました。
bench1.go
の内容
package main
import (
"time"
)
func main() {
PerfBenchmark(SleepBenchmark)
}
func SleepBenchmark(N int64) (metrics []PerfMetric, err error) {
time.Sleep(time.Duration(N) * time.Millisecond)
metrics = append(metrics, PerfMetric{"foo", 42})
return
}
bench2.go
の内容
package main
func Benchmark(N int64) error {
return nil
}
driver.go
の内容
package main
import (
"flag"
"fmt"
"log"
"time"
"runtime"
)
var (
benchNum = flag.Int("benchnum", 3, "run each benchmark that many times")
benchTime = flag.Duration("benchtime", 10*time.Second, "benchmarking time for a single run")
benchMem = flag.Int("benchmem", 64, "approx RSS value to aim at in benchmarks, in MB")
)
type PerfResult struct {
N int64
RunTime time.Duration
Metrics []PerfMetric
}
type PerfMetric struct {
Type string
Val int64
}
type BenchFunc func(N int64) ([]PerfMetric, error)
func PerfBenchmark(f BenchFunc) {
if !flag.Parsed() {
flag.Parse()
}
var res PerfResult
for i := 0; i < *benchNum; i++ {
res1 := RunBenchmark(f)
if res.RunTime == 0 || res.RunTime > res1.RunTime {
res = res1
}
}
fmt.Printf("GOPERF-METRIC:runtime=%v\n", int64(res.RunTime)/res.N)
for _, m := range res.Metrics {
fmt.Printf("GOPERF-METRIC:%v=%v\n", m.Type, m.Val)
}
}
func RunBenchmark(f BenchFunc) PerfResult {
var res PerfResult
for ChooseN(&res) {
log.Printf("Benchmarking %v iterations\\n", res.N)
res = RunOnce(f, res.N)
log.Printf("Done: %+v\\n", res)
}
return res
}
func RunOnce(f BenchFunc, N int64) PerfResult {
runtime.GC()
mstats0 := new(runtime.MemStats)
runtime.ReadMemStats(mstats0)
res := PerfResult{N: N}
t0 := time.Now()
var err error
res.Metrics, err = f(N)
res.RunTime = time.Since(t0)
if err != nil {
log.Fatalf("Benchmark function failed: %v\\n", err)
}
mstats1 := new(runtime.MemStats)
runtime.ReadMemStats(mstats1)
fmt.Printf("%+v\\n", *mstats1)
return res
}
func ChooseN(res *PerfResult) bool {
const MaxN = 1e12
last := res.N
if last == 0 {
res.N = 1
return true
} else if res.RunTime >= *benchTime || last >= MaxN {
return false
}
nsPerOp := max(1, int64(res.RunTime)/last)
res.N = int64(*benchTime) / nsPerOp
res.N = max(min(res.N+res.N/2, 100*last), last+1)
res.N = roundUp(res.N)
return true
}
func roundUp(n int64) int64 {
tmp := n
base := int64(1)
for tmp >= 10 {
tmp /= 10
base *= 10
}
switch {
case n <= base:
return base
case n <= (2 * base):
return 2 * base
case n <= (5 * base):
return 5 * base
default:
return 10 * base
}
panic("unreachable")
return 0
}
func min(a, b int64) int64 {
if a < b {
return a
}
return b
}
func max(a, b int64) int64 {
if a > b {
return a
}
return b
}
変更の背景
このコミットの背景は、コミットメッセージに明確に示されています。「I thought I am in a different repo...」(別のリポジトリにいると勘違いしていた…)というメッセージから、コミッターがGo言語の公式リポジトリではない、別の個人プロジェクトやテスト用のリポジトリで作業していた際に、誤ってGoリポジトリにこれらのベンチマーク関連ファイルをコミットしてしまったことが分かります。
このような「意図しないコミット」は、開発者が複数のプロジェクトやブランチを並行して作業している際によく発生するヒューマンエラーです。特に、Gitの作業ディレクトリやブランチの切り替えを頻繁に行う環境では、誤ったリポジトリでgit add
やgit commit
を実行してしまうリスクがあります。このコミットは、その誤りを修正し、Goリポジトリのクリーンな状態を維持するためのものです。
前提知識の解説
このコミットを理解するためには、以下の前提知識が役立ちます。
1. Gitの基本的な操作
- コミット (Commit): Gitにおける変更履歴の単位。一連の変更をまとめて記録します。
- リバート (Revert): 既存のコミットの変更内容を打ち消す新しいコミットを作成する操作。履歴を消すのではなく、変更を元に戻すための新しい履歴を追加します。今回のケースでは、誤って追加されたファイルを削除する変更が新しいコミットとして記録されています。
- リポジトリ (Repository): Gitが管理するプロジェクトの全てのファイルと変更履歴が保存されている場所。
2. Go言語のテストとベンチマーク
Go言語には、標準でテストとベンチマークの機能が組み込まれています。
go test
コマンド: Goのテストを実行するためのコマンド。_test.go
で終わるファイルに記述されたテスト関数やベンチマーク関数を実行します。- ベンチマーク関数:
BenchmarkXxx(*testing.B)
の形式で記述される関数で、コードのパフォーマンスを測定するために使用されます。testing.B
型の引数を通じて、ベンチマークの実行回数や時間を制御できます。 test/bench/perf
ディレクトリ: Go言語の公式リポジトリにおいて、test/bench
ディレクトリは通常、Goのランタイムや標準ライブラリのパフォーマンスを測定するためのベンチマークコードが格納される場所です。perf
サブディレクトリは、特定のパフォーマンス測定に関連するベンチマークをまとめるために使用されることがあります。
3. パフォーマンス測定の概念
- ベンチマーク (Benchmark): ソフトウェアやシステムの性能を測定するためのテスト。特定のタスクを実行し、その完了にかかる時間やリソース消費量を計測します。
- メトリクス (Metrics): 測定された性能指標。例えば、実行時間、メモリ使用量、操作あたりのナノ秒など。
- RSS (Resident Set Size): プロセスが物理メモリにロードしているメモリの量。ベンチマークにおいて、メモリ使用量の指標として用いられることがあります。
4. flag
パッケージ
Goの標準ライブラリである flag
パッケージは、コマンドライン引数を解析するために使用されます。driver.go
では、ベンチマークの実行回数 (-benchnum
)、実行時間 (-benchtime
)、目標RSS値 (-benchmem
) などのパラメータをコマンドラインから受け取るために利用されていました。
5. runtime
パッケージ
Goの標準ライブラリである runtime
パッケージは、Goランタイムとの相互作用を提供します。driver.go
では、runtime.GC()
(ガベージコレクションの強制実行)や runtime.ReadMemStats()
(メモリ統計の読み取り)が使用されており、ベンチマーク実行時のメモリ状態を制御・監視するために利用されていました。
技術的詳細
このコミットは、Goリポジトリから3つのGoソースファイルを削除しています。これらのファイルは、Goのベンチマークシステムを模倣した、あるいは拡張しようとしたと思われるカスタムのベンチマークフレームワークの一部でした。
-
test/bench/perf/bench1.go
:main
パッケージに属し、PerfBenchmark
関数を呼び出してSleepBenchmark
を実行しています。SleepBenchmark
は、指定されたミリ秒数だけスリープし、PerfMetric
型のメトリクス({"foo", 42}
)を返すシンプルなベンチマーク関数です。これは、ベンチマークの基本的な構造を示すための例、またはテスト用のダミーベンチマークとして機能していたと考えられます。
-
test/bench/perf/bench2.go
:main
パッケージに属し、Benchmark
という名前の関数を定義していますが、これは常にnil
エラーを返すだけの非常にシンプルなものです。これもまた、ベンチマーク関数のプレースホルダー、または最小限のベンチマーク例として存在していた可能性があります。
-
test/bench/perf/driver.go
:- このファイルは、カスタムベンチマークフレームワークの「ドライバー」または「ハーネス」として機能していました。
- コマンドライン引数:
flag
パッケージを使用して、ベンチマークの実行回数 (-benchnum
)、各ベンチマークの実行時間 (-benchtime
)、および目標とするRSS値 (-benchmem
) を設定できるようになっていました。 PerfResult
とPerfMetric
構造体: ベンチマークの結果(実行回数N
、実行時間RunTime
、カスタムメトリクスMetrics
)を格納するための構造体が定義されています。PerfMetric
は、型と値を持つ汎用的なメトリクスを表現します。PerfBenchmark
関数: 複数のベンチマーク実行を管理し、最も短い実行時間の結果を選択するロジックを含んでいます。RunBenchmark
とRunOnce
関数: ベンチマークの実際の実行ロジックをカプセル化しています。RunOnce
では、runtime.GC()
でガベージコレクションを強制実行し、runtime.ReadMemStats()
でメモリ統計を収集するなど、ベンチマークの精度を高めるための一般的なプラクティスが取り入れられています。ChooseN
関数: ベンチマークの実行回数N
を動的に調整するロジックが含まれています。これは、Goの標準ベンチマークツール (testing
パッケージ) が行うような、指定された時間内に安定した結果を得るために必要な実行回数を自動的に決定するアプローチに似ています。GOPERF-METRIC
出力:fmt.Printf("GOPERF-METRIC:...")
という形式で標準出力にメトリクスを出力しています。これは、Goのベンチマークツールが結果をパースしやすい形式で出力するのと同様のパターンです。この形式は、自動化されたテストシステムやCI/CDパイプラインでベンチマーク結果を収集・分析する際に利用されることが一般的です。- ユーティリティ関数:
roundUp
,min
,max
といったヘルパー関数も含まれており、ChooseN
関数での計算に使用されていました。
これらのファイルがGoの公式リポジトリに存在していた場合、Goの標準ベンチマークシステム (testing
パッケージ) とは異なる、独自のベンチマークフレームワークが導入されることになります。コミッターの意図しないコミットであったため、これらのファイルはGoリポジトリの標準的な開発プロセスやツールセットとは整合性がなく、削除されるのが適切でした。
コアとなるコードの変更箇所
このコミットのコアとなる変更は、以下の3つのファイルの削除です。
test/bench/perf/bench1.go
test/bench/perf/bench2.go
test/bench/perf/driver.go
これらのファイルは、Goリポジトリの test/bench/perf
ディレクトリから完全に削除されました。Gitの差分表示では、これらのファイルの内容がすべて削除行(-
で始まる行)として表示されています。
コアとなるコードの解説
削除された各ファイルは、Goのパフォーマンスベンチマークに関連する独自のコードを含んでいました。
-
bench1.go
:SleepBenchmark
という関数を定義しており、これはtime.Sleep
を使って意図的に遅延を発生させるシンプルなベンチマークです。PerfBenchmark
という関数を呼び出しており、これはdriver.go
で定義されているカスタムベンチマークドライバーの一部です。- このファイルは、カスタムベンチマークフレームワークを使って特定のベンチマークを実行するエントリポイントとして機能していました。
-
bench2.go
:Benchmark
という名前の関数を定義していますが、これは中身が空で、常にnil
を返すだけのプレースホルダーです。- おそらく、新しいベンチマークを追加する際のテンプレートとして、あるいは最小限のベンチマークの例として用意されていたと考えられます。
-
driver.go
:- このファイルは、削除されたファイルの中で最も重要であり、カスタムベンチマークフレームワークの主要なロジックを含んでいました。
- ベンチマーク実行の制御: コマンドライン引数 (
-benchnum
,-benchtime
,-benchmem
) を通じて、ベンチマークの実行回数、時間、メモリ使用量の目標を設定できます。 - 結果の集計と選択: 複数のベンチマーク実行の中から最適な結果(最も短い実行時間)を選択するロジックが含まれています。
- メモリ統計の取得:
runtime.GC()
とruntime.ReadMemStats()
を使用して、ベンチマーク実行前後のメモリ使用量を測定し、パフォーマンス分析に役立てようとしていました。 - 動的な実行回数調整:
ChooseN
関数は、目標とするベンチマーク時間に基づいて、適切な実行回数N
を動的に決定します。これにより、安定したベンチマーク結果を得るための試行回数を最適化します。 GOPERF-METRIC
形式での出力: ベンチマーク結果をGOPERF-METRIC:key=value
という特定の形式で標準出力に出力します。これは、自動化されたシステムがベンチマーク結果をパースしやすくするための一般的な手法です。
これらのファイルは、Goの標準的なベンチマークツール (go test -bench
) とは異なるアプローチでベンチマークを実行・管理しようとしていたものです。Goの公式リポジトリの文脈では、このようなカスタムツールは通常、標準ツールでカバーできない特定のニーズがある場合にのみ導入されますが、今回の場合はコミッターの誤りによるものであったため、削除されました。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/doc/
- Go言語のテストに関するドキュメント: https://go.dev/doc/code#testing
- Go言語のベンチマークに関するドキュメント (testingパッケージ): https://pkg.go.dev/testing
- Gitの
revert
コマンドについて: https://git-scm.com/docs/git-revert
参考にした情報源リンク
- Go言語の
testing
パッケージのソースコード (ベンチマークの実装を理解するため): https://github.com/golang/go/blob/master/src/testing/benchmark.go - Go言語の
flag
パッケージのドキュメント: https://pkg.go.dev/flag - Go言語の
runtime
パッケージのドキュメント: https://pkg.go.dev/runtime - Gitの公式ドキュメント: https://git-scm.com/doc