[Java] Get started with GraalVM

3 minute read

Foreword

Since this article focuses on the performance of GraalVM, that is, the performance when Graal is used as a JIT compiler, this article does not touch on one of the characteristics of GraalVM, such as native image and multiple languages.

Environment

  • macOS Mojava 10.14.4
  • Scala 2.13.1
  • sbt 1.3.8
  • GraalVM 20.1

What is GraalVM

It is a VM with Graal, the latest JIT compiler written in Java. According to GraalVM official document, Graal has the following features.

Graal itself is implemented in java, so powerful optimizations such as partial escape analysis not possible with standard JIT compilers can significantly speed up Java programs

I will explain later how much faster it actually becomes.

By the way, if it is troublesome to change the VM, it seems that you can change the JIT compiler to Graal by adding the following option. However, note that Reference can only be used with JDK 10 or later.

- XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler

GraalVM architecture

It has the following architecture. Compared to the standard JVM, the C2 part has been replaced by the Graal compiler. Graal and JVMCI parts are implemented in Java.

Graal.png

JVMCI

A compiler implemented in Java that can be used in the JVM as a dynamic compiler.

https://openjdk.java.net/jeps/243

Try it out

install

Do as written in official document. For macOS, here

Speed comparison between OpenJDK standard VM and GraalVM

A more detailed speed comparison is written in this article.

Run a program that spits out the top 10 from the word types contained in the 275Mb GraalTest.txt.


$ll
total 65368
- rw-r--r-- 1 kinsho staff 202B 5 31 14:04 FibTest.scala
- rw-r--r-- 1 kinsho staff 1.9K 5 31 17:13 GraalTest.scala
- rw-r--r-- 1 kinsho staff 275M 5 31 16:54 GraalTest.txt
drwxr-xr-x 4 kinsho staff 128B 5 31 14:28 project
drwxr-xr-x 5 kinsho staff 160B 5 31 14:35 target

Use GraalVM installed as above as a VM.

$ java -version
openjdk version "1.8.0_252"
OpenJDK Runtime Environment (build 1.8.0_252-b09)
OpenJDK 64-Bit Server VM GraalVM CE 20.1.0 (build 25.252-b09-jvmci-20.1-b02, mixed mode)

Below, the execution program

GraalTest.scala


import java.io.File

import scala.annotation.tailrec
import scala.io.BufferedSource

object GraalTest extends App {

  val fileName: String = "GraalTest.txt"
  val encode: String = "UTF-8"

  val source: BufferedSource = scala.io.Source.fromFile(fileName, encode)
  val lines: Iterator[String] = source.getLines()
  val sortedTextList = lines.toList.mkString(" ").split(" ").sorted.toList
  val value = createMap(sortedTextList)
  val top10Words = value.toList.sortBy(_._2).reverse.take(10)

  def createMap(wordList: List[String]): Map[String, Long] = {
    @tailrec
    def loop(list: List[String], acc: Map[String, Long]): Map[String, Long] = {
      wordList match {
        case head :: tail if acc.isEmpty => {
          loop(tail, acc + (head -> 1L))
        }
        case head :: tail => {
          acc.get(head) match {
            case Some(value) => {
              loop(tail, acc.updated(head, value + 1L))
            }
            case None => {
              loop(tail, acc + (head -> 1L))
            }
          }
        }
        case head :: Nil => {
          acc.get(head) match {
            case Some(value) => {
              acc.updated(head, value + 1L)
            }
            case None => {
              acc + (head -> 1L)
            }
          }
        }
      }
    }
    loop(wordList, Map.empty[String, Long])
  }
}

result

With the standard VM of OpenJDK! ?

For comparison, try running the program using the OpenJDK standard JIT compiler. If you specify -XX:-UseJVMCICompiler, you can use the standard JIT compiler of OpenJDK.

.jvmopts


- XX:-UseJVMCICompiler
- Xmx8G

run


$ /usr/bin/time sbt run GraalTest.scala

Then, the following result is obtained.

JVM result


[info] running GraalTest
List((the,3297996), (and,2198664), (of,2198664), (you,1648998), (a,1648998), (in,1648998), (about,1099332), (always,1099332), (with,1099332), (how,1099332))
[success] Total time: 64 s (01:04), completed 2020/06/04 0:38:37
       85.31 real 479.11 user 8.89 sys

With GraalVM! ?

Try running the above program on GraalVM.

.jvmopts


- Xmx8G

run


$ /usr/bin/time sbt run GraalTest.scala

Then, the following result is obtained.

GraalVM result


[info] running GraalTest
List((the,3297996), (and,2198664), (of,2198664), (you,1648998), (a,1648998), (in,1648998), (about,1099332), (always,1099332), (with,1099332), (how,1099332))
[success] Total time: 54 s, completed 2020/06/04 0:40:02
       75.29 real 333.95 user 9.23 sys

By using GraalVM, you can see that it is 10s faster. This is getting much faster!!!!!

↓ Codes used in performance measurement https://github.com/kinshotomoya/loginfrastructure/tree/master/jitCompiler

Summary

If you can use Graal instead of the current JIT compiler, you should use it.

Next time, I will summarize another feature of GraalVM, native-image.

Reference

  • https://medium.com/graalvm/graalvm-ten-things-12d9111f307d
  • https://www.graalvm.org/docs/examples/java-performance-examples/