[インデックス 17665] ファイルの概要
このコミットは、Go 1.2のリリースノートである doc/go1.2.html
ファイルに対する更新です。具体的には、Goランタイムにおけるゴルーチンのプリエンプション(横取り)と、cgo
を用いたC++コードのサポートに関する記述を、箇条書きからより詳細な説明に加筆修正しています。
コミット
commit e4c1fffcb12df2eaca4dc5edd62409901e41b27a
Author: Rob Pike <r@golang.org>
Date: Sat Sep 21 17:53:44 2013 +1000
doc/go1.2.html: pre-emption; cgo and C++
These were bullet points that I had neglected to flesh out.
R=golang-dev, dvyukov
CC=golang-dev
https://golang.org/cl/13816043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e4c1fffcb12df2eaca4dc5edd62409901e41b27a
元コミット内容
このコミットは、Go 1.2のリリースノート (doc/go1.2.html
) の記述を改善するものです。元々は箇条書きで簡潔に触れられていた「ランタイムのプリエンプション」と「cgoとC++」の項目について、より詳細な説明を追加し、読者がこれらの重要な変更点を深く理解できるようにしています。
変更の背景
Go 1.2のリリースにあたり、その新機能や改善点をユーザーに正確かつ詳細に伝える必要がありました。特に、Goランタイムのスケジューラにおけるプリエンプションの導入と、cgo
を介したC++コードとの連携機能は、Go言語の並行処理モデルと外部言語連携の柔軟性において重要な進歩でした。
コミットメッセージにある「These were bullet points that I had neglected to flesh out.(これらは私が肉付けを怠っていた箇条書きだった)」という記述から、これらの項目がリリースノートの初期ドラフトでは簡潔すぎたため、Rob Pike氏がその詳細を補完する必要性を感じたことが伺えます。これにより、Go 1.2のドキュメントの品質と情報量が向上し、ユーザーが新機能をより深く理解できるようになりました。
前提知識の解説
Goのゴルーチンとスケジューラ
Go言語は、軽量な並行処理の単位として「ゴルーチン(goroutine)」を提供します。ゴルーチンはOSのスレッドよりもはるかに軽量であり、数百万個のゴルーチンを同時に実行することも可能です。Goランタイムには、これらのゴルーチンをOSスレッドにマッピングし、実行を管理する「スケジューラ」が組み込まれています。
協調的スケジューリングとプリエンプション
Go 1.2以前のGoランタイムのスケジューラは、基本的に「協調的(cooperative)」でした。これは、ゴルーチンが自発的に実行を中断し、他のゴルーチンにCPUを譲る(例えば、I/O操作の待機時や runtime.Gosched()
の呼び出し時など)ことを前提としていました。このモデルの大きな問題点は、もしあるゴルーチンが無限ループに陥ったり、非常に長い計算を実行したりした場合、そのゴルーチンがCPUを独占し続け、同じOSスレッド上で実行されている他のゴルーチンが全く実行機会を得られなくなる(「スターベーション」と呼ばれる状態)可能性があったことです。特に GOMAXPROCS
が1に設定されている環境では、この問題が顕著でした。
cgo
cgo
は、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGoのツールです。これにより、既存のCライブラリをGoプロジェクトで再利用したり、パフォーマンスが重要な部分をCで記述したりすることが可能になります。cgo
は、GoとCの間のデータ型変換や関数呼び出しの橋渡しを行います。
C++のリンケージと名前修飾(Name Mangling)
C++は、関数オーバーロードや名前空間など、C言語にはない多くの機能を持っています。これらの機能をサポートするため、C++コンパイラは関数名や変数名を内部的に一意な形式に変換します。これを「名前修飾(Name Mangling)」と呼びます。例えば、void func(int)
と void func(double)
という2つの関数があった場合、C++コンパイラはこれらを異なる内部名に変換します。
C言語には名前修飾の概念がないため、C++の関数をC言語から(または cgo
を介してGoから)直接呼び出す場合、名前修飾によって生成された内部名を知る必要があります。これを避けるために、C++コードでは extern "C"
リンケージ指定子を使用することが一般的です。extern "C"
で宣言された関数や変数は、C言語のリンケージ規則に従って名前が修飾されないため、C言語から直接呼び出すことができます。
技術的詳細
Go 1.2におけるランタイムのプリエンプション
Go 1.2では、前述のスターベーション問題を部分的に解決するために、スケジューラに「関数呼び出し時のプリエンプション」が導入されました。これは「コンパイラによるプリエンプション」とも呼ばれます。
- 動作原理: Go 1.2では、コンパイラが関数のエントリポイント(関数の開始部分)に、スケジューラを呼び出すためのチェックを挿入するようになりました。
- 効果: これにより、無限ループであっても、そのループ内に(インライン化されていない)関数呼び出しが含まれていれば、その関数が呼び出されるたびにスケジューラが介入し、他のゴルーチンに実行機会を与えることができるようになりました。
- 限界: このプリエンプションはまだ「協調的」な側面が残っていました。なぜなら、関数呼び出しを含まない純粋な計算ループ(例:
for { i++ }
のようなタイトなループ)は、依然としてプリエンプションされず、CPUを独占し続ける可能性があったからです。完全な非協調的(非同期)プリエンプションは、Go 1.14で導入されることになります。しかし、Go 1.2でのこの変更は、Goの並行処理モデルの堅牢性を高めるための重要な一歩でした。
Go 1.2におけるcgoとC++のサポート
Go 1.2では、cgo
コマンドがC++コードのビルドをサポートするように拡張されました。
- C++コンパイラの呼び出し: 以前のバージョンではCコードのコンパイルが主でしたが、Go 1.2からは、リンク対象のライブラリにC++で書かれた部分が含まれている場合、
cgo
がC++コンパイラ(通常はg++
)を呼び出してそれらをビルドできるようになりました。 - ファイル拡張子のサポート: この機能拡張は、Go CL 8248043によって実現されました。この変更により、Goパッケージ内で
.cc
,.cpp
,.cxx
といったC++のソースファイルを含めることが可能になり、cgo
はこれらを適切に処理するようになりました。また、C++のヘッダファイル(.hh
,.hpp
,.hxx
)もHFiles
として認識されるようになりました。 - コンパイラフラグのサポート:
cgo
ディレクティブ内でCPPFLAGS
(CとC++の両方に適用)とCXXFLAGS
(C++のみに適用)がサポートされるようになりました。これにより、C++コンパイル時に必要な追加のフラグを指定できるようになりました。 pkg-config
との連携:pkg-config
cgoディレクティブも修正され、pkg-config --cflags
が返すフラグがCFLAGS
ではなくCPPFLAGS
に適用されるようになりました。これにより、CとC++の両方のファイルがpkg-config
から提供されるコンパイラフラグの恩恵を受けられるようになりました。- C++機能へのアクセス: このサポートは、GoからC++ライブラリをリンクし、C言語のインターフェースを介してその機能を利用することを容易にしました。ただし、C++のクラス、テンプレート、オーバーロードされた関数といった高度な機能に直接Goからアクセスするには、C++の名前修飾の問題を回避するために
extern "C"
ブロックを使用したり、SWIGのようなツールを併用したりする必要がある場合が多いです。
これらの改善により、Goプログラムが既存のC++コードベースと連携する際の柔軟性と利便性が大幅に向上しました。
コアとなるコードの変更箇所
このコミットは、Goのソースコード自体ではなく、Go 1.2のリリースノートのドキュメントファイル doc/go1.2.html
を変更しています。
--- a/doc/go1.2.html
+++ b/doc/go1.2.html
@@ -125,15 +125,25 @@ This is a backwards-compatible change that affects no existing programs.
<h2 id="impl">Changes to the implementations and tools</h2>
-<ul>
-<li>
-runtime: preemption of goroutines at function entry (CL 12371043).
-</li>
+<h3 id="preemption">Pre-emption in the scheduler</h3>
-<li>
-go/build: support including C++ code with cgo (CL 8248043).
-</li>
-</ul>
+<p>
+In prior releases, a goroutine that was looping forever could starve out other
+goroutines on the same thread, a serious problem when GOMAXPROCS
+provided only one user thread.
+In Go1.2, this is partially addressed: The scheduler is invoked occasionally
+upon entry to a function.
+This means that any loop that includes a (non-inlined) function call can
+be pre-empted, allowing other goroutines to run on the same thread.
+</p>
+
+<h3 id="cgo_and_cpp">Cgo and C++</h3>
+
+<p>
+The <a href="/cmd/cgo/"><code>cgo</code></a> command will now invoke the C++
+compiler to build any pieces of the linked-to library that are written in C++; the
+documentation has more detail.
+</p>
<h3 id="go_tools_godoc">Godoc and vet moved to the go.tools subrepository</h3>
コアとなるコードの解説
変更はHTMLドキュメントの構造と内容に集中しています。
- 箇条書きから詳細な段落へ:
- 元々
<ul>
と<li>
タグで囲まれていた簡潔な箇条書きが削除されています。 - 代わりに、
<h3>
タグで新しいサブセクションの見出しが追加され、その下に<p>
タグで囲まれた詳細な説明文が記述されています。
- 元々
- 「Pre-emption in the scheduler」セクション:
id="preemption"
を持つ<h3>
タグが追加されています。- Go 1.2以前のゴルーチンのスターベーション問題(特に
GOMAXPROCS=1
の場合)について説明しています。 - Go 1.2で導入された「関数のエントリポイントでのスケジューラの呼び出し」による部分的な解決策について述べています。
- これにより、非インライン関数呼び出しを含むループがプリエンプション可能になり、他のゴルーチンが同じスレッドで実行できるようになることを説明しています。
- 元の箇条書きにあった
CL 12371043
という記述は、このコミットでは削除されています。これは、このCL番号が一般に公開されているGoの変更リストとは異なるか、あるいはドキュメントの文脈で不要と判断されたためと考えられます。
- 「Cgo and C++」セクション:
id="cgo_and_cpp"
を持つ<h3>
タグが追加されています。cgo
コマンドがC++で書かれたライブラリの一部をビルドするためにC++コンパイラを呼び出すようになったことを説明しています。cgo
のドキュメントに詳細が記載されていることを示唆するリンク (<a href="/cmd/cgo/"><code>cgo</code></a>
) が含まれています。- 元の箇条書きにあった
CL 8248043
という記述は、このコミットでは削除されています。このCLは実際にcgoのC++サポートを導入した重要な変更ですが、リリースノートの文脈では具体的なCL番号よりも機能の説明が優先されたと考えられます。
これらの変更は、Go 1.2の重要な新機能に関する公式ドキュメントの質を向上させ、ユーザーがこれらの機能の背景、動作、および影響をより深く理解できるようにすることを目的としています。
関連リンク
- Go 1.2 Release Notes (公式ドキュメント): このコミットが更新しているドキュメントそのものです。Go 1.2のリリース時に公開された最終版を参照することで、この変更が全体の中でどのように位置づけられているかを確認できます。
- Goのスケジューリングに関する詳細な議論(Go 1.14での非同期プリエンプション導入など): Go 1.2のプリエンプションがどのように進化していったかを理解する上で役立ちます。
cgo
の公式ドキュメント:cgo
の使用方法とC/C++との連携に関する詳細な情報が提供されています。
参考にした情報源リンク
- Go 1.2 Release Notes (go.dev): https://go.dev/doc/go1.2
- Go 1.2 Preemption (unskilled.blog): https://unskilled.blog/go-1-2-preemption/
- Go's asynchronous preemption (go.dev blog): https://go.dev/blog/go1.14-preemption
- Go CL 8248043 (appspot.com): https://go-review.googlesource.com/c/go/+/8248043 (これは
golang.org/cl/
の古い形式のURLですが、内容が関連しています) - Stack Overflow: Go cgo and C++: https://stackoverflow.com/questions/tagged/go+cgo+c%2B%2B
- Medium: Go and C++ Interoperability: https://medium.com/@juliensalinas/go-and-c-interoperability-a-practical-guide-2023-09-20