Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 13886] ファイルの概要

このコミットは、Goランタイムの並列処理(parfor、おそらく並列ガベージコレクションの一部)に関連する統計情報をリセットする変更です。具体的には、src/pkg/runtime/parfor.cファイルにおいて、ガベージコレクション(GC)サイクル間で統計情報が累積してしまうのを防ぐため、各GCサイクルの終了時にこれらのカウンタをゼロにリセットするように修正されています。これにより、各GCサイクルのパフォーマンス特性をより正確に測定できるようになります。

コミット

commit d151fb9e20a808e91cd0c2fbfbe59a1515070705
Author: Dmitriy Vyukov <dvyukov@google.com>
Date:   Thu Sep 20 22:46:24 2012 +0400

    runtime: reset parfor stats
    Otherwise they sum up between GCs.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/6535048

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/d151fb9e20a808e91cd0c2fbfbe59a1515070705

元コミット内容

runtime: reset parfor stats
Otherwise they sum up between GCs.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6535048

変更の背景

Goランタイムのガベージコレクション(GC)は、プログラムの実行中に不要になったメモリを自動的に解放する重要なプロセスです。GoのGCは並列処理を活用しており、その効率はランタイムのパフォーマンスに直結します。parfor.cは、この並列処理、特に並列GCのタスク実行に関連する統計情報を収集する役割を担っていました。

このコミットが行われる前は、parforに関連する統計情報(nsteal, nstealcnt, nprocyield, nosyield, nsleepなど)がGCサイクル間で累積されていました。つまり、あるGCサイクルで収集された値が次のGCサイクルに引き継がれ、加算されていってしまっていたのです。

このような累積された統計情報では、個々のGCサイクルのパフォーマンス特性を正確に分析することができません。例えば、特定のGCサイクルでどれだけのワークスチールが発生したか、あるいはプロセッサがどれだけyieldしたかを知りたい場合、累積値ではそのサイクル固有の挙動を把握することが困難になります。

この問題を解決し、各GCサイクルのパフォーマンスを独立して評価できるようにするために、GCの終了時(または関連する並列処理タスクの終了時)にこれらの統計カウンタをゼロにリセットする必要がありました。このコミットは、まさにそのリセット処理を追加することで、統計情報の正確性を向上させることを目的としています。

前提知識の解説

Goランタイム

Goランタイムは、Go言語で書かれたプログラムの実行を管理するシステムです。これには、ガベージコレクション、ゴルーチン(軽量スレッド)のスケジューリング、メモリ管理、システムコールとのインターフェースなどが含まれます。Goプログラムは、OS上で直接実行されるのではなく、このランタイム上で動作します。

ガベージコレクション (GC)

ガベージコレクションは、プログラムが動的に確保したメモリのうち、もはやどの部分からも参照されなくなった(不要になった)メモリ領域を自動的に特定し、解放するプロセスです。これにより、プログラマは手動でのメモリ管理から解放され、メモリリークのリスクを低減できます。GoのGCは、並列かつコンカレントに動作するように設計されており、プログラムの実行を長時間停止させることなくメモリを回収します。

並列処理とparfor

並列処理とは、複数の計算タスクを同時に実行する能力を指します。Goランタイムは、マルチコアプロセッサを最大限に活用するために、GCを含む多くの内部処理で並列処理を利用します。 parfor(Parallel Forの略)は、Goランタイム内部で、特定のタスク(例えば、GC中のオブジェクト走査)を複数のプロセッサ(M: Machine)やゴルーチン(G: Goroutine)に分散して並列に実行するためのメカニズムです。これは、大規模なデータ構造を効率的に処理するために使用されます。

ワークスチール (Work Stealing)

ワークスチールは、並列処理環境における負荷分散のためのスケジューリング戦略の一つです。この戦略では、タスクキューが空になったアイドル状態のプロセッサ(またはワーカー)が、他のビジーなプロセッサのタスクキューからタスクを「盗んで」実行します。これにより、プロセッサの利用率が向上し、全体のスループットが改善されます。 このコミットでリセットされるnstealnstealcntは、それぞれワークスチールされたタスクの数とワークスチール試行回数に関連する統計情報であると考えられます。

runtime·xadd64

runtime·xadd64は、Goランタイム内部で使用されるアトミックな加算操作です。アトミック操作とは、複数のCPUコアやゴルーチンから同時にアクセスされた場合でも、その操作が中断されずに完全に実行されることを保証するものです。これにより、並列環境下でのデータ競合を防ぎ、カウンタの正確な更新を保証します。このコミットでは、統計情報を集計する際にこの関数が使用されています。

nprocyield, nosyield, nsleep

これらは、Goランタイムのスケジューラがゴルーチンを管理する際に発生する、プロセッサの「譲渡(yield)」や「スリープ」に関連する統計情報です。

  • nprocyield: プロセッサが他のゴルーチンに実行を譲った回数。
  • nosyield: OSレベルで実行を譲った回数。
  • nsleep: ゴルーチンがスリープ状態に入った回数。 これらの統計は、スケジューラの効率性やゴルーチンの待機状況を分析するために重要です。

技術的詳細

このコミットは、src/pkg/runtime/parfor.cファイルのparfor_end関数内に、以下の5行を追加しています。

	me->nsteal = 0;
	me->nstealcnt = 0;
	me->nprocyield = 0;
	me->nosyield = 0;
	me->nsleep = 0;

parfor_end関数は、並列処理タスク(おそらくGCの並列フェーズ)が終了する際に呼び出される関数です。この関数内で、各プロセッサ(またはワーカー)が収集したローカルな統計情報(me->nstealなど)をグローバルな統計情報(desc->nstealなど)にアトミックに加算(runtime·xadd64)した後、これらのローカルカウンタをゼロにリセットしています。

このリセット処理の導入により、以下の効果が期待されます。

  1. 正確なGCサイクルごとの統計: 各GCサイクルが開始される前に統計カウンタがゼロにリセットされるため、それぞれのGCサイクルで実際に発生したワークスチール、yield、スリープなどの回数を正確に測定できるようになります。
  2. パフォーマンス分析の改善: 開発者やランタイムの最適化担当者は、特定のGCサイクルがなぜ遅いのか、あるいはなぜ効率的でないのかを分析する際に、より信頼性の高いデータに基づいて判断を下せるようになります。例えば、あるGCサイクルでnstealが異常に高い場合、それはタスクの偏りやスケジューリングの問題を示唆する可能性があります。
  3. デバッグの容易化: 問題が発生した際に、特定の期間(GCサイクル)に限定して統計情報を確認できるため、デバッグ作業が容易になります。

この変更は、Goランタイムの内部動作の透明性を高め、将来的なパフォーマンス改善のための基盤を強化するものです。

コアとなるコードの変更箇所

diff --git a/src/pkg/runtime/parfor.c b/src/pkg/runtime/parfor.c
index 7ebbaac4cd..36dd65852f 100644
--- a/src/pkg/runtime/parfor.c
+++ b/src/pkg/runtime/parfor.c
@@ -196,6 +196,11 @@ exit:
 	runtime·xadd64(&desc->nprocyield, me->nprocyield);
 	runtime·xadd64(&desc->nosyield, me->nosyield);
 	runtime·xadd64(&desc->nsleep, me->nsleep);
+	me->nsteal = 0;
+	me->nstealcnt = 0;
+	me->nprocyield = 0;
+	me->nosyield = 0;
+	me->nsleep = 0;
 }
 
 // For testing from Go

コアとなるコードの解説

変更はsrc/pkg/runtime/parfor.cファイルのparfor_end関数内で行われています。

既存のコードでは、各プロセッサ(me)が収集したローカルな統計情報(me->nprocyield, me->nosyield, me->nsleepなど)を、グローバルな統計記述子(desc)にアトミックに加算しています。これは、複数のプロセッサが同時に統計を更新してもデータが壊れないようにするためです。

追加された5行は、この加算処理の直後に実行されます。

  • me->nsteal = 0;
  • me->nstealcnt = 0;
  • me->nprocyield = 0;
  • me->nosyield = 0;
  • me->nsleep = 0;

これらの行は、現在のプロセッサ(me)が保持している、ワークスチール関連のカウンタ(nsteal, nstealcnt)と、プロセッサの譲渡・スリープ関連のカウンタ(nprocyield, nosyield, nsleep)をすべてゼロにリセットしています。

このリセットにより、parfor_endが呼び出された時点(並列処理タスクの終了時)で、これらのカウンタは次の並列処理タスクの開始に備えてクリーンな状態になります。これにより、各タスク実行期間(またはGCサイクル)における統計情報が独立して測定され、累積されることがなくなります。

関連リンク

参考にした情報源リンク

  • Goのガベージコレクションに関する公式ドキュメントやブログ記事 (一般的なGCの概念理解のため)
  • Goランタイムのソースコード(src/runtime/ディレクトリ内の関連ファイル、特にスケジューラやGC関連のコード)
  • ワークスチールスケジューリングに関する一般的な情報 (コンピュータサイエンスの概念として)
  • アトミック操作に関する一般的な情報 (並列プログラミングの概念として)
  • Goの古いバージョンのランタイムに関する議論やドキュメント (2012年当時のGoのGCやスケジューラの実装を理解するため)```markdown

[インデックス 13886] ファイルの概要

このコミットは、Goランタイムの並列処理(parfor、おそらく並列ガベージコレクションの一部)に関連する統計情報をリセットする変更です。具体的には、src/pkg/runtime/parfor.cファイルにおいて、ガベージコレクション(GC)サイクル間で統計情報が累積してしまうのを防ぐため、各GCサイクルの終了時にこれらのカウンタをゼロにリセットするように修正されています。これにより、各GCサイクルのパフォーマンス特性をより正確に測定できるようになります。

コミット

commit d151fb9e20a808e91cd0c2fbfbe59a1515070705
Author: Dmitriy Vyukov <dvyukov@google.com>
Date:   Thu Sep 20 22:46:24 2012 +0400

    runtime: reset parfor stats
    Otherwise they sum up between GCs.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/6535048

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/d151fb9e20a808e91cd0c2fbfbe59a1515070705

元コミット内容

runtime: reset parfor stats
Otherwise they sum up between GCs.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6535048

変更の背景

Goランタイムのガベージコレクション(GC)は、プログラムの実行中に不要になったメモリを自動的に解放する重要なプロセスです。GoのGCは並列処理を活用しており、その効率はランタイムのパフォーマンスに直結します。parfor.cは、この並列処理、特に並列GCのタスク実行に関連する統計情報を収集する役割を担っていました。

このコミットが行われる前は、parforに関連する統計情報(nsteal, nstealcnt, nprocyield, nosyield, nsleepなど)がGCサイクル間で累積されていました。つまり、あるGCサイクルで収集された値が次のGCサイクルに引き継がれ、加算されていってしまっていたのです。

このような累積された統計情報では、個々のGCサイクルのパフォーマンス特性を正確に分析することができません。例えば、特定のGCサイクルでどれだけのワークスチールが発生したか、あるいはプロセッサがどれだけyieldしたかを知りたい場合、累積値ではそのサイクル固有の挙動を把握することが困難になります。

この問題を解決し、各GCサイクルのパフォーマンスを独立して評価できるようにするために、GCの終了時(または関連する並列処理タスクの終了時)にこれらの統計カウンタをゼロにリセットする必要がありました。このコミットは、まさにそのリセット処理を追加することで、統計情報の正確性を向上させることを目的としています。

前提知識の解説

Goランタイム

Goランタイムは、Go言語で書かれたプログラムの実行を管理するシステムです。これには、ガベージコレクション、ゴルーチン(軽量スレッド)のスケジューリング、メモリ管理、システムコールとのインターフェースなどが含まれます。Goプログラムは、OS上で直接実行されるのではなく、このランタイム上で動作します。

ガベージコレクション (GC)

ガベージコレクションは、プログラムが動的に確保したメモリのうち、もはやどの部分からも参照されなくなった(不要になった)メモリ領域を自動的に特定し、解放するプロセスです。これにより、プログラマは手動でのメモリ管理から解放され、メモリリークのリスクを低減できます。GoのGCは、並列かつコンカレントに動作するように設計されており、プログラムの実行を長時間停止させることなくメモリを回収します。

並列処理とparfor

並列処理とは、複数の計算タスクを同時に実行する能力を指します。Goランタイムは、マルチコアプロセッサを最大限に活用するために、GCを含む多くの内部処理で並列処理を利用します。 parfor(Parallel Forの略)は、Goランタイム内部で、特定のタスク(例えば、GC中のオブジェクト走査)を複数のプロセッサ(M: Machine)やゴルーチン(G: Goroutine)に分散して並列に実行するためのメカニズムです。これは、大規模なデータ構造を効率的に処理するために使用されます。

ワークスチール (Work Stealing)

ワークスチールは、並列処理環境における負荷分散のためのスケジューリング戦略の一つです。この戦略では、タスクキューが空になったアイドル状態のプロセッサ(またはワーカー)が、他のビジーなプロセッサのタスクキューからタスクを「盗んで」実行します。これにより、プロセッサの利用率が向上し、全体のスループットが改善されます。 このコミットでリセットされるnstealnstealcntは、それぞれワークスチールされたタスクの数とワークスチール試行回数に関連する統計情報であると考えられます。

runtime·xadd64

runtime·xadd64は、Goランタイム内部で使用されるアトミックな加算操作です。アトミック操作とは、複数のCPUコアやゴルーチンから同時にアクセスされた場合でも、その操作が中断されずに完全に実行されることを保証するものです。これにより、並列環境下でのデータ競合を防ぎ、カウンタの正確な更新を保証します。このコミットでは、統計情報を集計する際にこの関数が使用されています。

nprocyield, nosyield, nsleep

これらは、Goランタイムのスケジューラがゴルーチンを管理する際に発生する、プロセッサの「譲渡(yield)」や「スリープ」に関連する統計情報です。

  • nprocyield: プロセッサが他のゴルーチンに実行を譲った回数。
  • nosyield: OSレベルで実行を譲った回数。
  • nsleep: ゴルーチンがスリープ状態に入った回数。 これらの統計は、スケジューラの効率性やゴルーチンの待機状況を分析するために重要です。

技術的詳細

このコミットは、src/pkg/runtime/parfor.cファイルのparfor_end関数内に、以下の5行を追加しています。

	me->nsteal = 0;
	me->nstealcnt = 0;
	me->nprocyield = 0;
	me->nosyield = 0;
	me->nsleep = 0;

parfor_end関数は、並列処理タスク(おそらくGCの並列フェーズ)が終了する際に呼び出される関数です。この関数内で、各プロセッサ(またはワーカー)が収集したローカルな統計情報(me->nstealなど)をグローバルな統計情報(desc->nstealなど)にアトミックに加算(runtime·xadd64)した後、これらのローカルカウンタをゼロにリセットしています。

このリセット処理の導入により、以下の効果が期待されます。

  1. 正確なGCサイクルごとの統計: 各GCサイクルが開始される前に統計カウンタがゼロにリセットされるため、それぞれのGCサイクルで実際に発生したワークスチール、yield、スリープなどの回数を正確に測定できるようになります。
  2. パフォーマンス分析の改善: 開発者やランタイムの最適化担当者は、特定のGCサイクルがなぜ遅いのか、あるいはなぜ効率的でないのかを分析する際に、より信頼性の高いデータに基づいて判断を下せるようになります。例えば、あるGCサイクルでnstealが異常に高い場合、それはタスクの偏りやスケジューリングの問題を示唆する可能性があります。
  3. デバッグの容易化: 問題が発生した際に、特定の期間(GCサイクル)に限定して統計情報を確認できるため、デバッグ作業が容易になります。

この変更は、Goランタイムの内部動作の透明性を高め、将来的なパフォーマンス改善のための基盤を強化するものです。

コアとなるコードの変更箇所

diff --git a/src/pkg/runtime/parfor.c b/src/pkg/runtime/parfor.c
index 7ebbaac4cd..36dd65852f 100644
--- a/src/pkg/runtime/parfor.c
+++ b/src/pkg/runtime/parfor.c
@@ -196,6 +196,11 @@ exit:
 	runtime·xadd64(&desc->nprocyield, me->nprocyield);
 	runtime·xadd64(&desc->nosyield, me->nosyield);
 	runtime·xadd64(&desc->nsleep, me->nsleep);
+	me->nsteal = 0;
+	me->nstealcnt = 0;
+	me->nprocyield = 0;
+	me->nosyield = 0;
+	me->nsleep = 0;
 }
 
 // For testing from Go

コアとなるコードの解説

変更はsrc/pkg/runtime/parfor.cファイルのparfor_end関数内で行われています。

既存のコードでは、各プロセッサ(me)が収集したローカルな統計情報(me->nprocyield, me->nosyield, me->nsleepなど)を、グローバルな統計記述子(desc)にアトミックに加算しています。これは、複数のプロセッサが同時に統計を更新してもデータが壊れないようにするためです。

追加された5行は、この加算処理の直後に実行されます。

  • me->nsteal = 0;
  • me->nstealcnt = 0;
  • me->nprocyield = 0;
  • me->nosyield = 0;
  • me->nsleep = 0;

これらの行は、現在のプロセッサ(me)が保持している、ワークスチール関連のカウンタ(nsteal, nstealcnt)と、プロセッサの譲渡・スリープ関連のカウンタ(nprocyield, nosyield, nsleep)をすべてゼロにリセットしています。

このリセットにより、parfor_endが呼び出された時点(並列処理タスクの終了時)で、これらのカウンタは次の並列処理タスクの開始に備えてクリーンな状態になります。これにより、各タスク実行期間(またはGCサイクル)における統計情報が独立して測定され、累積されることがなくなります。

関連リンク

参考にした情報源リンク

  • Goのガベージコレクションに関する公式ドキュメントやブログ記事 (一般的なGCの概念理解のため)
  • Goランタイムのソースコード(src/runtime/ディレクトリ内の関連ファイル、特にスケジューラやGC関連のコード)
  • ワークスチールスケジューリングに関する一般的な情報 (コンピュータサイエンスの概念として)
  • アトミック操作に関する一般的な情報 (並列プログラミングの概念として)
  • Goの古いバージョンのランタイムに関する議論やドキュメント (2012年当時のGoのGCやスケジューラの実装を理解するため)