Graal JITコンパイラ操作マニュアル

パフォーマンスの測定 #

パフォーマンスを測定する際に最初に確認することは、Java仮想マシン(JVM)がGraal JITコンパイラを使用していることです。

GraalVMは、デフォルトで最上位層コンパイラとしてGraal JITコンパイラを使用するように構成されています。

Java HotSpot仮想マシンでGraal JITコンパイラを使用するには、-XX:+UseGraalJITオプションを使用します。(-XX:+UseGraalJITオプションは、この実験的な統合を有効にする-XX:+UnlockExperimentalVMOptionsオプションと一緒に使用する必要があります。)

次の例では、Graal JITコンパイラを有効にしてJavaアプリケーションcom.example.myappを実行します

java -XX:+UnlockExperimentalVMOptions -XX:+UseGraalJIT com.example.myapp

コマンドラインに-Djdk.graal.ShowConfiguration=infoオプションを追加することで、Graal JITコンパイラを使用していることを確認できます。コンパイラの初期化時に、以下のような出力行が生成されます

Using "Graal Enterprise compiler with Truffle extensions" loaded from a PGO optimized Native Image shared library

注:Graalコンパイラは、最初の最上位JITコンパイル要求時にのみ初期化されるため、アプリケーションの寿命が短い場合は、この出力が表示されない場合があります。

JVMベースのアプリケーションの最適化は、それ自体が科学です。パフォーマンスが低い場合、問題はJVMの他の部分(I/O、ガベージコレクション、スレッドなど)、または不適切に記述されたアプリケーション、またはサードパーティのライブラリコードにある可能性があるため、コンパイルは要因ではない場合があります。このため、JDK Mission Controlツールチェーンを使用して、アプリケーションの動作を診断することをお勧めします。

コマンドラインに-XX:-UseJVMCICompilerを追加することにより、JVMのネイティブ最上位層コンパイラとパフォーマンスを比較することもできます。

Graal JITコンパイラの使用時に重大なパフォーマンスの低下が見られる場合は、GitHubで問題を報告してください。調査を容易にし、修正の可能性を高めるために、Java Flight Recorderログと問題を再現するための手順を添付してください。さらに良いのは、アプリケーションの最もホットな部分(プロファイラによって識別される)を表すJMHベンチマークを送信できることです。これにより、最適化の機会の欠如を迅速に特定したり、パフォーマンスのボトルネックを回避または軽減するためのコードの再構築方法に関する提案を提供したりできます。

Graal JITコンパイラのトラブルシューティング #

セキュリティの脆弱性を発見した場合は、GitHubのIssueまたは公開メーリングリストではなく、脆弱性の報告ガイドに記載されているプロセスに従って報告してください。

コンパイル例外 #

コンパイラがJavaで記述されていることの利点の1つは、コンパイル中のJava例外が致命的なJVMエラーではないことです。代わりに、各コンパイルには、graal.CompilationFailureActionプロパティに基づいてアクションを実行する例外ハンドラがあります。

デフォルト値は`Silent`です。 `Diagnose`を指定すると、失敗したコンパイルは追加の診断を使用して再試行されます。この場合、JVMが終了する直前に、再試行されたコンパイル中にキャプチャされたすべての診断出力がZIPファイルに書き込まれ、その場所がコンソールに出力されます。次に例を示します。

Graal diagnostic output saved in /Users/demo/graal-dumps/1499768882600/graal_diagnostics_64565.zip

次に、ZIPファイルをGitHubの問題に添付できます。

`Silent`と`Diagnose`に加えて、 `graal.CompilationFailureAction`には次の値を使用できます。

  • `Print`:メッセージとスタックトレースをコンソールに出力しますが、再コンパイルは実行しません。
  • `ExitVM`: `Diagnose`と同じですが、再コンパイル後にJVMプロセスが終了します。

コード生成エラー #

コンパイラで発生する可能性のあるもう1つのタイプのエラーは、誤ったマシンコードの生成です。このエラーはJVMのクラッシュを引き起こし、JVMプロセスの現在の作業ディレクトリに_hs_err_pid_で始まるファイルが生成されます。ほとんどの場合、ファイルには、クラッシュ時のスタックを示すセクションがあり、次の例のように、スタック内の各フレームのコードのタイプが含まれています。

Stack: [0x00007000020b1000,0x00007000021b1000],  sp=0x00007000021af7a0,  free space=1017k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
J 761 JVMCI jdk.graal.compiler.core.gen.NodeLIRBuilder.matchComplexExpressions(Ljava/util/List;)V (299 bytes) @ 0x0000000108a2fc01 [0x0000000108a2fac0+0x141] (null)
j  jdk.graal.compiler.core.gen.NodeLIRBuilder.doBlock(Ljdk.graal.compiler/nodes/cfg/Block;Ljdk.graal.compiler/nodes/StructuredGraph;Ljdk.graal.compiler/core/common/cfg/BlockMap;)V+211
j  jdk.graal.compiler.core.LIRGenerationPhase.emitBlock(Ljdk.graal.compiler/nodes/spi/NodeLIRBuilderTool;Ljdk.graal.compiler/lir/gen/LIRGenerationResult;Ljdk.graal.compiler/nodes/cfg/Block;Ljdk.graal.compiler/nodes/StructuredGraph;Ljdk.graal.compiler/core/common/cfg/BlockMap;)V+65

この例は、最上位フレームがJVMCIコンパイラ(Graal JITコンパイラ)によってコンパイルされた(`J`)ことを示しています。クラッシュは、以下に対して生成されたマシンコードのオフセット`0x141`で発生しました

jdk.graal.compiler.core.gen.NodeLIRBuilder.matchComplexExpressions(Ljava/util/List;)V

スタック内の次の2つのフレームは解釈されました(`j`)。クラッシュの場所は、ファイルの上部付近にも、次のようなものが示されていることがよくあります。

# Problematic frame:
# J 761 JVMCI jdk.graal.compiler.core.gen.NodeLIRBuilder.matchComplexExpressions(Ljava/util/List;)V (299 bytes) @ 0x0000000108a2fc01 [0x0000000108a2fac0+0x141] (null)

この例では、`NodeLIRBuilder.matchComplexExpressions`に対してGraal JITコンパイラによって生成されたコードにエラーがある可能性があります。

このようなクラッシュについてGitHubに問題を報告する場合は、まず問題のあるメソッドのコンパイルに追加の診断を有効にして、クラッシュを再現してみてください。この例では、コマンドラインに次のオプションを追加します

-Djdk.graal.MethodFilter=NodeLIRBuilder.matchComplexExpressions, -Djdk.graal.Dump=:2

これらのオプションの詳細は、コンパイラのデバッグドキュメントに記載されています。簡単に言うと、これらのオプションは、Graal JITコンパイラに対し、単純名が`NodeLIRBuilder`であるクラスの`matchComplexExpressions`という名前のメソッドをコンパイルしている間に、冗長レベル2で状態のスナップショットをキャプチャするように指示します。 `MethodFilter`オプションの完全な形式は、MethodFilterHelp.txtに記載されています。

クラッシュの場所は、クラッシュログに記載されている問題のあるメソッドに直接存在するのではなく、インライン化されたメソッドからのものであることがよくあります。

このような場合、問題のあるメソッドをフィルタリングするだけでは、クラッシュを引き起こす誤ったコンパイルをキャプチャできない可能性があります。

誤ったコンパイルをキャプチャする可能性を高めるには、`MethodFilter`値を広げます。これをガイドするために、クラッシュを再現しようとする際に`-Djdk.graal.PrintCompilation=true`オプションを追加して、クラッシュの直前に何がコンパイルされたかを確認できるようにします。

コンソールからのサンプル出力を以下に示します

HotSpotCompilation-1218        Ljdk.graal.compiler/core/amd64/AMD64NodeLIRBuilder;                  peephole                                      (Ljdk.graal.compiler/nodes/ValueNode;)Z           |   87ms   428B   447B  1834kB
HotSpotCompilation-1212        Ljdk.graal.compiler/lir/LIRInstructionClass;                         forEachState                                  (Ljdk.graal.compiler/lir/LIRInstruction;Ljdk.graal.compiler/lir/InstructionValueProcedure;)V  |  359ms    92B   309B  6609kB
HotSpotCompilation-1221        Ljdk.graal.compiler/hotspot/amd64/AMD64HotSpotLIRGenerator;          getResult                                     ()Ljdk.graal.compiler/hotspot/HotSpotLIRGenerationResult;  |   54ms    18B   142B  1025kB
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x000000010a6cafb1, pid=89745, tid=0x0000000000004b03
#
# JRE version: OpenJDK Runtime Environment (8.0_121-b13) (build 1.8.0_121-graalvm-olabs-b13)
# Java VM: OpenJDK 64-Bit GraalVM (25.71-b01-internal-jvmci-0.30 mixed mode bsd-amd64 compressed oops)
# Problematic frame:
# J 1221 JVMCI jdk.graal.compiler.hotspot.amd64.AMD64HotSpotLIRGenerator.getResult()Ljdk.graal.compiler/hotspot/HotSpotLIRGenerationResult; (18 bytes) @ 0x000000010a6cafb1 [0x000000010a6caf60+0x51] (null)
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again

ここでは、最初のクラッシュとは異なるメソッドでクラッシュが発生しました。そのため、フィルター引数を`-Djdk.graal.MethodFilter=NodeLIRBuilder.matchComplexExpressions,AMD64HotSpotLIRGenerator.getResult`に拡張して、再実行します。

JVMがこのようにクラッシュした場合、Graalコンパイラの診断出力をアーカイブしたり、それが書き込まれたディレクトリを削除したりするシャットダウンコードは実行されません。これは、クラッシュ後に手動で行う必要があります。

デフォルトでは、ディレクトリは_$PWD/graal-dumps/<timestamp>_です(例:_./graal-dumps/1499938817387_)。ただし、`-Djdk.graal.DumpPath=<path>`オプションを使用してディレクトリを指定できます。

このディレクトリがコンパイラによって最初に使用されると、次のようなメッセージがコンソールに出力されます

Dumping debug output in /Users/demo/graal-dumps/1499768882600

このディレクトリには、次のような、クラッシュしたメソッドに関連するコンテンツが含まれている必要があります

ls -l /Users/demo/graal-dumps/1499768882600
-rw-r--r--  1 demo  staff    144384 Jul 13 11:46 HotSpotCompilation-1162[AMD64HotSpotLIRGenerator.getResult()].bgv
-rw-r--r--  1 demo  staff     96925 Jul 13 11:46 HotSpotCompilation-1162[AMD64HotSpotLIRGenerator.getResult()].cfg
-rw-r--r--  1 demo  staff  12600725 Jul 13 11:46 HotSpotCompilation-791[NodeLIRBuilder.matchComplexExpressions(List)].bgv
-rw-r--r--  1 demo  staff   1727409 Jul 13 11:46 HotSpotCompilation-791[NodeLIRBuilder.matchComplexExpressions(List)].cfg

このディレクトリのZIPファイルをGitHubの問題に添付する必要があります。

お問い合わせ