*Green Tea GCの開発タイムライン(出典: [Go公式ブログ](https://go.dev/blog/greenteagc))*
はじめに
Go 1.26 がリリースされ、ガベージコレクタ(GC)に大きな変更が加わりました。その名も Green Tea GC(緑茶GC)です。
公式ブログによると、この名前は2024年にGoランタイムチームのAustinが日本でカフェ巡りをしながら、大量の抹茶を飲みつつプロトタイプを開発したことに由来しているそうです。
Green Tea got its name in 2024 when Austin worked out a prototype of an earlier version while cafe crawling in Japan and drinking LOTS of matcha! This prototype showed that the core idea of Green Tea was viable. And from there we were off to the races.
Green Tea GCは Go 1.25 で GOEXPERIMENT=greenteagc として実験的に導入され、Go 1.26 からはデフォルトで有効化されました。本記事では、この新しいGCの仕組みと、実際にどの程度の性能改善が得られるのかを解説します。
TL;DR
- 10〜40%のGCオーバーヘッド削減を実現
- メモリアクセスの空間的局所性を大幅に改善
- Intel/AMDの最新CPUではAVX-512によるベクトル加速も利用可能
- Go 1.26からデフォルト有効(オプトアウト:
GOEXPERIMENT=nogreenteagc)
GoのGCの基本アルゴリズム
GoのGCは 並行マークスイープ(Concurrent Mark-Sweep) アルゴリズムを採用しています。
まず、従来から使われている基本的な仕組みを確認しましょう。
マーキングの仕組み
GoのGCは、プログラムが使用中のオブジェクトを特定するために「マーキング」を行います。
マーキングでは、「ルート」(グローバル変数やスタック上の変数)から参照をたどり、到達できるオブジェクトに「使用中」の印をつけていきます。印がつかなかったオブジェクトは、プログラムから到達不可能なのでGC対象となります。
処理の流れは次のとおりです:
- ルートから参照されているオブジェクトをワークリストに追加
- ワークリストからオブジェクトを取り出し、その中のポインタを調べる(これを「スキャン」と呼ぶ)
- 見つけたポインタが指すオブジェクトをワークリストに追加
- ワークリストが空になるまで繰り返す
【マーキング処理の流れ】 |
従来GCの問題点
上記のマーキング処理は、公式ブログで「グラフフラッド(graph flood)」と呼ばれています。ポインタをたどってオブジェクトを次々と訪問していく処理です。
しかし、この方法には問題がありました。ワークリストからオブジェクトを取り出してスキャンするたびに、メモリ上のまったく異なる場所にジャンプしてしまうのです。
メモリ空間(ページ単位で区切られている) |
ポインタをたどる順序とメモリ上の配置は無関係なため、あちこちに飛び回ることになってしまいます。
CPUには「キャッシュ」という高速な一時メモリがあります。メインメモリ(RAM)へのアクセスは遅いため、よく使うデータをキャッシュに置いて高速化しています。近くのメモリを連続してアクセスすればキャッシュが効きますが、離れた場所をランダムにアクセスすると、毎回メインメモリまで取りに行く必要があります。
公式ブログでは、この状況を「高速道路ではなく市街地を走るようなもの」と表現しています。先が見えず次に何が起こるか予測できないため、CPUは本来の性能を発揮できません。
Imagine the CPU driving down a road, where that road is your program. The CPU wants to ramp up to a high speed, and to do that it needs to be able to see far ahead of it, and the way needs to be clear. But the graph flood algorithm is like driving through city streets for the CPU. The CPU can’t see around corners and it can’t predict what’s going to happen next. To make progress, it constantly has to slow down to make turns, stop at traffic lights, and avoid pedestrians. It hardly matters how fast your engine is because you never get a chance to get going.
具体的な数字で見ると(公式ブログより):
- GC時間の 90% がマーキングに費やされる
- そのマーキング時間の 35%以上 がメモリアクセス待ち(ストール)
- メインメモリへのアクセスはキャッシュの 最大100倍遅い
Green Teaの仕組み
Green Tea GCの核心アイデアは非常にシンプルです:
「オブジェクト単位ではなく、ページ(スパン)単位で作業する」
ページ蓄積戦略
従来のGCは、ポインタを見つけるとすぐにそのオブジェクトをスキャンしていました。Green Teaでは異なるアプローチを取ります。
- ポインタを発見したら、ターゲットオブジェクトが存在するページ全体をワークリストに追加
- ページがキューで待機している間に、同じページ内の他のオブジェクトも蓄積
- ページを処理する時に、蓄積されたすべてのオブジェクトをメモリ順序で一括スキャン
【従来のGC】オブジェクト単位でスキャン → ランダムアクセス |
内部的には、各オブジェクトに「発見済み/スキャン済み」のビットを持たせることで、同じページ内のオブジェクトを効率的に追跡しています。
FIFOキュー
従来のGCはLIFO(スタック)でワークリストを管理していましたが、Green TeaはFIFO(キュー)を採用しています。
- LIFO(従来のGC): 最後に入れたものを最初に処理 → 蓄積する時間がない
- FIFO(Green Tea GC): 最初に入れたものを後で処理 → 待機中に同じページの他オブジェクトが蓄積される
ベクトル加速
2020年以降の新しいIntel/AMD CPUでは、CPU内蔵の高速演算機能(ベクトル命令)を活用して、追加で約10%の性能改善が得られます。
詳細は公式ドキュメントを参照してください:
なお、Green Teaは512バイト以下の小オブジェクトに最適化されており、各プロセッサが独自のスパンキューを持つことで、多コア環境でも効率的にスケールします。
実際に動作させて、計測してみる
実際にGreen Tea GCの効果を確認するため、GC負荷の高いベンチマークプログラムを作成して計測してみました。
ベンチマークコード
package main |
このコードのポイント:
- 64バイトの小オブジェクト: Green Teaの最適化対象となるサイズ
- リンクリスト構造: ポインタ追跡を強制し、GCスキャナに負荷をかける
- 1000万個 × 100回: 大量のオブジェクト生成と破棄を繰り返す
計測結果
Go 1.25 と Go 1.26rc1 でそれぞれ5回ずつ実行し、平均を取りました。
$ for i in {1..5}; do go run gc_bench.go 2>&1 | grep "Total Duration"; done |
| バージョン | Run 1 | Run 2 | Run 3 | Run 4 | Run 5 | 平均 |
|---|---|---|---|---|---|---|
| Go 1.25 | 42.81s | 41.47s | 40.13s | 41.51s | 40.28s | 41.24s |
| Go 1.26rc1 | 36.81s | 35.83s | 36.84s | 37.51s | 36.55s | 36.71s |
結果分析
改善: 41.24s → 36.71s |
また、GC回数の減少も計測できました(ex: 129回 → 109回)。これはGreen TeaのFIFOキュー戦略により、GCがより効率的にメモリを回収できるようになったためだと思われます。
このベンチマークは小オブジェクトのリンクリストという、Green Teaの得意パターンを直撃するワークロードです。実際のアプリケーションでは、ワークロードの特性によって改善率は変動します(公式発表では10〜40%)。
まとめ
Go 1.26のGreen Tea GCについて、変更点を整理します。
| 観点 | 従来のGC | Green Tea GC |
|---|---|---|
| 処理単位 | オブジェクト | ページ(スパン) |
| メモリアクセス | ランダム | シーケンシャル |
| キュー戦略 | LIFO | FIFO |
| キャッシュ効率 | — | 大幅に改善 |
導入方法
Go 1.26では何もしなくてもGreen Tea GCが有効です。もし問題が発生した場合は、以下でオプトアウトできます:
GOEXPERIMENT=nogreenteagc go build |
GCトレースの確認
GCの動作を詳しく確認したい場合は:
GODEBUG=gctrace=1 ./your_program |
おわりに
今回のアイデアの種は、2018年まで遡るようです。公式ブログでは次のように述べられています:
The seeds of this idea go all the way back to 2018. What’s funny is that everyone on the team thinks someone else thought of this initial idea.
「チームの全員が、このアイデアは他の誰かが考えたものだと思っている」というのも面白いエピソードですね。
Go 1.26へのアップグレードを検討している方は、ぜひ緑茶GCの効果を体感してみてください。