GraalVM’s JavaScript engine on JDK11 with high performance

Christian Wirth
graalvm
Published in
4 min readFeb 22, 2019

GraalVM is a runtime platform which provides support for Java and other Java bytecode based languages, but also for additional languages like JavaScript, Ruby, Python, or LLVM. And we firmly believe it’s the future of the virtual machines for languages as we currently know them.

However, we understand switching from the established Oracle JVM or OpenJDK to GraalVM is a bold step that you might not want to fully take just yet. But, you might want to try out some of the cool capabilities of the GraalVM project on a stock JVM.

In this blog post, we show how to use the GraalVM’s JavaScript engine from a sample Maven application. We also show how to use GraalVM’s JavaScript engine on a stock OpenJDK 11, including Graal as a JIT (just-in-time) compiler for better peak performance. That way, GraalVM’s JavaScript engine can be used as replacement for the deprecated Nashorn JavaScript engine. We will demonstrate on a Maven application, as all necessary artifacts are available on Maven central.

GraalVM’s JavaScript and the Graal compiler

One question that might arise is: What is the relation of GraalVM’s JavaScript engine with Graal; can I use just the JavaScript engine and omit Graal?

If you do so, things will fully work. You can parse and execute any (valid) JavaScript code then. GraalVM’s JavaScript engine is a Java application that works on any Java 8+ implementation. The JavaScript execution is built around an abstract syntax tree (AST) interpreter that can be executed like any other Java application. Every Java virtual machine will optimize it to some extent, but the JavaScript execution will remain interpreted, thus slow.

The Graal compiler, however, has some special tricks to translate this AST interpreter into highly optimized machine code. It will adapt to the JavaScript application executed and compile the application plus the interpreter into machine code, thus effectively turning it into a JavaScript compiler at runtime. After a short warmup phase, the JavaScript application will thus achieve near-native performance, if GraalVM’s JavaScript engine is executed on a JVM with the Graal compiler enabled.

Example application

We have prepared a sample application showing the relevant setup. This Maven-based example can serve as a starting point to enable you to transfer e.g. a Nashorn based JavaScript applications to GraalVM’s JavaScript with Graal support.

The example is centered around Prime number calculation in JavaScript, that is consumed from the Java code. It shows how to access GraalVM’s JavaScript with the preferred org.graalvm.polyglot interface, but also with the javax.script (JSR 223 ScriptEngine) interface. In addition, the example code is executed via ScriptEngine on the Nashorn engine (if available). An identical number of warmup and measurement iterations is executed and printed as a simple benchmark, showing the execution time necessary on the respective engine (lower values are better).

The example is available on GitHub:

https://github.com/graalvm/graal-js-jdk11-maven-demo

Please note that particular virtues of the code are not the interesting part of the discussion, normally one would separate Java and JavaScript sources into separate files and so on. This example is focused on the infrastructure for executing a polyglot Java and JavaScript code. The application should run out of the box if you compile and execute it with the following commands; ensure that you are using a JDK11 by having JAVA_HOME point to it.

export JAVA_HOME=/path/to/JDK11
mvn clean && mvn package && mvn exec:exec@graal

This will ensure you are using Graal for JIT compilation of GraalVM’s JavaScript code. On this app GraalVM’s JavaScript was about twice as fast as Nashorn when we measured (please try on your machine, YMMV). If you’re interested in a more detailed performance comparison, please refer to the previous post about Nashorn and GraalVM’s JavaScript.

For the sake of comparison, you can execute the same benchmark without Graal compilation enabled:

mvn clean && mvn package && mvn exec:exec@nograal

In that mode, executing without Graal being enabled as JIT compiler, the performance of GraalVM/JavaScript drops significantly.

Some technical insights

What is happening behind the scene? In the pom.xml file several dependencies to relevant packages are declared, including org.graalvm.compiler and org.graalvm.js. Those are available on Maven central and are downloaded from there.

Three arguments are passed to the JVM as declared in the plugin configuration section that enable Graal compilation on the JVM:

  • -XX:+UnlockExperimentalVMOptions
    This flag enables the following option
  • -XX:+EnableJVMCI
    Enables compilation of Java code via JVMCI (the Java Virtual Machine Compiler Interface)
  • --upgrade-module-path
    Puts the correct version of Graal on the module path so it can be used as compiler for GraalVM/JavaScript

Conclusion

In this post we looked at how one can consume GraalVM’s JavaScript engine artifacts from Maven Central and run it on a stock JDK. Without access to the Graal compiler performance of the JavaScript code in this setup might not be optimal — luckily you can enable Graal compiler with a bunch of command line options and get a much faster JavaScript execution. In the presence of the Graal compiler, GraalVM’s JavaScript is significantly faster than Nashorn. For a reference point here’s some benchmark results we got earlier running the Octane benchmark:

In the future, it might be possible to provide more artifacts of GraalVM on Maven Central and allow even more languages to be executed this way on other JVMs, without the need to install the GraalVM binaries itself.

Please tell us if this approach is helpful to you, or what you would like to see in the future. We are happy to receive comments here or as feature requests in our GitHub repositories for Graal or GraalVM/JavaScript.

--

--

Christian Wirth
graalvm

Manager with Oracle Labs, working on the Graal/JavaScript and TruffleRuby implementations. Opinions are my own.