BT

Diffuser les Connaissances et l'Innovation dans le Développement Logiciel d'Entreprise

Contribuez

Sujets

Sélectionner votre région

Accueil InfoQ Articles Faire Connaissance Avec Graal, le Nouveau Compilateur Juste-à-Temps de Java

Faire Connaissance Avec Graal, le Nouveau Compilateur Juste-à-Temps de Java

Favoris

Points Clés

  • L'actuel compilateur C2 arrive en fin de vie ;
  • Une nouvelle interface pour les compilateurs, JVMCI, autorise d'embarquer de nouveaux compilateurs ;
  • Oracle a développé Graal, un compilateur juste-à-temps (JIT), écrit en Java, qui doit prendre la place de C2 ;
  • Graal fonctionne aussi séparément de la JVM, et devient un composant majeur de la nouvelle plateforme ;
  • GraalVM constitue la nouvelle génération de VM polyglotte qui supporte de nombreux langages, au-delà de ceux qui génèrent du bytecode pour la JVM.

L'implémentation de Java que propose Oracle s'appuie sur le projet Open source OpenJDK. L'OpenJDK inclut la machine virtuelle HotSpot, disponible depuis Java 1.3. HotSpot contient deux compilateurs JIT séparés, connus sous le nom de C1 et C2, parfois appelés client et serveur. Une installation à jour de Java utilise ces deux compilateurs durant l'exécution normale d'un programme.

Un programme Java démarre en mode interprété. Après quelques exécutions, les méthodes fréquemment utilisées sont identifiées et compilées. Tout d'abord avec le compilateur C1, puis, si HotSpot détecte un très grand nombre d'appels, avec le compilateur C2. Cette stratégie est connue sous le nom de tiered compilation (compilation par tranches) ; c'est la stratégie que HotSpot utilise par défaut.

Cela fait de C2 un des éléments les plus importants de la plateforme Java pour la plupart des applications. Il produit un code compilé très optimisé pour les parties les plus importantes d'une application.

C2 fonctionne très bien et peut produire du code qui tient la comparaison avec le C++, et est parfois plus rapide. Cela est dû à des optimisations qui ne peuvent être mises en œuvre qu'à l'exécution. Ces optimisations ne sont pas accessibles aux compilateurs statiques tels que gcc ou Go.

Cela dit, C2 n'a que peu progressé ces dernières années et aucune avancée majeure a été implémentée. De plus, le code source de C2 est devenu très complexe à maintenir et à faire évoluer. Il est très difficile pour de nouveaux développeurs de prendre en main la base de code actuelle, écrite dans une version spécifique du C++.

En fait, certaines grandes sociétés comme Twitter, ou certains experts comme Cliff Click, pensent que l'organisation actuelle de C2 rend impossible les améliorations majeures. On ne peut attendre que des améliorations à la marge.

Un des seuls domaines dans lequel on a vu des améliorations dans les récentes versions sont l'utilisation de JVM intrinsics, une technique décrite dans la documentation de l'annotation @HotSpotIntrinsicCandidate de la façon suivante :

Une méthode est rendue intrinsèque si la VM HotSpot remplace la méthode annotée par du code assembleur écrit à la main pour améliorer les performances.

Quand la JVM démarre, elle sonde le processeur sur lequel elle s'exécute. Elle connait donc exactement les fonctionnalités de ce CPU. Elle construit alors une table de méthodes intrinsèques spécifiques à ce processeur. Cela lui permet d'exploiter les capacités de ce processeur de façon optimale.

Ce point constitue une différence avec les compilateurs classiques, qui génèrent de l'assembleur pour un CPU générique et font des suppositions conservatrices sur les fonctionnalités qui seront disponibles à l'exécution. Le binaire compilé de cette façon plantera s'il doit utiliser des fonctions qui ne sont pas supportées par le CPU sur lequel l'application va effectivement être exécutée.

HotSpot supporte déjà quelques méthodes intrinsèques : par exemple les méthodes compare and swap (CAS) utilisées dans les variables atomiques. Sur la plupart des processeurs récents, cette fonctionnalité est implémentée à l'aide d'une seule instruction assembleur.

Les méthodes intrinsèques sont connues à l'avance de la JVM et dépendent de fonctionnalités spécifiques du système d'exploitation ou du CPU. Elles sont donc dépendantes de la plateforme d'exécution. Toutes les méthodes intrinsèques ne sont pas supportées sur toutes les plateformes.

D'une façon générale, les méthodes intrinsèques doivent être considérées comme des fonctionnalités locales et non pas comme une technique générale. Elles ont l'avantage d'être puissantes, légères et souples à utiliser. Mais elles peuvent être coûteuses à écrire et à maintenir, dans la mesure où elles doivent être écrites pour de nombreuses plateformes d'exécution différentes.

C'est pourquoi, malgré les progrès réalisés grâce aux méthodes intrinsèques, C2 a atteint sa fin de vie et doit être remplacé.

Oracle a récemment annoncé la première version de GraalVM, un projet de recherche qui pourra à un certain point, constituer un remplacement pour HotSpot dans son intégralité.

Pour les développeurs Java, Graal peut être vu comme plusieurs projets séparés mais en relation. C'est un nouveau compilateur JIT pour HotSpot, et également une nouvelle machine virtuelle multilangage. On parlera de Graal pour le compilateur JIT et de GraalVM pour la machine virtuelle.

Le but global de GraalVM est de repenser comment la compilation JIT fonctionne en Java. Dans le cas de GraalVM, cela inclut également les autres langages. Le premier constat duquel Graal part est très simple :

Un compilateur JIT pour Java transforme du bytecode en langage machine. Du point de vue Java, cela revient à écrire une transformation qui prend un byte[] et retourne un autre byte[]. Donc que se passerait-il si le code de cette transformation était écrit en Java ?

Il se trouve qu'il y a quelques avantages majeurs à écrire une telle transformation en Java. Parmi elles :

  • Il sera beaucoup plus simple pour les nouveaux ingénieurs de comprendre le code du compilateur ;
  • Le compilateur fonctionnera dans un environnement mémoire contrôlé ;
  • Développer ce compilateur pourra s'appuyer sur les outils de développement Java ;
  • Les nouvelles fonctionnalités du compilateur pourront être écrites et testées beaucoup plus rapidement ;
  • Le compilateur devient indépendant de HotSpot ;
  • Le compilateur devrait être capable de se compiler lui-même à l'exécution, et de produire ainsi une version de lui-même beaucoup plus performante.

Graal utilise la nouvelle Interface de Compilation de la JVM : JVMCI, publiée dans la JEP 243. Graal est destiné à HotSpot, mais peut aussi être utilisée dans GraalVM. La technologie est présente et disponible aujourd'hui. On peut activer ce nouveau compilateur JIT depuis Java 10 à l'aide des options de lancement suivantes :

-XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler

Cela veut dire que l'on a trois façons d'exécuter un programme : la façon classique avec les compilateurs JIT existants, ou, depuis Java 10, avec le compilateur Graal qui implémente JVMCI, ou bien encore avec la machine virtuelle GraalVM elle-même.

Utilisons un exemple simple pour voir les effets de Graal. Cet exemple met suffisamment de temps à s'exécuter pour voir les effets de la compilation JIT. Il s'agit d'un simple hachage de chaîne de caractères.

package kathik;

public final class StringHash {

    public static void main(String[] args) {
        StringHash sh = new StringHash();
        sh.run();
    }

    void run() {
        for (int i=1; i<2_000; i++) {
            timeHashing(i, 'x');
        }
    }

    void timeHashing(int length, char c) {
        final StringBuilder sb = new StringBuilder();
        for (int j = 0; j < length  * 1_000_000; j++) {
            sb.append(c);
        }
        final String s = sb.toString();
        final long now = System.nanoTime();
        final int hash = s.hashCode();
        final long duration = System.nanoTime() - now;
        System.out.println("Length: "+ length +" took: "+ duration +" ns");
    }
}

On peut utiliser ce code en activant l'option PrintCompilation pour voir quelles méthodes sont compilées. Cela va nous permettre de comparer les comportements de Graal.

java -XX:+PrintCompilation -cp target/classes/ kathik.StringHash > out.txt

Exécutons le même code en activant Graal en tant que compilateur JIT sur Java 10 :

java -XX:+PrintCompilation \
     -XX:+UnlockExperimentalVMOptions \
     -XX:+EnableJVMCI \
     -XX:+UseJVMCICompiler \
     -cp target/classes/ \
     kathik.StringHash > out-jvmci.txt

Et pour GraalVM :

java -XX:+PrintCompilation \
     -cp target/classes/ \
     kathik.StringHash > out-graal.txt

Cela va nous générer trois fichiers de sortie. Leur contenu va ressembler à ça :

$ ls -larth out*
-rw-r--r--  1 ben  staff    18K  4 Jun 13:02 out.txt
-rw-r--r--  1 ben  staff   591K  4 Jun 13:03 out-graal.txt
-rw-r--r--  1 ben  staff   367K  4 Jun 13:03 out-jvmci.txt

Comme on peut s'y attendre, les exécutions qui utilisent Graal génèrent beaucoup plus d'informations. Cela est dû au fait que PrintCompilation fournit plus d'informations avec Graal. Cela n'est pas surprenant : la première chose que fait Graal c'est de compiler le compilateur JIT lui-même. On retrouve donc ces messages dans les premières secondes après le lancement de la JVM.

Examinons les premiers messages fournis par l'exécution Java 10 avec le compilateur Graal en utilisant le format classique de PrintCompilation.

$ grep graal out-jvmci.txt | head
    229  293       3       org.graalvm.compiler.hotspot.HotSpotGraalCompilerFactory::adjustCompilationLevelInternal (70 bytes)
    229  294       3       org.graalvm.compiler.hotspot.HotSpotGraalCompilerFactory::checkGraalCompileOnlyFilter (95 bytes)
    231  298       3       org.graalvm.compiler.hotspot.HotSpotGraalCompilerFactory::adjustCompilationLevel (9 bytes)
    353  414   !   1       org.graalvm.compiler.serviceprovider.JDK9Method::invoke (51 bytes)
    354  415       1       org.graalvm.compiler.serviceprovider.JDK9Method::checkAvailability (37 bytes)
    388  440       1       org.graalvm.compiler.hotspot.HotSpotForeignCallLinkageImpl::asJavaType (32 bytes)
    389  441       1       org.graalvm.compiler.hotspot.word.HotSpotWordTypes::isWord (31 bytes)
    389  443       1       org.graalvm.compiler.core.common.spi.ForeignCallDescriptor::getResultType (5 bytes)
    390  445       1       org.graalvm.util.impl.EconomicMapImpl::getHashTableSize (43 bytes)
    390  447       1       org.graalvm.util.impl.EconomicMapImpl::getRawValue (11 bytes)

Il faut prendre ces résultats avec précautions. Par exemple, la quantité de messages générés au démarrage de la JVM peut perturber la mesure des performances. Souvenons-nous que Java 10 et GraalVM utilisent le ramasse-miettes G1 par défaut. Cette quantité de messages va devenir tellement importante qu'ils seront stockés dans une région de mémoire spéciale que G1 utilise pour les objets de très grande taille : la Humongous Regions. Les performances du ramasse-miettes vont donc dépendre de sa capacité à gérer ce type de mémoire, ce qui ne correspond pas à un contexte d'exécution classique.

Avant de discuter de GraalVM, notons qu'il y a une autre façon d'utiliser le compilateur Graal en Java 10 : la compilation à froid (AOT : ahead of time compilation).

Rappelons-nous que le compilateur Graal est un nouveau développement qui implémente la JVMCI (JEP 243). Graal peut donc s'intégrer à HotSpot, mais n'ai pas lié à cette JVM.

Plutôt que d'utiliser une approche qui s'appuie sur la détection des méthodes les plus utilisées à chaud, on peut utiliser Graal pour compiler la totalité de notre application, hors ligne, sans exécuter de code. Cette fonctionnalité est appelée compilation à froid, "Ahead of Time Compilation", et est couverte par la JEP 295.

Dans le contexte HotSpot, on peut produire une librairie partagée (.so sous Linux ou .dylib sous MacOS) avec la commande suivante :

$ jaotc --output libStringHash.dylib kathik/StringHash.class

On peut alors utiliser le code ansi compilé dans notre application :

$ java -XX:AOTLibrary=./libStringHash.dylib kathik.StringHash

Cette utilisation de Graal a un unique objectif : accélérer le temps de démarrage jusqu'à ce que le compilateur classique d'HotSpot puisse prendre le relais. Dans l'absolu, sur une vraie application, on s'attend à ce que les performances du code généré par la compilation JIT soit meilleures que le code compilé à froid. Cette supposition dépend bien sûr du contexte et de la charge de l'application déployée.

La compilation à froid est une technologie encore en développement, et n'est pour le moment supportée qu'à titre expérimental sous Linux / x64. Si vous essayez de compiler le module java.base sous Mac, vous aurez les erreurs suivantes, bien qu'un fichier .dylib soit tout de même produit.

$ jaotc --output libjava.base.dylib --module java.base
Error: Failed compilation: sun.reflect.misc.Trampoline.invoke(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;: org.graalvm.compiler.java.BytecodeParser$BytecodeParserError: java.lang.Error: Trampoline must not be defined by the bootstrap classloader
       at parsing java.base@10/sun.reflect.misc.Trampoline.invoke(MethodUtil.java:70)
Error: Failed compilation: sun.reflect.misc.Trampoline.<clinit>()V: org.graalvm.compiler.java.BytecodeParser$BytecodeParserError: java.lang.NoClassDefFoundError: Could not initialize class sun.reflect.misc.Trampoline
       at parsing java.base@10/sun.reflect.misc.Trampoline.<clinit>(MethodUtil.java:50)

Ces erreurs peuvent être contrôlées en filtrant certaines méthodes qui ne seront donc pas prises en compte par cette compilation. On peut configurer ce point en fournissant un fichier de directives au compilateur. On peut se référer à la page de la JEP 295 pour plus de détails.

Malgré ces erreurs de compilation, on peut tout de même tenter l'exécution du module compilé à froid, à l'aide de la commande :

java -XX:+PrintCompilation \
     -XX:AOTLibrary=./libStringHash.dylib,libjava.base.dylib \
     kathik.StringHash

En passant l'option PrintCompilation, on peut suivre le travail du compilateur JIT, et se rendre compte qu'il n'a quasiment plus rien à faire au démarrage. Seules les méthodes de cœur du JDK nécessaires au démarrage initial sont à présent compilées à chaud par le JIT.

   111    1     n 0       java.lang.Object::hashCode (native)
   115    2     n 0       java.lang.Module::addExportsToAllUnnamed0 (native)   (static)

Le résultat est que notre application Java simple s'exécute à présent dans une forme quasiment entièrement compilée à froid.

Intéressons-nous à GraalVM et regardons une des principales fonctions offerte par cette plateforme : la capacité d'embarquer d'autres langage que Java dans nos applications.

Cela peut être vu comme un équivalent, ou un remplacement de la JSR 223 (Scripting for the Java Platform). L'approche de Graal va cependant beaucoup plus loin que les technologies équivalentes disponibles dans HotSpot à ce jour.

Cette fonctionnalité s'appuie sur GraalVM et le Graal JDK, fourni avec GraalVM. GraalJDK est disponible sur le classpath de GraalVM par défaut, mais doit être inclus explicitement lorsque l'on travaille dans un IDE.

<dependency>
    <groupId>org.graalvm</groupId>
    <artifactId>graal-sdk</artifactId>
    <version>1.0.0-rc1</version>
</dependency>

L'exemple le plus simple est un Hello World. On peut utiliser JavaScript pour cet exemple, puisque GraalVM en offre le support par défaut :

import org.graalvm.polyglot.Context;

public class HelloPolyglot {
    public static void main(String[] args) {
        System.out.println("Hello World: Java!");
        Context context = Context.create();
        context.eval("js", "print('Hello World: JavaScript!');");
    }
}

Ce code s'exécute normalement sur GraalVM. Tenter de l'exécuter en Java 10, pourtant fourni avec le Graal SDK, produit cette erreur, que l'on pouvait attendre :

$ java -cp target/classes:$HOME/.m2/repository/org/graalvm/graal-sdk/1.0.0-rc1/graal-sdk-1.0.0-rc1.jar kathik.HelloPolyglot
Hello Java!
Exception in thread "main" java.lang.IllegalStateException: No language and polyglot implementation was found on the classpath. Make sure the truffle-api.jar is on the classpath.
       at org.graalvm.polyglot.Engine$PolyglotInvalid.noPolyglotImplementationFound(Engine.java:548)
       at org.graalvm.polyglot.Engine$PolyglotInvalid.buildEngine(Engine.java:538)
       at org.graalvm.polyglot.Engine$Builder.build(Engine.java:367)
       at org.graalvm.polyglot.Context$Builder.build(Context.java:528)
       at org.graalvm.polyglot.Context.create(Context.java:294)
       at kathik.HelloPolyglot.main(HelloPolyglot.java:8)

Cela signifie que Truffle ne fonctionne que sur GraalVM, du moins pour le moment.

Une forme de support multilangage existe depuis Java 6, avec l'introduction de l'API Scripting. Ce support a été grandement étendu en Java 8 avec l'arrivée de Nashorn, l'implémentation de JavaScript construite sur invokedynamic.

Ce qui rend la technologie de GraalVM particulière est que l'écosystème inclut maintenant un SDK et les outils nécessaires à l'implémentation de plusieurs langages. De plus, GraalVM les exécute au même niveau, et leur permet d'inter-opérer.

La clé de cette avancée est le composant appelé Truffle. Il fonctionne sur une VM très simple, avec des fonctionnalités de base, uniquement capable d'exécuter du bytecode : SubstrateVM.

Truffle fournit un SDK et des outils qui permettent d'ajouter le support de nouveaux langages. L'approche générale est la suivante :

  • Commencer par écrire la grammaire du langage ;
  • Appliquer un générateur de parseur (par exemple Coco/R) ;
  • Utiliser Maven pour construire un interpréteur et un environnement d'exécution simple ;
  • Exécuter l'implémentation que vous obtenez sur GraalVM ;
  • Attendre que Graal (utilisé en mode JIT) démarre et améliore les performances du langage automatiquement ;
  • Eventuellement : utiliser Graal (en mode compilation à froid) pour compiler l'interpréteur et générer un lanceur natif.

Dans sa distribution par défaut, GraalVM supporte le byte code de la JVM, JavaScript et LLVM. Si l'on essaie d'appeler un autre langage, tel que Ruby, par exemple avec la commande suivante :

context.eval("ruby", "puts \"Hello World: Ruby\"");

L'exception suivante sera générée :

Exception in thread "main" java.lang.IllegalStateException: A language with id 'ruby' is not installed. Installed languages are: [js, llvm].
       at com.oracle.truffle.api.vm.PolyglotEngineImpl.requirePublicLanguage(PolyglotEngineImpl.java:559)
       at com.oracle.truffle.api.vm.PolyglotContextImpl.requirePublicLanguage(PolyglotContextImpl.java:738)
       at com.oracle.truffle.api.vm.PolyglotContextImpl.eval(PolyglotContextImpl.java:715)
       at org.graalvm.polyglot.Context.eval(Context.java:311)
       at org.graalvm.polyglot.Context.eval(Context.java:336)
       at kathik.HelloPolyglot.main(HelloPolyglot.java:10)

On peut télécharger et installer le support de Ruby (ou tout autre langage) pour Truffle, qui est disponible en bêta. Pour la version RC1 (ou RC2) de Graal, la commande est :

gu -v install -c org.graalvm.ruby

Notons que cela nécessite une commande sudo si GraalVM a été installé au niveau système, et rendue disponible aux utilisateurs au travers d'une variable $JAVA_HOME. Si vous utilisez une version Entreprise de GraalVM, non open-source (la seule disponible actuellement sur Mac), vous pouvez aller un peu plus loin : l'interpréteur Truffle peut lui-même être converti en code natif.

Construire l'image native du langage va améliorer les performances, mais on doit passer par la ligne de commandes de la façon suivante. Cela suppose que GraalVM a été installé au niveau du système, il s'agit donc d'une commande root.

$ cd $JAVA_HOME
$ sudo jre/lib/svm/bin/rebuild-images ruby

 

Tout ceci est encore en développement et passe par quelques étapes manuelles. Ce processus sera simplifié au cours du temps.

Si vous rencontrez des problèmes lors de la construction des composants natifs, pas d'inquiétude, les choses continueront à fonctionner sans avoir à reconstruire les images natives.

Examinons un exemple plus complexe avec plusieurs langages.

Context context = Context.newBuilder().allowAllAccess(true).build();
Value sayHello = context.eval("ruby",
        "class HelloWorld\n" +
        "   def hello(name)\n" +
        "      \"Hello #{name}\"\n" +
        "   end\n" +
        "end\n" +
        "hi = HelloWorld.new\n" +
        "hi.hello(\"Ruby\")\n");
String rubySays = sayHello.as(String.class);
Value jsFunc = context.eval("js",
        "function(x) print('Hello World: JavaScript with '+ x +'!');");
jsFunc.execute(rubySays);

Ce code est un peu dur à lire, il utilise à la fois TruffleRuby et JavaScript. Tout d'abord, on appelle ce morceau de code Ruby :

class HelloWorld
   def hello(name)
      "Hello #{name}"
   end
end

hi = HelloWorld.new
hi.hello("Ruby")

Cela crée une nouvelle classe Ruby, définit une méthode sur cette classe, l'instancie en un objet Ruby et appelle enfin la méthode hello() dessus. Cette méthode retourne une chaîne de caractères (sous forme d'une instance de String, en Ruby), qui est convertie en instance de String Java dans l'environnement d'exécution Java.

On crée ensuite la fonction anonyme JavaScript suivante :

function(x) print('Hello World: JavaScript with '+ x +'!');

On appelle cette fonction via execute() et on lui passe le résultat de notre appel Ruby, que cette fonction affiche, depuis l'environnement d'exécution JavaScript.

Notons que lors de la création de l'objet Context, nous avions besoin d'un accès étendu au contexte. C'est le cas pour Ruby, mais nous n'en avions pas besoin pour JavaScript, d'où sa construction un peu plus complexe. Il s'agit d'une limitation de l'implémentation Ruby actuellement disponible, qui pourra être levée dans le futur.

Examinons un dernier exemple multilangage et voyons jusqu'où l'on peut aller :

Value sayHello = context.eval("ruby",
        "class HelloWorld\n" +
        "   def hello(name)\n" +
        "      \"Hello Ruby: #{name}\"\n" +
        "   end\n" +
        "end\n" +
        "hi = HelloWorld.new\n" +
        "hi");
Value jsFunc = context.eval("js",
        "function(x) print('Hello World: JS with '+ x.hello('Cross-call') +'!');");
jsFunc.execute(sayHello);

Dans cette version, on retourne un objet Ruby complet, plus complexe qu'une simple String. On ne convertit pas cette chaîne de caractères Ruby en String Java, on la passe en fait directement à la fonction JavaScript.

function(x) print('Hello World: JS with '+ x.hello('Cross-call') +'!');

Cela fonctionne comme attendu, et produit le résultat suivant :

Hello World: Java!
Hello World: JS with Hello Ruby: Cross-call!

Cela signifie que l'environnement d'exécution JavaScript peut appeler une méthode d'un autre langage, dans un environnement d'exécution séparé, avec une conversion de type transparente, tout du moins dans les cas simples.

Cette possibilité d'échanger des objets d'un langage à l'autre, même quand ils ont des sémantiques ou des systèmes de type très différents fait l'objet de discussions parmi les ingénieurs de la JVM depuis au moins 10 ans. L'arrivée de GraalVM constitue une avancée majeure dans ce domaine.

Examinons rapidement comment ces objets multilangages sont représentés dans GraalVM. Utilisons pour cela un peu de JavaScript pour afficher l'objet en provenance de Ruby :

function(x) print('Hello World: JS with '+ x +'!');

Cette fonction affiche les informations suivantes :

Hello World: JS with foreign {is_a?: DynamicObject@540a903b<Method>, extend: DynamicObject@238acd0b<Method>, protected_methods: DynamicObject@34e20e6b<Method>, public_methods: DynamicObject@15ac59c2<Method>, ...}!

Cela nous montre que cette objet multilangage est représenté comme une collection de DynamicObject, qui vont déléguer les opérations sémantiques dans l'environnement d'exécution originel de l'objet.

Pour conclure cet article, disons deux mots au sujet des performances et du modèle de licence. Précisons tout d'abord que malgré les avancées prometteuses de Graal et de GraalVM, il s'agit toujours d'une technologie expérimentale et en développement.

Cette technologie n'est pas encore optimisée et n'est pas encore utilisable en production dans des cas d'usage classiques. Atteindre le niveau de qualité de HotSpot et de C2 prendra encore du temps. Certaines avancées sont déjà significatives sur certains points, mais on manque encore de benchmarks et de mesures de performance sur des applications réelles.

On peut considérer que le compilateur C2 est arrivé au maximum de ses performances et est à la fin de sa vie. Graal apporte la possibilité d'aller plus loin en termes de performance, et de repenser le design des VM et des compilateurs. Il ne s'agit toutefois pas encore d'une technologie mature, et il y a peu de chance qu'elle soit disponible avant quelques années.

Cela signifie que tous les tests de performance que l'on peut faire aujourd'hui doivent être pris avec grande précaution. Les tests comparatifs (notamment ceux entre HotSpot équipé de C2 d'une part et de GraalVM d'autre part) ne font que comparer des pommes et des prunes. On a d'un côté un environnement d'exécution parfaitement mature, utilisé en production, et de l'autre un environnement expérimental, à un stade très amont de son développement.

Notons également que le système de licence de GraalVM pourrait être très différent de ce que nous connaissons actuellement. En faisant l'acquisition de Sun, Oracle est également devenu propriétaire de HotSpot, déjà très mature et distribué librement. Il y a eu quelques tentatives limitées d'ajouter de la valeur à HotSpot et de monétiser cette valeur à l'aide de l'option UnlockCommercialFeatures. Maintenant que ces fonctionnalités ont été retirées, notamment depuis la diffusion open-source de Mission Control, on peut penser avec justesse que ce modèle n'a pas rencontré de grand succès commercial.

Graal est différent. Il est né en tant que projet d'Oracle Research et est en train de devenir un produit commercial. Oracle a fortement investit pour faire de Graal une réalité, et les talents dont Oracle a besoin pour ce projet sont rares et chers. Comme Graal s'appuie sur des technologies sous-jacentes différentes d'HotSpot, Oracle a toute liberté pour utiliser un modèle commercial différent, et pour tenter de monétiser GraalVM auprès de catégories plus larges de clientèles, y compris auprès de celles qui ne paient pas aujourd'hui pour HotSpot. Oracle pourrait même très bien décider que certaines fonctionnalités de GraalVM ne seront disponibles que pour une utilisation dans Oracle Cloud.

Pour le moment, Oracle publie une version communautaire (Community Edition ou CE) sous licence GPL, gratuite pour le développement et en production. Oracle publie aussi une version entreprise (EE), gratuite pour le développement et l'évaluation. Ces deux versions peuvent être téléchargées sur le site Oracle de GraalVM, où l'on trouve tous les détails sur ces points.

A propos de l'auteur

Ben Evans est un des co-fondateurs de jClarity, une société spécialisée dans l'optimisation des performances de la JVM. Il fait partie des organisateurs du LJC (London JUG) et membre du Comité Exécutif du JCP, qui aide à définir les standards de l'écosystème Java. Ben est Java Champion, trois fois Java Rockstar, auteur de "Well Grounded Java Developer", de la nouvelle édition de "Java in a Nutshell" et de "Optimizing Java". Il parle régulièrement de la plateforme Java, de performances, d'architecture, de programmation concurrente et de startups. Ben est parfois disponible pour présenter, donner des formations et pour des missions de conseils, merci de prendre contact pour plus de détails.

Evaluer cet article

Pertinence
Style

Contenu Éducatif

BT