Skip to content

Instantly share code, notes, and snippets.

@kawasima
Last active October 31, 2018 08:14
Show Gist options
  • Save kawasima/eedebf92a1e0c8ae7db1e6735b10bbda to your computer and use it in GitHub Desktop.
Save kawasima/eedebf92a1e0c8ae7db1e6735b10bbda to your computer and use it in GitHub Desktop.
Node学園31
.page-0 *{text-align:center}
.page-3 *{text-align:center}
.page-6 code{font-size:small;line-height:1.0}
.page-15 li{font-size:xx-large}
.page-19 h1{font-size:x-large}
.page-20 *{text-align:center}

Node.js開発者向けの

Graal入門

@kawasima

kawasima


Coverage area

Graal ≠ GraalVM

Graal ∈ GraalVM


Graal

  • Javaで書かれたJVMのJITコンパイラ


なぜJavaでJITコンパイラを?

  • 開発者のC++疲れ
  • 最適化ロジックをユーザサイドで拡張可能
  • JIT自体がJust in Timeにコンパイルされ速くなる。

public class CountUppercase {
     static final int ITERATIONS = Math.max(Integer.getInteger("iterations", 1), 1);
     public static void main(String[] args) {
         String sentence = String.join(" ", args);
         for (int iter = 0; iter < ITERATIONS; iter++) {
             if (ITERATIONS != 1) System.out.println("-- iteration " + (iter + 1) + " --");
             long total = 0, start = System.currentTimeMillis(), last = start;
             for (int i = 1; i < 10_000_000; i++) {
                 total += sentence.chars().filter(Character::isUpperCase).count();
                 if (i % 1_000_000 == 0) {
                     long now = System.currentTimeMillis();
                     System.out.printf("%d (%d ms)%n", i / 1_000_000, now - last);
                     last = now;
                 }
             }
             System.out.printf("total: %d (%d ms)%n", total, System.currentTimeMillis() - start);
         }
     }
 }

C1コンパイラ

~/w/graaltest ❯❯❯ java CountUppercase
 1 (179 ms)
 2 (102 ms)
 3 (92 ms)
 4 (54 ms)
 5 (100 ms)
 6 (53 ms)
 7 (53 ms)
 8 (101 ms)
 9 (85 ms)
 total: 0 (872 ms)

Graal

~/w/graaltest ❯❯❯ /opt/graalvm/bin/java CountUppercase
 1 (760 ms)                                                                                                                          
 2 (866 ms)                                                                                                                          
 3 (478 ms)
 4 (151 ms)
 5 (109 ms)
 6 (137 ms)
 7 (124 ms)
 8 (41 ms)
 9 (3 ms)
 total: 0 (2672 ms)

GraalVM

  • Graalを使ってJITコンパイルし、TruffleでPolyglotすると世界が変わるというマーケティング用語

Truffle

  • ASTの各ノードの処理を実装する
  • 実装例
  • 二村射影によって部分的に最適化されたバイトコード出力 (勉強中)

プログラミング言語の作りやすさ

  • 構文解析と各ノードの処理だけ書けばよい
  • GraalのJITコンパイルによりかなりの性能出る(はず)

みなさんも作ってみましょう! と言いたいところだが… ドキュメント少なすぎて…


Polyglot

  • 言語を混ぜこぜにする
var array = Polyglot.eval("ruby", "[1,2,42,4]")
console.log(array[2]);
  • 実用的な例は難しいが、COBOLのTruffle実装作ればあるいは…

Native Image

  • SubstrateVMを使ってNative Imageを出力
  • 近い将来には、WASM出力
  • そして… oracle/graal#373

Graalの性能


速い理由

  • Partial Escape Analysis
  • インライン化の強化

Escape Analysis

最適化前コードイメージ

public long noEscape() {
  long sum = 0;
  for (int i = 0; i < 1_000_000; i++) {
    MyObj foo = new MyObj(i); // foo does not escape the method (NoEscape)
    sum += foo.bar();
  }
  return sum;
}

Escape Analysis

最適化後コードイメージ

public long noEscape() {
  long sum = 0;
  for (int i = 0; i < 1_000_000; i++) {
    int foo_i = i; // foo does not escape the method (NoEscape)
    sum += foo_i * 2; // MyObj.bar() { return i * 2; }みたいなのがインライン化されたとして
  }
  return sum;
}

Partial Escape Analysis

cacheValueがエスケープしてるので ふつうは最適化できひんやん

Object getValue(int idx, Object ref) {
 	Key key = new Key(idx, ref);
  	if (key.equals(cacheKey)) {
   		return cacheValue;
   	} else {
    	cacheKey = key;
      	cacheValue = createValue(...);
       	return cacheValue;
 	}
}

Partial Escape Analysis

個々の分岐でオブジェクトのエスケープ可能性を解析する

Object getValue(int idx, Object ref) {
    Key tmp = cacheKey;
  	if (idx == tmp.idx && ref == tmp.ref)) {
   		return cacheValue; // Cache通るルートはHeapを使わない
   	} else {
        Key key = new Key(idx, ref);
    	cacheKey = key;
      	cacheValue = createValue(...);
       	return cacheValue;
 	}
}

それでもTurboFanには敵わない


そもそもJVMのJITの仕組み


JVMのJITフロー


TurboFan

似てる…


Machine Graphまでの最適化の差大きいと思われる

  • Hidden Class
  • Type Lowering

などなど、JavaScriptに特化したもの

※ 詳しい方、教えてください。

まとめ

  • 現状Node.jsアプリをGraalで動かすメリットは薄い
  • JavaScriptからRubyやJavaを呼びたいケッタイな人はどうぞ…
  • SubstrateVMのnative-imageには夢が拡がる
  • V8とJVM(Graal)の最適化手段は似ており同時に学ぶとよいかも!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment