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

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

このコミットは、Go言語のランタイムにおけるメモリ管理、特にガベージコレクション(GC)に関連する定数定義ファイルである src/pkg/runtime/malloc.h に加えられた変更を扱っています。malloc.h は、Goランタイムがメモリをどのように割り当て、管理し、そして解放するかを制御する低レベルのメカニズムを定義する上で重要な役割を果たします。このファイルには、GCの動作を決定する様々な定数や構造体が含まれています。

コミット

このコミットは、Goランタイムのガベージコレクタが利用するCPUプロセスの最大数を定義する MaxGcproc の値を、誤って変更された16から元の4に戻すものです。これは、以前の意図しない変更を元に戻す「リバート」コミットであり、Goのガベージコレクションの並列処理に関する重要な調整を示しています。

  • コミットハッシュ: eb0bc8164a950ef6c539048474ec7570a9b72856
  • 作者: Dmitriy Vyukov dvyukov@google.com
  • コミット日時: 2012年5月11日 金曜日 13:30:34 +0400

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

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

元コミット内容

runtime: revert MaxGcproc from 16 to 4
The change accidentally come in with this revision:
https://code.google.com/p/go/source/detail?spec=svn345cbca96c5550f2e89bc727703301933802923c&r=14c38c23c819a17021b1808cf4a34ef3a1a17db5

R=golang-dev
CC=golang-dev
https://golang.org/cl/6195073

変更の背景

このコミットの背景には、Goランタイムのガベージコレクタ(GC)の並列処理に関する意図しない変更がありました。コミットメッセージに記載されているように、MaxGcproc の値が16に設定された変更が、別のリビジョン(https://code.google.com/p/go/source/detail?spec=svn345cbca96c5550f2e89bc727703301933802923c&r=14c38c23c819a17021b1808cf4a34ef3a1a17db5)で「誤って」導入されてしまったことが原因です。

GoのGCは、その設計上、特定のCPUコア数に対して最適化されています。コメントにもあるように、「ガベージコレクタは4つのCPUでうまくスケールする」という認識が当時の開発チームにはありました。MaxGcproc を16に増やすことは、当時のGCの設計や実装が想定していない並列度であり、パフォーマンスの低下や予期せぬ動作を引き起こす可能性がありました。

そのため、このコミットは、意図せず導入された変更を元に戻し、Goランタイムの安定性とGCの効率性を維持することを目的としています。これは、ソフトウェア開発におけるリバートコミットの典型的な例であり、誤って導入された変更や問題を引き起こす変更を迅速に修正するために行われます。

前提知識の解説

Goランタイム

Goランタイムは、Go言語で書かれたプログラムを実行するための環境です。これには、スケジューラ(ゴルーチンをOSスレッドにマッピング)、メモリ管理(ヒープの割り当てと解放)、ガベージコレクタ(不要なメモリの自動回収)、およびその他の低レベルなシステムインタラクションが含まれます。Goプログラムは、OSのネイティブスレッド上で動作しますが、Goランタイムがその上でゴルーチンと呼ばれる軽量な並行処理単位を管理します。

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

ガベージコレクションは、プログラムが動的に確保したメモリのうち、もはや到達不可能(参照されていない)になったものを自動的に解放するプロセスです。これにより、開発者は手動でのメモリ管理の複雑さから解放され、メモリリークのリスクを低減できます。GoのGCは、並行(concurrent)かつ並列(parallel)に動作するように設計されており、プログラムの実行と同時にGC処理を進めることで、アプリケーションの一時停止(ストップ・ザ・ワールド)時間を最小限に抑えることを目指しています。

MaxGcproc

MaxGcproc は、Goランタイムのガベージコレクタが並列に動作するために利用できるCPUプロセスの最大数を定義する内部定数です。この値は、GCが同時に利用できるOSスレッドの数を制限し、GCの並列度を制御します。

  • 並列性: MaxGcproc の値が大きいほど、GCはより多くのCPUコアを利用して並列に動作しようとします。これにより、理論的にはGCの処理時間を短縮できる可能性があります。
  • スケーラビリティ: しかし、GCのアルゴリズムや実装によっては、CPUコア数が増えるにつれてパフォーマンスが線形に向上するわけではありません。ある程度の並列度を超えると、同期のオーバーヘッドやキャッシュの競合などにより、むしろパフォーマンスが低下する可能性があります。

並列GC

GoのGCは、複数のCPUコアを利用してガベージコレクションの作業を並列に実行します。これにより、GCサイクル全体の時間を短縮し、アプリケーションの応答性を向上させることができます。しかし、並列処理には、タスクの分割、スレッド間の通信、同期などのオーバーヘッドが伴います。MaxGcproc のような定数は、この並列処理のバランスを調整するために使用されます。

技術的詳細

このコミットの技術的詳細の中心は、MaxGcproc 定数の値がGoのガベージコレクタの動作に与える影響です。

Goのガベージコレクタは、並行マーク&スイープ方式を採用しており、プログラムの実行と並行してマークフェーズ(到達可能なオブジェクトを識別)とスイープフェーズ(不要なオブジェクトが占めるメモリを解放)を実行します。このプロセスを効率的に行うために、GCは複数のプロセッサ(CPUコア)を利用して作業を並列化します。

MaxGcproc は、GCが利用できるプロセッサの最大数を設定します。

  • MaxGcproc = 4: コミットメッセージのコメントにある「The garbage collector scales well to 4 cpus.」という記述は、当時のGoのGC実装が、4つのCPUコアで最も効率的にスケールし、それ以上のコア数では追加のパフォーマンスゲインが限定的であるか、あるいはオーバーヘッドが増加することを示唆しています。これは、GCアルゴリズムの特性、内部データ構造のロック競合、キャッシュの局所性、またはその他の同期メカニズムに起因する可能性があります。例えば、GCの特定のフェーズが本質的に並列化しにくい、あるいは並列化の粒度が粗い場合、コア数を増やしてもスループットは頭打ちになります。
  • MaxGcproc = 16 の問題: 意図せず MaxGcproc が16に設定された場合、GCは利用可能なCPUコアが4つ以上あるシステムで、最大16個のプロセッサを利用しようとします。しかし、当時のGCが4つのCPUで最適に動作するように設計されていたため、16個のプロセッサを無理に利用しようとすると、以下のような問題が発生する可能性がありました。
    • 同期オーバーヘッドの増加: より多くのプロセッサがGC作業に参加しようとすると、共有データ構造へのアクセスを調整するためのロックやアトミック操作の競合が増加し、これがボトルネックとなる可能性があります。
    • キャッシュ効率の低下: 多数のプロセッサが異なるメモリ領域を操作することで、CPUキャッシュのヒット率が低下し、メモリへのアクセスが遅くなる可能性があります。
    • スレッドスケジューリングの複雑化: OSレベルでのスレッドスケジューリングのオーバーヘッドが増加し、GC作業の効率が低下する可能性があります。

したがって、このリバートは、GoのGCが当時の実装で最も効率的に動作する並列度に戻すことで、ランタイム全体のパフォーマンスと安定性を確保するための重要な修正でした。これは、GCの設計と実装が、利用可能なハードウェアリソースをどのように最適に利用するかという複雑なバランスを考慮する必要があることを示しています。

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

変更は src/pkg/runtime/malloc.h ファイルの1箇所のみです。

--- a/src/pkg/runtime/malloc.h
+++ b/src/pkg/runtime/malloc.h
@@ -125,7 +125,7 @@ enum
 	// 2, 3, and 4 are all plausible maximums depending
 	// on the hardware details of the machine.  The garbage
 	// collector scales well to 4 cpus.
-	MaxGcproc = 16,
+	MaxGcproc = 4,
 };

コアとなるコードの解説

変更されたコードは、enum ブロック内で定義されている MaxGcproc という定数です。

  • - MaxGcproc = 16,: 変更前の行で、MaxGcproc16 に設定されていました。これは、以前の意図しない変更によって導入された値です。
  • + MaxGcproc = 4,: 変更後の行で、MaxGcproc4 に設定し直されています。これは、Goのガベージコレクタが当時最適にスケールすると考えられていたCPUコア数です。

この変更は、Goランタイムのガベージコレクタが並列処理に利用するプロセッサの最大数を直接制御します。MaxGcproc の値を4に戻すことで、GCは過剰な並列化によるオーバーヘッドを避け、当時のGoのGCアルゴリズムが最も効率的に動作する並列度で実行されるようになります。これにより、GCのパフォーマンスが安定し、全体的なアプリケーションの応答性が向上することが期待されます。

コメント // The garbage collector scales well to 4 cpus. は、この定数の値が4であることの根拠を明確に示しており、当時のGoのGC設計における重要な知見を反映しています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (Goのガベージコレクションに関する一般的な情報): https://go.dev/doc/effective_go#concurrency (一般的な並行処理の概念)
  • Goのガベージコレクションの歴史と進化に関する記事 (一般的な背景知識): (特定のURLはなし、Go GCの進化に関する一般的な知識に基づく)
  • Goのランタイムソースコード (src/pkg/runtime/ ディレクトリの構造と役割に関する一般的な理解): https://github.com/golang/go/tree/master/src/runtime (現在のGoリポジトリのランタイムディレクトリ)
  • Goのメモリ管理に関するブログ記事や論文 (GCの技術的詳細に関する一般的な理解): (特定のURLはなし、GoのGCに関する一般的な技術的議論に基づく)