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

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

このコミットは、Goコンパイラのガベージコレクション(GC)関連コードを生成する src/cmd/gc/pgen.c ファイルに対する変更です。具体的には、関数の引数、ローカル変数、および「デッド(到達不能)な」ポインタに関する情報(ポインタマップ)をバイナリに埋め込む処理の一部を扱っています。

コミット

cmd/gc: デッド値マップをまだ生成しない

このコミットは、Goコンパイラがデッド(到達不能)なポインタのマップ(FUNCDATA_DeadPointerMaps)を生成する処理を一時的に無効化するものです。これは、現在のガベージコレクタがこれらのデッド値をクリアしないため、そのデータをバイナリに含めることによるRSS(Resident Set Size)のコストが無駄であると判断されたためです。特に、ビルドマシンにおけるコンパイル時のメモリ使用量(RSS)への影響が考慮されています。

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

https://github.com/golang/go/commit/6965a752a799792997fc0cbf971b1893bcaf3d5b

元コミット内容

cmd/gc: do not generate dead value maps yet

We are not clearing dead values in the garbage collector so it
is not worth the RSS cost to materialize the data and write it
out to the binary.

R=golang-dev, iant, cshapiro
CC=golang-dev
https://golang.org/cl/38650043

変更の背景

この変更の背景には、Goランタイムのガベージコレクタの進化段階と、コンパイル時のリソース消費の最適化という二つの側面があります。

  1. ガベージコレクタの機能不足: コミットメッセージに明記されているように、当時のGoのガベージコレクタは「デッド値(dead values)」、すなわちプログラムから到達不可能になったがまだメモリ上に残っているオブジェクトを積極的にクリアする機能を持っていませんでした。デッド値マップは、これらの到達不能なポインタに関する情報を提供するためのものですが、GCがその情報を活用しないのであれば、生成しても意味がありません。
  2. RSS(Resident Set Size)のコスト: デッド値マップを生成し、それを最終的なバイナリに埋め込むことは、コンパイル時に追加のメモリ(RSS)を消費します。コミットメッセージでは「At present, the amount of additional RSS is substantial enough to affect our smallest build machines.」と述べられており、特にリソースが限られたビルドマシンにおいて、この無駄なメモリ消費が問題となっていたことが示唆されます。

したがって、このコミットは、ガベージコレクタの将来的な改善を見越した一時的な措置として、無駄なリソース消費を避けるためにデッド値マップの生成を停止したものです。

前提知識の解説

このコミットを理解するためには、以下の概念を把握しておく必要があります。

  • Goのガベージコレクタ (GC): Goは自動メモリ管理を採用しており、ガベージコレクタが不要になったメモリを自動的に解放します。GoのGCは、主にマーク&スイープ方式をベースに、並行処理や低レイテンシを実現するための様々な最適化が施されています。GCは、プログラムが実行中にどのメモリ領域がポインタを含んでいるか(ポインタマップ)を知る必要があります。
  • ポインタマップ (Pointer Maps): GoのGCは、スタックやヒープ上のオブジェクトがポインタを含んでいるかどうかを正確に知る必要があります。これにより、GCはポインタをたどって到達可能なオブジェクトを特定し、到達不能なオブジェクトを解放できます。この情報が「ポインタマップ」としてコンパイル時に生成され、バイナリに埋め込まれます。
    • FUNCDATA_ArgsPointerMaps: 関数の引数に含まれるポインタに関する情報。
    • FUNCDATA_LocalsPointerMaps: 関数のローカル変数に含まれるポインタに関する情報。
    • FUNCDATA_DeadPointerMaps: 関数内で、ある時点で到達不能になる(デッドになる)ポインタに関する情報。これは、GCがより積極的にメモリを解放するためのヒントとして使用される可能性があります。
  • RSS (Resident Set Size): プロセスが物理メモリ(RAM)上で現在使用しているメモリの量を示します。RSSが高いと、システム全体のメモリが圧迫され、パフォーマンスに影響を与える可能性があります。コンパイル時のRSSは、特にCI/CD環境やリソースが限られたビルドサーバにおいて重要な指標となります。
  • cmd/gc: Goの標準コンパイラです。Goのソースコードを機械語に変換する役割を担っており、この過程でガベージコレクタが必要とするメタデータ(ポインタマップなど)も生成します。
  • makefuncdatasym: src/cmd/gc/pgen.c 内で使用される関数で、特定の種類の関数データ(FUNCDATA_...)に対応するシンボルを作成します。これらのシンボルは、ランタイムがGCに必要な情報を参照するために使用されます。
  • TODO(cshapiro): コードベースでよく見られるコメント形式で、将来的に実装または修正が必要な項目を示します。この場合、cshapiro はこのタスクの担当者または提案者を示しています。

技術的詳細

このコミットの技術的詳細の中心は、FUNCDATA_DeadPointerMaps の扱いと、それがコンパイル時のメモリ使用量に与える影響です。

GoのGCは、正確なGC(precise GC)を目指しており、そのためにはメモリ上のどこにポインタが存在するかを正確に把握する必要があります。FUNCDATA_DeadPointerMaps は、特定のコードパスでポインタが「デッド」になる(つまり、そのポインタが指すメモリがもはや到達不可能になる)タイミングに関する情報を提供することを意図していました。理論的には、この情報があればGCはより早く、より効率的にメモリを解放できる可能性があります。

しかし、このコミットが作成された時点では、GoのGCはまだこの FUNCDATA_DeadPointerMaps の情報を活用してデッド値を積極的にクリアするロジックが実装されていませんでした。つまり、マップを生成してもGCの動作には影響がなく、単にバイナリサイズとコンパイル時のメモリ使用量を増やすだけでした。

pgen.c は、Goの関数をコンパイルする際に、その関数に関連するGCメタデータ(引数、ローカル変数、デッドポインタのマップ)を生成する役割を担っています。makefuncdatasym 関数は、これらのマップに対応するシンボルを作成し、最終的な実行可能ファイルに埋め込まれるようにします。

このコミットでは、gcdead = makefuncdatasym("gcdead·%d", FUNCDATA_DeadPointerMaps); という行がコメントアウトされ、代わりに if(0) ブロックが導入されています。if(0) は常に偽であるため、その中のコードは実行されません。これにより、gcdead シンボルは nil に設定され、FUNCDATA_DeadPointerMaps の生成とバイナリへの埋め込みが完全にスキップされます。

この変更は、GCの「pre-verification pass」(事前検証パス)が実装されるまでの暫定的な措置として行われました。事前検証パスは、GCが実際にデッド値マップを活用してメモリを解放するロジックの一部となる予定だったと考えられます。

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

--- a/src/cmd/gc/pgen.c
+++ b/src/cmd/gc/pgen.c
@@ -153,7 +153,15 @@ compile(Node *fn)
 
  	gcargs = makefuncdatasym("gcargs·%d", FUNCDATA_ArgsPointerMaps);
  	gclocals = makefuncdatasym("gclocals·%d", FUNCDATA_LocalsPointerMaps);
- 	gcdead = makefuncdatasym("gcdead·%d", FUNCDATA_DeadPointerMaps);
+ 	// TODO(cshapiro): emit the dead value map when the garbage collector
+ 	// pre-verification pass is checked in.  It is otherwise harmless to
+ 	// emit this information if it is not used but it does cost RSS at
+ 	// compile time.  At present, the amount of additional RSS is
+ 	// substantial enough to affect our smallest build machines.
+ 	if(0)
+ 		gcdead = makefuncdatasym("gcdead·%d", FUNCDATA_DeadPointerMaps);
+ 	else
+ 		gcdead = nil;
 
  	for(t=curfn->paramfld; t; t=t->down)
  		gtrack(tracksym(t->type));

コアとなるコードの解説

変更の中心は、gcdead 変数の初期化部分です。

変更前は以下のようになっていました。

gcdead = makefuncdatasym("gcdead·%d", FUNCDATA_DeadPointerMaps);

これは、gcdead というシンボルを FUNCDATA_DeadPointerMaps の種類で作成し、デッドポインタマップの情報をバイナリに含めることを意味していました。

変更後は、以下のようになっています。

// TODO(cshapiro): emit the dead value map when the garbage collector
// pre-verification pass is checked in.  It is otherwise harmless to
// emit this information if it is not used but it does cost RSS at
// compile time.  At present, the amount of additional RSS is
// substantial enough to affect our smallest build machines.
if(0)
	gcdead = makefuncdatasym("gcdead·%d", FUNCDATA_DeadPointerMaps);
else
	gcdead = nil;
  • TODO(cshapiro): ...: これは開発者向けのコメントで、将来的にガベージコレクタの「pre-verification pass」(事前検証パス)が実装された際には、このデッド値マップを再度生成する必要があることを示しています。また、現在この情報が使われていないにもかかわらず、コンパイル時のRSSコストが大きいことが強調されています。
  • if(0): C言語において if(0) は常に偽と評価されるため、このブロック内のコードは決して実行されません。これは、特定のコードパスを一時的に無効化する一般的な手法です。
  • gcdead = makefuncdatasym("gcdead·%d", FUNCDATA_DeadPointerMaps);: この行は if(0) ブロック内に移動されたため、実行されなくなりました。
  • else gcdead = nil;: if(0) が常に偽であるため、常に else ブロックが実行され、gcdeadnil(ヌルポインタ)に設定されます。これにより、デッドポインタマップに関する情報がバイナリに一切含まれなくなり、コンパイル時のRSSコストが削減されます。

この変更は、機能的にはデッドポインタマップの生成を停止するものであり、ガベージコレクタがその情報を活用できるようになるまでの暫定的な最適化として機能します。

関連リンク

参考にした情報源リンク

  • Goのガベージコレクションに関する公式ドキュメントやブログ記事 (一般的なGo GCの理解のため)
  • Goのソースコード(src/cmd/gc/pgen.c および関連ファイル) (変更箇所のコンテキスト理解のため)
  • RSS (Resident Set Size) の概念に関する一般的な情報
  • Goのポインタマップに関する技術記事や議論 (FUNCDATA_... の理解のため)

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

このコミットは、Goコンパイラのガベージコレクション(GC)関連コードを生成する src/cmd/gc/pgen.c ファイルに対する変更です。具体的には、関数の引数、ローカル変数、および「デッド(到達不能)な」ポインタに関する情報(ポインタマップ)をバイナリに埋め込む処理の一部を扱っています。

コミット

cmd/gc: デッド値マップをまだ生成しない

このコミットは、Goコンパイラがデッド(到達不能)なポインタのマップ(FUNCDATA_DeadPointerMaps)を生成する処理を一時的に無効化するものです。これは、現在のガベージコレクタがこれらのデッド値をクリアしないため、そのデータをバイナリに含めることによるRSS(Resident Set Size)のコストが無駄であると判断されたためです。特に、ビルドマシンにおけるコンパイル時のメモリ使用量(RSS)への影響が考慮されています。

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

https://github.com/golang/go/commit/6965a752a799792997fc0cbf971b1893bcaf3d5b

元コミット内容

cmd/gc: do not generate dead value maps yet

We are not clearing dead values in the garbage collector so it
is not worth the RSS cost to materialize the data and write it
out to the binary.

R=golang-dev, iant, cshapiro
CC=golang-dev
https://golang.org/cl/38650043

変更の背景

この変更の背景には、Goランタイムのガベージコレクタの進化段階と、コンパイル時のリソース消費の最適化という二つの側面があります。

  1. ガベージコレクタの機能不足: コミットメッセージに明記されているように、当時のGoのガベージコレクタは「デッド値(dead values)」、すなわちプログラムから到達不可能になったがまだメモリ上に残っているオブジェクトを積極的にクリアする機能を持っていませんでした。デッド値マップは、これらの到達不能なポインタに関する情報を提供するためのものですが、GCがその情報を活用しないのであれば、生成しても意味がありません。
  2. RSS(Resident Set Size)のコスト: デッド値マップを生成し、それを最終的なバイナリに埋め込むことは、コンパイル時に追加のメモリ(RSS)を消費します。コミットメッセージでは「At present, the amount of additional RSS is substantial enough to affect our smallest build machines.」と述べられており、特にリソースが限られたビルドマシンにおいて、この無駄なメモリ消費が問題となっていたことが示唆されます。

したがって、このコミットは、ガベージコレクタの将来的な改善を見越した一時的な措置として、無駄なリソース消費を避けるためにデッド値マップの生成を停止したものです。

前提知識の解説

このコミットを理解するためには、以下の概念を把握しておく必要があります。

  • Goのガベージコレクタ (GC): Goは自動メモリ管理を採用しており、ガベージコレクタが不要になったメモリを自動的に解放します。GoのGCは、主にマーク&スイープ方式をベースに、並行処理や低レイテンシを実現するための様々な最適化が施されています。GCは、プログラムが実行中にどのメモリ領域がポインタを含んでいるか(ポインタマップ)を知る必要があります。
  • ポインタマップ (Pointer Maps): GoのGCは、スタックやヒープ上のオブジェクトがポインタを含んでいるかどうかを正確に知る必要があります。これにより、GCはポインタをたどって到達可能なオブジェクトを特定し、到達不能なオブジェクトを解放できます。この情報が「ポインタマップ」としてコンパイル時に生成され、バイナリに埋め込まれます。
    • FUNCDATA_ArgsPointerMaps: 関数の引数に含まれるポインタに関する情報。
    • FUNCDATA_LocalsPointerMaps: 関数のローカル変数に含まれるポインタに関する情報。
    • FUNCDATA_DeadPointerMaps: 関数内で、ある時点で到達不能になる(デッドになる)ポインタに関する情報。これは、GCがより積極的にメモリを解放するためのヒントとして使用される可能性があります。
  • RSS (Resident Set Size): プロセスが物理メモリ(RAM)上で現在使用しているメモリの量を示します。RSSが高いと、システム全体のメモリが圧迫され、パフォーマンスに影響を与える可能性があります。コンパイル時のRSSは、特にCI/CD環境やリソースが限られたビルドサーバにおいて重要な指標となります。
  • cmd/gc: Goの標準コンパイラです。Goのソースコードを機械語に変換する役割を担っており、この過程でガベージコレクタが必要とするメタデータ(ポインタマップなど)も生成します。
  • makefuncdatasym: src/cmd/gc/pgen.c 内で使用される関数で、特定の種類の関数データ(FUNCDATA_...)に対応するシンボルを作成します。これらのシンボルは、ランタイムがGCに必要な情報を参照するために使用されます。
  • TODO(cshapiro): コードベースでよく見られるコメント形式で、将来的に実装または修正が必要な項目を示します。この場合、cshapiro はこのタスクの担当者または提案者を示しています。

技術的詳細

このコミットの技術的詳細の中心は、FUNCDATA_DeadPointerMaps の扱いと、それがコンパイル時のメモリ使用量に与える影響です。

GoのGCは、正確なGC(precise GC)を目指しており、そのためにはメモリ上のどこにポインタが存在するかを正確に把握する必要があります。FUNCDATA_DeadPointerMaps は、特定のコードパスでポインタが「デッド」になる(つまり、そのポインタが指すメモリがもはや到達不可能になる)タイミングに関する情報を提供することを意図していました。理論的には、この情報があればGCはより早く、より効率的にメモリを解放できる可能性があります。

しかし、このコミットが作成された時点では、GoのGCはまだこの FUNCDATA_DeadPointerMaps の情報を活用してデッド値を積極的にクリアするロジックが実装されていませんでした。つまり、マップを生成してもGCの動作には影響がなく、単にバイナリサイズとコンパイル時のメモリ使用量を増やすだけでした。

pgen.c は、Goの関数をコンパイルする際に、その関数に関連するGCメタデータ(引数、ローカル変数、デッドポインタのマップ)を生成する役割を担っています。makefuncdatasym 関数は、これらのマップに対応するシンボルを作成し、最終的な実行可能ファイルに埋め込まれるようにします。

このコミットでは、gcdead = makefuncdatasym("gcdead·%d", FUNCDATA_DeadPointerMaps); という行がコメントアウトされ、代わりに if(0) ブロックが導入されています。if(0) は常に偽であるため、その中のコードは実行されません。これにより、gcdead シンボルは nil に設定され、FUNCDATA_DeadPointerMaps の生成とバイナリへの埋め込みが完全にスキップされます。

この変更は、GCの「pre-verification pass」(事前検証パス)が実装されるまでの暫定的な措置として行われました。事前検証パスは、GCが実際にデッド値マップを活用してメモリを解放するロジックの一部となる予定だったと考えられます。

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

--- a/src/cmd/gc/pgen.c
+++ b/src/cmd/gc/pgen.c
@@ -153,7 +153,15 @@ compile(Node *fn)
 
  	gcargs = makefuncdatasym("gcargs·%d", FUNCDATA_ArgsPointerMaps);
  	gclocals = makefuncdatasym("gclocals·%d", FUNCDATA_LocalsPointerMaps);
- 	gcdead = makefuncdatasym("gcdead·%d", FUNCDATA_DeadPointerMaps);
+ 	// TODO(cshapiro): emit the dead value map when the garbage collector
+ 	// pre-verification pass is checked in.  It is otherwise harmless to
+ 	// emit this information if it is not used but it does cost RSS at
+ 	// compile time.  At present, the amount of additional RSS is
+ 	// substantial enough to affect our smallest build machines.
+ 	if(0)
+ 		gcdead = makefuncdatasym("gcdead·%d", FUNCDATA_DeadPointerMaps);
+ 	else
+ 		gcdead = nil;
 
  	for(t=curfn->paramfld; t; t=t->down)
  		gtrack(tracksym(t->type));

コアとなるコードの解説

変更の中心は、gcdead 変数の初期化部分です。

変更前は以下のようになっていました。

gcdead = makefuncdatasym("gcdead·%d", FUNCDATA_DeadPointerMaps);

これは、gcdead というシンボルを FUNCDATA_DeadPointerMaps の種類で作成し、デッドポインタマップの情報をバイナリに含めることを意味していました。

変更後は、以下のようになっています。

// TODO(cshapiro): emit the dead value map when the garbage collector
// pre-verification pass is checked in.  It is otherwise harmless to
// emit this information if it is not used but it does cost RSS at
// compile time.  At present, the amount of additional RSS is
// substantial enough to affect our smallest build machines.
if(0)
	gcdead = makefuncdatasym("gcdead·%d", FUNCDATA_DeadPointerMaps);
else
	gcdead = nil;
  • TODO(cshapiro): ...: これは開発者向けのコメントで、将来的にガベージコレクタの「pre-verification pass」(事前検証パス)が実装された際には、このデッド値マップを再度生成する必要があることを示しています。また、現在この情報が使われていないにもかかわらず、コンパイル時のRSSコストが大きいことが強調されています。
  • if(0): C言語において if(0) は常に偽と評価されるため、このブロック内のコードは決して実行されません。これは、特定のコードパスを一時的に無効化する一般的な手法です。
  • gcdead = makefuncdatasym("gcdead·%d", FUNCDATA_DeadPointerMaps);: この行は if(0) ブロック内に移動されたため、実行されなくなりました。
  • else gcdead = nil;: if(0) が常に偽であるため、常に else ブロックが実行され、gcdeadnil(ヌルポインタ)に設定されます。これにより、デッドポインタマップに関する情報がバイナリに一切含まれなくなり、コンパイル時のRSSコストが削減されます。

この変更は、機能的にはデッドポインタマップの生成を停止するものであり、ガベージコレクタがその情報を活用できるようになるまでの暫定的な最適化として機能します。

関連リンク

参考にした情報源リンク

  • Goのガベージコレクションに関する公式ドキュメントやブログ記事 (一般的なGo GCの理解のため)
  • Goのソースコード(src/cmd/gc/pgen.c および関連ファイル) (変更箇所のコンテキスト理解のため)
  • RSS (Resident Set Size) の概念に関する一般的な情報
  • Goのポインタマップに関する技術記事や議論 (FUNCDATA_... の理解のため)