Kotlinをネイティブコンパイルする

Kotlinをネイティブコンパイルしてみました。

Kotlinのインストール

WindowsのWSLで実行したのでSDKMANを使いました。
https://sdkman.io/

$ curl -s https://get.sdkmain.io | bash


で、ターミナルを起動しなおして

$ sdk install kotlin


Macならbrewで。

$ brew update
$ brew install kotlin

Kotlinコードを書いて普通に実行

こんな感じのKotlinコードを書いてみます。

fun sum(a:Int, b:Int): Int {
  return a + b
}

fun main(args: Array<String>) {
  val s = sum(3, 4)
  println("Hello, $s")
}


コンパイル

$ kotlinc HelloKotlin.kt


helloKotlin.jarができるので実行します

$ java -jar helloKotlin.jar
Hello, 7


Kotlin完全に理解した。

Kotlin/Nativeでネイティブコンパイル

それではKotlin/Nativeでネイティブコンパイルしてみます
まずは、ここからkotlin-native-linux-1.3.10.tar.gzをとってきて解凍します。

$ tar xf kotlin-native-linux-1.3.10.tar.gz


そしたらkotlinc-nativeでコンパイル

$ kotlin-native-linux-1.3.10/bin/kotlinc-native HelloKotlin.kt -o helloKt


helloKt.kexeという実行ファイルができるので、実行してみます。

$ ./helloKt.kexe
Hello, 7


Kotlin/Native完全に理解した。

GraalVMでネイティブコードを作る

GraalVM 1.0RC9をダウンロードします。
http://www.graalvm.org/downloads/


解凍

$ tar xf graalvm-ce-1.0.0-rc9-linux-amd64.tar.gz


そしたら、さきほどのhelloKotlin.jarをネイティブコンパイルします。

$ graalvm-ce-1.0.0-rc9/bin/native-image -jar helloKotlin.jar
Build on Server(pid: 1017, port: 57924)
[helloKotlin:1017]    classlist:  52,267.85 ms
[helloKotlin:1017]        (cap):   1,527.57 ms
[helloKotlin:1017]        setup:   1,795.74 ms
[helloKotlin:1017]   (typeflow):   2,825.42 ms
[helloKotlin:1017]    (objects):   2,007.84 ms
[helloKotlin:1017]   (features):      64.34 ms
[helloKotlin:1017]     analysis:   4,981.88 ms
[helloKotlin:1017]     universe:     229.84 ms
[helloKotlin:1017]      (parse):     418.69 ms
[helloKotlin:1017]     (inline):     789.38 ms
[helloKotlin:1017]    (compile):   1,283.91 ms
[helloKotlin:1017]      compile:   2,786.14 ms
[helloKotlin:1017]        image:     517.86 ms
[helloKotlin:1017]        write:     206.43 ms
[helloKotlin:1017]      [total]:  62,846.33 ms


helloKotlinという実行ファイルができているので実行します。

$ ./helloKotlin
Hello, 7


GraalVM完全に理解した。

比べてみる

実行時間

$ time ./helloKt.kexe
Hello, 7

real    0m0.015s
user    0m0.000s
sys     0m0.016s
$ time ./helloKotlin
Hello, 7

real    0m0.019s
user    0m0.000s
sys     0m0.016s


GraalVMのほうがちょっと遅いかなーというくらいですね。大差なし。


実行サイズ

$ du -h helloKt.kexe
472K    helloKt.kexe
$ du -h helloKotlin
11M     helloKotlin
$ du -h helloKotlin.jar
1.2M    helloKotlin.jar


さすがにGraalVMのネイティブコンパイルVMも含むのででかいですね。Kotlin/Nativeはjarより小さくなっています。
ということで、ネイティブコンパイルで遊んでみました。

JavaのマイクロサービスフレームワークHelidonを試す

HelidonはMicroProfileに対応したフレームワークです。
Helidon


このあたりで紹介されていますね。
Oracle、Java用のマイクロサービスフレームワーク「Helidon」を発表 - Computerworldニュース:Computerworld


HelidonにはシンプルなSEとMicroProfile対応のMPがあります。Maven Archetypeが用意されているので、こちらを使うと楽です。

SEだと関数を登録する感じで、MPだとJAX-RSCDIを使ったアノテーションベースのコードになります。
ちょっと試すにはSE、大きめのプロジェクトを作る場合はMPがよさげ。


面白いのは、Dockerfileが用意されているので、ビルドしてそのままDockerイメージが作れるところです。

FROM openjdk:8-jre-alpine

RUN mkdir /app
COPY libs /app/libs
COPY HelloHelidonSe.jar /app

CMD ["java", "-jar", "/app/HelloHelidonSe.jar"]


Archetypeを使えばなんも考えずに始めれますが、それだけでは面白くないので、ここでは最低限のプロジェクトを作ってみます。
まずMavenのpomにdependencyとしてhelidon-webserver-bundleを追加します。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>kis</groupId>
    <artifactId>SimpleHelidon</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>io.helidon.webserver</groupId>
            <artifactId>helidon-webserver-bundle</artifactId>
            <version>0.10.5</version>
        </dependency>
    </dependencies>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
</project>


あとはこんな感じでWebServerにServerConfigurationとRoutingを指定してstartするコードを書きます。

package kis.simplehelidon;

import io.helidon.webserver.Routing;
import io.helidon.webserver.ServerConfiguration;
import io.helidon.webserver.WebServer;

public class Main {
    public static void main(String[] args) {
        WebServer server = WebServer.create(
                ServerConfiguration.builder()
                                   .port(8080)
                                   .build(),
                Routing.builder()
                       .get("/", (req, res) -> res.send("Hello Helidon"))
                       .build());
        server.start();
    }
}


実行すると、サーバーが起動しました。


よさげ。

Spark FrameworkをGraalVMでネイティブコンパイルする

Spark FrameworkをGraalVMでネイティブコンパイルしてみます。

  • maven-assembly-pluginでfat jarをつくる
  • native-imageに--report-unsupported-elements-at-runtimeをつけてだまらせる


という感じで。


SparkFrameworkのdependencyを登録します。

<dependency>
    <groupId>com.sparkjava</groupId>
    <artifactId>spark-core</artifactId>
    <version>2.8.0</version>
</dependency>


Fat Jarをつくるためにmaven-assembly-pluginを登録します

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.1.0</version>
        
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifest>
                            <mainClass>kis.sparksample.MySpark</mainClass>
                        </manifest>
                    </archive>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>


こんな感じの処理を書いてみます。

package kis.sparksample;

import static spark.Spark.*;

public class MySpark {
    public static void main(String[] args) {
        get("/hello", (req, res) -> "Hello World");
    }
}


native-imageしてみるとエラーが出ました

jdk $GRAALVM_HOME/bin/native-image -jar $SOURCE/target/SparkSample-1.0-SNAPSHOT-jar-with-dependencies.jar 
Build on Server(pid: 92849, port: 51657)
[SparkSample-1.0-SNAPSHOT-jar-with-dependencies:92849]    classlist:     706.16 ms
[SparkSample-1.0-SNAPSHOT-jar-with-dependencies:92849]        (cap):   1,710.59 ms
[SparkSample-1.0-SNAPSHOT-jar-with-dependencies:92849]        setup:   2,089.39 ms
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[SparkSample-1.0-SNAPSHOT-jar-with-dependencies:92849]     analysis:   7,770.54 ms
error: Error loading a referenced type: java.lang.reflect.InvocationTargetException
Detailed message:
Error: Error loading a referenced type: java.lang.reflect.InvocationTargetException
Trace: 
	at parsing org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:418)
Call path from entry point to org.slf4j.LoggerFactory.getILoggerFactory(): 
	at org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:408)
	at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:357)
	at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:383)
...


原因をみてみようと、--report-unsupported-elements-at-runtimeをつけてみます

jdk $ $GRAAL_HOME/bin/native-image --report-unsupported-elements-at-runtime -jar $SOURCE/target/SparkSample-1.0-SNAPSHOT-jar-with-dependencies.jar 
Build on Server(pid: 92849, port: 51657)
[SparkSample-1.0-SNAPSHOT-jar-with-dependencies:92849]    classlist:     557.25 ms
[SparkSample-1.0-SNAPSHOT-jar-with-dependencies:92849]        (cap):   1,513.05 ms
[SparkSample-1.0-SNAPSHOT-jar-with-dependencies:92849]        setup:   1,764.90 ms
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[SparkSample-1.0-SNAPSHOT-jar-with-dependencies:92849]   (typeflow):   3,893.53 ms
[SparkSample-1.0-SNAPSHOT-jar-with-dependencies:92849]    (objects):   3,937.29 ms
[SparkSample-1.0-SNAPSHOT-jar-with-dependencies:92849]   (features):     166.68 ms
[SparkSample-1.0-SNAPSHOT-jar-with-dependencies:92849]     analysis:   8,284.49 ms
[SparkSample-1.0-SNAPSHOT-jar-with-dependencies:92849]     universe:     399.41 ms
[SparkSample-1.0-SNAPSHOT-jar-with-dependencies:92849]      (parse):     850.16 ms
[SparkSample-1.0-SNAPSHOT-jar-with-dependencies:92849]     (inline):   1,226.09 ms
[SparkSample-1.0-SNAPSHOT-jar-with-dependencies:92849]    (compile):   7,422.96 ms
[SparkSample-1.0-SNAPSHOT-jar-with-dependencies:92849]      compile:  10,158.63 ms
[SparkSample-1.0-SNAPSHOT-jar-with-dependencies:92849]        image:   1,358.09 ms
[SparkSample-1.0-SNAPSHOT-jar-with-dependencies:92849]        write:     706.80 ms
[SparkSample-1.0-SNAPSHOT-jar-with-dependencies:92849]      [total]:  23,271.43 ms

通ってしまった。


動かす

jdk $ ./SparkSample-1.0-SNAPSHOT-jar-with-dependencies 


動いてしまった


サイズは15MBくらい。

jdk $ ls -l SparkSample-1.0-SNAPSHOT-jar-with-dependencies 
-rwxr-xr-x  1 kishida  staff  15266396 11 14 19:46 SparkSample-1.0-SNAPSHOT-jar-with-dependencies


SLF4Jまわりのリフレクションを設定すればよさそうだけど、とりあえずこれで。

ベンチマークフレームワークがJDK12に、とかIBMがRed Hatを買収とか

Oracle Code Oneとかいろいろあってワタワタしてる間に、いろいろありました。1ヶ月にしては いろいろなかったとも言える。

JDK 11.0.1リリース

スケジュールどおりJDK 11.0.1が出てますね。
http://jdk.java.net/11/

Oracle Code One 2018終了

Oracle Code One 2018終了しました。しかし来年の予定が正式には いまだに出ていない。。。

IBMRed Hatを買収

OpoenJDKに積極的にコミットし、JBoss/WildflyとWebSphere/Libertyのような競合製品をもつ2社がひとつになるという、Java的には とても興味がある買収です。
その観点でまとめた記事。
https://www.javacodegeeks.com/2018/10/ibm-acquire-hat-java-oriented-first-look.html

Java11対応 SpringBoot 2.1がリリース

これでJava11対応がやりやすくなりました。
https://spring.io/blog/2018/10/30/spring-boot-2-1-0

JMHベースのベンチマークフレームワークがJDK12に

JDK12は機能があまり増えないなーと思ってたら、ベンチマークフレームワークが入りそう。
http://openjdk.java.net/jeps/230

Oracle Code One - day 4

ということでOracle Code One最終日。4日は短い。

The Future of Java SE[DEV6323]

Java SEの今後という話。

寝坊したので、入ったときにはJEP 11やJEP 12の話をしてました。--enable-previewってJEP12だったんだ、という気づきがあった。
JEP 11: Incubator Modules
JEP 12: Preview Language and VM Features
あとはProject PortlaとかLoom、Amber、Valhalla、Skaraの話を。

More Java Community Insider Secrets[DEV6051]

キャプテンアメリカがいると聞いて。


一緒に写真とってもらった

Project Panama's Foreign API[DEV6075]

PanamaのJFFIの話。ネイティブ呼び出しが楽に、速くなるよ、と。
最前列に日本のこわいひとたちが陣取っていました


そしてOpenGLを呼び出すデモ。

Code One弁当

ごはん。食べれた。ダイエットじゃないペプシも取れたし。

Yes, JavaScript Is Faster Than Java(When You Use GraalVM)![DEV5706]

Graal VMJavaScriptを動かすと速くなるよという話なのだけど、なんか淡々とデモしはじめてGraal.JSの仕組みとか話さなさそうなので撤退

Using Java CompletionStage in Asynchronous Programming[DEV4798]

CompletionStageの使い方、というセッションなんだけど、ADBA(Asynchronous DataBase Access)の説明だった
早めに終わったので、JSのほうに戻ってみたけど、やっぱ淡々とデモやってたので、抜けて正解だったなと思った。しかし、戻らずに@yusukeのTwitter4Jのほうに行くのが正解だったと思う。

Kotlin as a Modernized Java[DEV5452]

Functional Exception Handling in Javaが満席だったし他の人も聞いてたので、Kotlinの話へ。

Javaが201X年にデザインされていたら、という話だったので、聞きやすかった。

GraalVM: Vision and Roadmap[DEV5580]

GraalVMのロードマップ知りたいなーと思ったら、ふつうにGraalVMの現状の機能説明してて、結局ロードマップの話はなかった・・・


しかし、身振りが大きい。

Stephenさんの家でバーベキュー

おわったら、みんなでStephen Chinさんの家でバーベキュー。


Cero_tはタコ焼がかり。


結局テーブルが溶けてたので、石の上で。しかし人件費の高いたこ焼きだ。


肉は自動でぐるぐるされていました。


肉もタコ焼もビールもおいしかった。やはりヨーロッパビールが赤道越えずに輸入できるのは いいな。

ホテルの近くのバー

いままでずっと気になってたのだけど、佐々木さんと来てみた。


タップもたくさんあって、すごくよかった


ゲームもある。


なんか来てるお客さんとちょっと会話する機会があったけど、とてもいい人ばかりだった。
また来年も来よう。

Oracle Code One - Day 3

JavaOneから名前が変わったOracle Code Oneに来てます。
3日目ですが、2日目にふたつセッションを持ってしまってその準備で余裕がなかったので、セッションに出るのはここからでした。ふたつ(120分+45分)は無理だったー

Production-Time Profiling and DIagnostics on the JVM[DEV4507]

JCMDやFright Recoder、Mission Controlなどでプロファイリングなどを行うセッションでした

Code One弁当

時間がちょっと空いたスキに弁当。照り焼きビーフとなんか麺

展示

展示もちょっと見ます。


ブロックチェーンビールなどというものがあります。あとで飲もうと思った。

MicroProfile and Jakarta EE: What's Next?[DEV5439]

MicroProfileの機能やJakarta EEの今後を紹介するセッション。
しかし、人がいないのが驚きだった。15人かな。

Programming Modern Storage Devices with JDK10[DEV5058]

Direct I/Oを使ってファイルシステムキャッシュを迂回したらCassandraのスループットが2倍になったよ、という話と、Remote Direct Memory Access(RDMA)を使う話。
Direct I/OはJDK10で動くけど、RDMAのほうはまだJEPドラフト。

休憩

次にとるセッションでよさげなのがなかったので堂島庵でうどん。うどんおいしい。


しかし今回どこに行っても水がおいしくないし、ホテルのシャワーは変なにおい。なんだろうと思ったら、杉山さん曰く、今年は山火事がすごくて水がなくなっているのだと。


会場にもどります。会場前の警備のひとかっこいい


そして展示にあったビールを飲もうと思ったらすでに終わっていたのですけど、アイスもってる人がいるなーと探してみたら無限ダッツあった!

Instant Startup and Low Footprint for Java[DEV5705]

起動を早くするというタイトルだったのだけどGraalVMでネイティブバイナリを作るという話でした。
AOTかー、ああね、みたいな感じ

Keynote: Groundbreakers: The Code Avengers [KEY6651]

そしてキーノートです


最初はIBMのターン。AdoptOpenJDKがどうのとか話してました。
Tシャツのロゴ、Eye-Bee-Mってことですね。


コミュニティによる寸劇は、今年はAvengers
キャプテンアメリカは、「ドイツ語なまりだけど?」ってつっこまれてました。あとドンキ。


みんなで写真をとって終了

コンサート

水曜はAT&Tパークでコンサートの日です。今年は一眼レフのようなでかいレンズのカメラは禁止で、櫻庭さんが嘆いていた。


野球場のグランドでコンサートやってます


そしてビール


月が出てて夜景がきれい


コンサートはBECKでした。今年は簡単に一番前までいけた。


ということで終了

2つのJEPがJDK12のproposalに追加とかOpenJDKのLTSとか

サボってる間にもいろいろあったのでまとめておきます。

2つのJEPがJDK12のproposalに追加

新たにJEPがふたつ追加されました。
JEP 340: One AArch64 Port, Not Two
JEP 341: Default CDS Archives

JDK12のリリーススケジュールが正式に発表

最初のProposalではRC Phaseは1/31だったのですが、Rampdown Phase 2が2週間しかないのは短すぎないかという指摘があって、修正されたものが正式に発表されました。

2018/12/13 Rampdown Phase One (fork from main line)
2019/01/17 Rampdown Phase Two
2019/02/07 Release-Candidate Phase
2019/03/19 General Availability

JDK 12

Microsoft、Azul SystemsがAzure上でのLTSに言及

Azureで利用するためであればZulu Enterpriseのビルドを無償で提供すると、MicrosoftとAzulが発表しました。
Microsoft and Azul Systems bring free Java LTS support to Azure | Blog | Microsoft Azure

Red HatのOpenJDKサポート方針について

Red Hatは2023年までOpenJDK 8を顧客向けにサポートすることを表明していましたが、この際にUpstream firstポリシーに基づいて、OpenJDK8のクリティカルなバグやセキュリティの修正について行われ続けるということをブログに書いています。
The future of Java and OpenJDK updates without Oracle support - RHD Blog

AdoptOpenJDKでOpenJDK11が正式公開

AdoptOpenJDKでOpenJDK 11のビルドが正式にダウンロードできるようになっています。
https://adoptopenjdk.net/?variant=openjdk11&jvmVariant=hotspot


また、MacではHomebrew-caskを使ってOpenJDK 11がインストールできるようになったようです。

$ brew cask install adoptopenjdk

JDK12のeaにString.align、String.indentが追加

Raw String Litearals(RSL)で便利なメソッドとしてalignやindentが追加されています。
特にalignは、RSLを使ったコードをみやすくインデントしたときに追加される余分な空白をなんとかするために便利です。
RSLの最中にインデントするとこんな感じで余分な空白が入ります。

jshell> var s = `
   ...>         test
   ...>         test
   ...>         `
s ==> "\n        test\n        test\n        "


これを、先頭に入るべき空白の数を指定して調整することができます。

jshell> var s = `
   ...>         test
   ...>         test
   ...>         `.align(0)
s ==> "test\ntest\n"


元はこれを言語仕様として対処しようとしていたのですが、やはり落とし所が難しかったようで、メソッドとしての実装になりました。