Graal/Truffleについて軽く

κeenです。これは言語実装 Advent Calendar 201714日目の記事です。 JVMのコンパイラエンジンGraalと高速インタプリタ作成フレームワークのTruffleについて。

この記事の前に昨日の記事を読んでおくと理解の助けになるかもしれません。

Graalについて

公式ページ/GitHub

Oracle Labで開発されているJavaのJITエンジンの1つです。 JVMのコンパイラインタフェース(JVMCI)を利用してJavaでコンパイラを書いたものです。 従来はコンパイル部分はC++で書かれてましたが曰くJavaも十分速くなったし高級で安全なJavaでコンバイラを書いてもいいだろとのこと。 この絶妙にランタイムが拡張可能でかつパフォーマンスを損なわない感じはJavaならではですね。 また、Javaで書かれているのでユーザがJavaで拡張可能でもあり、最適化や機械語生成を拡張できます。

Graalはピーク性能の改善を目標としていて、Scalaアプリケーションなど一部のケースで既に従来のJVMの性能を上回ってるそうです。

GraalをデフォルトでonにしたGraalVMというのもOracle Labから配布されているようです。

Truffleについて

GitHub

JavaのASTインタプリタフレームワークです。 多少言語実装をかじった人ならASTインタプリタは遅いことをご存知かと思いますがTruffleはそのままASTで解釈するだけではありません。 昨日のRPythonのようにASTインタプリタは意味論を与えるのが主な目的で、実行時にはGraalと連携して最適化していきます。

Truffleが行う最適化は自己最適化です。 Javaで実装された各ASTノードがexecuteメソッドを持っていて実行される訳ですが、ASTを実行中に書き換えることで高速化していきます。 抽象的なインタプリタからセマンティクスを得て高速化していく点では似てますが、昨日のRPythonのJITとは違って全てがJVMの実行中に起きるのでちょっと図には書けないですね…。 このゲスト言語のソースコードに合わせた(部分評価した)最適化と評価器そのものの最適化が表裏一体な感じがなんともいえませんね。

さて、Truffleの目的は高速化以外にもあります。polyglot、つまり同じVM上で複数の言語を動かすのです。 現状RubyPython、JavaScript、RLLVM IRなどが動きます。 なにやらCLI/CLRとIronプロジェクトが去来しますがRuby、Python、JS、RはJVM実装の実績がありますしまあ大丈夫でしょう。 Truffle Tutorial: Embedding Truffle Languages in Javaを見ても分かるように相互連携はそれなりに出来るようです。

Substrate VM

資料。 あまり情報がないのですが、どうやらTruffleで書かれたインタプリタをコンパイルしてくれるようです。 出来上がるバイナリはJVMやJava Byte Codeを含まないらしいので面白いですね。

Truffleチラ見

simplelanguageというレポジトリがあって、おもちゃ言語をTruffleで実装するデモが置いてあります。

エントリポイント

日本語コメントはκeenによる。

// SLMain.java
public final class SLMain {

    /**
     * The main entry point.
     */
    public static void main(String[] args) throws IOException {
        Source source;
        if (args.length == 0) {
            // @formatter:off
            source = Source.newBuilder(new InputStreamReader(System.in)).
                name("<stdin>").
                // MIME Typeを指定することでその言語のインタプリタを指定する
                mimeType(SLLanguage.MIME_TYPE).
                build();
            // @formatter:on
        } else {
            source = Source.newBuilder(new File(args[0])).build();
        }

        executeSource(source, System.in, System.out);
    }

    private static void executeSource(Source source, InputStream in, PrintStream out) {
        out.println("== running on " + Truffle.getRuntime().getName());

        PolyglotEngine engine = PolyglotEngine.newBuilder().setIn(in).setOut(out).build();
        assert engine.getLanguages().containsKey(SLLanguage.MIME_TYPE);

        try {
            // ここで実行
            Value result = engine.eval(source);

            if (result == null) {
                throw new SLException("No function main() defined in SL source file.");
            } else if (result.get() != SLNull.SINGLETON) {
                out.println(result.get());
            }

        }
        ...
    }
    ...
}

Nodeと解釈

// nodes/expression/SLMulNode.java

package com.oracle.truffle.sl.nodes.expression;

import java.math.BigInteger;

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.sl.nodes.SLBinaryNode;

/**
 * This class is similar to the extensively documented {@link SLAddNode}.
 */
@NodeInfo(shortName = "*")
// 辿っていくと、`com.oracle.truffle.api.nodes.Node`の子クラスになっている
public abstract class SLMulNode extends SLBinaryNode {

    @Specialization(rewriteOn = ArithmeticException.class)
    protected long mul(long left, long right) {
        return Math.multiplyExact(left, right);
    }

    @Specialization
    @TruffleBoundary
    protected BigInteger mul(BigInteger left, BigInteger right) {
        // 本当に計算するだけ。
        return left.multiply(right);
    }
}

Builtin

ビルトイン関数も定義されます。

// builtins/SLPrintlnBuiltin
@NodeInfo(shortName = "println")
public abstract class SLPrintlnBuiltin extends SLBuiltinNode {
   ...

    @TruffleBoundary
    private static void doPrint(PrintWriter out, Object value) {
        out.println(value);
    }
}

まとめ

またしても締まりがないですが

  • GraalというJavaで拡張可能なJITエンジンがあるよ
  • Truffleという簡単にインタプリタを書けるフレームワークがあるよ
  • TruffleはGraalを使いながら裏ですごい最適化をするよ
  • Truffleで書いたインタプリタはSVMというツールでバイナリにもできるよ。

参考資料

Written by κeen
Later article
diesel小ネタ集