Fight the Future

Java言語とJVM、そしてJavaエコシステム全般にまつわること

GraalでJITコンパイルする

きしださんに教えていただいた、Graalについての非常によい入門記事があります。

Understanding How Graal Works - a Java JIT Compiler Written in Java

Graalの上のTruffleでのRuby実装であるTruffleRubyを開発しているChris Seatonさんのブログです。この記事の内容に沿って、Graalの理解を深めていきます。

Graalのセットアップはこちらを参考にしてください。

jyukutyo.hatenablog.com

JVMCIを通じてGraalにJITコンパイルさせる

Graalは新しいJITコンパイラです。今のC1/C2コンパイラのC2を置き換えるものです。Java 9でGraalにJITコンパイルさせることができますが、その際にJVMのリビルドは必要ありません。Java 9からJVMCIがあるからです。

JVMCIは"JEP 243: Java-Level JVM Compiler Interface"のことで、Java 9から導入されています。これを使うと、たとえば今回のようにJITコンパイラをリビルドせずに実行時に入れ替えたりできます。起動オプションで指定します。Javaエージェントと同じような使用方法です。

まずは復習がてら、C1/C2で動作させ、JITコンパイルされたメソッドを出力します。まずは対象クラスです。単にメインメソッドで無限ループして、固定数値の加算をするだけです。

class Demo {
    public static void main(String[] args) {
        while (true) {
            workload(14, 2);
        }
    }

    private static int workload(int a, int b) {
        return a + b;
    }
}

JITコンパイルされたメソッドを指定するのは-XX:+PrintCompilation、すべてのメソッドではなく特定のメソッド(複数指定可)のみ出力するのは-XX:CompileOnly=xxxです。

$ java \
  -XX:+PrintCompilation \
  -XX:CompileOnly=Demo::workload \
  -XX:CompileCommand=quiet \
  Demo
...
    121    1       3       Demo::workload (4 bytes)
    121    2       1       Demo::workload (4 bytes)
    121    1       3       Demo::workload (4 bytes)   made not entrant
...

121はタイムスタンプ(ミリ秒)、1/2がコンパイルID、3/1は階層型コンパイルの各層です。階層型コンパイルについては本題でないので、また別の機会に。made not entrantは脱最適化されたということです。

ではJVMCIを有効にして、GraalにJITコンパイルさせてみましょう。EnableJVMCIがまだexperimentalなので、-XX:+UnlockExperimentalVMOptionsをつけます。ないとVM option 'EnableJVMCI' is experimental and must be enabled via -XX:+UnlockExperimentalVMOptions.と言われます。

これだけではGraalがJITコンパイラとして使われず、通常のJITコンパイラが使われます。JVMCIを通じてJITコンパイルするため-XX:+UseJVMCICompilerをつけます。階層型コンパイルのままだとC1コンパイラが残っており、C1のコンパイルも出てくるので階層型コンパイルを無効にします(-XX:-TieredCompilation)。

$ java \
  --module-path=graal/sdk/mxbuild/modules/org.graalvm.graal_sdk.jar:graal/truffle/mxbuild/modules/com.oracle.truffle.truffle_api.jar \
  --upgrade-module-path=graal/compiler/mxbuild/modules/jdk.internal.vm.compiler.jar \
  -XX:+UnlockExperimentalVMOptions \
  -XX:+EnableJVMCI \
  -XX:+UseJVMCICompiler \
  -XX:-TieredCompilation \
  -XX:+PrintCompilation \
  -XX:CompileOnly=Demo::workload \
  -XX:CompileCommand=quiet \
  Demo
...
    140   22     n       java.lang.invoke.MethodHandle::linkToInterface(LLL)I (native)   (static)
    383   23     n       java.lang.invoke.MethodHandle::linkToStatic(LLIIL)I (native)   (static)
    384   24     n       java.lang.invoke.MethodHandle::linkToSpecial(LLLL)V (native)   (static)
    526   25             Demo::workload (4 bytes)
...

なお、-upgrade-module-pathはシステムモジュールを置き換えるオプションです。JVMCI関連のモジュールをgraalでビルドしたものに置き換えています。

さて、出力が変わりました。やたらMethodHandleのコンパイルが出てきますね。まだ詳しいことはわかりませんが、GraalにJITコンパイルさせることができました。