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

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

このコミットは、Goランタイムのビルドプロセスに関連する変更です。具体的には、src/cmd/dist/buildruntime.cファイルに修正が加えられ、GoランタイムスケジューラのP(Processor)構造体に対するオフセット生成機能が追加されました。これにより、アセンブリコードからP構造体のフィールドにアクセスする際に必要なオフセット情報が利用可能になります。

コミット

commit 5d340de1f682d3b2cf10b2319c25ef3799e84078
Author: Russ Cox <rsc@golang.org>
Date:   Fri Jul 19 15:40:32 2013 -0400

    cmd/dist: generate offsets for P structure
    
    So far no checked-in assembly needs these,
    but it matches having them for M and G.
    I needed these for some manual testing.
    
    R=golang-dev, dvyukov
    CC=golang-dev
    https://golang.org/cl/11595043

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

https://github.com/golang/go/commit/5d340de1f682d3b2cf10b2319c25ef3799e84078

元コミット内容

cmd/dist: generate offsets for P structure

So far no checked-in assembly needs these,
but it matches having them for M and G.
I needed these for some manual testing.

R=golang-dev, dvyukov
CC=golang-dev
https://golang.org/cl/11595043

変更の背景

この変更の背景には、Goランタイムの内部構造、特にスケジューラが使用するM(Machine/OS Thread)、G(Goroutine)、P(Processor/Context)という3つの主要なエンティティの管理があります。Goランタイムは、これらの構造体のフィールドにアセンブリコードから直接アクセスする必要がある場合があります。

コミットメッセージによると、この変更が行われた時点では、Goの標準ライブラリに含まれるアセンブリコードでP構造体のオフセットを直接必要とするものはなかったようです。しかし、MとG構造体については既にオフセットが生成されており、P構造体についても同様の対応をすることで、一貫性が保たれます。

コミットの作者であるRuss Cox氏(Go言語の主要な設計者の一人)は、自身が行っていた手動テストにおいてP構造体のオフセットが必要になったと述べています。これは、ランタイムのデバッグや最適化、あるいは新しい機能のプロトタイピングの際に、P構造体の内部状態をアセンブリレベルで検査・操作する必要があったことを示唆しています。

Goランタイムは、パフォーマンスが非常に重要であるため、一部のクリティカルなパスではアセンブリ言語が使用されます。アセンブリコードからC言語やGo言語で定義された構造体のフィールドにアクセスするには、そのフィールドが構造体の先頭からどれだけ離れているか(オフセット)を知る必要があります。このオフセット情報は、コンパイル時に生成され、アセンブリコードに埋め込まれるか、あるいはアセンブリコードが参照できる形で提供されます。

前提知識の解説

Goランタイムスケジューラ (M, G, Pモデル)

Goランタイムスケジューラは、Goの並行処理の根幹をなす部分であり、M、G、Pという3つの抽象概念を中心に設計されています。

  • G (Goroutine): Goにおける軽量な実行単位です。OSのスレッドよりもはるかに軽量で、数百万個作成することも可能です。各Goroutineは独立したスタックを持ち、Goの関数呼び出しによって生成されます。
  • M (Machine/OS Thread): オペレーティングシステムのスレッドを表します。Goのコード(ユーザーコード、スケジューラ、ガベージコレクタなど)はすべてM上で実行されます。OSのスケジューラがMをCPUコアに割り当てます。
  • P (Processor/Context): 論理プロセッサ、またはコンテキストを表します。MがGoコードを実行するために必要なリソース(例えば、ローカルなGoroutineキュー)を提供します。Pの数は通常、マシンの論理CPU数に設定されます(GOMAXPROCS環境変数で制御可能)。各Pは、実行準備ができたGoroutineを保持するローカルな実行キュー(LRQ)を持っています。

M, G, Pの相互作用: Goスケジューラは、G(実行するコード)をM(実行する場所)とP(実行する権利とリソース)にマッチさせる役割を担います。

  1. Goプログラムが起動すると、ランタイムは論理CPU数に応じたMとPのセットを作成し、各MをPに関連付けます。
  2. 新しいGoroutineが作成されると、通常は利用可能なPのローカル実行キューに追加されます。
  3. Mは、関連付けられたPのローカル実行キューからGoroutineを選択し、実行します。
  4. Goroutineがブロッキングシステムコール(例: ファイル読み込み)を実行すると、そのGoroutineを実行していたMはブロックされます。Pがアイドル状態になるのを防ぐため、GoランタイムはブロックされたMをPから切り離し、新しいMを作成(またはアイドルなMを再利用)してそのPに関連付け、他のGoroutineの実行を継続させます。
  5. MがPのローカル実行キュー内のすべてのGoroutineの実行を終えると、他のPのローカル実行キューやグローバル実行キューからGoroutineを「盗む」(work stealing)ことができます。

このM, G, Pモデルにより、Goは限られた数のOSスレッド上で多数のGoroutineを効率的に多重化し、リソース利用を最適化し、高い並行性を提供します。

cmd/distbuildruntime.c

  • cmd/dist: Goのディストリビューションに含まれるツールで、Goツールチェーン自体のブートストラップ、ビルド、テストを担当します。Goのコンパイラ、リンカ、アセンブラなどのツールをビルドするために使用されます。
  • buildruntime.c: src/cmd/dist/buildruntime.cに位置するC言語のソースファイルです。Go 1.5より前のバージョンでは、このファイルがランタイムの一部のアセンブリコードを生成する役割を担っていました。特に、Goのコールバック関数を処理するruntime·callbackasmのようなランタイム関数用のアセンブリコードを生成していた可能性があります。Go 1.5以降、Goツールチェーンの多くがGo自身で書き直されたため、このファイルの機能は現在ではbuildruntime.goのようなGoソースファイルによって処理されている可能性があります。

構造体フィールドのオフセットとアセンブリからのアクセス

Goの構造体はメモリ上に連続して配置されます。構造体内の各フィールドは、構造体の先頭からの特定のオフセットに位置します。アセンブリコードから構造体のフィールドにアクセスするには、以下の手順が必要です。

  1. メモリレイアウトの理解: Goの構造体は、定義されたフィールドの順序に従ってメモリに配置されます。コンパイラはアライメントのためにフィールドの順序を最適化したり、パディングを追加したりすることがありますが、特定の構造体定義に対しては、フィールドのオフセットは固定されます。
  2. フィールドオフセットの決定: 各フィールドは、構造体のベースアドレスからの特定のオフセットを持ちます。Goでは、unsafe.Offsetof関数を使用してこれらのオフセットを決定できます。アセンブリでは、これらのオフセットを直接使用します。
  3. アセンブリからのフィールドアクセス:
    • 構造体のベースアドレスは通常、レジスタ(例: AX, BX, RBP, RSPなど、呼び出し規約とコンテキストによる)に保持されます。
    • フィールドのオフセットをこのベースアドレスに加算することで、フィールドのメモリアドレスが得られます。
    • 最後に、MOV(データ移動)、ADD(算術演算)などの命令と、ベースレジスタとオフセットを含むアドレッシングモードを使用してフィールドにアクセスします。

このコミットは、buildruntime.cがGoランタイムのP構造体のフィールドオフセットを生成するように拡張することで、アセンブリコードがP構造体の内部フィールドに効率的にアクセスできるようにするためのものです。

技術的詳細

このコミットは、src/cmd/dist/buildruntime.cファイルにわずか2行の追加を行うものです。このファイルは、Goランタイムのビルドプロセスの一部として、Goランタイムの内部構造に関する情報を生成するために使用されます。具体的には、アセンブリコードがGoの内部構造体(M, G, Pなど)のフィールドにアクセスするために必要なオフセット情報を生成します。

buildruntime.cの役割は、GoのランタイムがC言語で書かれていた時代に、C言語の構造体の定義からアセンブリコードが利用できるオフセット情報を抽出することでした。この情報は、アセンブリコードが構造体の特定のフィールドに直接アクセスするために不可欠です。例えば、M構造体のg0フィールドやG構造体のstackフィールドなど、ランタイムのコア機能で頻繁にアクセスされるフィールドは、アセンブリから高速にアクセスできるようにオフセットが事前に計算されます。

変更前のコードでは、MG構造体についてはオフセット生成のロジックが存在していましたが、P構造体については欠けていました。このコミットは、その欠落を補完するものです。

追加された行は以下の通りです。

+			else if(streq(fields.p[1], "P"))
+				aggr = "p";

このコードスニペットは、buildruntime.c内の構造体フィールドを処理するロジックの一部です。fields.p[1]は、現在処理している構造体の名前(またはその一部)を表していると推測されます。もしその名前が "P" と一致した場合、aggrという変数に "p" という文字列を割り当てています。このaggr変数は、おそらく生成されるオフセット情報の識別子として使用されるものと考えられます。

つまり、この変更により、buildruntime.cはGoランタイムのP構造体に関するオフセット情報を認識し、それをビルドプロセスの一部として出力するようになります。この出力されたオフセット情報は、GoランタイムのアセンブリコードがP構造体の特定のフィールドにアクセスする際に利用されます。

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

--- a/src/cmd/dist/buildruntime.c
+++ b/src/cmd/dist/buildruntime.c
@@ -248,6 +248,8 @@ ok:
 			aggr = "g";
 		else if(streq(fields.p[1], "M"))
 			aggr = "m";
+		else if(streq(fields.p[1], "P"))
+			aggr = "p";
 		else if(streq(fields.p[1], "Gobuf"))
 			aggr = "gobuf";
 		else if(streq(fields.p[1], "WinCall"))

コアとなるコードの解説

変更はsrc/cmd/dist/buildruntime.cファイルの248行目付近にあります。

このC言語のコードは、Goランタイムのビルドプロセスの一部として、特定の構造体(GMGobufWinCallなど)のフィールドオフセットを識別し、それに対応するアセンブリコード生成のためのメタデータを準備している部分です。

  • streq(fields.p[1], "M")streq(fields.p[1], "G") は、現在処理している構造体の名前が "M""G" であるかをチェックしています。
  • もし一致した場合、aggrという変数にそれぞれ "m""g" という文字列を割り当てています。このaggr変数は、おそらく後続の処理で、これらの構造体に関連するオフセット情報を生成する際の識別子として使われるものです。

今回のコミットで追加された2行は、この既存のロジックにP構造体に対する同様の処理を追加しています。

+		else if(streq(fields.p[1], "P"))
+			aggr = "p";

これは、もし現在処理している構造体の名前が "P" であるならば、aggr変数に "p" を割り当てることを意味します。これにより、buildruntime.cP構造体のフィールドオフセットも他の重要なランタイム構造体(MG)と同様に処理し、アセンブリコードがこれらのオフセットを利用できるようにするための準備が整います。

この変更は、Goランタイムの低レベルな部分、特にアセンブリ言語で書かれたコードがGoの内部データ構造に効率的にアクセスできるようにするための、基盤的な改善と言えます。これにより、Goランタイムのデバッグ、プロファイリング、あるいは将来的な最適化の際に、P構造体の内部状態をより柔軟に操作できるようになります。

関連リンク

参考にした情報源リンク

  • GoのM, G, Pスケジューラに関するWeb検索結果
  • cmd/distおよびbuildruntime.cに関するWeb検索結果
  • Goアセンブリにおける構造体フィールドアクセスに関するWeb検索結果
  • Goのコミット履歴 (GitHub)
  • Goのコードベース (GitHub)