コンパイラがサイズ特化のメモリ割り当てルーチン呼び出しを生成し、小さいオブジェクトの割り当てを高速化
Faster memory allocation
概要
コンパイラが、80バイト未満の小さいオブジェクトの割り当てに対して、サイズクラスごとに特化したメモリ割り当てルーチンへの呼び出しを生成するようになった。これにより、対象となる小さい割り当てのコストが最大30%削減される。改善幅はワークロードに依存するが、実際の割り当てが多いプログラムでは全体として約1%の改善が見込まれる。一方でバイナリサイズはワークロードによらず約60KB増加する。
回帰が見られた場合はissueを作成して報告できる。ビルド時に GOEXPERIMENT=nosizespecializedmalloc を指定するとこの機能を無効化できるが、このオプトアウト設定はGo 1.28で削除される予定である。
移行時の注意
本機能はデフォルトで有効であり、通常はユーザー側のコード変更は不要である。ただし、バイナリサイズの増加(約60KB)や、割り当て処理の変化に伴う回帰が疑われる場合は、ビルド時に GOEXPERIMENT=nosizespecializedmalloc を指定することで一時的に無効化できる。このオプトアウト手段はGo 1.28で削除される予定のため、恒久的な回避策としては利用できない点に注意が必要である。
実装解説
コンパイラ側では、cmd/compile/internal/ssagen パッケージの newObject のコンパイル処理内で、specializedMallocSym 関数(ssa.go)が割り当てサイズと型がポインタを含むかどうかに応じて、汎用の runtime.mallocgc 呼び出しの代わりにサイズ特化したシンボル(mallocgcSmallScanNoHeader系、mallocgcSmallNoScan系、mallocgcTiny)への呼び出しを選択している。この特化は sizeSpecializedMallocEnabled 関数(ssa.go)により GOEXPERIMENT の SizeSpecializedMalloc フラグと、asan/msan/raceやランタイム自体のコンパイル中でないことを条件に有効化される。
サイズクラスごとに特化した実際の割り当て関数は、runtime/_mkmalloc/mkmalloc.go にあるコード生成ツール(mkmalloc.go)によって runtime/malloc_generated.go に出力されている。生成されたファイルには、サイズクラスごとの mallocgcSmallScanNoHeaderSC1〜SC7、mallocgcSmallNoScanSC2〜SC7、mallocgcTinySC2 などのインライン化されたファストパス関数群(malloc_generated.go)と、それぞれに対応するスローパス関数が含まれている。
GOEXPERIMENT フラグ自体は internal/goexperiment/flags.go の SizeSpecializedMalloc フィールド(flags.go)として定義されており、internal/buildcfg/exp.go のベースライン設定でデフォルト値が true になっている(exp.go)。