[インデックス 17030] ファイルの概要
このコミットは、Goランタイムから singleproc 変数を削除するものです。これは、古いスケジューラで一時的に GOMAXPROCS よりも多くのスレッドが存在する可能性があったために必要とされていましたが、新しいスケジューラでは GOMAXPROCS が常に尊重されるようになったため、不要になりました。
コミット
commit d5ab7846113004e7fa3aaf64691d3d170988c311
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Mon Aug 5 22:58:02 2013 +0400
runtime: remove singleproc var
It was needed for the old scheduler,
because there temporary could be more threads than gomaxprocs.
In the new scheduler gomaxprocs is always respected.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/12438043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d5ab7846113004e7fa3aaf64691d3d170988c311
元コミット内容
runtime: remove singleproc var
It was needed for the old scheduler,
because there temporary could be more threads than gomaxprocs.
In the new scheduler gomaxprocs is always respected.
変更の背景
このコミットの背景には、Goランタイムのスケジューラの大幅な変更があります。Goの初期のスケジューラ(通称「古いスケジューラ」)では、GOMAXPROCS 環境変数で指定された論理プロセッサ数(P)に対して、一時的にそれ以上のOSスレッド(M)が実行される可能性がありました。これは、システムコールやCGO呼び出しなどでGoルーチンがブロックされた際に、新しいMが作成されることで発生し、GOMAXPROCS の制約が一時的に破られる状況を生み出していました。
singleproc 変数は、このような状況、特に GOMAXPROCS=1 の場合に、複数のスレッドが同時にランタイムのクリティカルセクションにアクセスするのを防ぐための特別なフラグとして使用されていました。GOMAXPROCS=1 の場合、通常は単一のOSスレッドで全てのGoルーチンが実行されることが期待されますが、古いスケジューラの挙動により、一時的に複数のスレッドが存在しうるため、このフラグが必要とされていました。
しかし、Go 1.1で導入された新しいスケジューラ(通称「M:Nスケジューラ」)では、この挙動が改善されました。新しいスケジューラは、GOMAXPROCS の値をより厳密に尊重し、Goルーチンがブロックされた場合でも、GOMAXPROCS で指定されたPの数を超えてMが作成されることを防ぐように設計されています。具体的には、ブロックされたMはPを解放し、他のMがそのPを利用できるようになります。これにより、GOMAXPROCS の制約が常に維持されるようになり、singleproc 変数のような特別なフラグが不要になりました。
このコミットは、新しいスケジューラの導入によって singleproc 変数がその役割を終え、コードベースから削除できるようになったことを示しています。これにより、ランタイムのコードが簡素化され、保守性が向上します。
前提知識の解説
このコミットを理解するためには、以下のGoランタイムの概念を理解しておく必要があります。
- Goルーチン (Goroutine): Go言語における軽量な並行実行単位です。OSスレッドよりもはるかに軽量で、数百万個作成することも可能です。
- OSスレッド (M - Machine): オペレーティングシステムが管理するスレッドです。Goランタイムは、GoルーチンをOSスレッド上で実行します。
- 論理プロセッサ (P - Processor): GoランタイムがGoルーチンを実行するために使用する論理的なコンテキストです。
GOMAXPROCS環境変数によってその数が制御されます。Pの数は、同時に実行できるGoルーチンの数を制限します。 - スケジューラ: Goランタイムのスケジューラは、GoルーチンをPに割り当て、PをMに割り当てる役割を担います。これにより、Goルーチンが効率的にOSスレッド上で実行されます。
- GOMAXPROCS: Goプログラムが同時に実行できるOSスレッドの最大数を設定する環境変数です。Go 1.5以降はデフォルトでCPUのコア数に設定されますが、それ以前はデフォルトで1でした。この値は、Goランタイムが利用できるPの数を決定します。
- 古いスケジューラ: Go 1.0で導入された初期のスケジューラです。このスケジューラは、Goルーチンがシステムコールなどでブロックされた際に、新しいOSスレッド(M)を一時的に作成することがありました。これにより、
GOMAXPROCSで指定されたPの数を超えてMが存在する状況が発生し、特にGOMAXPROCS=1の場合に競合状態を防ぐための特別な考慮が必要でした。 - 新しいスケジューラ (M:Nスケジューラ): Go 1.1で導入された改善されたスケジューラです。このスケジューラは、Goルーチンがブロックされた際に、そのGoルーチンを実行していたMがPを解放し、他のMがそのPを利用できるようにすることで、
GOMAXPROCSの制約をより厳密に維持します。これにより、GOMAXPROCSの値が常に尊重され、ランタイムの並行処理がより予測可能で効率的になりました。
singleproc 変数は、古いスケジューラの挙動に起因する特定の競合状態を回避するために存在していました。新しいスケジューラでは、この問題が根本的に解決されたため、singleproc は不要になったのです。
技術的詳細
このコミットは、Goランタイムの内部実装における singleproc 変数の削除と、それに伴うコードの変更を示しています。
singleproc 変数は bool 型で、runtime·gomaxprocs が1である場合に true に設定されていました。この変数は、主にメモリ管理(mgc0.c)のコード内で、GOMAXPROCS=1 の場合に特定の操作(メモリの割り当て、解放、特殊なブロックの設定など)をアトミックに行うための条件分岐として使用されていました。
古いスケジューラでは、GOMAXPROCS=1 であっても、システムコールなどでGoルーチンがブロックされると、新しいOSスレッドが作成され、一時的に複数のOSスレッドが同時に動作する可能性がありました。この状況下で、singleproc が true であれば、メモリ操作がロックなしで安全に行えるという前提が崩れるため、singleproc が false の場合(つまり GOMAXPROCS > 1 の場合、または GOMAXPROCS=1 でも一時的に複数のスレッドが存在する場合)には、アトミック操作やロック機構を使用して競合状態を防ぐ必要がありました。
新しいスケジューラでは、GOMAXPROCS の値が常に尊重されるため、GOMAXPROCS=1 の場合は常に単一のOSスレッドがGoルーチンを実行します(システムコールなどでブロックされた場合でも、Pが解放され、他のMがそのPを利用することはないため、同時に複数のMがアクティブになることはありません)。したがって、singleproc 変数で表現されていた「GOMAXPROCS=1 であり、かつ単一のOSスレッドで実行されている」という状態は、単に runtime·gomaxprocs == 1 という条件で表現できるようになりました。
コミットの変更点を見ると、src/pkg/runtime/mgc0.c 内の runtime·singleproc を参照していた箇所が、直接 runtime·gomaxprocs == 1 に置き換えられています。これにより、singleproc 変数の宣言と、procresize 関数内での singleproc の更新ロジックが不要になり、削除されています。
この変更は、ランタイムのコードを簡素化し、新しいスケジューラの設計思想に合致させるものです。不要な変数を削除することで、コードの可読性と保守性が向上し、将来的な最適化の余地も生まれます。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は以下の3つのファイルです。
-
src/pkg/runtime/mgc0.c:runtime·markallocated関数内でif(runtime·singleproc)がif(runtime·gomaxprocs == 1)に変更。runtime·markfreed関数内でif(runtime·singleproc)がif(runtime·gomaxprocs == 1)に変更。runtime·setblockspecial関数内でif(runtime·singleproc)がif(runtime·gomaxprocs == 1)に変更。
-
src/pkg/runtime/proc.c:runtime·singleproc変数の宣言が削除。procresize関数内でruntime·singleproc = new == 1;の行が削除。
-
src/pkg/runtime/runtime.h:extern bool runtime·singleproc;の宣言が削除。
コアとなるコードの解説
src/pkg/runtime/mgc0.c の変更
このファイルはGoランタイムのメモリ管理、特にガベージコレクションに関連する部分を扱っています。変更された3つの関数は、メモリブロックのマーク(割り当て済み、解放済み、特殊なブロック)を行う際に、GOMAXPROCS=1 の場合の最適化されたパスを持っていました。
runtime·markallocated(void *v, uintptr n, bool noptr): メモリブロックが割り当てられたことをマークする関数。runtime·markfreed(void *v, uintptr n): メモリブロックが解放されたことをマークする関数。runtime·setblockspecial(void *v, bool s): メモリブロックに特殊な属性を設定する関数。
これらの関数内では、if(runtime·singleproc) という条件分岐がありました。これは、GOMAXPROCS=1 であり、かつ単一のOSスレッドで実行されている場合に、ロックなしで直接メモリのビットマップを更新する高速パスを提供していました。それ以外の場合(singleproc が false の場合)は、ループ内でアトミック操作(runtime·cas - Compare And Swap)を使用して、複数のスレッドからの同時アクセスによる競合を防いでいました。
変更後、runtime·singleproc が runtime·gomaxprocs == 1 に置き換えられたことで、この条件分岐は GOMAXPROCS の値に直接依存するようになりました。新しいスケジューラでは、GOMAXPROCS=1 であれば常に単一のOSスレッドで実行されることが保証されるため、この置き換えは機能的に同等であり、かつ singleproc 変数の管理が不要になります。
src/pkg/runtime/proc.c の変更
このファイルはGoランタイムのプロセッサ(P)とスケジューラに関連する主要なロジックを含んでいます。
runtime·singleproc変数の宣言 (bool runtime·singleproc;) が削除されました。これは、この変数がもはやランタイム全体で必要とされないことを意味します。procresize(int32 new)関数内でのruntime·singleproc = new == 1;の行が削除されました。この関数はGOMAXPROCSの値を変更する際に呼び出され、以前はsingleproc変数をnew == 1に応じて更新していました。この行が削除されたことで、singleproc変数のライフサイクルが完全に終了しました。
src/pkg/runtime/runtime.h の変更
このファイルはGoランタイムのグローバル変数や関数の宣言を含むヘッダーファイルです。
extern bool runtime·singleproc;の宣言が削除されました。これにより、singleproc変数がランタイムの外部から参照されることがなくなり、完全に内部から消滅しました。
これらの変更は、新しいスケジューラの導入によって singleproc 変数がその役割を終え、コードベースから安全に削除できるようになったことを明確に示しています。これにより、ランタイムのコードがよりシンプルで理解しやすくなりました。
関連リンク
- Go 1.1 Release Notes: https://go.dev/doc/go1.1 (特に "Runtime" セクション)
- Goスケジューラの設計に関する議論 (古い情報も含む): https://go.dev/blog/go1.1 (Go 1.1のスケジューラ改善について言及)
- Goのスケジューラに関する詳細な解説記事 (例: "Go's work-stealing scheduler"): https://www.dev.to/joshuabaker/go-s-work-stealing-scheduler-300 (一般的な概念理解に役立つ)
参考にした情報源リンク
- Goの公式ドキュメントとリリースノート
- Goのソースコード (特に
src/runtimeディレクトリ) - Goコミュニティのブログ記事や技術解説 (Goスケジューラに関するもの)
- コミットメッセージと関連するGo CL (Change List) の情報
- Goのスケジューラに関する一般的な知識 (M:Nスケジューリングなど)
(注: 上記のリンクは一般的な情報源の例であり、特定の記事やドキュメントを直接参照したものではありません。Goのスケジューラに関する情報は多岐にわたるため、具体的な情報源は検索結果によって異なります。)