[インデックス 16884] ファイルの概要
このコミットは、Goランタイムにおけるメモリ割り当て関数 runtime·mallocgc
のリファクタリングに関するものです。主な目的は、mallocgc
関数が型情報を受け取るように変更し、複数のフラグを統合することで、コードの可読性と保守性を向上させ、将来的にはガベージコレクション(GC)関連のパフォーマンス改善に繋げることです。特に、settype
関数の呼び出し箇所を削減し、その機能を markallocated
と統合する準備を進めています。
コミット
commit f8a850b250655bd26f5da4cfe7299b4a32be28fa
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Fri Jul 26 21:17:24 2013 +0400
runtime: refactor mallocgc
Make it accept type, combine flags.
Several reasons for the change:
1. mallocgc and settype must be atomic wrt GC
2. settype is called from only one place now
3. it will help performance (eventually settype
functionality must be combined with markallocated)
4. flags are easier to read now (no mallocgc(sz, 0, 1, 0) anymore)
R=golang-dev, iant, nightlyone, rsc, dave, khr, bradfitz, r
CC=golang-dev
https://golang.org/cl/10136043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f8a850b250655bd26f5da4cfe7299b4a32be28fa
元コミット内容
Goランタイムの mallocgc
関数をリファクタリングし、型情報を受け入れるように変更し、フラグを統合する。
変更の理由は以下の通り:
mallocgc
とsettype
はGCに対してアトミックである必要がある。settype
は現在、1箇所からのみ呼び出される。- パフォーマンス向上に役立つ(最終的に
settype
の機能はmarkallocated
と統合される必要がある)。 - フラグが読みやすくなった(
mallocgc(sz, 0, 1, 0)
のような記述がなくなる)。
変更の背景
このコミットは、Goランタイムのメモリ管理とガベージコレクション(GC)の効率化と改善を目的としています。特に、mallocgc
(メモリ割り当てとGCの連携を担う関数)と settype
(割り当てられたオブジェクトの型情報を設定する関数)の間の相互作用に焦点を当てています。
当時のGoランタイムでは、メモリ割り当てと型情報の関連付けがGCの観点からアトミックに処理されていない可能性がありました。これは、GCがオブジェクトをスキャンする際に、まだ型情報が完全に設定されていないオブジェクトに遭遇するリスクを意味し、GCの正確性や効率に影響を与える可能性がありました。
また、settype
関数が複数の場所から呼び出されており、その管理が複雑になっていました。これを一元化することで、コードの保守性を高め、将来的な最適化(特に settype
の機能を markallocated
と統合すること)の道を開くことが意図されています。markallocated
は、GCがオブジェクトをマークする際に使用される機能であり、型情報の設定とマーク処理を統合することで、GCのオーバーヘッドを削減できる可能性があります。
さらに、mallocgc
の引数として渡されるフラグが、mallocgc(sz, 0, 1, 0)
のように数値で表現されており、その意味が直感的ではありませんでした。これをより読みやすい名前付きフラグに置き換えることで、コードの可読性を向上させることも目的の一つです。
前提知識の解説
このコミットを理解するためには、Goランタイムのメモリ管理とガベージコレクションに関する基本的な知識が必要です。
- Goランタイム: Goプログラムの実行を管理する低レベルのシステム。スケジューラ、メモリ管理、ガベージコレクタなどが含まれます。
- ガベージコレクション (GC): プログラムが不要になったメモリを自動的に解放するプロセス。GoのGCは、主にマーク&スイープ方式を採用しています。
- マークフェーズ: GCが到達可能なオブジェクト(まだ使用されている可能性のあるオブジェクト)を識別し、マークします。
- スイープフェーズ: マークされていないオブジェクト(不要になったオブジェクト)が占めるメモリを解放します。
mallocgc
: Goランタイムにおける主要なメモリ割り当て関数。Goのヒープからメモリを割り当て、GCと連携して動作します。割り当てられたメモリはGCの対象となります。settype
: 割り当てられたメモリブロックに、そのメモリが保持するGoのオブジェクトの型情報を関連付ける関数。GCは、この型情報を使用して、オブジェクト内のポインタを識別し、到達可能なオブジェクトを正確にマークします。- アトミック操作: 複数の操作が不可分な単一の操作として実行されること。特に並行処理環境において、データの一貫性を保つために重要です。GCの文脈では、メモリ割り当てと型情報の関連付けがGCのスキャン中に中断されないようにすることが求められます。
- フラグ: 関数の動作を制御するためのビットマスクや列挙型。このコミットでは、
mallocgc
に渡される数値フラグを、より意味のある名前付きフラグに置き換えています。例えば、FlagNoPointers
は、割り当てられたメモリブロックにポインタが含まれていないことをGCに伝えるフラグです。 markallocated
: GCのマークフェーズで、割り当てられたオブジェクトをマークする際に使用される内部関数。
技術的詳細
このコミットの主要な変更点は、runtime·mallocgc
関数のシグネチャ変更と、それに伴う呼び出し箇所の修正です。
変更前:
void* runtime·mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed)
変更後:
void* runtime·mallocgc(uintptr size, uintptr typ, uint32 flag)
typ
引数の導入: 変更後のmallocgc
は、uintptr typ
という新しい引数を受け取ります。これは、割り当てられるオブジェクトの型情報(Type
構造体へのポインタ、またはTypeInfo_Chan
などの特殊な型情報)を直接mallocgc
に渡すことを可能にします。これにより、メモリ割り当てと同時に型情報を関連付けることができ、GCに対するアトミック性が向上します。- フラグの統合と拡張: 以前は
flag
,dogc
,zeroed
と分かれていた引数が、uint32 flag
に統合されました。dogc
(do GC) の機能は、新しいFlagNoInvokeGC
フラグによって制御されます。このフラグが設定されていない場合、mallocgc
は必要に応じてGCをトリガーします。zeroed
(zero memory) の機能は、新しいFlagNoZero
フラグによって制御されます。このフラグが設定されていない場合、割り当てられたメモリはゼロクリアされます。- 既存の
FlagNoPointers
,FlagNoProfiling
,FlagNoGC
に加えて、FlagNoZero
とFlagNoInvokeGC
が追加されました。これにより、mallocgc
の呼び出し元が、メモリのゼロクリアやGCのトリガーをより細かく制御できるようになります。
runtime·settype
の呼び出し削減:mallocgc
が型情報を受け取るようになったため、多くの場所で明示的にruntime·settype
を呼び出す必要がなくなりました。コミットメッセージにあるように、settype
は「現在、1箇所からのみ呼び出される」ようになり、その呼び出しはmallocgc
の内部、またはmallocgc
が型情報を受け取らない特殊なケースに限定されます。これにより、mallocgc
とsettype
の間のアトミック性の問題が解消され、コードの複雑性も軽減されます。- パフォーマンスへの影響:
settype
の機能をmarkallocated
と統合するという将来の計画は、GCの効率を向上させる可能性を秘めています。型情報の設定とオブジェクトのマークを単一の操作にまとめることで、GCのオーバーヘッドを削減し、全体的なパフォーマンスを改善できると考えられます。
コアとなるコードの変更箇所
このコミットでは、主に以下のファイルが変更されています。
src/pkg/runtime/chan.c
:runtime·makechan_c
関数内でruntime·mal
とruntime·settype
の呼び出しが、新しいruntime·mallocgc
の呼び出しに置き換えられています。- c = (Hchan*)runtime·mal(sizeof(*c) + hint*elem->size);
- runtime·settype(c, (uintptr)t | TypeInfo_Chan);
+ c = (Hchan*)runtime·mallocgc(sizeof(*c) + hint*elem->size, (uintptr)t | TypeInfo_Chan, 0);
src/pkg/runtime/hashmap.c
:hash_init
,evacuate
,hash_grow
,hash_insert
関数内でruntime·mallocgc
の呼び出しが、新しいフラグFlagNoZero
を使用するように変更されています。- buckets = runtime·mallocgc(bucketsize << B, 0, 1, 0);
+ buckets = runtime·mallocgc(bucketsize << B, 0, FlagNoZero);
src/pkg/runtime/malloc.goc
:runtime·mallocgc
関数のシグネチャが変更されています。- runtime·mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed)
+ runtime·mallocgc(uintptr size, uintptr typ, uint32 flag)
runtime·mallocgc
の内部ロジックが、新しいtyp
引数と統合されたflag
引数に対応するように修正されています。特に、ゼロクリアの条件 (!(flag & FlagNoZero)
) やGC呼び出しの条件 (!(flag & FlagNoInvokeGC)
) が変更されています。runtime·settype
関数が削除されています。その機能はruntime·mallocgc
の内部、またはruntime·settype_flush
に統合されています。runtime·mal
,runtime·new
,cnew
などの関数が、新しいruntime·mallocgc
のシグネチャに合わせて呼び出しが変更されています。
src/pkg/runtime/malloc.h
:runtime·mallocgc
のプロトタイプ宣言が変更され、runtime·settype
のプロトタイプ宣言が削除されています。新しいフラグFlagNoZero
とFlagNoInvokeGC
がenum
に追加されています。src/pkg/runtime/mfinal.c
,src/pkg/runtime/mgc0.c
,src/pkg/runtime/proc.c
,src/pkg/runtime/stack.c
,src/pkg/runtime/string.goc
: これらのファイルでも、runtime·mallocgc
の呼び出しが新しいシグネチャとフラグに合わせて更新されています。
コアとなるコードの解説
このコミットの核心は、runtime·mallocgc
関数の変更と、それに伴うランタイム全体でのメモリ割り当てロジックの統一です。
以前の mallocgc
は、dogc
(GCを実行するかどうか) と zeroed
(メモリをゼロクリアするかどうか) を個別の int32
引数として受け取っていました。また、オブジェクトの型情報は settype
関数を別途呼び出すことで設定されていました。
新しい mallocgc
は、uintptr typ
と uint32 flag
という2つの引数に集約されました。
typ
: 割り当てられるオブジェクトの型情報を直接渡すことで、メモリ割り当てと型情報の関連付けをアトミックに行えるようになりました。これにより、GCが不完全なオブジェクトをスキャンするリスクが低減されます。flag
:FlagNoPointers
,FlagNoProfiling
,FlagNoGC
に加えて、FlagNoZero
とFlagNoInvokeGC
が追加され、より表現力豊かなビットマスクとして機能します。FlagNoZero
: このフラグが設定されている場合、mallocgc
は割り当てられたメモリをゼロクリアしません。これは、呼び出し元が既にメモリを初期化することがわかっている場合や、ゼロクリアが不要な場合にパフォーマンスを向上させます。FlagNoInvokeGC
: このフラグが設定されている場合、mallocgc
はメモリ割り当て中にGCをトリガーしません。これは、GCが既に実行中である場合や、GCのトリガーが望ましくない特定のランタイム内部処理で使用されます。
runtime·settype
関数の削除は、このリファクタリングの重要な成果です。型情報の設定が mallocgc
の内部に統合されたことで、コードの重複が減り、mallocgc
がメモリ割り当てと型情報の関連付けの唯一の責任を持つようになりました。これにより、GCの正確性と効率が向上し、将来的なGCの最適化(settype
と markallocated
の統合)への道が開かれました。
例えば、src/pkg/runtime/chan.c
の runtime·makechan_c
関数では、チャネルオブジェクトの作成時に runtime·mal
と runtime·settype
を別々に呼び出していたのが、新しい runtime·mallocgc
の単一の呼び出しに置き換えられています。
// 変更前
// c = (Hchan*)runtime·mal(sizeof(*c) + hint*elem->size);
// runtime·settype(c, (uintptr)t | TypeInfo_Chan);
// 変更後
c = (Hchan*)runtime·mallocgc(sizeof(*c) + hint*elem->size, (uintptr)t | TypeInfo_Chan, 0);
この変更により、チャネルオブジェクトのメモリ割り当てと型情報の関連付けがアトミックに行われるようになり、GCの整合性が保たれます。
関連リンク
- Go言語のガベージコレクションに関する公式ドキュメントやブログ記事
- Goランタイムのソースコード(特に
src/runtime
ディレクトリ) - このコミットが参照しているGoの変更リスト (CL): https://golang.org/cl/10136043
参考にした情報源リンク
- Goの公式ドキュメント
- Goのソースコードリポジトリ
- Goのガベージコレクションに関する技術ブログや論文 (例: "Go's new GC: Less latency and more throughput")
- Goのランタイムに関するディスカッションフォーラムやメーリングリスト
- Goのメモリ管理に関する書籍やオンラインリソース