Goブログ

Goランタイム:4年後

Michael Knyszek
2022年9月26日

2018年のGoのGCに関する前回のブログ投稿以降、GoのGC、そしてより広範なGoランタイムは着実に改善されてきました。私たちは、現実世界のGoプログラムとGoユーザーが直面する現実の課題に動機付けられた、いくつかの大規模なプロジェクトに取り組んできました。ハイライトを簡単にご紹介します!

新機能

  • メモリ再利用のためのGC対応ツールであるsync.Poolは、レイテンシへの影響が低減され、メモリを以前よりはるかに効率的に再利用するようになりました。(Go 1.13)

  • Goランタイムは、不要なメモリをオペレーティングシステムにはるかに積極的に返却するようになり、過剰なメモリ消費とメモリ不足エラーの可能性を軽減します。これにより、アイドル状態のメモリ消費量が最大20%削減されます。(Go 1.13および1.14)

  • Goランタイムは多くの場合、ゴルーチンをより容易にプリエンプトできるようになり、stop-the-worldレイテンシを最大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にメモリをより積極的に返却します。

しかし、メモリ制限は強力なツールですが、注意して使用する必要があります。大きな注意点の1つは、GCスラッシングにつながることです。GCスラッシングとは、プログラムがGCの実行に時間をかけすぎる状態であり、有意義な進捗を遂げるのに十分な時間がなくなる状態です。たとえば、プログラムに必要なメモリ量に対してメモリ制限が低すぎる場合、Goプログラムはスラッシングが発生する可能性があります。GCスラッシングは、GOGCがメモリ使用量を重視するように明示的に調整されていない限り、以前は起こりにくいものでした。メモリ不足よりもスラッシングを優先することにしたので、軽減策として、ランタイムはメモリ制限を超える場合でも、GCを総CPU時間の50%に制限します。

これらすべてを考慮するのは大変な作業であるため、この作業の一環として、新しいGCガイドをリリースしました。インタラクティブな視覚化を使用して、GCコストとそれらを操作する方法を理解するのに役立ちます。

結論

メモリ制限を試してみてください!本番環境で使用してみましょう!GCガイドを読んでみましょう!

Goの改善方法に関するフィードバックを常に求めていますが、Goがうまく機能したときについても教えていただけると幸いです。フィードバックを送信してください!

次の記事:Goの13年間
前の記事:Go開発者調査2022年第2四半期結果
ブログインデックス