Ten things you can do with GraalVM

Original author: Chris Seaton
  • Transfer


From the translator: GraalVM is a new, interesting technology, but on Habré there are not many articles on it that could show examples of the features of Graal. The article below is not just a listing of what GraalVM can do, but also a small workshop similar to the one that Chris Seaton and Oleg Shelayev performed on Oracle CodeOne 2018. Following the author, I urge you - try to make examples from the article, this is really interesting.


In GraalVM a lot of things different, and if you've heard this name before, or even seen reports that there is still a lot of things that you probably do not already know, but who can do GraalVM. In this article we will look at the various possibilities that GraalVM provides and show what can be done with their help.


  1. Fast java execution
  2. Reduced start time and memory consumption for Java
  3. Combining JavaScript, Java, Ruby and R
  4. Execution of programs written in platform-specific languages
  5. Common tools for all programming languages
  6. JVM application extension
  7. Addition of platform-specific applications
  8. Java code as a platform-dependent library
  9. Support for multiple programming languages ​​in the database
  10. Creating programming languages ​​for GraalVM

You can do everything that is shown in this article using GraalVM 1.0.0 RC1, which is available via a link from the GraalVM website . I used the Enterprise Edition on MacOS, but the code that is written here will work on Linux and on GraalVM Community Edition.


When you read the article, run the programs that are described in it! The code can be downloaded from GitHub .


Installation


After downloading from http://graalvm.org/downloads, I added the path to GraalVM's executables $PATH. By default, this adds support for running Java and JavaScript.


$ git clone https://github.com/chrisseaton/graalvm-ten-things.git
$ cd foo
$ tar -zxf graalvm-ee-1.0.0-rc1-macos-amd64.tar.gz
    # or graalvm-ee-1.0.0-rc1-linux-amd64.tar.gz on Linux
$ export PATH=graalvm-1.0.0-rc1/Contents/Home/bin:$PATH
    # or PATH=graalvm-1.0.0-rc1/bin:$PATH on Linux

GraalVM comes with built-in support for JavaScript and contains a package manager, which is called gu, adding the ability to install support for languages ​​other than Java and JavaScript. I additionally installed Ruby, Python and R, they are downloaded from GitHub.


$ gu install -c org.graalvm.ruby
$ gu install -c org.graalvm.python
$ gu install -c org.graalvm.R

Now, if you run the javaor command js, you will see the GraalVM versions of these engines.


$ java -version
java version "1.8.0_161"
Java(TM) SE Runtime Environment (build 1.8.0_161-b12)
GraalVM 1.0.0-rc1 (build 25.71-b01-internal-jvmci-0.42, mixed mode)
$ js --version
Graal JavaScript 1.0 (GraalVM 1.0.0-rc1)

1. Quick Java execution


“Graal” in GraalVM is the name of the compiler. He alone is created to rule by all ! This means that this is a single compiler implementation written in the form of a library that can be used for a large number of different things. For example, we use Graal to compile both ahead-of-time and just-in-time to compile code written in various programming languages, including for different processor architectures.


The first and easiest way to use Graal is to use it as a Java JIT compiler.


As an example, we will use a program that gives out 10 of the most frequently encountered words in a document. The program uses the capabilities of the modern Java language, such as Streams and collections.


import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
publicclassTopTen{
    publicstaticvoidmain(String[] args){
        Arrays.stream(args)
                .flatMap(TopTen::fileLines)
                .flatMap(line -> Arrays.stream(line.split("\\b")))
                .map(word -> word.replaceAll("[^a-zA-Z]", ""))
                .filter(word -> word.length() > 0)
                .map(word -> word.toLowerCase())
                .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
                .entrySet().stream()
                .sorted((a, b) -> -a.getValue().compareTo(b.getValue()))
                .limit(10)
                .forEach(e -> System.out.format("%s = %d%n", e.getKey(), e.getValue()));
    }
    privatestatic Stream<String> fileLines(String path){
        try {
            return Files.lines(Paths.get(path));
        } catch (IOException e) {
            thrownew RuntimeException(e);
        }
    }
}

GraalVM includes a compiler javac, but for this demo there is no difference, use it or a standard compiler. Therefore you can use the standard compiler javacif you want.


$ javac TopTen.java

If we run a command javathat is included in GraalVM, then the Graal JIT compiler will be used automatically - no additional actions need to be taken. I will use the command timein order to get real data about the time that was spent on executing the program from beginning to end, instead of deploying a complex microbenchmark. There will also be used a large amount of input data so that there are no insinuations about the saved couple of seconds here or there. File size large.txt- 150 MB.


$ make large.txt
$ time java TopTen large.txt
sed = 502701
ut = 392657
in = 377651
et = 352641
id = 317627
eu = 317627
eget = 302621
vel = 300120
a = 287615
sit = 282613
real 0m17.367s
user 0m32.355s
sys 0m1.456s

Graal is written in Java, not C ++, like most other JIT compilers for Java. We think that this allows us to improve it faster than existing compilers, complementing new powerful optimizations (such as, for example, partial escape analysis) that are not available in the standard JIT compiler for HotSpot.
And it can make your java programs much faster.


For comparison purposes, to run programs without the Graal JIT compiler, I will use the flag -XX:-UseJVMCICompiler. JVMCI is the interface between Graal and JVM. You can also run an example on a standard JVM and compare the results.


$ time java -XX:-UseJVMCICompiler TopTen large.txt
sed = 502701
ut = 392657
in = 377651
et = 352641
id = 317627
eu = 317627
eget = 302621
vel = 300120
a = 287615
sit = 282613
real 0m23.511s
user 0m24.293s
sys 0m0.579s

This test shows that Graal runs our Java program in about three-quarters of the time it takes to run it with a standard HotSpot compiler. Where we believe that a few percent improvement in productivity is a significant achievement, 25% is a big deal.


Twitter is the only company that currently uses Graal on “combat” servers , and they say it’s justified for them, in real money saving terms. Twitter uses Graal to execute applications written in Scala - Graal runs at the JVM bytecode level, i.e. applicable to any JVM language.


This is the first use of GraalVM — simply replacing the JIT compiler with a better version for your existing Java applications.


2. Reduce start time and memory consumption for Java


The strengths of the Java platform are particularly evident when dealing with long-running processes and peak loads. In contrast, short-lived processes suffer from a long startup time and relatively large memory usage.


For example, if we run the application from the previous section, submitting to it a much smaller amount of input data — about 1 KB instead of 150 MB — then it seems to take an unreasonably long time and quite a lot of memory — about 60 MB, in order to process such a small file . Use the parameter -lto print out the amount of used memory in addition to the execution time.


$ make small.txt
$ /usr/bin/time -l java TopTen small.txt
      # -v on Linux instead of -l
sed = 6
sit = 6
amet = 6
mauris = 3
volutpat = 3
vitae = 3
dolor = 3
libero = 3
tempor = 2
suscipit = 2
        0.32 real         0.49 user         0.05 sys
  59846656  maximum resident set size

GraalVM gives us a tool that solves this problem. We said that Graal is a compiler library and it can be used in many different ways. One of them is compiling ahead-of-time into a platform -dependent executable image, instead of just-in-time compiling at runtime. This is similar to how a regular compiler works, for example gcc.


$ native-image --no-server TopTen
   classlist:   1,513.82 ms
       (cap):   2,333.95 ms
       setup:   3,584.09 ms
  (typeflow):   4,642.13 ms
   (objects):   3,073.58 ms
  (features):     156.34 ms
    analysis:   8,059.94 ms
    universe:     353.02 ms
     (parse):   1,277.02 ms
    (inline):   1,412.08 ms
   (compile):  10,337.76 ms
     compile:  13,776.23 ms
       image:   2,526.63 ms
       write:   1,525.03 ms
     [total]:  31,439.47 ms

This command creates a platform-dependent executable file called topten. This file does not launch the JVM, it is not linked to the JVM and it generally does not include the JVM in any way. The command native-imagetruly compiles your Java code and the Java libraries you use into fully-fledged machine code. For runtime components, such as the garbage collector, we run our own new VM, called SubstrateVM, which, like Graal, is also written in Java.


If you look at the dependencies that you use topten, you will see that these are only standard system libraries. We can only transfer this one file to a system on which the JVM has never been installed and run it there to verify that it does not use the JVM or any other files. Toptenalso small enough - the executable code takes up less than 6 MB.


$ otool -L topten    # ldd topten on Linux
topten:
 .../CoreFoundation.framework ...
 .../libz.1.dylib ...
 .../libSystem.B.dylib ...
$ du -h topten 
5.7M topten

If we run this executable file, we will see that it runs about an order of magnitude faster and uses about an order of magnitude less memory than the same program running under the JVM. The launch is so fast that you won't notice how long it took. If you use the command line, you will not feel the pause that is usually present when you run a small, short-lived program under the JVM


$ /usr/bin/time -l ./topten small.txt
sed = 6
sit = 6
amet = 6
mauris = 3
volutpat = 3
vitae = 3
dolor = 3
libero = 3
tempor = 2
suscipit = 2
        0.02 real         0.00 user         0.00 sys
   4702208  maximum resident set size

The utility native-imagehas some limitations . So, at compile time you should have all the classes, also there are limitations in the use of the Reflection API. But there are some additional advantages over basic compilation, such as running static initializers at compile time. In this way, the amount of work performed each time an application is loaded is reduced.


This is the second application of GraalVM - distribution and execution of existing Java programs, with a quick start and less memory consumption. This method eliminates configuration problems such as finding the right jar at runtime, and also allows you to create smaller Docker images.


3. Combining JavaScript, Java, Ruby and R


Along with Java, GraalVM includes new implementations of JavaScript, Ruby, R, and Python engines. They are written using a new framework called Truffle . This framework makes it possible to create interpreters of languages ​​that are both simple and high-performance. When you write an interpreter of a language using Truffle, it will automatically use Graal to provide JIT compilation for your language. Thus, Graal is not only a JIT compiler and an AOT compiler for Java, it can also be a JIT compiler for JavaScript, Ruby, R and Python.


Support for third-party languages ​​in GraalVM aims to be a transparent replacement for existing engines of different languages. For example, we can install the “color” module for Node.js:


$ npm install --global color
...
+ color@3.0.0
added 6 packages in 14.156s

Then write a program using this module to convert RGB HTML colors to HSL:


var Color = require('color');
process.argv.slice(2).forEach(function (val) {
  print(Color(val).hsl().string());
});

And run it in the usual way:


$ node color.js '#42aaf4'
hsl(204.89999999999998, 89%, 60.8%)

The performance engines of different languages ​​in GraalVM work together - there is an API that allows you to run code from one language in a program written in another language. And it allows you to write multilingual programs - programs written in more than one programming language.


This may be necessary if you write most of your program in one language, but would like to use a library written in another programming language. For example, imagine that we need to write an application to convert the color name from CSS into its numeric representation in Node.js, but we want to use the Ruby color library instead of writing the conversion ourselves.


var express = require('express');
var app = express();
color_rgb = Polyglot.eval('ruby', `
  require 'color'
  Color::RGB
`);
app.get('/css/:name', function (req, res) {
  color = color_rgb.by_name(req.params.name).html()
  res.send('<h1 style="color: ' + color + '" >' + color + '</h1>');
});
app.listen(8080, function () {
  console.log('serving at http://localhost:8080')
});

In this code, we wrote that we needed to execute Ruby code as a string, but note that we didn’t do much here - we just connected the libraries and then returned the Ruby object. In Ruby, we would have used it as follows: Color::RGB.by_name (name).html. If you look at how it is color_rgbused further in JavaScript, then you will see that we actually call the same methods from JavaScript, although these are Ruby objects and methods. And we pass them as JavaScript strings and connect the result, which is a Ruby string, with a JavaScript string.


Install both dependencies - Ruby and JavaScript.


$ gem install color
Fetching: color-1.8.gem (100%)
Successfully installed color-1.8
1 gem installed
$ npm install express
+ express@4.16.2
updated 1 package in 10.393s

Then you need to start nodewith a couple of additional options: --polyglotto say that we need access to other languages, and --jvmbecause the executable image nodeby default does not include anything other than JavaScript.


$ node --polyglot --jvm color-server.js
serving at http://localhost:8080

And then we go to the URL http: // localhost: 8080 / css / orange (or some other color), as usual, in your browser.

Let's try to make an example more serious that uses more languages ​​and modules.


JavaScript does not support very large integers. I found several modules like big-integer , but they are all ineffective, because store the components of the number as javascript floating point numbers. A class BigIntegerin Java is more efficient; let's use it to do several arithmetic operations with large integers.


There is also no built-in support for drawing graphs in JavaScript, whereas R perfectly draws graphics. Let's use the module svgfrom R to draw a dotted graph of the trigonometric function in 3D space.


In both cases, we will use the API to support multilingualism from GraalVM (hereinafter referred to as Polyglot API) and we can simply insert the results of executing programs in other languages ​​into JavaScript.


const express = require('express')
const app = express()
const BigInteger = Java.type('java.math.BigInteger')
app.get('/', function (req, res) {
  var text = 'Hello World from Graal.js!<br> '// Using Java standard library classes
  text += BigInteger.valueOf(10).pow(100)
          .add(BigInteger.valueOf(43)).toString() + '<br>'// Using R interoperability to create graphs
  text += Polyglot.eval('R',
    `svg();
     require(lattice);
     x <- 1:100
     y <- sin(x/10)
     z <- cos(x^1.3/(runif(1)*5+10))
     print(cloud(x~y*z, main="cloud plot"))
     grDevices:::svg.off()
    `);
  res.send(text)
})
app.listen(3000, function () {
  console.log('Example app listening on port 3000!')
})

Open http: // localhost: 3000 / in your browser to see the result:

This is the third thing we can do with GraalVM — run programs written in several languages ​​and use the modules from these languages ​​together in one program. We present this as a way to unify runtimes and libraries — you can use the programming language that you think is best suited for solving the current problem and any library you want, regardless of what programming language it is written in.


4. Execution of programs written in platform-dependent languages


Another language that GraalVM supports is C. GraalVM can execute C code in the same way that it executes programs written in JavaScript and Ruby.


What GraalVM actually supports is launching the code resulting from running the LLVM utilities, i.e. bitcode, rather than direct support for C. This means that you can use existing tools for the C language and others that support LLVM, such as C ++, Fortran, and potentially more languages ​​in the future. For ease of demonstration, I run a special version of gzip , which is compiled into a single file (this version is supported by Stephen McCamant ). This is just the source code gzipand configuration autoconfcombined into one file for simplicity. I had to patch a couple of things to make it work on macOS and with clang, but I didn't do anything specifically to support GraalVM.


We compile gzip using the standard clang(LLVM compiler for C) and want it to make us an LLVM bitcode, not a platform-specific build, because GraalVM will not launch it. I am using clang4.0.1.


$ clang -c -emit-llvm gzip.c

And then we run the result, directly using the lli command (interpreter of the LLVM bitcode) from GraalVM. Let's try to compress the file using my gzip system archiver, and then unzip it using gzip running under GraalVM.


$ cat small.txt
Lorem ipsum dolor sit amet...
$ gzip small.txt
$ lli gzip.bc -d small.txt.gz
$ cat small.txt
Lorem ipsum dolor sit amet...

Implementations of Ruby and Python in GraalVM use the same technique to run extensions written in C for these languages. This means that you can run these extensions inside the VM and this allows us to maintain high execution speed, even if we use outdated platform-specific extension interfaces.


This is the fourth way to use GraalVM — launch programs written in platform-dependent languages, such as C or C ++, as well as launching extensions to languages ​​such as Python or Ruby that the JVM implementations of these languages, such as JRuby, are unable to do.


5. Common tools for all programming languages


If you are programming in Java, then you probably use very high-quality tools such as IDE, debuggers, and profilers. Not all languages ​​have such a set of tools, but you can get such a set if you use languages ​​that are supported by GraalVM.


Support for all languages ​​in GraalVM (except for Java, at the moment) is implemented using a common framework - Truffle. This allows us to make functionality, for example, a debugger, once and use it for all languages.


To try this, we will write the simplest program - FizzBuzz , because it is visual (prints something on the screen) and there are clear branches in it that are used only in some iterations. Thus, it will be easier for us to set breakpoints. Let's start with javascript implementation.


functionfizzbuzz(n) {
  if ((n % 3 == 0) && (n % 5 == 0)) {
    return'FizzBuzz';
  } elseif (n % 3 == 0) {
    return'Fizz';
  } elseif (n % 5 == 0) {
    return'Buzz';
  } else {
    return n;
  }
}
for (var n = 1; n <= 20; n++) {
  print(fizzbuzz(n));
}

Run the program as usual, using the utility js, under GraalVM.


$ js fizzbuzz.js
1
2
Fizz
4
Buzz
Fizz

We can also run a program with a flag --inspect. This will give us a link that you can open in Chrome and stop the program in the debugger.


$ js --inspect fizzbuzz.js
Debugger listening on port 9229.
To start debugging, open the following URL in Chrome:
    chrome-devtools://devtools/bundled/inspector.html?ws=127.0.0.1:9229/6c478d4e-1350b196b409

You can put a breakpoint in the FizzBuzz code and then continue execution. When the program interrupts execution, we will see the value of the variable nin the debugger and we can continue the execution of the program or study the debugger interface.

The Chrome debugger is usually used for JavaScript, but for GraalVM in JavaScript there is nothing different from other languages. The flag is --inspectalso available and works in the implementation of Python, Ruby and R. I will not show you the source code of each program, but they run the same way and you get the same debugger in Chrome for each of them.


$ graalpython --jvm --inspect fizzbuzz.py


$ ruby --inspect fizzbuzz.rb


$ Rscript --inspect fizzbuzz.r


Another tool that you probably know from Java is VisualVM. It provides a user interface that you can use to join a running JVM on your local machine or via the network to inspect various aspects of program execution, such as memory usage or execution threads.


GraalVM includes VisualVM as a standard utility jvisualvm.


$ jvisualvm &> /dev/null &

If we run VisualVM while the Java program is running TopTen, we can observe the memory usage or, for example, take a snapshot of the memory contents and see what types of objects we use memory on the heap.


$ java TopTen large.txt


I wrote this Ruby program to generate some garbage in memory at runtime.


require'erb'
x = 42
template = ERB.new <<-EOF
  The value of x is: <%= x %>
EOF
loop do
  puts template.result(binding)
end

If you run the standard Ruby implementation on JVM - JRuby, you will be disappointed with VisualVM, because you will see only internal Java objects instead of objects in your language.


If you are using the version of Ruby for GraalVM, then VisualVM recognizes Ruby objects. We need to use a parameter --jvmto use VisualVM, because it does not support native versions of Ruby.


$ ruby --jvm render.rb

You can, if necessary, look at the snapshot of the heap with internal Java objects, as we saw earlier, or, on the Summary tab, you can select the Ruby Heap and look instead at the real Ruby objects.

The Truffle framework is something like Nexus for languages ​​and tools . If you create your own language engine using Truffle and you make your own tools such as the debugger, taking into account the Truffle API for the toolkit, then each such tool will work for any Truffle-based language, and you will need to write it only time.


So, the fifth application of GraalVM is a platform for creating high-quality tools for programming languages ​​that do not always have good tools. With Truffle and GraalVM, you can use other development tools, such as the debugger in Chrome or VisualVM.


6. Supplementing JVM Applications


In addition to independent execution, as well as joint, in multilingual programs, support for other languages ​​can be added to a Java application. A new API in the package org.graalvm.polyglotallows you to download and run code written in other languages ​​and use the values ​​from them.


import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
publicclassExtendJava{
    publicstaticvoidmain(String[] args){
        String language = "js";
        try (Context context = Context.newBuilder().allowNativeAccess(true).build()) {
            for (String arg: args) {
                if (arg.startsWith("-")) {
                    language = arg.substring(1);
                } else {
                    Value v = context.eval(language, arg);
                    System.out.println(v);
                }
            }
        }
    }
}

If you use the command javac, and javafrom the distribution GraalVM, the imports org.graalvm.*already be in your classpath, ie it will be possible to compile and run the code without any additional flags.


$ javac ExtendJava.java
$ java ExtendJava '14 + 2'
16
$ java ExtendJava -js 'Math.sqrt(14)'
3.7416573867739413
$ java ExtendJava -python '[2**n for n in range(0, 8)]'
[1, 2, 4, 8, 16, 32, 64, 128]
$ java ExtendJava -ruby '[4, 2, 3].sort'
[2, 3, 4]

The engines used to execute commands in different languages ​​are the same high-performance multilingual versions that are launched using commands nodeor rubyexecuted by GraalVM.


This is the sixth way to use GraalVM - as a single interface for embedding code in other programming languages ​​into your Java application. The Polyglot API allows you to take the objects of the “guest” language and use them as Java interfaces, and also provides more complex ways to interact.


7. Addition of platform-specific applications.


GraalVM already includes one library, compiled in the manner indicated in the header - a library that allows you to run code in any language supported by GraalVM from platform-specific applications. For example, a JavaScript engine like V8, a Python-CPython interpreter, is often embedded in other programs, i.e. they can be linked to another application as a library. GraalVM allows you to use any language in the embedded context in the same way - by linking to your Polyglot library.


When you install GraalVM, this library is compiled and present there, but by default it only includes native support for JavaScript. You can rebuild the Polyglot library to include other languages ​​using the command:


$ graalvm-1.0.0-rc1/Contents/Home/jre/lib/svm/bin/rebuild-images libpolyglot

Create a simple C program that executes commands written in any language supported by GraalVM, which are transmitted on the command line. We are going to make the equivalent of our program ExtendJavafrom the previous section, but using C as the main language.


C code
#include<stdlib.h>#include<stdio.h>#include<polyglot_api.h>intmain(int argc, char **argv){
  graal_isolate_t *isolate = NULL;
  graal_isolatethread_t *thread = NULL;
  if (graal_create_isolate(NULL, &isolate) != 0 || (thread = graal_current_thread(isolate)) == NULL) {
    fprintf(stderr, "initialization error\n");
    return1;
  }
  poly_context context = NULL;
  if (poly_create_context(thread, NULL, 0, &context) != poly_ok) {
    fprintf(stderr, "initialization error\n");
    return1;
  }
  char* language = "js";
  for (int n = 1; n < argc; n++) {
    if (argv[n][0] == '-') {
      language = &argv[n][1];
    } else {
      poly_value result = NULL;
      if (poly_context_eval(thread, context, language, "unicalc", argv[n], &result) != poly_ok) {
        fprintf(stderr, "eval error\n");
        return1;
      }
      char buffer[1024];
      size_t length;
      if (poly_value_to_string_utf8(thread, result, buffer, sizeof(buffer), &length) != poly_ok) {
        fprintf(stderr, "to string error\n");
        return1;
      }
      buffer[length] = '\0';
      printf("%s\n", buffer);
      poly_destroy_handle(thread, result);
    }
  }
  return0;
}

Now we will compile and run this code using the system C compiler and link the polyglot library from GraalVM. And again, notice, we don't need a JVM.


$ clang -Igraalvm-1.0.0-rc1/Contents/Home/jre/lib/polyglot /
      -rpath graalvm-1.0.0-rc1/Contents/Home /
      -Lgraalvm-1.0.0-rc1/Contents/Home/jre/lib/polyglot /
      -lpolyglot extendc.c -o extendc
$ otool -L extendc
extendc:
 .../libpolyglot.dylib ...
 .../libSystem.B.dylib ...

$ ./extendc '14 + 2'
16
$ ./extendc -js 'Math.sqrt(14)'
3.7416573867739413
$ ./extendc -python '[2**n for n in range(0, 8)]'
[1, 2, 4, 8, 16, 32, 64, 128]

This is the seventh thing you can do with GraalVM — use a single library in your application to implement any language supported by GraalVM.


8. Java code as a platform-dependent library


Java has a great ecosystem with a large number of very high-quality libraries that are often unavailable to other ecosystems, including platform-dependent applications, as well as other managed programming languages. If you want to use the Java library from a platform-specific application, then you can embed a JVM into this application, but this will pretty quickly make it large and complex.


GraalVM allows you to take a Java library, ready-made or self-written, and compile it into a separate platform-specific library, then re-use it in other programs for this platform. As in the case of compilation, which was discussed earlier, Java libraries compiled in this way do not require a JVM.


I wrote an application that uses the excellent Apache SIS library for working with geodata to calculate the shortest distance ( orthodromy ) between two points on Earth. I used SIS 0.8, which I downloaded separately from http://sis.apache.org/ and pulled the jar out of it.


import org.apache.sis.distance.DistanceUtils;
publicclassDistance{
    publicstaticvoidmain(String[] args){
        finaldouble aLat   = Double.parseDouble(args[0]);
        finaldouble aLong  = Double.parseDouble(args[1]);
        finaldouble bLat   = Double.parseDouble(args[2]);
        finaldouble bLong  = Double.parseDouble(args[3]);
        System.out.printf("%.2f km%n", DistanceUtils.getHaversineDistance(aLat, aLong, bLat, bLong));
    }
    publicstaticdoubledistance(IsolateThread thread, double aLat, double aLong, double bLat, double bLong){
        return DistanceUtils.getHaversineDistance(aLat, aLong, bLat, bLong);
    }
}

Compile it in the usual way and use it to get the distance between London and New York.


$ javac -cp sis.jar -parameters Distance.java
$ java -cp sis.jar:. Distance 51.507222 -0.1275 40.7127 -74.0059
5570.25 km

We can compile this into an executable file, as we did for the program topten.


$ native-image --no-server -cp sis.jar:. Distance
...
$ ./distance 51.507222 -0.1275 40.7127 -74.0059
5570.25 km

And we can build it as a library, not an executable file. To do this, we will declare one or more methods as@CEntryPoint


...
import org.graalvm.nativeimage.IsolateThread;
import org.graalvm.nativeimage.c.function.CEntryPoint;
publicclassDistance{
    ...
    @CEntryPoint(name = "distance")
    publicstaticdoubledistance(IsolateThread thread,
          double a_lat, double a_long,
          double b_lat, double b_long){
        return DistanceUtils.getHaversineDistance(a_lat, a_long, b_lat, b_long);
    }
    ...
}

We do not need to change the command javac, because GraalVM automatically puts the necessary API in classpath. Compile the code in the shared library and the automatically generated header file.


$ native-image --no-server -cp sis.jar:. -H:Kind=SHARED_LIBRARY \
     -H:Name=libdistance
$ otool -L libdistance.dylib   # .so on Linux
libdistance.dylib:
 .../libdistance.dylib ...
 .../CoreFoundation.framework ...
 .../libz.1.dylib ...
 .../libSystem.B.dylib ...
$ du -h libdistance.dylib
4.8M libdistance.dylib

Now we will write a small C program that will use this library. To use the interface of our library, you need to do a small ceremony: since the VM needs to manage the heap, the threads, the garbage collector, and other services, you need to create an instance of the system and tell our main thread about this.


#include<stdlib.h>#include<stdio.h>#include<libdistance.h>intmain(int argc, char **argv){
  graal_isolate_t *isolate = NULL;
  graal_isolatethread_t *thread = NULL;
  if (graal_create_isolate(NULL, &isolate) != 0 || (thread = graal_current_thread(isolate)) == NULL) {
    fprintf(stderr, "initialization error\n");
    return1;
  }
  double a_lat   = strtod(argv[1], NULL);
  double a_long  = strtod(argv[2], NULL);
  double b_lat   = strtod(argv[3], NULL);
  double b_long  = strtod(argv[4], NULL);
  printf("%.2f km\n", distance(thread, a_lat, a_long, b_lat, b_long));
  return0;
}

We compile it using standard system tools, and we can run our executable file (must be installed LD_LIBRARY_PARTH=.on Linux)


$ clang -I. -L. -ldistance distance.c -o distance
$ otool -L distance
distance:
 .../libdistance.dylib ...
 .../libSystem.B.dylib ...
$ ./distance 51.507222 -0.1275 40.7127 -74.0059
5570.25 km

This is the eighth way GraalVM is used to compile java code into a platform-specific library, which can then be used in applications without using JVM


9. Support for multiple programming languages ​​in the database


One of the applications for the Polyglot library is the use of an Oracle database. We used the library to create the Oracle Database Multilingual Engine (MLE), which includes support for the execution of languages ​​on GraalVM and SQL modules.


For example, you have a front-end already written in JavaScript and we validate email addresses using the JavaScript module validator. If we have some logic for the same application in the database, then it is probably written in SQL or PL / SQL. And we would like to use exactly the same validator to ensure the consistency of the results.


Download the MLE as a Docker image from here:


https://oracle.github.io/oracle-db-mle/releases/0.2.7/docker/


Then we upload the image to Docker Daemon.


$ docker load --input mle-docker-0.2.7.tar.gz

Start the image using Docker, and then when the download is finished (it may take a few minutes), start the Bash terminal inside the image.


$ docker run mle-docker-0.2.7
$ docker ps
$ docker exec -ti <container_id> bash -li

If we can run sqlplus(interactive SQL tool) in the terminal Bash to join the database, this means that the database is up and running.


$ sqlplus scott/tiger@localhost:1521/ORCLCDB

If everything went well, exit sqlplus. Now, in the Bash terminal in Docker, we will install the validator module and then run the command dbjsto load this module into the database. Then run sqlplusagain.


$ npm install validator
$ npm install @types/validator
$ dbjs deploy -u scott -p tiger -c localhost:1521/ORCLCDB validator
$ sqlplus scott/tiger@localhost:1521/ORCLCDB

After installation, we can use functions from the module validatoras part of the SQL expression.


SQL> select validator.isEmail('hello.world@oracle.com') from dual;
VALIDATOR.ISEMAIL('HELLO.WORLD@ORACLE.COM')
-------------------------------------------
                                          1
SQL> select validator.isEmail('hello.world') from dual;
VALIDATOR.ISEMAIL('HELLO.WORLD')
--------------------------------
                               0

And this is the ninth thing you can do with GraalVM — run the languages ​​supported by GraalVM within the Oracle database. Thus, you can use logic from the front-end or back-end, but inside the database, instead of always pulling data for processing from the database to the application server.


10. Creating programming languages ​​for GraalVM


Oracle Labs and our colleagues from the academic environment were able to create new high-performance implementations of JavaScript, R, Ruby, Python, and C with a relatively small team, because we developed the Truffle framework, which facilitates the process of creating languages ​​for GraalVM.


Truffle is a java library that helps to write an abstract syntax tree (AST) interpreter . The AST interpreter is probably the easiest way to implement a language, because it works directly with the output of the parser and does not include interpretation of bytecode or compilation, but this approach is often rather slow. We combined this with a technique called partial computing, which allows Truffle to use Graal to automatically compile the JIT language, based solely on the output of your AST interpreter.


You can use Truffle to create your own programming language for GraalVM, or a high-performance version of an existing programming language, or DSL. We talk a lot about the details of Truffle and Graal in our project, but we often forget to mention that Truffle is a really simple way to implement a programming language for GraalVM. And after creating you will have things like, for example, a debugger, automatically. Anyone who has just completed an elementary course in programming languages ​​will have the necessary skill set for this job. Oracle labs made the basic version of Ruby faster than all previous projects, with just one trainee in just a few months.


We don’t have much space here to fully demonstrate the creation of a language, even the most microscopic, but SimpleLanguage is a tutorial on creating your own language with Truffle, based on a simple JavaScript language. In order to understand how this works, you can, for example, look at the implementation of the if statement .


Other languages ​​written using Truffle by people outside of Oracle Labs include the SmallTalk , Newspeak, and Lisp variant . The example for Lisp has a tutorial that you can also learn and do exercises from.


Conclusion


GraalVM includes a large variety of new functionality - it is a platform on which you can build programming languages ​​and tools, and use them in more environments. This allows you to choose the language and modules you need, regardless of the environment in which the program is running or the language you are currently using.


To try GraalVM, go to http://www.graalvm.org/ . There are links to downloads and documentation, and more examples similar to those shown in this article.


Try to make those examples that we talked about here and try to play with them with them and see what else you can do. Let us know how the experiments with GraalVM went through in your application and send us feedback on @ChrisGSeaton or @shelajev .


Acknowledgments: Oleg Šelajev , Olya Gupalo and Doug Simon


Also popular now: