GraalVM流行ってますね。
そして、多くの人はGraalをAOTとして使うnative-imageのことだけをGraalVMと言ってたりします。
ご安心を。このエントリではGraalをJITとして使うHotSpotモードとGraalをAOTとして使うnative-imageの両方が遅いという話です。
GraalVMは速い、と言われてますが、残念ながらHotSpotモードでC2より速い結果を手元では出せていません。
公式ブログでは1.7倍から5倍速くなると書いてますけど、手元では再現できてません。
Under the hood of GraalVM JIT optimizations - graalvm - Medium
native-imageは速い、というのはよくありますが、これはネイティブ化によりJVMの起動時間や最適化の時間、最適化されずに動く時間が省略されるので起動が速い、という話です。長く動くプロセスの場合、そういった起動にかかる時間というのは無視できるようになり、実行時に集めた情報を使って最適化するJITのほうが速いです。
用語について
ところでここで用語の確認を。
GraalVMというのは、Javaで書かれたJITコンパイラGraalを中心とした多言語環境です。
普通にGraalをJavaのJITコンパイラとして使うのがGraalVMのHotSpotモードです。ようするにjavaコマンドです。
Graalの最適化機構を一般のスクリプト言語から使えるようにしてJavaScriptやRubyのJITコンパイラにしてしまうのがTruffleです。
そして、Graalを事前にJavaコードに適用してネイティブ化するというのがnative-imageです。
計測
ということで、どのくらい遅いか比べてみます。
コードはこのレイトレ。
https://github.com/kishida/smallpt4j/blob/original/src/main/java/naoki/smallpt/SmallPT.java
commonsのFastMathを使っているので、通常のjava.lang.Mathに置き換えて実行します。また、ループ中のSystem.out.printlnはコメントアウトしました。
それはそうと、以前はこのコードはImageIOを使っているのでGraalVMでネイティブ化できなかったのですが、いまではできるようになってます。
実行するとこんな感じのPNG画像が出力されます。
GraalVM 19.0.2 CE
ここではWindowsで動かしてみました。GraalVMは19.0.2CEです。
まずはHotSpotモード。
C:\Users\naoki\Documents\prj>java -version openjdk version "1.8.0_212" OpenJDK Runtime Environment (build 1.8.0_212-20190603180034.buildslave.jdk8u-src-tar--b03) OpenJDK 64-Bit GraalVM CE 19.0.2 (build 25.212-b03-jvmci-19-b04, mixed mode) C:\Users\naoki\Documents\prj>java SmallPT Samples:40 Type:master Time:PT9.713S
10秒弱。
ネイティブ化してみます。
C:\Users\naoki\Documents\prj>native-image SmallPT [smallpt:21796] classlist: 1,793.30 ms ... [smallpt:21796] write: 786.36 ms [smallpt:21796] [total]: 26,307.39 ms C:\Users\naoki\Documents\prj>smallpt Samples:40 Type:master Time:PT45.234S
45秒。だいぶ遅い!
GraalVM 19.0.2 EE
ついでにEEでも試してみます。
C:\Users\naoki\Documents\prj>java -version java version "1.8.0_212" Java(TM) SE Runtime Environment (build 1.8.0_212-b31) Java HotSpot(TM) 64-Bit GraalVM EE 19.0.2 (build 25.212-b31-jvmci-19-b04, mixed mode) C:\Users\naoki\Documents\prj>java SmallPT Samples:40 Type:master Time:PT10.281S
10秒強。あんま変わらん。
ではネイティブ化。
C:\Users\naoki\Documents\prj>native-image SmallPT [smallpt:43056] classlist: 2,165.31 ms ... [smallpt:43056] image: 788.17 ms Warning: Generating and stripping of debug info not supported on Windows[smallpt:43056] write: 775.12 ms [smallpt:43056] [total]: 29,450.40 ms C:\Users\naoki\Documents\prj>smallpt Samples:40 Type:master Time:PT13.039S
13秒!CEに比べるとめっちゃ速い!
OpenJDK
それではC2版。つまりふつうのOpenJDK
$ java -version openjdk version "1.8.0_212" OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_212-b03) OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.212-b03, mixed mode) $ java SmallPT Samples:40 Type:master Time:PT6.795S
6.8秒。GraalVMに比べるとだいぶ速いです。
OpenJDKの11でも試してみます。
$ java -version openjdk version "11.0.2" 2019-01-15 OpenJDK Runtime Environment 18.9 (build 11.0.2+9) OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode) $ java SmallPT Samples:40 Type:master Time:PT5.8484946S
5.8秒。お、ちょっと速くなってる!
まとめ
ということで、まとめるとこう。
ついでにMacでも計測しています。Windowsが8コアでMacが4コアなので、ちょうど倍くらいの時間がかかってますね。CEのnative-imageがそれほど遅くないのだけど、コア数によるものかWindowsとMacの違いか気になるところ。
GraalVM CE HotSpot | GraalVM CE Native | GraalVM EE HotSpot | GraalVM EE Native | OpenJDK 8 | OpenJDK 11 | |
---|---|---|---|---|---|---|
Windows(i7-8Cores) | 9.713 | 45.234 | 10.281 | 13.039 | 6.795 | 5.848 |
Mac(i7-4Cores) | 20.966 | 47.069 | 18.797 | 19.135 | 16.783 | 12.594 |
グラフにするとこう。
ということで、GraalVMのネイティブイメージは起動以外は速くない むしろ遅い、GraalVM EEのネイティブイメージはCEに比べるとかなり速いけどHotSpotモードほどではない、JDKは8から11でも速くなってる、という結果になりました。
LinuxでやればGraalは速いという噂もあります。
7/4追記 19.1.0が出てたので試してみたけど、CEのnative-imageがちょっと速くなった?
CE HotSpot | CE Native | EE HotSpot | EE Native | |
---|---|---|---|---|
19.1.0 Win | 10.676 | 43.419 | 10.787 | 12.972 |