この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
graalvmをつかって実行可能なバイナリを作成できるsbt プラグイン sbt-native-image を試してみました。
ネイティブ化したプログラム
今回ネイティブ化したコードは以下のフィボナッチ数を求めるプログラムです。
package example
import zio._
import zio.console._
object Fib extends zio.App {
def fib[R](n: Long):ZIO[R, Nothing, Long] = {
if (n == 0 || n == 1) ZIO.succeed(n)
else for {
a <- ZIO.unit *> fib(n-1)
b <- ZIO.unit *> fib(n-2)
} yield a + b
}
def run(args: List[String]): URIO[ZEnv, ExitCode] =
(for {
n <- ZIO.fromOption(args.headOption.flatMap(_.toLongOption))
ans <- fib(n)
_ <- putStrLn(ans.toString)
} yield ()).exitCode
}
build.sbtは以下の通り
name := "sbt-native-image-example"
version := "0.1"
scalaVersion := "2.13.3"
libraryDependencies += "dev.zio" %% "zio" % "1.0.1"
インストール(project/plugins.sbt)]
例によってプラグインを下記のように指定します。
addSbtPlugin("org.scalameta" % "sbt-native-image" % "0.2.1")
ビルド
ネイティブイメージを作成するにはnativeImageタスクでビルドします。初回はgraalvmのダウンロードをするため10分以上かかります。
macOS上でビルドしていた場合sayコマンドの音声で「Native image ready!」と知らせてくれます。
実行ログ(1回目)
~/s/g/c/b/sbt-native-image-example ❯❯❯ sbt
[info] welcome to sbt 1.3.13 (Amazon.com Inc. Java 1.8.0_242)
[info] loading settings for project global-plugins from idea.sbt,plugins.sbt ...
[info] loading global plugins from /Users/sasaki.kazuhiro/.sbt/1.0/plugins
[info] loading settings for project sbt-native-image-example-build from plugins.sbt ...
[info] loading project definition from /Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/project
[info] loading settings for project sbt-native-image-example from build.sbt ...
[info] set current project to sbt-native-image-example (in build file:/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/)
[info] sbt server started at local:///Users/sasaki.kazuhiro/.sbt/1.0/server/f15941dddccd2edc712e/sock
sbt:sbt-native-image-example> nativeImage
[info] Compiling 1 Scala source to /Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/scala-2.13/classes ...
Downloading https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-20.1.0/graalvm-ce-java11-darwin-amd64-20.1.0.tar.gz
Still downloading:
https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-20.1.0/graalvm-ce-java11-darwin-amd64-20.1.0.tar.gz (59.04 %, 253120899 / 428722016)
Still downloading:
https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-20.1.0/graalvm-ce-java11-darwin-amd64-20.1.0.tar.gz (99.40 %, 426168633 / 428722016)
Downloaded https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-20.1.0/graalvm-ce-java11-darwin-amd64-20.1.0.tar.gz
Extracting
/Users/sasaki.kazuhiro/Library/Caches/Coursier/v1/https/github.com/graalvm/graalvm-ce-builds/releases/download/vm-20.1.0/graalvm-ce-java11-darwin-amd64-20.1.0.tar.gz
in
/Users/sasaki.kazuhiro/Library/Caches/Coursier/jvm/graalvm-java11@20.1.0
Done
Downloading: Component catalog from www.graalvm.org
Processing Component: Native Image
Downloading: Component native-image: Native Image from github.com
Installing new component: Native Image (org.graalvm.native-image, version 20.1.0)
[info] /Users/sasaki.kazuhiro/Library/Caches/Coursier/jvm/graalvm-java11@20.1.0/Contents/Home/bin/native-image -cp /Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image-internal/manifest.jar example.Fib /Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example
Build on Server(pid: 10427, port: 60389)*
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] classlist: 2,600.36 ms, 0.96 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] (cap): 4,170.49 ms, 0.96 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] setup: 5,685.32 ms, 0.96 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] (clinit): 590.75 ms, 1.72 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] (typeflow): 8,507.30 ms, 1.72 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] (objects): 9,635.14 ms, 1.72 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] (features): 405.95 ms, 1.72 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] analysis: 19,657.76 ms, 1.72 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] universe: 461.65 ms, 1.72 GB
Warning: Reflection method java.lang.Class.getDeclaredMethod invoked at zio.internal.stacktracer.impl.AkkaLineNumbers$.getStreamForLambda(AkkaLineNumbers.scala:201)
Warning: Aborting stand-alone image build due to reflection use without configuration.
Warning: Use -H:+ReportExceptionStackTraces to print stacktrace of underlying exception
Build on Server(pid: 10427, port: 60389)
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] classlist: 92.97 ms, 2.21 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] (cap): 2,152.91 ms, 2.21 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] setup: 2,455.07 ms, 2.21 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] (clinit): 151.36 ms, 2.21 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] (typeflow): 2,721.18 ms, 2.21 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] (objects): 3,078.55 ms, 2.21 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] (features): 94.06 ms, 2.21 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] analysis: 6,192.40 ms, 2.21 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] universe: 211.55 ms, 2.21 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] (parse): 726.45 ms, 2.29 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] (inline): 1,246.53 ms, 2.29 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] (compile): 6,047.58 ms, 3.22 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] compile: 8,400.81 ms, 3.22 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] image: 873.75 ms, 3.22 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] write: 315.83 ms, 3.22 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:10427] [total]: 18,610.73 ms, 3.22 GB
Warning: Image '/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example' is a fallback image that requires a JDK for execution (use --no-fallback to suppress fallback image generation and to print more detailed information why a fallback image was necessary).
[info] Native image ready!
[info] /Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example
[success] Total time: 683 s (11:23)
オプションの指定
上記の実行結果ログを見ると以下のようにJDKをつかったイメージが作成されているのが分かります。メッセージで示されているように -no-fallback
を指定して再実行してみます。
- Warning: Image '/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example' is a fallback image that requires a JDK for execution (use --no-fallback to suppress fallback image generation and to print more detailed information why a fallback image was necessary).
オプションはnativeImageOptions
に指定します。
nativeImageOptions ++= List("--no-fallback")
実行ログ(2回目)
2回目の実行結果は以下の通りです。
sbt:sbt-native-image-example> nativeImage
[info] Compiling 1 Scala source to /Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/scala-2.13/classes ...
[info] /Users/sasaki.kazuhiro/Library/Caches/Coursier/jvm/graalvm-java11@20.1.0/Contents/Home/bin/native-image -cp /Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image-internal/manifest.jar --no-fallback example.Fib /Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example
Build on Server(pid: 29968, port: 56106)*
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:29968] classlist: 2,565.24 ms, 0.96 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:29968] (cap): 4,323.05 ms, 0.96 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:29968] setup: 5,702.13 ms, 0.96 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:29968] (clinit): 547.54 ms, 1.72 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:29968] (typeflow): 8,567.28 ms, 1.72 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:29968] (objects): 9,627.95 ms, 1.72 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:29968] (features): 417.20 ms, 1.72 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:29968] analysis: 19,631.94 ms, 1.72 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:29968] universe: 453.48 ms, 1.72 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:29968] (parse): 1,295.92 ms, 2.30 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:29968] (inline): 1,597.05 ms, 2.30 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:29968] (compile): 9,656.70 ms, 3.29 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:29968] compile: 13,209.95 ms, 3.29 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:29968] image: 1,394.82 ms, 3.29 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:29968] write: 808.47 ms, 4.73 GB
[/Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example:29968] [total]: 44,011.19 ms, 4.73 GB
[info] Native image ready!
[info] /Users/sasaki.kazuhiro/src/github.com/cm-kazup0n/blog-examples/sbt-native-image-example/target/native-image/sbt-native-image-example
[success] Total time: 53 s
起動時間の比較
作成したバイナリ実行時間をsbt-assemblyで作成したFatJarをJVMで実行した結果と比較してみます。 起動時間がかなり短縮されているのが分かります。
~/s/g/c/b/sbt-native-image-example ❯❯❯ java -version
openjdk version "1.8.0_242"
OpenJDK Runtime Environment Corretto-8.242.08.1 (build 1.8.0_242-b08)
OpenJDK 64-Bit Server VM Corretto-8.242.08.1 (build 25.242-b08, mixed mode)
~/s/g/c/b/sbt-native-image-example ❯❯❯ time ./target/native-image/sbt-native-image-example 5
5
0.01 real 0.00 user 0.00 sys
~/s/g/c/b/sbt-native-image-example ❯❯❯ time java -jar target/scala-2.13/sbt-native-image-example-assembly-0.1.jar 5
5
0.83 real 1.46 user 0.20 sys
まとめ
軽く触ってみましたがプラグインの導入だけで(graalvmのインストールをしなくても)バイナリの作成が行えるのは手軽でいいのかなと思いました。