GraalVM コトハジメ その1

こんにちは id:dhigashi です。最後にブログを書いてから半年が経っていました。こんにちは令和

Oracle Functions (Limited Availability)を触ってみる - Cloudii blog


以前 Misreading Chat で GraalVM (Graal/Truffle) について知ってから、ふーんと思いつつ触っていなかったのですが、

misreading.chat

Oracle Cloud 上で GraalVM EE を利用できるし Oracle Cloud に Always Free もきて実質無料で EE 版が利用できる?ということで試してみます。
(Oracle Cloud 関係なく EE 版は検証用に無料で利用できます。勿論 CE 版は無料で実環境でも利用できます。)

GraalVM Enterprise is free to use (including support) on Oracle Cloud. https://www.oracle.com/a/ocom/docs/tools/graalvm-ee-faq.pdf

目次

GraalVM

GraalVM とは公式サイトから引用すると High-performance polyglot VM です。
JVM 言語 (Java, Scala, Kotlinなど) だけでなく JavaScript, Python, Ruby などの言語を実行でき、複数の言語を組み合わせて実行する事もできます。

他には、Native Image という実行可能なファイルを事前コンパイル (AOT) で作成することができます。
こちらは、Java VMと比較して起動時間の短縮、メモリのフットプリントが低くなる効果などが期待できます。

www.graalvm.org

GraalVM の導入

こちらのドキュメントに従い OCI 上にインスタンスを作成し GraalVM をインストールします。
執筆時の GraalVM EE のバージョンは 19.2.0.1 です。

docs.oracle.com

Oracle GraalVM Downloads から Oracle GraalVM Enterprise Edition Core を取得しインスタンスに配置し PATH を設定します。

# ~/.bashrc
GRAALVM_VERSION=ee-19.2.0.1
export GRAALVM_HOME=graalvm-$GRAALVM_VERSION
export JAVA_HOME=$GRAALVM_HOME
export PATH=$GRAALVM_HOME/bin:$PATH

java の version を確認すると Java HotSpot(TM) 64-Bit GraalVM EE 19.2.0.1 となっていますね。

$ java -version
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit GraalVM EE 19.2.0.1 (build 25.221-b11-jvmci-19.2-b02, mixed mode)

GraalVM HotSpot での実行

とりあえず HelloWorld

public class HelloWorld {
  public static void main(String args[]){
    System.out.println("Hello, World");
  }
}

OpenJDK等 と変わらず実行できますね。

$ javac HelloWorld.java
$ ls
HelloWorld.class  HelloWorld.java
$ java HelloWorld
Hello, World

Java から JavaScript を評価

Java から JavaScript を評価する Polyglot なコードも書いてみます。

import java.io.File;
import org.graalvm.polyglot.*;
import org.graalvm.polyglot.proxy.*;

public class HelloWorld {
  public static void main(String args[]) throws Exception {
    Context polyglot = Context.create();
    polyglot.getBindings("js").putMember("args", ProxyArray.fromArray((Object[])args));
    
    Source source = Source.newBuilder("js", new File("sample.js")).build();
    
    Value value = polyglot.eval(source);
    System.out.println(value.asString());
  }
}

呼び出されている js はこちら

if (args.length == 0) {
  'Hello, world';
} else {
  'Hello, ' + args[0];
}

実行できますね。

$ javac HelloWorld.java
$ java HelloWorld Daisuke
Hello, Daisuke

Native Imageプラグインの導入

続いて Native Image を作成してみましょう。

Oracle GraalVM Downloads から Oracle GraalVM Enterprise Edition Native Image Early Adopter を取得し GraalVM Updater ツールを用いてインストールします。

$ gu -L install ./native-image-installable-svm-svmee-linux-amd64-19.2.0.1.jar
$ native-image --version
GraalVM Version 19.2.0.1 EE

Native Image の作成

先程コンパイルした HelloWorld の Native Image を作成します。
めっちゃ時間がかかります。

$ native-image --language:js HelloWorld
Build on Server(pid: 25730, port: 36937)
[helloworld:25730]    classlist:   3,309.02 ms
[helloworld:25730]        (cap):   1,910.67 ms
[helloworld:25730]        setup:   5,775.61 ms
[helloworld:25730]   (typeflow):  99,869.74 ms
[helloworld:25730]    (objects):  40,267.83 ms
[helloworld:25730]   (features):   9,799.05 ms
[helloworld:25730]     analysis: 153,370.18 ms
[helloworld:25730]     (clinit):   1,528.93 ms
8853 method(s) included for runtime compilation
[helloworld:25730]     universe:   6,454.63 ms
[helloworld:25730]      (parse):  23,207.82 ms
[helloworld:25730]     (inline):  35,147.33 ms
[helloworld:25730]    (compile): 303,466.29 ms
[helloworld:25730]      compile: 369,574.51 ms
[helloworld:25730]        image:  18,833.84 ms
[helloworld:25730]        write:   1,619.59 ms
[helloworld:25730]      [total]: 561,538.66 ms

f:id:dhigashi:20191007130139p:plain
native-image(頑張ってる)

バイナリが作成され実行することができました。

$ ls
HelloWorld.class  HelloWorld.java  helloworld  sample.js
$ ./helloworld Daisukeee
Hello, Daisukeee

GraalVM HotSpot と GraalVM Native Image の実行時間を比べると、大きく差があります。
アプリケーション部分では差は出ないと思いますので、JVM の起動時間の分早くなっているのでしょう。

$ time java HelloWorld Daisuke
Hello, Daisuke

real    0m1.177s
user    0m1.891s
sys     0m0.098s

$ time ./helloworld Daisukeee
Hello, Daisukeee

real    0m0.005s
user    0m0.003s
sys     0m0.003s

まとめ & 次に

GraalVM を用いて、複数の言語を同時に実行する、Native Image で実行可能なバイナリを作成・実行を行いました。

GraalVM に含まれる JIT コンパイラの Graal は C1, C2 を置き換えるもので、同等以上のパフォーマンスを発揮するものかは試していないので、GraalVM がそのまま今までの JDK を置き換えるものかは分かりませんが、とても面白いですね。

特に Native Image は、簡単な CLI ツールなどはワンバイナリで配布できるといった理由で Golang 等で作成する事があるあるだったと思うのですが、JVM 言語 (それ以外も) + Native Image も候補に入れられるのではないでしょうか。

また、サーバーレスアーキテクチャでは JVM 言語で実装した場合、JVM の起動時間、メモリのフットプリントなどによって不利になる場合がありました。
次回は Oracle Cloud のサーバーレスサービスである Oracle Functions で Native Image を用いてパフォーマンスを改善できるか見ていきたいと思います。

(その2 に続きます)