[インデックス 18638] ファイルの概要
このコミットは、Go言語の標準ライブラリ sync
パッケージ内のベンチマークテストにおいて、並行処理のテスト方法を改善するものです。具体的には、手動でゴルーチンを管理し、runtime.Gosched()
や atomic
パッケージを用いていた既存のベンチマークコードを、testing
パッケージが提供する b.RunParallel
関数を使用するように変更しています。これにより、ベンチマークの記述が簡潔になり、より正確で効率的な並行ベンチマークが可能になります。
コミット
commit ec0c9f270e8c09b00df5e45d87cfa4c85df63271
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Tue Feb 25 14:39:12 2014 +0400
sync: use RunParallel in benchmarks
LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/68050043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ec0c9f270e8c09b00df5e45d87cfa4c85df63271
元コミット内容
このコミットの元のメッセージは以下の通りです。
sync: use RunParallel in benchmarks
LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/68050043
これは、sync
パッケージのベンチマークで RunParallel
を使用するように変更したことを簡潔に示しています。
変更の背景
Go言語のベンチマークは、testing
パッケージの Benchmark
関数によって実行されます。以前のGoのバージョンでは、並行処理のベンチマークを行う際に、開発者が手動で複数のゴルーチンを起動し、runtime.GOMAXPROCS
や runtime.Gosched()
、sync/atomic
パッケージなどを用いて並行性を制御する必要がありました。これは、ベンチマークコードを複雑にし、正確な測定を困難にする可能性がありました。
testing.B.RunParallel
関数は、Go 1.2で導入された機能であり、並行ベンチマークをより簡単かつ正確に記述するためのものです。この関数は、GOMAXPROCS
の値に基づいて自動的に適切な数のゴルーチンを起動し、各ゴルーチンにベンチマークのイテレーションを均等に分散させます。これにより、手動でのゴルーチン管理や競合状態のシミュレーションが不要になり、ベンチマークの信頼性と保守性が向上します。
このコミットは、sync
パッケージ内の Mutex
, Once
, Pool
, runtime_sema
, RWMutex
, WaitGroup
といった並行プリミティブのベンチマークを、この新しい b.RunParallel
メカニズムに移行することを目的としています。これにより、これらの重要な並行プリミティブの性能測定がより堅牢になります。
前提知識の解説
Go言語のベンチマーク
Go言語では、testing
パッケージを使用してベンチマークテストを記述します。ベンチマーク関数は BenchmarkXxx(*testing.B)
というシグネチャを持ち、go test -bench=.
コマンドで実行されます。*testing.B
型の b
オブジェクトは、ベンチマークの実行回数 (b.N
) や、並行ベンチマークのための b.RunParallel
などのメソッドを提供します。
testing.B.RunParallel
b.RunParallel
は、並行ベンチマークを実行するためのメソッドです。引数として func(pb *testing.PB)
型の関数を受け取ります。この関数は、b.RunParallel
によって起動された各ゴルーチンで実行されます。*testing.PB
型の pb
オブジェクトは、pb.Next()
メソッドを提供し、ベンチマークの各イテレーションが完了したことを通知します。pb.Next()
は、すべてのイテレーションが完了すると false
を返します。
b.RunParallel
は、runtime.GOMAXPROCS
の値に基づいて、自動的に適切な数のゴルーチンを起動します。これにより、手動でゴルーチンを管理する手間が省け、また、GOMAXPROCS
の設定変更にも自動的に対応できるため、より現実的な並行性能を測定できます。
sync
パッケージ
sync
パッケージは、Go言語における基本的な並行処理のプリミティブを提供します。このコミットで変更されている主なコンポーネントは以下の通りです。
sync.Mutex
: 排他ロック。複数のゴルーチンが共有リソースに同時にアクセスするのを防ぎます。sync.Once
: ある処理が一度だけ実行されることを保証します。初期化処理などで使用されます。sync.Pool
: 一時的なオブジェクトのプール。オブジェクトの再利用を促進し、ガベージコレクションの負荷を軽減します。sync.RWMutex
: 読み書きロック。複数の読み取りは同時に許可しますが、書き込みは排他的に行われます。sync.WaitGroup
: 複数のゴルーチンの完了を待機するためのメカニズム。
runtime.Gosched()
と sync/atomic
変更前のベンチマークコードでは、手動でゴルーチンを管理するために以下の関数やパッケージが使用されていました。
runtime.Gosched()
: 現在のゴルーチンを一時停止し、他のゴルーチンにCPUを譲ります。これにより、明示的にコンテキストスイッチを発生させ、並行性をシミュレートしていました。sync/atomic
: アトミック操作を提供します。複数のゴルーチンから共有されるカウンタなどを安全に更新するために使用されていました。例えば、atomic.AddInt32
は、ベンチマークの総イテレーション数を複数のゴルーチンで共有し、各ゴルーチンが処理したイテレーション数を減算するために使われていました。
これらの手動での並行性制御は、b.RunParallel
の導入により不要になります。
技術的詳細
このコミットの主要な変更点は、sync
パッケージ内の各ベンチマーク関数から、手動でゴルーチンを起動し、runtime.Gosched()
や sync/atomic
を使用してイテレーションを制御するロジックを削除し、代わりに b.RunParallel
を呼び出すように変更したことです。
変更前は、以下のようなパターンが頻繁に見られました。
runtime.GOMAXPROCS(-1)
を呼び出して、利用可能なCPUコア数を取得。- そのコア数に基づいて、またはそれよりも多くのゴルーチンを
go func()
で起動。 - 各ゴルーチン内で、
atomic.AddInt32(&N, -1)
のようなアトミック操作で全体のイテレーション数を管理し、N
が0になるまでループ。 - ループ内で
runtime.Gosched()
を呼び出し、他のゴルーチンに実行を譲る。 - 結果をチャネル (
c := make(chan bool, procs)
) で収集し、すべてのゴルーチンの完了を待機。
このコミットでは、上記の複雑なロジックが b.RunParallel
の呼び出しに置き換えられています。b.RunParallel
は、内部でこれらの並行処理の管理を自動的に行います。
例えば、BenchmarkMutexUncontended
の変更を見てみましょう。
変更前:
func BenchmarkMutexUncontended(b *testing.B) {
type PaddedMutex struct {
Mutex
pad [128]uint8
}
const CallsPerSched = 1000
procs := runtime.GOMAXPROCS(-1)
N := int32(b.N / CallsPerSched)
c := make(chan bool, procs)
for p := 0; p < procs; p++ {
go func() {
var mu PaddedMutex
for atomic.AddInt32(&N, -1) >= 0 {
runtime.Gosched()
for g := 0; g < CallsPerSched; g++ {
mu.Lock()
mu.Unlock()
}
}
c <- true
}()
}
for p := 0; p < procs; p++ {
<-c
}
}
変更後:
func BenchmarkMutexUncontended(b *testing.B) {
type PaddedMutex struct {
Mutex
pad [128]uint8
}
b.RunParallel(func(pb *testing.PB) {
var mu PaddedMutex
for pb.Next() {
mu.Lock()
mu.Unlock()
}
})
}
この変更により、コードが大幅に簡潔になり、runtime.Gosched()
や atomic
パッケージへの依存がなくなっています。b.RunParallel
が自動的に複数のゴルーチンを起動し、pb.Next()
が各ゴルーチンに次のイテレーションを実行するように指示します。
また、benchmarkMutex
のように、slack
(ゴルーチンの数を増やす) オプションを持つベンチマークでは、b.SetParallelism(10)
のような呼び出しが追加され、RunParallel
が起動するゴルーチンの数を制御できるようになっています。これは、特定のシナリオでの並行性をシミュレートするために使用されます。
コアとなるコードの変更箇所
このコミットでは、以下のファイルのベンチマーク関数が変更されています。
src/pkg/sync/mutex_test.go
src/pkg/sync/once_test.go
src/pkg/sync/pool_test.go
src/pkg/sync/runtime_sema_test.go
src/pkg/sync/rwmutex_test.go
src/pkg/sync/waitgroup_test.go
各ファイルにおいて、BenchmarkXxx
という名前の関数内で、手動でゴルーチンを起動し、runtime.Gosched()
や sync/atomic
を使用してイテレーションを制御していた部分が、b.RunParallel
の呼び出しに置き換えられています。
具体的な変更例は以下の通りです。
src/pkg/sync/mutex_test.go
の BenchmarkMutexUncontended
:
runtime.GOMAXPROCS(-1)
の呼び出しを削除。atomic.AddInt32(&N, -1)
を使用したイテレーション管理ループを削除。runtime.Gosched()
の呼び出しを削除。- チャネル (
c
) を使用したゴルーチン間の同期を削除。 - 代わりに
b.RunParallel(func(pb *testing.PB) { ... })
を使用し、ループ条件をfor pb.Next()
に変更。
src/pkg/sync/once_test.go
の BenchmarkOnce
:
- 同様に、手動のゴルーチン管理と
atomic
操作を削除し、b.RunParallel
に置き換え。
src/pkg/sync/pool_test.go
の BenchmarkPool
および BenchmarkPoolOverlflow
:
WaitGroup
とatomic
を使用した並行処理の制御を削除し、b.RunParallel
に置き換え。
src/pkg/sync/runtime_sema_test.go
の BenchmarkSemaUncontended
および benchmarkSema
:
- 手動のゴルーチン管理と
atomic
操作を削除し、b.RunParallel
に置き換え。 benchmarkSema
では、block
シナリオでの初期セマフォ取得ロジックもb.RunParallel
の外に移動し、done
チャネルで同期する形に変更。
src/pkg/sync/rwmutex_test.go
の BenchmarkRWMutexUncontended
および benchmarkRWMutex
:
- 手動のゴルーチン管理と
atomic
操作を削除し、b.RunParallel
に置き換え。
src/pkg/sync/waitgroup_test.go
の BenchmarkWaitGroupUncontended
, benchmarkWaitGroupAddDone
, benchmarkWaitGroupWait
:
- 手動のゴルーチン管理と
atomic
操作を削除し、b.RunParallel
に置き換え。
全体として、この変更は、Goのベンチマークフレームワークの進化に合わせて、sync
パッケージのベンチマークコードを現代化し、より堅牢で保守しやすいものにすることを目的としています。
コアとなるコードの解説
このコミットのコアとなる変更は、Goのベンチマークにおける並行処理の記述方法のパラダイムシフトを示しています。
変更前のアプローチ(手動ゴルーチン管理)の問題点:
- 複雑性: 複数のゴルーチンを起動し、
runtime.Gosched()
でコンテキストスイッチを促し、atomic
操作でイテレーション数を同期させるなど、ベンチマークコード自体が複雑になりがちでした。これは、ベンチマークの意図を理解し、保守するのを困難にしていました。 - 正確性の課題:
runtime.Gosched()
の呼び出し頻度や、GOMAXPROCS
の設定がベンチマーク結果に与える影響を正確に制御するのが難しく、再現性や信頼性に課題が生じる可能性がありました。特に、runtime.Gosched()
はスケジューラのヒントであり、必ずしも期待通りに動作するとは限りません。 - スケーラビリティ:
GOMAXPROCS
の値が変わるたびに、手動でゴルーチン数を調整する必要があるなど、環境への適応性が低いという問題がありました。
変更後のアプローチ(b.RunParallel
の利用)の利点:
- 簡潔性:
b.RunParallel
を使用することで、並行ベンチマークのロジックが大幅に簡素化されます。開発者は、各ゴルーチンが実行すべき単一のイテレーションの処理に集中できます。 - 自動的な並行性管理:
b.RunParallel
は、GOMAXPROCS
の設定に基づいて最適な数のゴルーチンを自動的に起動し、ベンチマークのイテレーションをそれらのゴルーチンに均等に分散させます。これにより、手動での調整が不要になり、異なる環境でも一貫した結果が得られやすくなります。 - 正確性と信頼性:
b.RunParallel
は、Goのテストフレームワークによって最適化されており、より正確で信頼性の高い並行ベンチマーク測定を可能にします。内部的には、各ゴルーチンがpb.Next()
を呼び出すことで、テストフレームワークがイテレーションの進行を追跡し、全体のベンチマーク時間を正確に測定します。 - 保守性: コードが簡潔になることで、ベンチマークの保守が容易になります。
このコミットは、Goのベンチマークフレームワークが成熟し、より高度な並行ベンチマーク機能を提供できるようになったことを示しています。sync
パッケージのような低レベルの並行プリミティブのベンチマークにおいて、この変更は特に重要であり、これらのプリミティブの性能特性をより正確に評価するための基盤を強化します。
関連リンク
- Go言語の
testing
パッケージのドキュメント: https://pkg.go.dev/testing - Go 1.2 リリースノート (
testing.B.RunParallel
の導入について言及): https://go.dev/doc/go1.2#testing - Go言語の
sync
パッケージのドキュメント: https://pkg.go.dev/sync
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード (特に
src/testing/benchmark.go
やsrc/pkg/sync
以下のテストファイル) - Go言語のコミット履歴とコードレビューコメント
testing.B.RunParallel
に関する技術記事や解説(一般的なGoのベンチマークに関する情報)- Go言語の
sync/atomic
パッケージのドキュメント: https://pkg.go.dev/sync/atomic - Go言語の
runtime
パッケージのドキュメント: https://pkg.go.dev/runtime
[インデックス 18638] ファイルの概要
このコミットは、Go言語の標準ライブラリ sync
パッケージ内のベンチマークテストにおいて、並行処理のテスト方法を改善するものです。具体的には、手動でゴルーチンを管理し、runtime.Gosched()
や atomic
パッケージを用いていた既存のベンチマークコードを、testing
パッケージが提供する b.RunParallel
関数を使用するように変更しています。これにより、ベンチマークの記述が簡潔になり、より正確で効率的な並行ベンチマークが可能になります。
コミット
commit ec0c9f270e8c09b00df5e45d87cfa4c85df63271
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Tue Feb 25 14:39:12 2014 +0400
sync: use RunParallel in benchmarks
LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/68050043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ec0c9f270e8c09b00df5e45d87cfa4c85df63271
元コミット内容
このコミットの元のメッセージは以下の通りです。
sync: use RunParallel in benchmarks
LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/68050043
これは、sync
パッケージのベンチマークで RunParallel
を使用するように変更したことを簡潔に示しています。
変更の背景
Go言語のベンチマークは、testing
パッケージの Benchmark
関数によって実行されます。以前のGoのバージョンでは、並行処理のベンチマークを行う際に、開発者が手動で複数のゴルーチンを起動し、runtime.GOMAXPROCS
や runtime.Gosched()
、sync/atomic
パッケージなどを用いて並行性を制御する必要がありました。これは、ベンチマークコードを複雑にし、正確な測定を困難にする可能性がありました。
testing.B.RunParallel
関数は、Go 1.2で導入された機能であり、並行ベンチマークをより簡単かつ正確に記述するためのものです。この関数は、GOMAXPROCS
の値に基づいて自動的に適切な数のゴルーチンを起動し、各ゴルーチンにベンチマークのイテレーションを均等に分散させます。これにより、手動でのゴルーチン管理や競合状態のシミュレーションが不要になり、ベンチマークの信頼性と保守性が向上します。
このコミットは、sync
パッケージ内の Mutex
, Once
, Pool
, runtime_sema
, RWMutex
, WaitGroup
といった並行プリミティブのベンチマークを、この新しい b.RunParallel
メカニズムに移行することを目的としています。これにより、これらの重要な並行プリミティブの性能測定がより堅牢になります。
前提知識の解説
Go言語のベンチマーク
Go言語では、testing
パッケージを使用してベンチマークテストを記述します。ベンチマーク関数は BenchmarkXxx(*testing.B)
というシグネチャを持ち、go test -bench=.
コマンドで実行されます。*testing.B
型の b
オブジェクトは、ベンチマークの実行回数 (b.N
) や、並行ベンチマークのための b.RunParallel
などのメソッドを提供します。
testing.B.RunParallel
b.RunParallel
は、並行ベンチマークを実行するためのメソッドです。引数として func(pb *testing.PB)
型の関数を受け取ります。この関数は、b.RunParallel
によって起動された各ゴルーチンで実行されます。*testing.PB
型の pb
オブジェクトは、pb.Next()
メソッドを提供し、ベンチマークの各イテレーションが完了したことを通知します。pb.Next()
は、すべてのイテレーションが完了すると false
を返します。
b.RunParallel
は、runtime.GOMAXPROCS
の値に基づいて、自動的に適切な数のゴルーチンを起動します。これにより、手動でゴルーチンを管理する手間が省け、また、GOMAXPROCS
の設定変更にも自動的に対応できるため、より現実的な並行性能を測定できます。
sync
パッケージ
sync
パッケージは、Go言語における基本的な並行処理のプリミティブを提供します。このコミットで変更されている主なコンポーネントは以下の通りです。
sync.Mutex
: 排他ロック。複数のゴルーチンが共有リソースに同時にアクセスするのを防ぎます。sync.Once
: ある処理が一度だけ実行されることを保証します。初期化処理などで使用されます。sync.Pool
: 一時的なオブジェクトのプール。オブジェクトの再利用を促進し、ガベージコレクションの負荷を軽減します。sync.RWMutex
: 読み書きロック。複数の読み取りは同時に許可しますが、書き込みは排他的に行われます。sync.WaitGroup
: 複数のゴルーチンの完了を待機するためのメカニズム。
runtime.Gosched()
と sync/atomic
変更前のベンチマークコードでは、手動でゴルーチンを管理するために以下の関数やパッケージが使用されていました。
runtime.Gosched()
: 現在のゴルーチンを一時停止し、他のゴルーチンにCPUを譲ります。これにより、明示的にコンテキストスイッチを発生させ、並行性をシミュレートしていました。sync/atomic
: アトミック操作を提供します。複数のゴルーチンから共有されるカウンタなどを安全に更新するために使用されていました。例えば、atomic.AddInt32
は、ベンチマークの総イテレーション数を複数のゴルーチンで共有し、各ゴルーチンが処理したイテレーション数を減算するために使われていました。
これらの手動での並行性制御は、b.RunParallel
の導入により不要になります。
技術的詳細
このコミットの主要な変更点は、sync
パッケージ内の各ベンチマーク関数から、手動でゴルーチンを起動し、runtime.Gosched()
や sync/atomic
を使用してイテレーションを制御するロジックを削除し、代わりに b.RunParallel
を呼び出すように変更したことです。
変更前は、以下のようなパターンが頻繁に見られました。
runtime.GOMAXPROCS(-1)
を呼び出して、利用可能なCPUコア数を取得。- そのコア数に基づいて、またはそれよりも多くのゴルーチンを
go func()
で起動。 - 各ゴルーチン内で、
atomic.AddInt32(&N, -1)
のようなアトミック操作で全体のイテレーション数を管理し、N
が0になるまでループ。 - ループ内で
runtime.Gosched()
を呼び出し、他のゴルーチンに実行を譲る。 - 結果をチャネル (
c := make(chan bool, procs)
) で収集し、すべてのゴルーチンの完了を待機。
このコミットでは、上記の複雑なロジックが b.RunParallel
の呼び出しに置き換えられています。b.RunParallel
は、内部でこれらの並行処理の管理を自動的に行います。
例えば、BenchmarkMutexUncontended
の変更を見てみましょう。
変更前:
func BenchmarkMutexUncontended(b *testing.B) {
type PaddedMutex struct {
Mutex
pad [128]uint8
}
const CallsPerSched = 1000
procs := runtime.GOMAXPROCS(-1)
N := int32(b.N / CallsPerSched)
c := make(chan bool, procs)
for p := 0; p < procs; p++ {
go func() {
var mu PaddedMutex
for atomic.AddInt32(&N, -1) >= 0 {
runtime.Gosched()
for g := 0; g < CallsPerSched; g++ {
mu.Lock()
mu.Unlock()
}
}
c <- true
}()
}
for p := 0; p < procs; p++ {
<-c
}
}
変更後:
func BenchmarkMutexUncontended(b *testing.B) {
type PaddedMutex struct {
Mutex
pad [128]uint8
}
b.RunParallel(func(pb *testing.PB) {
var mu PaddedMutex
for pb.Next() {
mu.Lock()
mu.Unlock()
}
})
}
この変更により、コードが大幅に簡潔になり、runtime.Gosched()
や atomic
パッケージへの依存がなくなっています。b.RunParallel
が自動的に複数のゴルーチンを起動し、pb.Next()
が各ゴルーチンに次のイテレーションを実行するように指示します。
また、benchmarkMutex
のように、slack
(ゴルーチンの数を増やす) オプションを持つベンチマークでは、b.SetParallelism(10)
のような呼び出しが追加され、RunParallel
が起動するゴルーチンの数を制御できるようになっています。これは、特定のシナリオでの並行性をシミュレートするために使用されます。
コアとなるコードの変更箇所
このコミットでは、以下のファイルのベンチマーク関数が変更されています。
src/pkg/sync/mutex_test.go
src/pkg/sync/once_test.go
src/pkg/sync/pool_test.go
src/pkg/sync/runtime_sema_test.go
src/pkg/sync/rwmutex_test.go
src/pkg/sync/waitgroup_test.go
各ファイルにおいて、BenchmarkXxx
という名前の関数内で、手動でゴルーチンを起動し、runtime.Gosched()
や sync/atomic
を使用してイテレーションを制御していた部分が、b.RunParallel
の呼び出しに置き換えられています。
具体的な変更例は以下の通りです。
src/pkg/sync/mutex_test.go
の BenchmarkMutexUncontended
:
runtime.GOMAXPROCS(-1)
の呼び出しを削除。atomic.AddInt32(&N, -1)
を使用したイテレーション管理ループを削除。runtime.Gosched()
の呼び出しを削除。- チャネル (
c
) を使用したゴルーチン間の同期を削除。 - 代わりに
b.RunParallel(func(pb *testing.PB) { ... })
を使用し、ループ条件をfor pb.Next()
に変更。
src/pkg/sync/once_test.go
の BenchmarkOnce
:
- 同様に、手動のゴルーチン管理と
atomic
操作を削除し、b.RunParallel
に置き換え。
src/pkg/sync/pool_test.go
の BenchmarkPool
および BenchmarkPoolOverlflow
:
WaitGroup
とatomic
を使用した並行処理の制御を削除し、b.RunParallel
に置き換え。
src/pkg/sync/runtime_sema_test.go
の BenchmarkSemaUncontended
および benchmarkSema
:
- 手動のゴルーチン管理と
atomic
操作を削除し、b.RunParallel
に置き換え。 benchmarkSema
では、block
シナリオでの初期セマフォ取得ロジックもb.RunParallel
の外に移動し、done
チャネルで同期する形に変更。
src/pkg/sync/rwmutex_test.go
の BenchmarkRWMutexUncontended
および benchmarkRWMutex
:
- 手動のゴルーチン管理と
atomic
操作を削除し、b.RunParallel
に置き換え。
src/pkg/sync/waitgroup_test.go
の BenchmarkWaitGroupUncontended
, benchmarkWaitGroupAddDone
, benchmarkWaitGroupWait
:
- 手動のゴルーチン管理と
atomic
操作を削除し、b.RunParallel
に置き換え。
全体として、この変更は、Goのベンチマークフレームワークの進化に合わせて、sync
パッケージのベンチマークコードを現代化し、より堅牢で保守しやすいものにすることを目的としています。
コアとなるコードの解説
このコミットのコアとなる変更は、Goのベンチマークにおける並行処理の記述方法のパラダイムシフトを示しています。
変更前のアプローチ(手動ゴルーチン管理)の問題点:
- 複雑性: 複数のゴルーチンを起動し、
runtime.Gosched()
でコンテキストスイッチを促し、atomic
操作でイテレーション数を同期させるなど、ベンチマークコード自体が複雑になりがちでした。これは、ベンチマークの意図を理解し、保守するのを困難にしていました。 - 正確性の課題:
runtime.Gosched()
の呼び出し頻度や、GOMAXPROCS
の設定がベンチマーク結果に与える影響を正確に制御するのが難しく、再現性や信頼性に課題が生じる可能性がありました。特に、runtime.Gosched()
はスケジューラのヒントであり、必ずしも期待通りに動作するとは限りません。 - スケーラビリティ:
GOMAXPROCS
の値が変わるたびに、手動でゴルーチン数を調整する必要があるなど、環境への適応性が低いという問題がありました。
変更後のアプローチ(b.RunParallel
の利用)の利点:
- 簡潔性:
b.RunParallel
を使用することで、並行ベンチマークのロジックが大幅に簡素化されます。開発者は、各ゴルーチンが実行すべき単一のイテレーションの処理に集中できます。 - 自動的な並行性管理:
b.RunParallel
は、GOMAXPROCS
の設定に基づいて最適な数のゴルーチンを自動的に起動し、ベンチマークのイテレーションをそれらのゴルーチンに均等に分散させます。これにより、手動での調整が不要になり、異なる環境でも一貫した結果が得られやすくなります。 - 正確性と信頼性:
b.RunParallel
は、Goのテストフレームワークによって最適化されており、より正確で信頼性の高い並行ベンチマーク測定を可能にします。内部的には、各ゴルーチンがpb.Next()
を呼び出すことで、テストフレームワークがイテレーションの進行を追跡し、全体のベンチマーク時間を正確に測定します。 - 保守性: コードが簡潔になることで、ベンチマークの保守が容易になります。
このコミットは、Goのベンチマークフレームワークが成熟し、より高度な並行ベンチマーク機能を提供できるようになったことを示しています。sync
パッケージのような低レベルの並行プリミティブのベンチマークにおいて、この変更は特に重要であり、これらのプリミティブの性能特性をより正確に評価するための基盤を強化します。
関連リンク
- Go言語の
testing
パッケージのドキュメント: https://pkg.go.dev/testing - Go 1.2 リリースノート (
testing.B.RunParallel
の導入について言及): https://go.dev/doc/go1.2#testing - Go言語の
sync
パッケージのドキュメント: https://pkg.go.dev/sync
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード (特に
src/testing/benchmark.go
やsrc/pkg/sync
以下のテストファイル) - Go言語のコミット履歴とコードレビューコメント
testing.B.RunParallel
に関する技術記事や解説(一般的なGoのベンチマークに関する情報)- Go言語の
sync/atomic
パッケージのドキュメント: https://pkg.go.dev/sync/atomic - Go言語の
runtime
パッケージのドキュメント: https://pkg.go.dev/runtime