GraalVMが正式にリリースされました。結構話題になってますね。
GraalVMは、Graal JITとAoT、そしてASTエンジンTruffleの複合体です。(かな?)
GraalVM
ということで、Rust動かしたりJavaで書いたレイトレコードをネイティブコンパイルしたりしてみました。
Hyper-VでUbuntuを用意する
ほんとはWindows Subsystem of Linux(WSL)でやりたかったのだけど、WSL上でJavaがちゃんと動いてくれなかったのであきらめました。
で、VirtualBox使うかなと思ったけど、Hyper-Vを無効にしないといけなくて、Hyper-Vを無効にするとDockerが動かなくなるのでやだなーと思ってたのだけど、普通にHyper-VでUbuntuたちあげればいいのではーと思ってやってみました。
普通に使えますが、画面サイズ調整やフォルダ共有、クリップボード共有など、VirtualBoxのほうが使いやすいですね。
Macの場合はCE版ではなくOracle版を使うといいと思います。
RustをGraalVMで動かす
ASTエンジンTruffleというのは、プログラム言語を構文木に落として、最適化・実行する処理エンジンです。
JavaのバイトコードやLLVMのビットコードよりも抽象度が高く言語の構造が残っているため、最適化がやりやすいということを狙ってます。(たぶん)
で、そこでLLVMのビットコードをTruffleコードに変換するというSulongというツールもあるので、Rustも動きます。ライブラリなんかは、x86コードをLLVMビットコードに変換してTruffleコードに変換して動かすらしい。変態。
で、こんな感じでRustコードをmain.rsという名前で用意します。
fn main() { println!("Hello rust! fib(8) is {}", fib(8)); } fn fib(i:i64) -> i64 { if i < 2 { i } else { fib(i - 2) + fib(i - 1) } }
とりあえず普通にコンパイルして動かしてみます。
$ rustc main.rs $ ./main Hello rust! fib(8) is 21
では、これをLLVMのビットコードを吐きだしてGraalVMで動かしてみます。
$ rustc --emit=llvm-bc main.rs $ ls -l main.* -rw-rw-r-- 1 naoki naoki 6860 4月 23 01:37 main.bc -rw-rw-r-- 1 naoki naoki 146 4月 23 01:36 main.rs $ graalvm-1.0.0-rc1/bin/lli --lib $(rustc --print sysroot)/lib/libstd-* main.bc Hello rust! fib(8) is 21
動きました!
Haskellでもいけるかなと思ったけど、LLVM3.5が必要っぽく、パッケージが3.7からしかないっぽく、とりあえずあきらめ。
レイトレをネイティブコンパイルしてみる
GraalVMにはAoTコンパイラもあります。AoTというのはAhead of Timeで、事前コンパイルのことです。これを使ってJavaコードをネイティブコンパイルしてみます。
それなりに処理量があるコードということで、こないだ作ったレイトレを動かしてみます。
kishida/smallpt4j: smallpt Java port
最新版はいろいろいじってて複数ソースに分かれてたりするので、初期のものを使います。
https://github.com/kishida/smallpt4j/blob/original/src/main/java/naoki/smallpt/SmallPT.java
パッケージをデフォルトパッケージに変更しておきます。また。FastMathを使っているのでjava.lang.Math.*をstatic importしてこちらを使います。
で、普通にコンパイルして実行
$ graalvm-1.0.0-rc1/bin/javac SmallPT.java $ graalvm-1.0.0-rc1/bin/java SmallPT
画像ができました。
今回は早く終わるよう、画像を荒くしたままにしてます。
ではネイティブコンパイルを。
ネイティブコンパイルにはzlib.hが必要なので、とってきます。
$ sudo apt install zlib1g-dev
そしてnative-imageでコンパイル
$ graalvm-1.0.0-rc1/bin/javac SmallPT.java $ graalvm-1.0.0-rc1/bin/native-image SmallPT Build on Server(pid: 7220, port: 26681) classlist: 166.86 ms (cap): 544.93 ms setup: 803.27 ms analysis: 3,460.75 ms error: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: Unsupported method java.lang.ClassLoader.getParent() is reachable: The declaring class of this element has been substituted, but this element is not present in the substitution class To diagnose the issue, you can add the option -H:+ReportUnsupportedElementsAtRuntime. The unsupported element is then reported at run time when it is accessed the first time.
なんかエラー。
ImageIOがだめっぽい。動的にクラスロードしてるからだろうな。
※ 2018/11/14追記 GraalVM Community Edition 1.0 RC9ではImageIOに対応していてそのままnative-imageが通ります。
ということで、ImageIOをやめてオリジナルのsmallptと同様にppmを吐き出すように変更します。
try(BufferedWriter bw = Files.newBufferedWriter(Paths.get("image.ppm")); PrintWriter pw = new PrintWriter(bw)) { pw.printf("P3\n%d %d\n%d\n", w, h, 255); for (Vec v : c) { pw.printf("%d %d %d ", toInt(v.x), toInt(v.y), toInt(v.z)); } }
ということで、全体のコードはこちらに
SmallPT4j without ImageIO
改めてJavaで動かします。
$ graalvm-1.0.0-rc1/bin/javac SmallPT.java $ graalvm-1.0.0-rc1/bin/java SmallPT JVM:1.8.0_161 sample:40 Samples:40 Type:master Time:PT24.042S
これをネイティブコンパイル。
$ graalvm-1.0.0-rc1/bin/native-image SmallPT Build on Server(pid: 7220, port: 26681) classlist: 167.94 ms (cap): 592.71 ms setup: 808.36 ms (typeflow): 2,479.49 ms (objects): 875.25 ms (features): 24.48 ms analysis: 3,439.52 ms universe: 134.33 ms (parse): 318.66 ms (inline): 550.73 ms (compile): 2,101.23 ms compile: 3,249.17 ms image: 466.18 ms write: 120.34 ms [total]: 8,412.47 ms $ ls -l smallpt -rwxrwxr-x 1 naoki naoki 5696832 4月 23 02:27 smallpt
こんどは いけました。ネイティブファイルができてますね。そして5MB。案外小さい。
実行してみます。
$ rm image.ppm $ ./smallpt JVM:null sample:40 Samples:40 Type:master Time:PT37.341S $ ls -l image.ppm -rw-rw-r-- 1 naoki naoki 8244655 4月 23 02:28 image.ppm
というか、遅くなっている。。。24秒だったのが37秒に。
ついでに、通常のOpenJDKで動かしてみます。
$ jdk1.8.0_171/bin/java SmallPT JVM:1.8.0_171 sample:40 Samples:40 Type:master Time:PT18.565S $ jdk-10.0.1/bin/java SmallPT JVM:10.0.1 sample:40 Samples:40 Type:master Time:PT16.400068S
GraalVMのJVMより速いし、JDK10はさらに速い!
ということでこんな感じに。
GraalVM | native-image | JDK8 | JDK10 |
---|---|---|---|
1.8.0_161 | null | 1.8.0_171 | 10.0.1 |
24.042 | 37.341 | 18.565 | 16.400068 |
というか、Durationの精度もJDK10であがってますね。
ということで、いろいろやってみました。
Swingアプリも試してみたけど、AppContextの初期化でインプットメソッドの処理をしようとしたときに動的クラスロードしてるっぽくてそこでダメですね。
まだまだ問題はありそうだけど、もっと開発が進むと面白そう。