Learning to use Wholly GraalVM!
Introduction
In the post Truffle served in a Holy Graal: Graal and Truffle for polyglot language interpretation on the JVM, we got a brief introduction and a bit of deep dive into Graal, Truffle and some of the concepts around it. But no technology is fun without diving deep into its practicality, otherwise its like Theoretical Physics or Pure Maths — abstract for some, boring for others (sorry the last part was just me ranting).
In this post we will be taking a look into the GraalVM, by installing it, comparing SDK differences and looking at a some of the examples that illustrate how different languages can be compiled and run on the GraalVM, and also how they can be run in the same context and finally natively (more performant).
GraalVM is similar to any Java SDK (JDK) that we download from any vendor, except that it has JVMCI: Java-level JVM Compiler Interface support and Graal is the default JIT compiler. It can, not just execute Java code but also languages like JS, Ruby, Python and R. It can also enable building ahead-of-time (AOT) compiled executable (native images) or share library for Java programs and other supported languages. Although we won’t be going through every language but only a selected few of them.
Just to let you know, that all of the commands and actions have been performed on a Ubuntu 16.04 operating system environment (should work on the MacOSX with minor adaptations, on Windows a bit more changes would be required – happy to receive feedback with the differences, will update post with them).
Practical hands-on
We can get our hands on the GraalVM in more than one way, either build it on our own or download a pre-built version from a vendor website:
- build on our own: some cloning and other magic (we can see later on)
- download a ready-made JVM: OTN download site
- hook up a custom JIT to an existing JDK with JVMCI support (we can see later on)
As we are using a Linux environment, we it would be best to download the linux (preview) version of GraalVM based on JDK8 (> 500MB file, need to Accept the license, need to be signed in on OTN or you will be taken to https://login.oracle.com/mysso/signon.jsp) and install it.
Follow the installation information on the download page after unpacking the archive, you will find a folder by the name graalvm-0.30
(at the time of the writing of this post), after executing the below command:
$ tar -xvzf graalvm-0.30-linux-amd64-jdk8.tar.gz
Eagle eyeing: compare SDKs
We will quickly check the contents of the SDK to gain familiarity, so let’s check the contents of the GraalVM SDK folder:
$ cd graalvm-0.30 $ ls
which looks familiar, and has similarities, when compared with the traditional Java SDK folder (i.e. JDK 1.8.0_44):
$ cd /usr/lib/jdk1.8.0_44 $ ls
Except we have quite a few additional artifacts to learn about, i.e. the launchers on the VM for the supported languages, like FastR, JS (GraalJS), NodeJS (GraalNodeJS), Python, Ruby and Sulong (C/C++, Fortran).
Comparing the bin
folder between the GraalVM SDK and say JDK 1.8.0_44 SDK, we can see that we have a handful of additional files in there:
(use tools like meld
or just diff
to compare directories)
Similarly we can see that the jre
folder has interesting differences, although semantically similar to the traditional Java SDKs. A few items that look interesting in the list are Rscript, lli and ployglot.
Now we haven’t literally compared the two SDKs to mark elements that are different or missing in one or the other, but the above gives us an idea about what is offered with the pre how to use the features it provides – well this SDK has them baked into it the examples
folder.
$ tree -L 1
(use the tree
command – sudo apt-get tree
to see the above, available on the MacOSX & Windows)
Each of the sub-folders contain examples for the respective languages supported by the GraalVM, including embed
and native-image
which we will also be looking at.
Exciting part: hands-on using the examples
Let’s get to the chase, but before we can execute any code and see what the examples do, we should move the graalvm-0.30
to where the other Java SDKs reside, lets say under /usr/lib/jvm/
and set an environment variable called GRAAL_HOME
to point to it:
$ sudo mv -f graalvm-0.30 /usr/lib/jvm $ export GRAAL_HOME=/usr/lib/jvm/graalvm-0.30 $ echo "export GRAAL_HOME=/usr/lib/jvm/graalvm-0.30" >> ~/.bashrc $ cd examples
R language
Let’s pick the R
and run some R
scripts files:
$ cd R $ $GRAAL_HOME/bin/Rscript --help # to get to see the usage text
Beware we are running Rscript
and not R
, both can run R scripts, the later is a R
REPL.
Running hello_world.R
using Rscript
:
$ $GRAAL_HOME/bin/Rscript hello_world.R [1] "Hello world!"
JavaScript
Next we try out some Javascript
:
$ cd ../js/ $ $GRAAL_HOME/bin/js --help # to get to see the usage
Running hello_world.js
with js
:
$ $GRAAL_HOME/bin/js hello_world.js Hello world!
Embed
Now lets try something different, what if you wish to run code written in multiple languages, all residing in the same source file, on the JVM — never done before, which is what is meant by embed.
$ cd ../embed
We can do that using the org.graalvm.polyglot.context
class. Here’s a snippet of code from HelloPolyglotWorld.java
:
import org.graalvm.polyglot.*; public class HelloPolyglotWorld { public static void main(String[] args) throws Exception { System.out.println("Hello polyglot world Java!"); Context context = Context.create(); context.eval("js", "print('Hello polyglot world JavaScript!');"); context.eval("ruby", "puts 'Hello polyglot world Ruby!'"); context.eval("R", "print('Hello polyglot world R!');"); context.eval("python", "print('Hello polyglot world Python!');"); } }
Compile it with the below to get a.class
file created:
$ $GRAAL_HOME/bin/javac HelloPolyglotWorld.java
And run it with the below command to see how that works:
$ $GRAAL_HOME/bin/java HelloPolyglotWorld Hello polyglot world Java! Hello polyglot world JavaScript! Hello polyglot world Ruby! [1] "Hello polyglot world R!" Hello polyglot world Python!
You might have noticed a bit of sluggishness with the execution when switching between languages and printing the “Hello polyglot world….” messages, hopefully we will learn why this happens, and maybe even be able to fix it.
Native image
The native image feature with the GraalVM SDK helps improve startup time of Java applications and give it smaller footprint. Effectively its converting byte-code that runs on the JVM (on any platform) to native code for a specific OS/platform — which is where the performance comes from. It’s using aggressive ahead-of-time (aot) optimisations to achieve good performance.
Let’s see how that works.
$ cd ../native-image
Lets take a snippet of Java code from HelloWorld.java
in this folder:
public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } }
Compile it into byte-code:
$ $GRAAL_HOME/bin/javac HelloWorld.java
Compile the byte-code (HelloWorld.class) into native code:
$ $GRAAL_HOME/bin/native-image HelloWorld classlist: 740.68 ms (cap): 1,042.00 ms setup: 1,748.77 ms (typeflow): 3,350.82 ms (objects): 1,258.85 ms (features): 0.99 ms analysis: 4,702.01 ms universe: 288.79 ms (parse): 741.91 ms (inline): 634.63 ms (compile): 6,155.80 ms compile: 7,847.51 ms image: 1,113.19 ms write: 241.73 ms [total]: 16,746.19 ms
Taking a look at the folder we can see the Hello World source and the compiled artifacts:
3.8M -rwxrwxr-x 1 xxxxx xxxxx 3.8M Dec 12 15:48 helloworld 12K -rw-rw-r-- 1 xxxxx xxxxx 427 Dec 12 15:47 HelloWorld.class 12K -rw-rw-r-- 1 xxxxx xxxxx 127 Dec 12 13:59 HelloWorld.java
The first file helloworld is the native binary that runs on the platform we compiled it on, using the native-image command, which can be directly executed with the help of the JVM:
$ helloworld Hello, World!
Even though we gain performance, we might be loosing out on other features that we get running in the byte-code form on the JVM — the choice of which route to take is all a matter of what is the use-case and what is important for us.
It’s a wrap up!
That calls for a wrap up, quite a lot to read and try out on the command-line, but well worth the time to explore the interesting GraalVM.
To sum up, we went about downloading the GraalVM from Oracle Lab’s website, unpacked it, had a look at the various folders and compared it with our traditional looking Java SDKs, noticed and noted the differences.
We further looked at the examples provided for the various Graal supported languages, and picked up a handful of features which gave us a taste of what the GraalVM can offer. While we can run our traditional Java applications on it, we now also have the opportunity to write applications that expressed in multiple supported languages in the same source file or the same project. This also gives us the ability to do seamlessly interop between the different aspects of the application written in a different language. Ability to even re-compile our existing applications for native environments (native-image) for performance and a smaller foot-print.
Published on Java Code Geeks with permission by Mani Sarkar, partner at our JCG program. See the original article here: LEARNING TO USE WHOLLY GRAALVM! Opinions expressed by Java Code Geeks contributors are their own. |