Présentation et utilisation de GraalVM du point de vue d'un programmeur Scala.
GraalVM?
GraalVM semble être capable (parfois) d'accélérer les programmes Scala (Java) si vous franchissez le pas.
Si vous lisez cette zone, elle est écrite un peu plus en détail. https://www.graalvm.org/docs/why-graal/#for-java-programs
Pour résumer brièvement, les fonctionnalités sont
Trois points sont énumérés.
Dans cet article, je vais aborder 1 et 3. Voir Intégrer les langues avec l'API Graal Polyglot pour 2.
La version de GraalVM est CE-1.0.0-rc9.
Depuis qu'il a été téléchargé sur Github, téléchargez-le et décompressez-le. [Releases · oracle/graal](https://github.com/oracle/graal/releases
$ wget https://github.com/oracle/graal/releases/download/vm-1.0.0-rc9/graalvm-ce-1.0.0-rc9-macos-amd64.tar.gz
$ tar zxvf graalvm-ce-1.0.0-rc9-macos-amd64.tar.gz
$ export GRAALVM_HOME=$PWD/graalvm-ce-1.0.0-rc9
De plus, une variable d'environnement appelée GRAALVM_HOME
est définie.
C'est très facile à utiliser, il suffit d'installer et d'utiliser au lieu de Java.
$ java -jar main.jar #Exécuter avec Oracle JDK
$ $GRAALVM_HOME/bin/java -jar main.jar #Exécuter sur Graal VM
Utilisez sbt-jmh pour vérifier les performances pour voir si cela seul va vraiment accélérer. Le code de vérification des performances est compté à partir de la chaîne de caractères en se référant aux Démos Graal VM: Graal Performance Examples for Java. J'ai essayé de le faire. Un exemple de code est disponible sur Github.
package net.petitviolet.example
import org.openjdk.jmh.annotations._
@State(Scope.Thread)
class ForBench {
private val sentence = "In 2017 I would like to run ALL languages in one VM."
private val answer = 7
@Benchmark
@BenchmarkMode(Array(Mode.Throughput))
def bench_upperCaseCount() = {
upperCaseCount(sentence)
}
private def upperCaseCount(args: String) = {
val sentence = String.join(" ", args)
require(sentence.filter(Character.isUpperCase).length == answer)
}
}
Exécutez jmh sur ce code.
Vous pouvez éventuellement passer -jvm
à la tâche jmh: run
, alors essayez d'exécuter le benchmark lors du changement.
Tout d'abord, Oracle JDK 1.8.0_192. La sortie résultante est éclaircie de manière appropriée.
sbt:main> jmh:run -i 10 -wi 10 -f1 -t 1 -jvm /path/to/java/1.8/bin/java
[info] Running (fork) org.openjdk.jmh.Main -i 10 -wi 10 -f1 -t 1 -jvm /path/to/java/1.8/bin/java
[info] # JMH version: 1.21
[info] # VM version: JDK 1.8.0_192, Java HotSpot(TM) 64-Bit Server VM, 25.192-b12
[info] # VM invoker: /path/to/java/1.8/bin/java
[info] # Benchmark mode: Throughput, ops/time
[info] # Benchmark: net.petitviolet.example.ForBench.bench_upperCaseCount
[info] Benchmark Mode Cnt Score Error Units
[info] ForBench.bench_upperCaseCount thrpt 10 2548794.689 ± 212771.778 ops/s
Puis exécutez avec GraalVM 1.0.0-rc9.
sbt:main> jmh:run -i 10 -wi 10 -f1 -t 1 -jvm /path/to/graalvm-ce-1.0.0-rc9/Contents/Home/bin/java
[info] Running (fork) org.openjdk.jmh.Main -i 10 -wi 10 -f1 -t 1 -jvm /path/to/graalvm-ce-1.0.0-rc9/Contents/Home/bin/java
[info] # JMH version: 1.21
[info] # VM version: JDK 1.8.0_192, GraalVM 1.0.0-rc9, 25.192-b12-jvmci-0.49
[info] # *** WARNING: JMH support for this VM is experimental. Be extra careful with the produced data.
[info] # Benchmark mode: Throughput, ops/time
[info] # Benchmark: net.petitviolet.example.ForBench.bench_upperCaseCount
[info] # Run progress: 0.00% complete, ETA 00:03:20
[info] Benchmark Mode Cnt Score Error Units
[info] ForBench.bench_upperCaseCount thrpt 10 2904523.828 ± 28572.650 ops/s
[success] Total time: 203 s, completed Nov 25, 2018 9:46:55 PM
En comparant simplement les résultats, GraalVM était un peu plus rapide.
résultat | OracleJDK | GraalVM |
---|---|---|
Throughput | 2548794.689 | 2904523.828 |
Cependant, cette fois, c'est un code simple, il est donc possible que GraalVM soit plus avantageux, mais au moins il y a des cas où le simple fait d'utiliser GraalVM comme celui-ci le rend plus rapide.
NativeImage
Ensuite, au lieu de la compilation JIT, la compilation AOT est utilisée pour cracher du code natif, c'est-à-dire des binaires exécutables. SubstrateVM est la technologie qui le prend en charge.
Substrate VM is a framework that allows ahead-of-time (AOT) compilation of Java applications under closed-world assumption into executable images or shared objects (ELF-64 or 64-bit Mach-O).
Il y a Scala Native dans Scala, mais je ne le mentionnerai pas ici.
Tout d'abord, rendez «native-image» utilisable. Cependant, si vous l'avez déjà téléchargé, vous pouvez simplement le mettre dans votre PATH.
$ export PATH=$PATH:$GRAALVM_HOME/Contents/Home/bin
$ native-image --version
GraalVM Version 1.0.0-rc9
Il peut être utilisé pour convertir des programmes Scala (Java) en code natif.
Plus précisément, vous pouvez donner fat-JAR à l'argument de la commande native-image
.
Si vous utilisez sbt, vous pouvez facilement générer du fat-JAR en utilisant sbt-assembly, afin de pouvoir l'utiliser.
Pour l'exécuter, tapez la commande suivante.
$ native-image \
-jar main.jar \ # sbt-La graisse crache dans l'assemblage-Spécifiez JAR
-H:IncludeResources=".*.xml|.*.conf" \
-H:+ReportUnsupportedElementsAtRuntime \
-H:Name=app \ #Chemin binaire de sortie
--verbose
Pour l'argument de -jar
, spécifiez le fat-JAR craché par sbt-assembly.
Avec -H: IncludeResources
, vous pouvez spécifier le fichier de ressources à inclure dans le binaire avec une expression régulière.
Cette fois, je voulais inclure logback.xml et application.conf, alors je l'ai spécifié.
Au fait, si vous utilisez -cp
au lieu de -jar
, vous pouvez le faire sans fat-JAR, mais il est recommandé car il est moins mémorable.
Voir Options de génération d'image car les autres commandes sont facultatives.
Ce qui me rend heureux d'être natif, c'est que la startup est explosive. Java améliore de toute façon considérablement le spin-up lourd de JVM.
Le code source qui s'exécute dans cet exemple est [scala_graalvm_prac / Application.scala](https://github.com/petitviolet/scala_graalvm_prac/blob/adad5c144534d473e5b61de38ec5e86f6d809172/modules/net/rc/petala/petolet/application J'utilise celui de scala), et c'est une application qui démarre et quitte simplement une application Web qui utilise http4s.
Tout d'abord, essayez de l'exécuter en java à des fins de comparaison.
$ /usr/bin/time java -jar main.jar TimeTest
2018-11-26 17:24:29.129 INFO [main][o.h.b.c.n.NIO1SocketServerGroup] - Service bound to address /0:0:0:0:0:0:0:0:8080
2018-11-26 17:24:29.135 INFO [main][o.h.s.b.BlazeBuilder] - _ _ _ _ _
2018-11-26 17:24:29.135 INFO [main][o.h.s.b.BlazeBuilder] - | |_| |_| |_ _ __| | | ___
2018-11-26 17:24:29.135 INFO [main][o.h.s.b.BlazeBuilder] - | ' \ _| _| '_ \_ _(_-<
2018-11-26 17:24:29.135 INFO [main][o.h.s.b.BlazeBuilder] - |_||_\__|\__| .__/ |_|/__/
2018-11-26 17:24:29.135 INFO [main][o.h.s.b.BlazeBuilder] - |_|
2018-11-26 17:24:29.230 INFO [main][o.h.s.b.BlazeBuilder] - http4s v0.18.9 on blaze v0.12.13 started at http://[0:0:0:0:0:0:0:0]:8080/
2018-11-26 17:24:29.230 INFO [main][n.p.e.Application] - start server.
2018-11-26 17:24:29.230 INFO [main][n.p.e.Application] - start shutting down immediately.
2018-11-26 17:24:29.235 INFO [main][o.h.b.c.ServerChannel] - Closing NIO1 channel /0:0:0:0:0:0:0:0:8080 at Mon Nov 26 17:24:29 JST 2018
2018-11-26 17:24:29.237 INFO [main][o.h.b.c.n.NIO1SocketServerGroup] - Closing NIO1SocketServerGroup
2018-11-26 17:24:29.237 INFO [main][n.p.e.Application] - shutting down completed.
1.74 real 1.84 user 0.20 sys
Le résultat.
Ensuite, essayez d'exécuter le binaire créé avec native-image.
$ /usr/bin/time ./app TimeTest
2018-11-26 17:24:37.190 INFO [main][o.h.b.c.n.NIO1SocketServerGroup] - Service bound to address /0:0:0:0:0:0:0:0:8080
2018-11-26 17:24:37.190 INFO [main][o.h.s.b.BlazeBuilder] - _ _ _ _ _
2018-11-26 17:24:37.190 INFO [main][o.h.s.b.BlazeBuilder] - | |_| |_| |_ _ __| | | ___
2018-11-26 17:24:37.190 INFO [main][o.h.s.b.BlazeBuilder] - | ' \ _| _| '_ \_ _(_-<
2018-11-26 17:24:37.190 INFO [main][o.h.s.b.BlazeBuilder] - |_||_\__|\__| .__/ |_|/__/
2018-11-26 17:24:37.190 INFO [main][o.h.s.b.BlazeBuilder] - |_|
2018-11-26 17:24:37.190 INFO [main][o.h.s.b.BlazeBuilder] - http4s v0.18.9 on blaze v0.12.13 started at http://[0:0:0:0:0:0:0:0]:8080/
2018-11-26 17:24:37.190 INFO [main][n.p.e.Application] - start server.
2018-11-26 17:24:37.190 INFO [main][n.p.e.Application] - start shutting down immediately.
2018-11-26 17:24:37.190 INFO [main][o.h.b.c.ServerChannel] - Closing NIO1 channel /0:0:0:0:0:0:0:0:8080 at Mon Nov 26 17:24:37 JST 2018
2018-11-26 17:24:37.190 INFO [main][o.h.b.c.n.NIO1SocketServerGroup] - Closing NIO1SocketServerGroup
2018-11-26 17:24:37.190 INFO [main][n.p.e.Application] - shutting down completed.
0.03 real 0.01 user 0.01 sys
Presque rien n'a changé dans la sortie de l'application, mais comme elle est de 1,74 s → 0,03 s, on peut voir que le démarrage est clairement plus rapide.
On dit que Twitter a été mis en production, mais il peut être possible de l'adopter en fonction du résultat de la vérification du fonctionnement / des performances. Un autre avantage est qu'il est facile de rentrer même s'il y a un problème.
Cependant, il est encore trop tôt pour introduire le codage natif en utilisant "native-image".
Les restrictions sont décrites dans LIMITATIONS.md. Le gros problème est que le chargement de classe dynamique n'est pas pris en charge et que certaines réflexions ne sont pas disponibles. Scala utilise souvent des macros, mais cela ne fonctionne souvent pas. Pour cette raison, je ne pouvais pas utiliser Playframework ou Akka-HTTP. Il existe encore de nombreuses restrictions, par exemple lorsque vous essayez d'utiliser l'Appender Async de Logback autre que le framework d'application Web, cela ne fonctionne pas bien. Depuis AOT est juste pré-compilé, il est devenu difficile de vouloir le faire au moment de l'exécution. À propos, il y avait une démo GraalVM qui a converti le compilateur Scala en une image native, vous pouvez donc vous y référer également. graalvm-demos/scala-days-2018/scalac-native at master · graalvm/graalvm-demos
Cependant, cela ne signifie pas qu'il peut être utilisé du tout. Par exemple, cela peut être une option pour faire fonctionner des outils pour CLI avec Scala (Java) + native-image
au lieu de Golang.
S'il devient plus polyvalent, il est possible que l'incompatibilité entre l'ère Docker / Kubernetes et le spin-up JVM soit améliorée, donc j'aimerais m'y attendre à l'avenir.
Recommended Posts