[インデックス 16821] ファイルの概要
このコミットは、Goランタイムのガベージコレクタ(GC)のパフォーマンス最適化を目的としています。具体的には、Goのメモリヒープを管理する mheap
構造体をGCの対象から除外することで、GCの不要なスキャン処理を削減し、GCサイクルを高速化します。mheap
は多くのスパン(メモリブロック)の先頭へのポインタを含んでいますが、それ自体がGCが追跡すべき「興味深い」ポインタ(ユーザーデータへのポインタ)を含んでいるわけではないため、GCスキャンから除外しても問題がないと判断されました。
コミット
commit 2a6520c2d367020951648379d9df7228f8d7151c
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Fri Jul 19 17:47:40 2013 +0400
runtime: hide mheap from GC
It contains pointers to first blocks of lots of spans.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/11416046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2a6520c2d367020951648379d9df7228f8d7151c
元コミット内容
--- a/src/pkg/runtime/malloc.goc
+++ b/src/pkg/runtime/malloc.goc
@@ -15,6 +15,8 @@ package runtime
#include "race.h"
#include "stack.h"
+// Mark mheap as 'no pointers', it does not contain interesting pointers but occupies ~45K.
+#pragma dataflag 16
MHeap runtime·mheap;
int32 runtime·checking;
変更の背景
Go言語のランタイムは、効率的なメモリ管理とガベージコレクション(GC)を非常に重視しています。このコミットが行われた2013年当時、GoのGCは主にマーク・アンド・スイープ方式を採用しており、プログラムが使用しているメモリ領域(ヒープ)を定期的にスキャンし、到達可能なオブジェクト(ライブオブジェクト)を特定し、到達不能なオブジェクト(デッドオブジェクト)が占めるメモリを解放していました。
mheap
はGoランタイムのメモリ管理において中心的な役割を果たすデータ構造であり、Goプログラムが動的に確保するメモリ(ヒープメモリ)の全体を管理しています。具体的には、メモリを小さなブロック(スパン)に分割し、それらのスパンの割り当てや解放、そして空きスパンの管理などを行います。mheap
自身は、これらのスパンの先頭ブロックへのポインタを多数保持しています。
しかし、mheap
が保持するポインタは、Goプログラムのユーザーが作成したオブジェクトへのポインタではなく、あくまでランタイム内部のメモリ管理のためのポインタです。GCの目的は、ユーザーが作成したライブオブジェクトを特定することであり、ランタイム内部の管理構造体自体は常にライブであるため、その内部を詳細にスキャンすることはGCにとって無駄なオーバーヘッドとなります。
このコミットの背景には、mheap
が約45KBという比較的小さなサイズでありながら、その内部に多数のポインタが含まれているため、GCが mheap
をスキャンする際に不要な処理時間が発生しているという問題意識がありました。mheap
をGCのポインタスキャン対象から除外することで、GCの効率を向上させ、全体のパフォーマンスを改善することが狙いです。
前提知識の解説
Goランタイムのメモリ管理と mheap
Goランタイムは、独自のメモリ管理システムを持っています。これは、オペレーティングシステムから大きなメモリブロックを確保し、それをGoプログラムのニーズに合わせて細かく分割・管理するものです。この管理の中心となるのが mheap
(Memory Heap) 構造体です。
mheap
は、Goのヒープメモリ全体を抽象化し、以下のような機能を提供します。
- スパンの管理: Goのメモリは「スパン (span)」と呼ばれる連続したメモリブロックの単位で管理されます。
mheap
はこれらのスパンの割り当て、解放、そして空きスパンのリストを管理します。 - アリーナ (Arena):
mheap
は、OSから取得したメモリをアリーナと呼ばれる大きなチャンクに分割し、そのアリーナからスパンを切り出して使用します。 - メモリ割り当て: プログラムが
make
やnew
などでメモリを要求すると、mheap
は適切なサイズのスパンを見つけて割り当てます。
ガベージコレクション (GC) とポインタスキャン
Goのガベージコレクタは、主に「マーク・アンド・スイープ」アルゴリズムに基づいています。
- マークフェーズ: GCは、プログラムのルート(グローバル変数、スタック上の変数など)から到達可能なすべてのオブジェクトを「ライブ」としてマークします。このプロセスでは、オブジェクトが持つポインタをたどって、さらに到達可能なオブジェクトをマークしていきます。
- スイープフェーズ: マークされなかったオブジェクト(デッドオブジェクト)が占めるメモリを解放し、再利用可能な状態にします。
このマークフェーズにおいて、GCはメモリ上のポインタを識別し、そのポインタが指す先をたどる必要があります。もし、あるデータ構造がGCが追跡すべきポインタを含んでいないにもかかわらず、GCがそのデータ構造をスキャンしようとすると、それは無駄な処理時間となります。
pragma dataflag 16
Goランタイムのコードは、C言語に似た goc
(Go C) という言語で書かれています。この言語には、Goコンパイラやランタイムに特定の指示を与えるための pragma
ディレクティブが存在します。
#pragma dataflag
は、特定のグローバル変数やデータセクションの特性をコンパイラに伝えるために使用されます。dataflag
の後に続く数値は、その特性を示すビットフラグです。
このコミットで追加された dataflag 16
は、Goランタイムの内部的な定義において、そのデータセクションが「ポインタを含まない」ことを示します。つまり、GCがこのセクションをスキャンしても、ユーザーが作成したオブジェクトへのポインタは見つからないため、スキャンをスキップしても安全であるという指示になります。これにより、GCは mheap
の内部構造を詳細に検査する手間を省くことができます。
技術的詳細
このコミットの技術的な核心は、Goランタイムのガベージコレクタが mheap
構造体をスキャンする際の挙動を変更することにあります。
GoのGCは、ヒープ上のオブジェクトをスキャンしてライブオブジェクトを特定する際に、そのオブジェクトがポインタを含んでいるかどうかを判断する必要があります。ポインタを含まないオブジェクトであれば、その内部を再帰的にスキャンする必要はありません。
mheap
は MHeap
型のグローバル変数として定義されています。この MHeap
構造体自体は、メモリ管理のための内部的なポインタ(例えば、空きスパンのリストへのポインタや、アリーナの管理構造体へのポインタなど)を多数含んでいます。しかし、これらのポインタは、GCがユーザープログラムのライブオブジェクトを追跡するために必要なポインタとは異なります。mheap
自体はランタイムの根幹をなす構造体であり、常にライブであるため、その内部のポインタをたどってユーザーオブジェクトの到達可能性を判断する必要はありません。
#pragma dataflag 16
ディレクティブを MHeap runtime·mheap;
の定義の直前に追加することで、コンパイラは runtime·mheap
変数が配置されるデータセクションに「ポインタを含まない」というフラグを付与します。このフラグは、GCがヒープをスキャンする際に参照され、runtime·mheap
の領域はポインタスキャンの対象から除外されるようになります。
これにより、GCは mheap
の約45KBのメモリ領域をスキャンする時間を節約できます。これは、GCサイクルごとに繰り返される処理であるため、特に大規模なGoアプリケーションや、GCの頻度が高いアプリケーションにおいて、GCの一時停止時間(ストップ・ザ・ワールド)の短縮や、全体的なスループットの向上に寄与します。
この最適化は、Goランタイムが内部的にどのようにメモリを管理し、GCがどのように動作するかを深く理解しているからこそ可能なものです。mheap
の内部構造がGCのポインタスキャンにとって「興味深いポインタ」を含まないという前提が重要であり、この前提が崩れると、GCがライブオブジェクトを見落とす可能性が出てきます。しかし、このコミットは、その前提が安全に満たされていることを確認した上で行われています。
コアとなるコードの変更箇所
変更は src/pkg/runtime/malloc.goc
ファイルの1箇所のみです。
--- a/src/pkg/runtime/malloc.goc
+++ b/src/pkg/runtime/malloc.goc
@@ -15,6 +15,8 @@ package runtime
#include "race.h"
#include "stack.h"
+// Mark mheap as 'no pointers', it does not contain interesting pointers but occupies ~45K.
+#pragma dataflag 16
MHeap runtime·mheap;
int32 runtime·checking;
コアとなるコードの解説
追加された2行は以下の通りです。
// Mark mheap as 'no pointers', it does not contain interesting pointers but occupies ~45K.
#pragma dataflag 16
-
// Mark mheap as 'no pointers', it does not contain interesting pointers but occupies ~45K.
これはコメントであり、この変更の意図を明確に説明しています。mheap
を「ポインタなし」としてマークすること、そしてそれが「興味深いポインタ」を含まず、約45KBのメモリを占有していることを示しています。ここでいう「興味深いポインタ」とは、GCがライブオブジェクトを追跡するために必要な、ユーザーデータへのポインタを指します。 -
#pragma dataflag 16
これが実際の機能的な変更です。MHeap runtime·mheap;
の定義の直前にこのpragma
ディレクティブを挿入することで、Goコンパイラに対してruntime·mheap
変数が配置されるデータセクションが、GCがスキャンすべきポインタを含まないことを指示しています。dataflag 16
は、Goランタイムの内部的な定義において、このデータ領域がGCのポインタスキャンから除外されるべきであることを示す特定のビットフラグです。これにより、GCはmheap
の内部をスキャンする手間を省き、GCの効率を向上させます。
この変更は、Goランタイムのメモリ管理とGCの内部動作に深く関わる低レベルな最適化であり、Goプログラムのユーザーからは直接見えない部分でのパフォーマンス改善に貢献します。
関連リンク
- Go CL 11416046: https://golang.org/cl/11416046
参考にした情報源リンク
- Goのガベージコレクションに関する情報 (2013年当時の状況を含む):
- Goのメモリ管理と
mheap
に関する一般的な情報:- https://go.dev/src/runtime/mheap.go (現在のGoのソースコードですが、概念は共通)
- Goの
pragma
ディレクティブに関する情報 (一般的な概念):- Goの内部的な
goc
言語の特性に関するドキュメントや議論
- Goの内部的な
- Web検索: "Go runtime mheap GC hide 2013"
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGiiSUyGKhqDpW6OA0dcgs05qvQdeaMbzDMtpET16zJbfJ-L_hcdYA1iwcctCPH1DAaY11oK3YYejv2r3DeUOVMnr2iPonkK39G0egSYAcxAoZGJ9Gozg==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEaDdwqRU49V0iA6Zvb_d3KwE058mraGzyszH0tGYMNT9rxPMSEmlwPfwwl_b4IoseuCabl0Mnkk7goibKUlDyDSUSwA9ah8edusnEDLZq9409lui7TEkHNiFZliepaV7Ct0G6pLQlvtVuVEiZQDo-kpxzIpwiBFJZw_hbbs2EMpK8YTjt-6Qc0
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFwwSLyf5zfkZOuD4HgfT35WiaZDymbDjfG9NlP9QSTMkcvSx8T40KNnj8SkZTJLdQohhwSP5r2HvUFzyRIU3cll3Z2HL8yVQYBEhIk7lTa_bjg4FQ8r7xjwAlncGSj-nJHpw_Z6D0kZ7jR7DX57UbOlFXE3aIHzeOedbqonHx8MPbhp5ZPLe2xOAuMfXoVY_HEgfIL1HYSCgrPXttz_GQw7nkmAYfE7cj7VtrBSKdOY9-_vHTMNog=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFY4wrtLgSDl61TQQaHGRUyAxmQUdBL2fFLLVjeFq3-95hClqaqWhTfFDY40nQgIIeD8obP-ZVqoDSlJ9YmeIST9KQnJxvlgJ21SyBktfKshrZ80dzB-CMyJnUnD_pTJaRABm0LCWpYEY7-Pzno04lbrGEqzPKiNY1MSH6kwqpEBrP6nmiNyTSA3r3ykZ0=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFAB8DcFzisqAjqh02IJIHmTNglJrDbhsXO7z0g5SP080ECQ2Teli608wL3LJuEGHl5keHDCPi6KzDC1vIKtfv236TiEIPvwS6Gr7sbMOIX9TLFfHIW5szbrr-D