Go 1.27 で 80 バイト以内の割り当てが 20~30% 高速化、代償は icache
Go 1.27 で 80 バイト以下の割り当てがより高速なメモリ割り当てに。こうした割り当ては最大 20~30% 高速になり、割り当ての多いプログラムは最大 1% 高速に。Go ランタイムは特定サイズの割り当てに特化した関数を導入することで、これらの割り当ての性能を向上させた。
日本語
コピー
The Go Blog
サイズ特化したメモリ割り当て
Michael Matloob
2026年9月16日
Go 1.27 では、80 バイト以下の割り当てが速くなった。
この種の割り当ては最大 20〜30% 高速になり、割り当ての多いプログラムは最大 1% 速くなる。
Go ランタイムは、特定のサイズの割り当てに特化した関数を導入することで、
これらの割り当ての性能を引き上げた。特化によって関数はある種の前提を置けるようになり、
高速になるだけでなく最適化もしやすくなる。
この記事では、その仕組みと、それがどうプログラムを速くするのかを説明する。
ヒープ割り当てはランタイムの mallocgc 関数が行う。この関数は、割り当てのサイズと、そこにポインタが含まれるかどうかを知る必要がある。コンパイラがオブジェクトのヒープへのエスケープを判定した場合、あるいは動的な割り当てが必要な場合、
コンパイラは newobject の呼び出しを挿入する。これは単純なラッパー関数で、オブジェクトのサイズとポインタを含むかどうかを取り出し、mallocgc に渡す。
この 2 つの情報が、アロケータの仕事の大半を決める。サイズが重要なのは、アロケータが
「サイズクラス」と呼ばれるサイズ範囲を定義しているからだ。あまり大きくも小さくもないほとんどの割り当てでは、
アロケータは空きオブジェクトのリストからメモリを返す。これらのオブジェクトのサイズは、そのサイズクラスの上限に等しい。たとえばサイズクラス 3
は 17〜24 バイトだ。つまり 17 バイトを割り当てても 24 バイトを割り当てても、アロケータは 24 バイトのオブジェクトのリストから
次の空き 24 バイトオブジェクトを渡す。
以下は、80 バイト以内の各サイズクラスのサイズ範囲の表だ。
空きリストは、割り当てにポインタが含まれるかどうかに応じて 2 種類に分かれる。これを span と呼ぶ。
ガベージコレクタがそれに応じた記帳をする必要があるからだ。
| サイズクラス | サイズ範囲 |
|---|---|
1 | 1〜8 バイト |
2 | 9〜16 バイト |
3 | 17〜24 バイト |
4 | 25〜32 バイト |
5 | 33〜48 バイト |
6 | 49〜64 バイト |
7 | 65〜80 バイト |
割り当てのサイズクラスとポインタを含むかどうかが、どの span からメモリを割り当てるかを決めるため、アロケータには span class と呼ばれる型がある。その値はこの両方をエンコードしている。定義は `sizeClass