Ahead of Time Compilation(AoT)

  • link:/category/programming/ [プログラミング]

1. 前書き

この記事では、https://openjdk.java.net/jeps/295 [JEP-295]で説明され、Javaの実験的な機能として追加されたJava Ahead of Time(AOT)コンパイラーを見ていきます。 9。
まず、AOTが何であるかを確認し、次に、簡単な例を見ていきます。 第三に、AOTのいくつかの制限を確認し、最後に、いくつかの可能なユースケースについて説明します。

2. Ahead of Time Compilationとは何ですか?

  • AOTコンパイルは、Javaプログラムのパフォーマンス、特にJVMの起動時間を改善する1つの方法です*。 JVMはJavaバイトコードを実行し、頻繁に実行されるコードをネイティブコードにコンパイルします。 これは、ジャストインタイム(JIT)コンパイルと呼ばれます。 JVMは、実行中に収集されたプロファイリング情報に基づいて、JITにコンパイルするコードを決定します。

    この手法により、JVMは高度に最適化されたコードを生成し、ピークパフォーマンスを改善できますが、実行されたコードはまだJITコンパイルされていないため、起動時間は最適ではない可能性があります。 * AOTは、このいわゆる「ウォームアップ期間」の改善を目指しています*。 AOTに使用されるコンパイラはGraalです。
    *この記事では、JITとGraalについて詳しくは説明しません。* link:/java-10-performance-improvements[Javaのパフォーマンス改善]の概要については、他の記事を参照してください。 9および10]、およびahttps://www.baeldung.com/graal-java-jit-compiler [Graal JIT Compilerの詳細]。

3. 例

この例では、非常に単純なクラスを使用してコンパイルし、結果のライブラリの使用方法を確認します。

3.1. AOTコンパイル

サンプルクラスを簡単に見てみましょう。
public class JaotCompilation {

    public static void main(String[] argv) {
        System.out.println(message());
    }

    public static String message() {
        return "The JAOT compiler says 'Hello'";
    }
}
AOTコンパイラを使用する前に、Javaコンパイラでクラスをコンパイルする必要があります。
javac JaotCompilation.java
次に、結果の_JaotCompilation.class_をpassAOTコンパイラに渡します。AOTコンパイラは、標準のJavaコンパイラと同じディレクトリにあります。
jaotc --output jaotCompilation.so JaotCompilation.class
これにより、現在のディレクトリにlibrary _jaotCompilation.so_が作成されます。

3.2. プログラムを実行する

その後、プログラムを実行できます。
java -XX:AOTLibrary=./jaotCompilation.so JaotCompilation
引数_-XX:AOTLibrary_は、ライブラリへの相対パスまたはフルパスを受け入れます。 または、ライブラリをJavaホームディレクトリの_lib_フォルダにコピーし、ライブラリの名前のみを渡すこともできます。

3.3. ライブラリが呼び出されて使用されていることを確認する

JVM引数として_-XX:PrintAOT_を追加すると、ライブラリが実際にロードされたことがわかります。
java -XX:+PrintAOT -XX:AOTLibrary=./jaotCompilation.so JaotCompilation
出力は以下のようになります。
77    1     loaded    ./jaotCompilation.so  aot library
ただし、これはライブラリがロードされたことを示しているだけで、実際に使用されたことを示しているわけではありません。 引数_-verbose_を渡すことにより、ライブラリ内のメソッドが実際に呼び出されることがわかります。
java -XX:AOTLibrary=./jaotCompilation.so -verbose -XX:+PrintAOT JaotCompilation
出力には次の行が含まれます。
11    1     loaded    ./jaotCompilation.so  aot library
116    1     aot[ 1]   jaotc.JaotCompilation.<init>()V
116    2     aot[ 1]   jaotc.JaotCompilation.message()Ljava/lang/String;
116    3     aot[ 1]   jaotc.JaotCompilation.main([Ljava/lang/String;)V
The JAOT compiler says 'Hello'
  • AOTコンパイル済みライブラリには_class Fingerprint_が含まれています。これは_.class_ファイルのフィンガープリントと一致する必要があります。*

    クラス_JaotCompilation.java_のコードを変更して、別のメッセージを返しましょう。
public static String message() {
    return "The JAOT compiler says 'Good morning'";
}
変更したクラスをAOTコンパイルせずにプログラムを実行する場合:
java -XX:AOTLibrary=./jaotCompilation.so -verbose -XX:+PrintAOT JaotCompilation
その場合、出力には以下のみが含まれます
 11 1 loaded ./jaotCompilation.so aot library
The JAOT compiler says 'Good morning'
*クラスのバイトコードが変更されているため、ライブラリのメソッドは呼び出されないことがわかります。*この背後にある考え方は、AOTコンパイル済みライブラリがロードされていても、プログラムは常に同じ結果を生成するということです。か否か。

4. その他のAOTおよびJVM引数

4.1. JavaモジュールのAOTコンパイル

モジュールをAOTコンパイルすることもできます。
jaotc --output javaBase.so --module java.base
結果のライブラリ_javaBase.so_のサイズは約320 MBであり、ロードに時間がかかります。 AOTコンパイルするパッケージとクラスを選択することにより、サイズを縮小できます。
以下でその方法を見ていきますが、すべての詳細を深く掘り下げることはしません。

4.2. コンパイルコマンドを使用した選択的コンパイル

  • JavaモジュールのAOTコンパイル済みライブラリが大きくなりすぎるのを防ぐために、コンパイルコマンドを追加して、AOTをコンパイルする範囲を制限できます。*これらのコマンドは、テキストファイルにある必要があります。この例では、 _complileCommands.txt_ファイルを使用します。

compileOnly java.lang.*
次に、コンパイルコマンドに追加します。
jaotc --output javaBaseLang.so --module java.base --compile-commands compileCommands.txt
結果のライブラリには、_package java.lang_にAOTコンパイル済みクラスのみが含まれます。
*実際のパフォーマンスを向上させるには、JVMのウォームアップ中にどのクラスが呼び出されるかを調べる必要があります*。
これは、いくつかのJVM引数を追加することで実現できます。
java -XX:+UnlockDiagnosticVMOptions -XX:+LogTouchedMethods -XX:+PrintTouchedMethodsAtExit JaotCompilation
この記事では、この手法については詳しく説明しません。

4.3. 単一クラスのAOTコンパイル

引数_–class-name_を使用して単一のクラスをコンパイルできます。
jaotc --output javaBaseString.so --class-name java.lang.String
結果のライブラリには、_String_クラスのみが含まれます。

4.4. 階層型にコンパイル

デフォルトでは、AOTでコンパイルされたコードが常に使用され、ライブラリに含まれるクラスのJITコンパイルは行われません。 *プロファイル情報をライブラリに含める場合は、引数_compile-for-tiered _:*を追加できます
jaotc --output jaotCompilation.so --compile-for-tiered JaotCompilation.class
ライブラリ内のプリコンパイルされたコードは、バイトコードがJITコンパイルの対象になるまで使用されます。

5. AOTコンパイルの可能なユースケース

AOTの使用例の1つは、JITコンパイルが発生する前に実行を終了する短い実行プログラムです。
別のユースケースは、JITが不可能な組み込み環境です。
この時点で、AOTでコンパイルされたライブラリは、同一のバイトコードを持つJavaクラスからしかロードできないため、JNIを介してロードできないことに注意する必要があります。

6. 結論

この記事では、JavaクラスとモジュールをAOTコンパイルする方法を説明しました。 これはまだ実験的な機能であるため、AOTコンパイラはすべてのディストリビューションの一部ではありません。 実際の例を見つけることはまだまれであり、AOTを適用するための最適なユースケースを見つけるのはJavaコミュニティ次第です。
この記事のすべてのコードスニペットは、https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-9 [GitHubリポジトリ]にあります。