Goブログ

Go GC: 低レイテンシとシンプルさを優先

Richard Hudson
2015年8月31日

準備

Goは、2015年だけでなく、2025年以降も使えるガベージコレクター(GC)を構築しています。それは、今日のソフトウェア開発をサポートし、今後10年間、新しいソフトウェアやハードウェアと共にスケールするGCです。そのような未来には、Goのような安全でセキュアな言語のより広範な利用を妨げてきた、stop-the-world GCの一時停止の余地はありません。

この未来の最初の片鱗であるGo 1.5は、1年前に設定した10ミリ秒の目標をはるかに下回るGCレイテンシを実現しました。Gopherconでの講演(Go GC: Latency Problem Solved presentation)では、いくつかの印象的な数値を発表しました。レイテンシの改善は多くの注目を集めています。Robin Verlangen氏のブログ記事「1日に数十億のリクエストがGo 1.5と出会う」(Billions of requests per day meet Go 1.5)は、エンドツーエンドの結果で私たちの方向性を検証しています。また、Alan Shreve氏のプロダクションサーバーのグラフと彼の「85%削減とはすごい」というコメントも特に楽しめました。

今日、16ギガバイトのRAMは100ドルで、CPUにはそれぞれ複数のハードウェア スレッドを持つ多くのコアが搭載されています。10年後にはこのハードウェアは古風なものに見えるでしょうが、今日Goで構築されているソフトウェアは、拡大するニーズと次の大きなものに対応するためにスケールする必要があります。ハードウェアがスループットを向上させる力を提供することを考えると、Goのガベージコレクターは、低レイテンシと、単一ノブによる調整のみを優先するように設計されています。Go 1.5はこの道の最初の大きな一歩であり、これらの最初の一歩は、Goとそれが最もよくサポートするアプリケーションに永遠に影響を与えるでしょう。このブログ記事では、Go 1.5コレクターのために私たちが行ったことの概要を説明します。

装飾

今後10年間のガベージコレクターを作成するために、私たちは数十年前のアルゴリズムに目を向けました。Goの新しいガベージコレクターは、*並行*、*三色*、*マークスイープ*コレクターであり、1978年にDijkstraによって最初に提案されたアイデアです。これは、今日のほとんどの「エンタープライズ」グレードのガベージコレクターからの意図的な逸脱であり、現代のハードウェアの特性と現代のソフトウェアのレイテンシ要件によく適合していると私たちが信じているものです。

三色コレクターでは、すべてのオブジェクトは白、灰色、または黒のいずれかであり、ヒープを接続されたオブジェクトのグラフとして見なします。GCサイクルの開始時に、すべてのオブジェクトは白です。GCは、グローバル変数やスタック上のものなど、アプリケーションから直接アクセスできるオブジェクトであるすべての*ルート*にアクセスし、これらを灰色に塗ります。次に、GCは灰色のオブジェクトを選択し、黒く塗りつぶし、他のオブジェクトへのポインターをスキャンします。このスキャンで白いオブジェクトへのポインターが見つかると、そのオブジェクトは灰色に変わります。このプロセスは、灰色のオブジェクトがなくなるまで繰り返されます。この時点で、白いオブジェクトは到達不能であることがわかり、再利用できます。

これはすべて、*ミューテーター*として知られるアプリケーションと同時に発生し、コレクターの実行中にポインターを変更します。したがって、ミューテーターは、黒いオブジェクトが白いオブジェクトを指さないという不変条件を維持する必要があります。そうしないと、ガベージコレクターは、既にアクセスしたヒープの一部にインストールされたオブジェクトを見失ってしまいます。この不変条件を維持するのが*書き込みバリア*の役割です。これは、ヒープ内のポインターが変更されるたびにミューテーターによって実行される小さな関数です。Goの書き込みバリアは、現在白い場合、到達可能になったオブジェクトを灰色に塗りつぶし、ガベージコレクターが最終的にポインターをスキャンするようにします。

すべての灰色のオブジェクトを見つける作業がいつ完了したかを判断するのは微妙であり、ミューテーターのブロックを避けたい場合は、費用と時間がかかる可能性があります。物事をシンプルにするために、Go 1.5は可能な限り多くの作業を同時に行い、その後、世界を一時停止して、灰色のオブジェクトの潜在的なソースをすべて検査します。この最終的なstop-the-worldに必要な時間と、このGCが行う作業の総量の間のスイートスポットを見つけることは、Go 1.6の主要な成果です。

もちろん、悪魔は細部に宿ります。GCサイクルはいつ開始しますか?その決定を行うためにどのようなメトリックを使用しますか?GCはGoスケジューラとどのように対話する必要がありますか?スタックをスキャンするのに十分な時間、ミューテータースレッドをどのように一時停止しますか?白、灰色、黒をどのように表現すれば、灰色のオブジェクトを効率的に見つけてスキャンできますか?ルートはどこにあるか、どのようにわかりますか?オブジェクトポインターがどこにあるか、どのようにわかりますか?メモリ断片化を最小限に抑えるにはどうすればよいですか?キャッシュのパフォーマンスの問題にどのように対処しますか?ヒープのサイズはどれくらいにする必要がありますか?などなど、割り当てに関連するもの、到達可能なオブジェクトの検索に関連するもの、スケジューリングに関連するものなどがありますが、多くはパフォーマンスに関連しています。

より高いレベルでは、パフォーマンスの問題を解決する1つのアプローチは、パフォーマンスの問題ごとに1つずつ、GCノブを追加することです。次に、プログラマーは、アプリケーションに適切な設定を探してノブを回すことができます。欠点は、毎年1つまたは2つの新しいノブを使用して10年後、GCノブターナー雇用法で終わることです。Goはその道を進んでいません。代わりに、GOGCと呼ばれる単一のノブを提供します。この値は、到達可能なオブジェクトのサイズに対するヒープの合計サイズを制御します。デフォルト値の100は、合計ヒープサイズが、最後の収集後の到達可能なオブジェクトのサイズよりも100%大きい(つまり2倍)ことを意味します。200は、合計ヒープサイズが到達可能なオブジェクトのサイズの200%大きい(つまり3倍)ことを意味します。GCに費やす合計時間を短縮したい場合は、GOGCを増やします。より少ないメモリのためにより多くのGC時間をトレードオフしたい場合は、GOGCを下げます。

さらに重要なことに、次世代のハードウェアでRAMが2倍になると、GOGCを2倍にするだけでGCサイクルの数が半分になります。一方、GOGCは到達可能なオブジェクトサイズに基づいているため、到達可能なオブジェクトを2倍にすることで負荷を2倍にしても、再調整は必要ありません。アプリケーションは単にスケールします。さらに、数十個のノブの継続的なサポートに悩まされることなく、ランタイムチームは実際の顧客アプリケーションからのフィードバックに基づいてランタイムの改善に集中できます。

結論

Go 1.5のGCは、stop-the-worldの一時停止が、安全でセキュアな言語への移行の障壁ではなくなる未来の到来を告げるものです。ハードウェアと共にアプリケーションが楽にスケールし、ハードウェアがより強力になるにつれて、GCがより優れたスケーラブルなソフトウェアの障害にならない未来です。今後10年間、そしてそれ以降も、Goは良い場所に位置しています。1.5 GCとレイテンシの問題をどのように解消したかについての詳細は、Go GC:レイテンシの問題解決のプレゼンテーションまたはスライドをご覧ください。

次の記事:Golang UK 2015
前の記事:Go 1.5がリリースされました
ブログインデックス