- GitHub: https://github.com/kawasima
- Qiita: https://qiita.com/kawasima
- SlideShare: http://www.slideshare.net
- Twitter: @kawasima
http://tech-radar.netlify.com/?src=https://gist.githubusercontent.com/kawasima/b18ab2657de8e421bebcb072e507e939/raw/eb1248e623be4bc0f682c0ca7f1834abaeeb9619/my-tech-radar.json
- Javaで書かれたJVMの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)
- Graalを使ってJITコンパイルし、TruffleでPolyglotすると世界が変わるというマーケティング用語
- ASTの各ノードの処理を実装する
- 実装例
- 二村射影によって部分的に最適化されたバイトコード出力 (勉強中)
- 構文解析と各ノードの処理だけ書けばよい
- GraalのJITコンパイルによりかなりの性能出る(はず)
みなさんも作ってみましょう! と言いたいところだが… ドキュメント少なすぎて…
- 言語を混ぜこぜにする
var array = Polyglot.eval("ruby", "[1,2,42,4]")
console.log(array[2]);
- 実用的な例は難しいが、COBOLのTruffle実装作ればあるいは…
- SubstrateVMを使ってNative Imageを出力
- 近い将来には、WASM出力
- そして… oracle/graal#373
- Partial 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;
}
最適化後コードイメージ
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;
}
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;
}
}
個々の分岐でオブジェクトのエスケープ可能性を解析する
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;
}
}
似てる…
- Hidden Class
- Type Lowering
などなど、JavaScriptに特化したもの
- 現状Node.jsアプリをGraalで動かすメリットは薄い
- JavaScriptからRubyやJavaを呼びたいケッタイな人はどうぞ…
- SubstrateVMのnative-imageには夢が拡がる
- V8とJVM(Graal)の最適化手段は似ており同時に学ぶとよいかも!