2018年10月22日から25日にかけての4日間、米サンフランシスコのMoscone CenterにおいてOracle主催の開発者向けイベント「Oracle Code One 2018」が開催されました。これは、これまでは「JavaOne Conference」として開催されていたイベントの後継となるものです。名称が変更された背景には、対象となる技術の裾野を広げてエンジニア同士のコラボレーションを促進しようという目的があります。多言語対応という時代の流れを取り入れた形と言えます。
Oracle自身、多言語対応の鍵となるさまざま技術を開発していますが、その1つに「GraalVM」があります。これはJava技術をベースとした多言語対応の汎用仮想マシンです。本稿では、このGraalVMについて、Oracle Code Oneで見た姿をレポートします。
GraalVMとは
GraalVMは、Java仮想マシン(以下、JVM)およびJITコンパイラ(Just-in-Timeコンパイラ)の技術を利用して作成された多言語対応の汎用仮想マシンです。Oracleが開発し、オープンソースで公開されています。
もともとJVMは、ScalaやKotlin、Groovyなど、Java以外の言語の実行にも対応しています。一般的には、これはコンパイラがJVM用のバイトコードを生成し、それをJVMが実行するという仕組みで実現されており、総称して「JVM言語」と呼ばれています。JRubyやJythonといった、既存言語のJVM用実装もJVM言語の一種と言えます。
これに対してGraalVMは、JavaやJVM言語だけでなく、JavaScriptやR言語、RubyやPython、そしてC/C++のようなLLVMベースの言語の実行にも対応します。同一の仮想マシンを用いて異なる複数の言語を実行できるだけでなく、複数の言語のコードが混在したプログラムを実行することもできます。
GraalVMには、「Graal」と「Truffle」という2つの主要技術が含まれています。GraalはJVM用の新しいJITコンパイラで、Java 9以降のバージョンで利用できるようになりました。JavaのJITコンパイラは、コンパイラが生成したJavaバイトコードを読み込んで機械語を生成します。現在のJVMは、このJITコンパイラのエンジン部分のみを差し替えて実行できる仕組みを備えています。GraalVMは、このJITコンパイラ部分にGraalを採用することで高速化を実現しています。
Truffleは、自前のプログラミング言語を実装するためのフレームワークで、任意の言語インタプリタを比較的容易に実装することができます。Truffleはプログラムのソースコードを解析してAST(抽象構文木)を生成するためのAPIを提供しています。GraalはTruffleの生成するASTを解釈し、極めて高いパフォーマンスで実行することができるため、GraalVMはこの2つを組み合わせて高性能な多言語環境を実現しています。ただし、JVM言語に関してはもともとGraalで実行できるため、TruffleはJavaScriptやRubyなどJVM言語以外の言語のために使われています。
GraalVMの10の使い方
では、実際にGraalVMはどのような使い方ができるのでしょうか。OracleでGraalVMの開発を担当しているChris Seaton氏は、GraalVMについて以下に挙げる10の使い方を提案しています。
ハイパフォーマンスなJava実行環境として使う
GraalVMに搭載されているGraalは、モダンな実装を取り入れた、AOT(Ahead-Of-Time)としてもJITとしても動作するコンパイラです。既存のコンパイラよりも高速に動作するケースも多いため、コンパイラエンジンを既存のものからGraalに置き換えるだけで、アプリケーションの実行パフォーマンスが上がる可能性があります。
低フットプリント・高速スタートアップのJava実行ファイルを作る
GraalVMには、Javaプログラムをコンパイルしてネイティブの実行ファイルを生成するツールが付属しています。バイトコードではな実行ファイルなので、可搬性という利点は失われますが、通常のJavaバイトコードをJVMに読み込ませて実行するよりもフットプリントが小さく、起動が高速なネイティブプログラムを作ることができます。
マルチ言語の実行環境として利用する
前述のように、GraalVMはJavaScriptやRuby、R、Pythonなどといった複数のプログラミング言語をサポートしています。単一の言語のプログラムを実行できるだけでなく、複数の言語を組み合わせて相互にAPIを呼び出すこともできるようになっています。これによって、各言語の得意分野を活かしたアプリケーション開発が可能になります。
ネイティブアプリケーションをJVM上で実行する
GraalVMでは、C言語で作られたプログラムも実行することもできます。原理的には、GraalVMはLLVMによって生成されたビットコードを実行することができるため、C言語以外にも、C++などのLLVMがサポートする他の言語にも対応できます。C言語で書かれた既存のプログラムもLLVMでコンパイルし直せば実行できるので、すでにあるさまざまな資産を容易にGraalVMに移行して再利用できるというメリットがあります。
さまざまな言語対応のデバッグ/プロファイルツールとして使う
GraalVMにはChrome拡張として作られたデバッガが付属しています。また、JVM用のプロファイラであるVisualVMも付属しています。これを使うことで、メモリの使用状況などといったJVM内の状態を調べることができます。これらのツールはGraalVM上で実行されるすべてのプログラムに対応しているため、JVM言語をはじめとして、JavaScriptやPythonといったGraalVMが対応するあらゆる言語の共通的な開発ツールとして利用できます。
JVM言語を多言語拡張する
GraalVMでは、JavaをはじめとするJVMベースの言語から、他の言語のコードを読み込んで実行するためのAPIが提供されています。これを使うことで、Javaプログラム内からJavaScriptを実行するなどといった、クロス言語な実装が可能になります。
ネイティブアプリケーションを多言語拡張する
GraalVMでは、ネイティブアプリケーションからGraalVM用のプログラムを呼び出すライブラリも用意されています。すなわち、この多言語ライブラリを通して、C言語のプログラムからGraalVMが対応するさまざまな言語を呼び出すことができるということです。この多言語ライブラリはデフォルトではJavaScript用にビルドされていますが、再ビルドすることで他の言語にも対応します。
Javaコードをネイティブライブラリとして使う
GraalVMでは、Javaのコードをスタンドアロンのネイティブライブラリにコンパイルすることもできます。通常、ネイティブアプリケーションでJavaのコードを実行するにはJVMを組み込む必要がありますが、GraalVMでネイティブライブラリに変換してしまえば、JVM無しでJavaで書かれた資産を利用することが可能です。
データベース内で任意の組込みプログラムを実行する
Oracleが提供するOracle Database 12cには、実験的にOracle Database Multilingual Engine(MLE)と呼ばれる機能が用意されています。これはデータベース内で任意のプログラムを実行できるようにする拡張機能です。MLEは、GraalVMを組込むことで、任意の言語のプログラムをサポートできるようになっています。この機能によって、たとえばJavaScriptをSQLクエリに組み込んで実行するといったようなことが可能になります。
Oracle DatabaseのほかにMySQLでも同様の拡張機能が利用できます。現段階ではJavaScriptのみサポートしていますが、将来的には他の言語にも対応する予定とのことです。
独自のプログラミング言語を作成する
Truffleを使えば、自前のプログラミング言語のためのインタプリタを比較的容易に作成することができます。Truffleで実装された言語はGraalVMの高性能なエンジンで実行することが可能です。TruffleのインタプリタはJavaで実装できるので、Javaプログラマにとっての親和性は極めて高いというメリットもあります。もちろん、ここまで説明してきたGraalVMの使い方は、Truffle製の独自言語にも通用します。
GraalVMで高速化を図るTwitter社
Twitter社はいちはやくGraalVMを導入した会社の1つです。同社によるGraal VMの導入事例についてはTwitter VMチームのChris Thalinger氏が多くの発表を行なっており、そのプレゼンテーションの様子がYouTubeなどに投稿されています(たとえば2018年3月のVOXXEDDAYSでの講演はこちらで見られます)。
Oracle Code Oneでは、Thalinger氏がGraalVMを使った新しい試みについて発表しました。これは、GraalVMに対してベイズ的手法を用いて最適な実行パラメータを探し、自動でチューニングするというものです。ベイズ的手法を用いた分析には、Whetlab(機械学習に関する技術のスタートアップで、2015年に同社が買収した)が展開するBOaaS(Bayesian Optimization-as-a-Service)を利用したとのことです。
テスト対象としたパラメータは、インライン展開する閾値を決定する以下の3つです。
- TrivialInliningSize
- MaximumInliningSize
- SmallCompiledLowLevelGrapthSize
この3つのパラメータを指定した範囲で変動させながら、BOaaSを利用して最適な組み合せを見つけ、自動的に調整します。テストの結果、CPU使用時間やレイテンシーについて、既存のパラメータを利用した場合に比べてさらなる改善が見られたとのことです。
GraalVMとTruffleをを使ったGoldman Sachsの挑戦
Goldman Sachs社(以下、GS)では、自社開発の「Slang」というプログラミング言語を使用しているそうです。この言語は25年前から開発されており、同社にはSlangベースの莫大な量のコードが現在も稼働しています。しかし、25年という数字でも容易に想像ができるように、レガシーな資産によくある多くの問題を抱えているそうです。特に重大な問題としては次のようなものが挙げられています。
- 時間の経過とともに進化しており、明確な言語仕様がない
- C/C++の複雑なコードで作られているため、最適化が難しい
- 過去の最適化によって、SlangやLLVMでコンパイル済みのバイナリコードが存在する
- 1億5,000万行のSlangコードがクリティカルなシステムで現在も稼働している
GSでは、TruffleとGraalVM、そしてSulongを利用して、これらの問題の解消を試みているとのことです。Sulongは、GraalVMに組み込まれているハイパフォーマンスなLLVMのビットコードインタプリタです。GSの挑戦は、既存のSlangインタプリタの実装を活かしつつ、SlangのコードをハイパフォーマンスなGraalVM上で実行できるようにするというものです。具体的には、既存のインタプリターと新たに実装したTruffleベースのインタプリタを、Sulongを用いて相互に連携させます。これによって、既存のSlangコードがTruffle側の変数にアクセスしたり、Truflle側からSlangのコードを呼び出したりできるようになります。
この方針でPoCを実施した結果として、GraalVMのツールチェーンを活用できる点や、既存インタプリタとの相互運用に関しては満足できる結果が得られたとのことです。一方で、使用するC++ライブラリの違い(Sulongがlibc++に依存するのに対して、GCではlibstdc++を使っている)や、ネイティブ環境とVM環境の同時利用などに関しては、まだ改善の余地があると考えているそうです。
GSのチャレンジはまだスタート地点とのことですが、GraalVMの大規模な活用事例として極めて興味深いものです。