Improving G1 out-of-the-box performance

原文はこちら。
The original article was written by Stefan Johansson (HotSpot GC team at Oracle).
https://kstefanj.github.io/2020/04/16/g1-ootb-performance.html

数週間前、JDK 8 と JDK 14 を比較したベンチマーク結果が Phoronixから発表されました。その中で発表されたSPECjbb® 2015の結果が、自身で実施したテストの結果とは比較にならないので、調査が必要でした。調査結果を掘り下げていく前に、まずはJDK 8とJDK 14の大きな違いの背景から話を始めます。

OpenJDK 14 Has Some Performance Improvements But OpenJDK 8 Still Strong
https://www.phoronix.com/scan.php?page=article&item=openjdk-14-benchmark

The switch

JDK 8がリリースされてから6年以上にわたり、Javaプラットフォームは大きく進化してきました。詳細には触れませんが、一つだけ確かなことは、全体的な性能が格段に向上したということです。JDK 9で行った大きな変更点は、G1をデフォルトGCにしたことです。以前のデフォルトであるParallel GCでは、正味のスループットに重きを置いていましたが、この変更により、スループットとレイテンシを同様に重視するという、よりバランスのとれたモデルへとプラットフォームがシフトしました。

G1は、Parallel GCのようなstop-the-worldコレクターで最終的に発生する、時折発生する長時間のFull GCを避けるように設計されています。これを実現するために、G1はJavaアプリケーションの実行と並行してGCを行います。これにより若干のスループット・コストがかかるため、スループットのみを測定するベンチマークでは特に目立ってしまいます。 Java 8 からそれ以降のバージョンの Java に移行する際にパフォーマンスが低下したように見えるのは、これが主な理由でした。つまり、GCのアプローチがよりレイテンシとスループットのバランスにシフトしたためです。最大のスループットを求めるアプリケーションは、(-XX:+UseParallelGCというJVMオプションを使って)Parallel GCに切り替えて、JDK 14に搭載されている他のすべてのパフォーマンス向上の利点を享受できます。

JDK 8 vs JDK 14

SPECjbb® 2015 [1] はJavaの全体的なパフォーマンスを測定し、異なるGCアルゴリズムの影響を測定するための良いベンチマークであり、当社のテストでは継続的にこのベンチマークを実行しています。ほとんどの場合、固定のヒープサイズで実行するようにチューニングします。これは、安定した再現性の高い結果を得るためのよく知られた良い方法です。-Xmx4g -Xms4gというJVMオプションでヒープを固定した場合の結果は以下の通りです。

Fixed 4 GB Heap Chart

メトリックmax-jOPSとcritical-jOPSはどちらもスループットを測定しています。違いは、critical-jOPSがレイテンシの要件を考慮に入れている点です。グラフからは、レイテンシゲインに比べてスループットのトレードオフが小さいことから、G1がデフォルトGCに適していることがわかります。

Unexpected results

Phoronixのレポートで提示されていた結果はかなり異なっていました。JDK 14とJDK 8を比較すると、両方のメトリクスに大きな回帰が見られました。問題を分析できるようにするために、回帰を再現したいと考えていましたが、どのオプションを使用したかの情報がなく、テスト対象のシステムが16GBのRAMを搭載していたことだけが記されていました。このようなシステムを模倣するために、-XX:MaxRAM=16g 以外のオプションを付けずにベンチマークを実行してみました。この設定では、ヒープの初期サイズを 256 MB、最大 4 GB になるため、JVMのオプション -Xms256m -Xmx4g を設定するのと同じです。

期待していたのは、同じサイズでヒープを固定した場合に比べて結果が少し悪くなることを予想していましたが、これほどとは…。

OOTB Chart

これらの結果は予想以上に悪かったため、GCログを分析した結果、固定ヒープで実行するのとしないのとでは明確な違いがあることがわかりました。ヒープのリージョンサイズが違っていました。並行してGCを実行するのとは別に、G1のもう一つの重要な特徴は、ヒープを一定のサイズの複数の領域(リージョン)に分割することです。G1ではこれらのリージョンを個別にGCすることができるため、G1ではコストのかかるFull GCを回避できます。このケースでは、リージョンの大きさをやや小さくしようとするしくみとベンチマークの特性が逆効果になってしまいました。

リージョンのサイズは起動時に決定され、デフォルトの計算では初期ヒープサイズと最大ヒープサイズの両方を考慮します。そのため、固定ヒープの場合はリージョンサイズが2MBになるのに対し、標準では1MBのリージョンサイズを使用します。ちょっとした違いのように聞こえるかもしれませんが、ベンチマークでは、1MBのリージョンを使用する際に特別な処理が必要な大きなオブジェクトを大量に使用します。この特別対応により、使用できない多くのメモリが生まれ、その結果、Full GCと全体的に悪いパフォーマンスにつながります。

この問題にすぐに対処することにしました。挙動改善のための変更はすでにJDK 15(JDK-8241670)にプッシュされています。基本的な考え方は、デフォルトではより大きなリージョンサイズを狙っており、次のようにして達成しようとしています。

8241670: Enhance heap region size ergonomics to improve OOTB performance
https://hg.openjdk.java.net/jdk/jdk/rev/a54ff90a3015
JDK-8241670 : Enhance heap region size ergonomics to improve OOTB performance
https://bugs.openjdk.java.net/browse/JDK-8241670

  • リージョンサイズ決定にあたっては、最大ヒープサイズのみを考慮する
  • リージョンサイズを最も近い2の累乗の値に(切り捨てるのではなく)切り上げる

この変更の後、パフォーマンスは以下のようになりました。

OOTB JDK 15 Chart

追加設定をしない状態でのG1GCのパフォーマンスが改善されていることがわかります。

A quick look at pause times

前述のように、G1ではレイテンシーの短縮のためにスループットを犠牲にしています。SPECjbb® 2015実行時のGCポーズタイムについて、これが何を意味するのかを簡単に見てみましょう。ポーズタイムを比較するために、compliant runで報告されているmax-jOPSの約80%で噴射率を固定し、ベンチマークをresearchモードで実行しました。これにより、負荷がかかったシステムをシミュレートしていますが、実際にはシステムに最大負荷をかけてはいません。researchモードでベンチマークを実行してもスコアは出ませんが、GCが噴射率に追いつかないと実行は失敗します。

Pause Time Chart

これらのポーズタイムは1時間の長時間実行によるものです。G1は長時間のFull GCを避けることができるため、99.9パーセンタイルのポーズタイムはよい結果です。これらは全て4GBの固定ヒープで実行しています。G1@30msの実行では、-XX:MaxGCPauseMillis=30 を指定して実行しています。これがG1GCの主たるチューニングオプションです。このオプションを設定すると、今回の場合は30msですが、G1GCは最大30msecのポーズタイムを目標にします。ご覧になるとわかるように、この設定がポーズタイムを短く保つようにしてくれます。

Better out-of-the-box performance

すべてのGC、そしてJava全体の目標は、チューニングなしで良好なパフォーマンスを発揮することですが、これだけ多種多様なアプリケーションが出回っているため、これは困難なことです。この目標に向けた第一歩が、Javaプラットフォームがレイテンシーをより気にかけるようにすることであり、その流れでJDK 9ではデフォルトのGCをG1に変更しました。継続的にG1を改良改善しており、今回の変更は、追加設定なしでより優れた体験をもたらすためにもう一歩進めました。

この変更のもう一つの嬉しい副作用が、スタートアップの改善です。ヒープ内のリージョンが少ないということは、スタートアップ時の作業が少ないということであり、フットプリントのオーバーヘッドを減らすことにもつながります。弊社のJavaスタートアップのエキスパートであるClaes(Claes Redestad)にこの事を伝え、GCの世界に彼をおびき寄せることができました。彼は効率的でないクリーンアップも発見してくれ、結果としてスタートアップとフットプリントのオーバーヘッドをさらに減らすことができました。Javaのスタートアップのパフォーマンスに興味がある方は、彼のブログをぜひご覧ください。

Claes Redestad’s blog
https://cl4es.github.io/


[1] SPEC® およびベンチマーク名である SPECjbb® はStandard Performance Evaluation Corporationの登録商標です。
SPECjbbに関する詳細は、以下のURLからどうぞ。

Standard Performance Evaluation Corporation
https://www.spec.org/jbb2015/

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください