GraalVMを使ってみる


前書き

GraalVMのパフォーマンス、しいては、GraalをJITコンパイラとして利用した際のパフォーマンスにフォーカスした記事なので、GraalVMの特徴の一つであるnative imageや、multiple languagesなどに関しては、この記事では触れていない。

環境

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

GraalVMとは

Javaで書かれた最新のJITコンパイラ、Graalを搭載したVMである。
GraalVM公式ドキュメントによると、Graalには以下の特徴がある。

Graal自体がjavaで実装されているので、標準のJITコンパイラではできなかった部分的なエスケープ分析などの強力な最適化により、Javaプログラムを大幅に高速化できる

実際にどのくらい速くなるのかは後述している。

ちなみに、VM変更するのは面倒であれば、以下のオプションをつけることで、JITコンパイラだけGraalに変えることもできるらしい。
しかし、参考によるとJDK10以降でしか使えないので注意。

-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler

特徴は以下の資料にまとめている
https://drive.google.com/file/d/1UKw_7dM-2SLQhnrM5cq4hemL9yqDxhPI/view?usp=sharing

GraalVMのアーキテクチャ

以下のようなアーキテクチャになっている。
標準JVMと比べて、C2部分がGraalコンパイラに置き換わっている。
GraalとJVMCIの部分が、Javaで実装されている。

JVMCI

Javaで実装されたコンパイラを動的コンパイラとしてJVMで利用できるようにしたもの。

実際に使ってみる

インストール

公式ドキュメントに書いている通りにする。
macOSの場合は、ここ

OpenJDKの標準VMとGraalVMで速度比較してみる

より詳細な速度比較は、こちらの記事に書いている。

275MbのGraalTest.txt に含まれている単語の種類からトップ10を吐き出すプログラムを実行する。


$ 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

上述でインストールした、GraalVMを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)

以下、実行プログラム

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])
  }
}

結果

OpenJDKの標準VMでは!?

比較のために、OpenJDKの標準JITコンパイラを使ってプログラムを実行してみる。
-XX:-UseJVMCICompilerを指定すると、OpenJDKの標準JITコンパイラを利用できる。

.jvmopts
-XX:-UseJVMCICompiler
-Xmx8G
実行
$ /usr/bin/time sbt run GraalTest.scala

すると、以下のような結果が得られる。

JVM結果
[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

GraalVMでは!?

上記のプログラムをGraalVM上で実行してみる。

.jvmopts
-Xmx8G
実行
$ /usr/bin/time sbt run GraalTest.scala

すると、以下のような結果が得られる。

GraalVM結果
[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

GraalVMを使うことで、10sも早くなっていることがわかる。
これは、だいぶ早くなっている!!!!!

↓パフォーマンス測定で使ったコード達
https://github.com/kinshotomoya/loginfrastructure/tree/master/jitCompiler

まとめ

現状のJITコンパイラの代わりにGraalを利用するのは、使えるなら使うべき。

次回は、GraalVMのもう一つの特徴である、native-imageに関してまとめる。

参考