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

[インデックス 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 関数をリファクタリングし、型情報を受け入れるように変更し、フラグを統合する。

変更の理由は以下の通り:

  1. mallocgcsettype はGCに対してアトミックである必要がある。
  2. settype は現在、1箇所からのみ呼び出される。
  3. パフォーマンス向上に役立つ(最終的に settype の機能は markallocated と統合される必要がある)。
  4. フラグが読みやすくなった(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 に加えて、FlagNoZeroFlagNoInvokeGC が追加されました。これにより、mallocgc の呼び出し元が、メモリのゼロクリアやGCのトリガーをより細かく制御できるようになります。
  • runtime·settype の呼び出し削減: mallocgc が型情報を受け取るようになったため、多くの場所で明示的に runtime·settype を呼び出す必要がなくなりました。コミットメッセージにあるように、settype は「現在、1箇所からのみ呼び出される」ようになり、その呼び出しは mallocgc の内部、または mallocgc が型情報を受け取らない特殊なケースに限定されます。これにより、mallocgcsettype の間のアトミック性の問題が解消され、コードの複雑性も軽減されます。
  • パフォーマンスへの影響: settype の機能を markallocated と統合するという将来の計画は、GCの効率を向上させる可能性を秘めています。型情報の設定とオブジェクトのマークを単一の操作にまとめることで、GCのオーバーヘッドを削減し、全体的なパフォーマンスを改善できると考えられます。

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

このコミットでは、主に以下のファイルが変更されています。

  • src/pkg/runtime/chan.c: runtime·makechan_c 関数内で runtime·malruntime·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 のプロトタイプ宣言が削除されています。新しいフラグ FlagNoZeroFlagNoInvokeGCenum に追加されています。
  • 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 typuint32 flag という2つの引数に集約されました。

  • typ: 割り当てられるオブジェクトの型情報を直接渡すことで、メモリ割り当てと型情報の関連付けをアトミックに行えるようになりました。これにより、GCが不完全なオブジェクトをスキャンするリスクが低減されます。
  • flag: FlagNoPointers, FlagNoProfiling, FlagNoGC に加えて、FlagNoZeroFlagNoInvokeGC が追加され、より表現力豊かなビットマスクとして機能します。
    • FlagNoZero: このフラグが設定されている場合、mallocgc は割り当てられたメモリをゼロクリアしません。これは、呼び出し元が既にメモリを初期化することがわかっている場合や、ゼロクリアが不要な場合にパフォーマンスを向上させます。
    • FlagNoInvokeGC: このフラグが設定されている場合、mallocgc はメモリ割り当て中にGCをトリガーしません。これは、GCが既に実行中である場合や、GCのトリガーが望ましくない特定のランタイム内部処理で使用されます。

runtime·settype 関数の削除は、このリファクタリングの重要な成果です。型情報の設定が mallocgc の内部に統合されたことで、コードの重複が減り、mallocgc がメモリ割り当てと型情報の関連付けの唯一の責任を持つようになりました。これにより、GCの正確性と効率が向上し、将来的なGCの最適化(settypemarkallocated の統合)への道が開かれました。

例えば、src/pkg/runtime/chan.cruntime·makechan_c 関数では、チャネルオブジェクトの作成時に runtime·malruntime·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のメモリ管理に関する書籍やオンラインリソース