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

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

このコミットは、Goランタイムのプロファイリングメカニズムに関する修正です。具体的には、Windows環境におけるプロファイリング処理において、g0スタック(Goランタイムのスケジューラが使用する特別なゴルーチンスタック)上であってもプロファイルを収集できるように変更を加えています。これにより、ランタイム内部の動作を含め、より包括的なプロファイリングデータが得られるようになります。

コミット

  • コミットハッシュ: 4207897dcc27a6badb177df115455153f8d4e843
  • Author: Alex Brainman alex.brainman@gmail.com
  • Date: Fri Oct 4 13:53:34 2013 +1000

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

https://github.com/golang/go/commit/4207897dcc27a6badb177df115455153f8d4e843

元コミット内容

runtime: collect profiles even while on g0 stack

Fixes #6417

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

変更の背景

この変更は、Goランタイムのプロファイリング機能が、特定の状況下(特にg0スタック上で実行されている場合)で適切に機能しないという問題(Issue #6417)を修正するために行われました。

Goのプロファイラは、プログラムの実行中にCPUがどこで時間を費やしているかを特定するのに役立ちます。しかし、以前の実装では、プロファイリングの対象となるゴルーチンがg0(スケジューラが使用する特別なゴルーチン)である場合や、システムコール(Gsyscallステータス)を実行している場合には、プロファイル収集が行われませんでした。これは、ランタイム自体の動作やシステムコール中のパフォーマンス特性を正確に把握する上で障害となっていました。

このコミットの目的は、g0スタック上であってもプロファイル情報を収集できるようにすることで、プロファイリングの網羅性を高め、より詳細なパフォーマンス分析を可能にすることです。

前提知識の解説

このコミットを理解するためには、以下のGoランタイムの概念とプロファイリングに関する基本的な知識が必要です。

  • Goランタイム (Go Runtime): Goプログラムの実行を管理するシステムです。これには、ガベージコレクション、ゴルーチン管理、スケジューリング、メモリ割り当てなどが含まれます。Goプログラムは、オペレーティングシステム上で直接実行されるのではなく、Goランタイムによって抽象化された環境で実行されます。
  • ゴルーチン (Goroutine): Goにおける軽量な実行スレッドです。OSのスレッドよりもはるかに軽量で、数千から数百万のゴルーチンを同時に実行できます。Goランタイムがゴルーチンのスケジューリングと管理を行います。
  • g0スタック (g0 stack): Goランタイムには、通常のユーザーゴルーチンとは別に、特別なゴルーチンであるg0が存在します。g0は、Goスケジューラやガベージコレクタなどのランタイム内部の処理を実行するためのスタックとして機能します。ユーザーゴルーチンがシステムコールを実行する際など、OSとのやり取りが必要な場合にも、g0スタックに切り替わって処理が行われることがあります。g0は、Goランタイムの心臓部とも言える存在です。
  • プロファイリング (Profiling): ソフトウェアのパフォーマンス特性を分析する手法です。CPUプロファイリングは、プログラムがCPU時間をどこで消費しているかを特定し、ボトルネックを特定するのに役立ちます。Goのプロファイラは、定期的に実行中のゴルーチンのスタックトレースをサンプリングし、どの関数がCPU時間を多く使っているかを統計的に分析します。
  • CONTEXT_CONTROL (Windows API): Windowsオペレーティングシステムにおいて、スレッドのコンテキスト(レジスタの状態など)を操作するためのフラグです。プロファイリングにおいては、実行中のスレッドのCPUレジスタの状態を取得するために使用されます。
  • TLS (Thread Local Storage): スレッドごとに独立したデータを格納するためのメカニズムです。Goランタイムでは、現在のOSスレッドがどのゴルーチンを実行しているかなどの情報をTLSに格納し、高速にアクセスできるようにしています。runtime·tls0は、Goランタイムが使用するTLSのオフセットまたはインデックスを指します。

技術的詳細

このコミットの技術的な核心は、Goランタイムのプロファイリングロジックにおける条件式の変更にあります。

Goランタイムは、定期的に各OSスレッド(M: Machine)のプロファイルを収集しようとします。この処理はprofilem関数で行われます。profilem関数は、現在のOSスレッドが実行しているゴルーチン(gp)を特定し、そのゴルーチンの状態に基づいてプロファイルを収集するかどうかを判断します。

変更前のコードでは、プロファイルを収集するための条件として、以下の3つの条件がすべて真である必要がありました。

  1. gp != nil: 現在のゴルーチンが存在すること。
  2. gp != mp->g0: 現在のゴルーチンがg0(スケジューラゴルーチン)ではないこと。
  3. gp->status != Gsyscall: 現在のゴルーチンがシステムコール中ではないこと。

この条件により、g0スタック上で実行されている処理や、システムコール中のゴルーチンのプロファイルは収集されませんでした。これは、ランタイム内部の動作やシステムコールがパフォーマンスに与える影響を分析する上で、重要な情報が欠落することを意味していました。

このコミットでは、この条件式を以下のように簡略化しました。

if(gp != nil)

これにより、g0スタック上での実行や、システムコール中のゴルーチンであっても、gpnilでない限りプロファイル収集の対象となります。

この変更は、特にGoランタイム自体のパフォーマンス特性を分析する際に重要です。g0はGoスケジューラやガベージコレクタなどの重要なランタイム処理を実行するため、そのスタック上でのCPU使用率をプロファイリングできることは、ランタイムのボトルネックを特定し、最適化を行う上で不可欠です。また、システムコール中のプロファイリングも、I/O処理などのパフォーマンスを理解するのに役立ちます。

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

変更は src/pkg/runtime/os_windows.c ファイルの profilem 関数内で行われました。

--- a/src/pkg/runtime/os_windows.c
+++ b/src/pkg/runtime/os_windows.c
@@ -402,7 +402,7 @@ profilem(M *mp)
 		tls = runtime·tls0;
 		gp = *(G**)tls;

-	if(gp != nil && gp != mp->g0 && gp->status != Gsyscall) {
+	if(gp != nil) {
 		// align Context to 16 bytes
 		r = (Context*)((uintptr)(&rbuf[15]) & ~15);
 		r->ContextFlags = CONTEXT_CONTROL;

コアとなるコードの解説

profilem 関数は、Goランタイムが各OSスレッド(Mmpで表現される)のプロファイルを収集するために呼び出される関数です。

  1. tls = runtime·tls0;: スレッドローカルストレージ(TLS)のオフセットまたはインデックスを取得します。GoランタイムはTLSを使用して、現在のOSスレッドが実行しているゴルーチンに関する情報を格納しています。
  2. gp = *(G**)tls;: TLSから現在のOSスレッドが実行しているゴルーチン(G構造体へのポインタ)を取得し、gp変数に格納します。gpは "goroutine pointer" の略です。
  3. 変更された条件式:
    • 変更前: if(gp != nil && gp != mp->g0 && gp->status != Gsyscall)
      • gp != nil: 現在のゴルーチンが存在することを確認します。
      • gp != mp->g0: 現在のゴルーチンが、このOSスレッド(mp)に紐づくg0ゴルーチンではないことを確認します。mp->g0はそのOSスレッドのg0スタックを指します。
      • gp->status != Gsyscall: 現在のゴルーチンのステータスがGsyscall(システムコール中)ではないことを確認します。 この条件は、g0スタック上での実行やシステムコール中のゴルーチンをプロファイリングの対象から除外していました。
    • 変更後: if(gp != nil)
      • 単にgpnilでないこと、つまり有効なゴルーチンが存在することだけを確認します。 この変更により、g0スタック上での実行やシステムコール中のゴルーチンであっても、プロファイル収集の対象に含まれるようになりました。

この条件式の変更が、このコミットの最も重要な部分です。これにより、プロファイリングの対象範囲が広がり、Goランタイムのより深い部分のパフォーマンス特性を分析できるようになりました。

関連リンク

  • Go issue #6417: (コミットメッセージに記載されているが、詳細な内容はウェブ検索では見つからなかった)
  • Go CL 14231047: (コミットメッセージに記載されているが、詳細な内容はウェブ検索では見つからなかった)

参考にした情報源リンク

  • Go言語の公式ドキュメント (Go Runtime, Goroutines, Profilingに関する一般的な情報)
  • Go言語のソースコード (runtimeパッケージのos_windows.cおよび関連ファイル)
  • Windows APIドキュメント (CONTEXT_CONTROLに関する情報)