The Go Blog
Goランタイム: 4年後
2018年のGo GCに関する前回のブログ記事以降、Go GC、そしてより広範にはGoランタイムは着実に改善を続けてきました。私たちは、実際のGoプログラムやGoユーザーが直面している実際の課題に動機づけられた、いくつかの大規模なプロジェクトに取り組みました。そのハイライトをご紹介しましょう!
新機能
-
メモリの再利用のためのGC対応ツールである
sync.Poolは、以前よりもレイテンシの影響が少なくなり、メモリをはるかに効率的に再利用するようになりました。(Go 1.13) -
Goランタイムは不要なメモリをオペレーティングシステムにより積極的に返却するようになり、過剰なメモリ消費とメモリ不足エラーの可能性を減らしました。これにより、アイドル時のメモリ消費量が最大20%削減されます。(Go 1.13 および 1.14)
-
Goランタイムは多くの場合、ゴルーチンをより容易にプリエンプトできるようになり、ストップ・ザ・ワールドのレイテンシを最大90%削減します。Gophercon 2020での講演はこちらでご覧いただけます。 (Go 1.14)
-
Goランタイムは、特に多数のCPUコアを持つマシンで、以前よりもタイマーを効率的に管理するようになりました。(Go 1.14)
-
deferステートメントで遅延された関数呼び出しは、ほとんどの場合、通常の関数呼び出しとほぼ同じコストになりました。Gophercon 2020での講演はこちらでご覧いただけます。 (Go 1.14) -
メモリアロケータの遅いパスは、CPUコアにより良くスケールするようになり、特に並列度の高いプログラムにおいて、スループットを最大10%向上させ、テールレイテンシを最大30%削減します。(Go 1.14 および 1.15)
-
Goのメモリ統計は、よりきめ細かく、柔軟で、効率的なAPIであるruntime/metricsパッケージでアクセスできるようになりました。これにより、ランタイム統計の取得レイテンシが2桁(ミリ秒からマイクロ秒へ)削減されます。(Go 1.16)
-
Goスケジューラは、新しい作業を見つけるためにスピンするCPU時間を最大30%削減します。(Go 1.17)
-
Goコードは、amd64、arm64、ppc64でレジスタベースの呼び出し規約に従うようになり、CPU効率が最大15%向上します。(Go 1.17 および Go 1.18)
-
Go GCの内部的な会計とスケジューリングは再設計され、効率と堅牢性に関する長年の様々な問題を解決しました。これにより、ゴルーチンのスタックがメモリ使用量の大部分を占めるアプリケーションにおいて、アプリケーションのテールレイテンシが大幅に(最大66%)減少します。(Go 1.18)
-
Go GCは、アプリケーションがアイドル状態のときに自身のCPU使用率を制限するようになりました。これにより、非常にアイドル状態のアプリケーションではGCサイクル中のCPU使用率が75%低下し、ジョブシェイパーを混乱させる可能性のあるCPUスパイクを削減します。(Go 1.19)
これらの変更はほとんどユーザーには見えないものでした。彼らが知って愛用しているGoコードは、Goをアップグレードするだけで、より良く動作するようになりました。
新しいツマミ
Go 1.19には、使用するには少し手間がかかりますが、大きな可能性を秘めた長年要望されていた機能、すなわちGoランタイムのソフトメモリ制限が追加されました。
長年、Go GCにはGOGCという1つのチューニングパラメータしかありませんでした。GOGCは、ユーザーがGo GCがCPUオーバーヘッドとメモリオーバーヘッドの間で行うトレードオフを調整することを可能にします。長年、この「ツマミ」は、Goコミュニティの多様なユースケースにうまく対応してきました。
Goランタイムチームは、新しいツマミをGoランタイムに追加することには慎重でした。それには正当な理由があります。新しいツマミはすべて、私たちがテストし、潜在的に永久に維持する必要がある構成空間における新しい次元を表すからです。ツマミの増加はまた、Go開発者がそれらを効果的に理解し使用することに負担をかけ、ツマミが増えるほどそれはより困難になります。したがって、Goランタイムは常に、最小限の構成で合理的に動作することを目指してきました。
では、なぜメモリ制限のツマミを追加したのでしょうか?
メモリはCPU時間ほど交換可能ではありません。CPU時間には、少し待てば常に将来に増えるものがあります。しかし、メモリには、持っているものに限界があります。
メモリ制限は2つの問題を解決します。
1つ目は、アプリケーションのピークメモリ使用量が予測できない場合、GOGCだけではメモリ不足に対する保護が事実上ないことです。GOGCだけでは、Goランタイムは利用可能なメモリ量を単純に認識していません。メモリ制限を設定することで、ランタイムはメモリオーバーヘッドを削減するためにより懸命に作業する必要がある時期を認識できるようになり、一時的で回復可能な負荷スパイクに対して堅牢になります。
2つ目は、メモリ制限を使用せずにメモリ不足エラーを回避するためには、GOGCをピークメモリに合わせてチューニングする必要があり、アプリケーションがピークメモリ使用量に達しておらず、十分なメモリが利用可能である場合でも、低いメモリオーバーヘッドを維持するためにGC CPUオーバーヘッドが高くなることです。これは、プログラムが特定の分離されたメモリ予約を持つボックスに配置されるコンテナ化された世界において特に重要です。それらを活用すべきです!負荷スパイクからの保護を提供することで、メモリ制限を設定すると、CPUオーバーヘッドに関してGOGCをはるかに積極的にチューニングできるようになります。
メモリ制限は、導入が容易で堅牢に設計されています。例えば、Goヒープだけでなく、アプリケーションのGo部分全体のメモリフットプリントに対する制限なので、ユーザーはGoランタイムのオーバーヘッドを考慮する必要がありません。ランタイムはまた、メモリの圧力に応じてOSにメモリをより積極的に返却するように、メモリ回収ポリシーをメモリ制限に応じて調整します。
しかし、メモリ制限は強力なツールである一方で、ある程度の注意を払って使用する必要があります。大きな注意点の一つは、GCスラッシング(プログラムがGCの実行に多くの時間を費やしすぎ、意味のある進捗をほとんどしない状態)に陥る可能性があることです。例えば、プログラムが必要とするメモリ量に対してメモリ制限が低すぎる場合、Goプログラムはスラッシングに陥る可能性があります。GCスラッシングは、GOGCがメモリ使用量を優先して明示的に強くチューニングされていない限り、以前は起こりにくいものでした。私たちはスラッシングよりもメモリ不足を優先することにしたので、緩和策として、ランタイムはメモリ制限を超えることを意味する場合でも、GCを総CPU時間の50%に制限します。
これらすべては考慮すべきことがたくさんあるので、この作業の一環として、GCコストとそれらを操作する方法を理解するのに役立つインタラクティブな視覚化を備えた真新しいGCガイドを公開しました。
まとめ
メモリ制限を試してみてください!本番環境で使ってみてください!GCガイドを読んでください!
私たちはGoを改善する方法について常にフィードバックを求めていますが、うまく機能しているという話を聞くことも役立ちます。フィードバックをお寄せください!
次の記事: Goの13年
前の記事: Go開発者アンケート 2022年第2四半期結果
ブログインデックス