Top Banner
森田創 Community Engine, Inc o m o @d o d g s o n .o r g 12-B-5 ブラウザ ブラウザ JavaScript JavaScript 高速化 高速化 JIT JIT バトル最終決戦 バトル最終決戦
85

devsummit2009js

Apr 22, 2015

Download

Technology

Hajime Morita

 
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: devsummit2009js

森田創Com m unit y Eng ine, Incom o@d od gson.or g

12-B-5

ブラウザブラウザ JavaScriptJavaScript 高速化高速化JITJIT バトル最終決戦バトル最終決戦

Page 2: devsummit2009js

2

本日の趣旨

JavaSc r ip t VM に使われている

高速化技法を

紹介します。

Page 3: devsummit2009js

3

自己紹介

森田創● 勤め先 : Community Eng ine ( 株 )

オンラインゲームなどの開発や開発支援

オンラインゲームのミドルウェア販売

● 仕事 :↑ らのためにコードを書くこと● 主に C++ ( たまに ruby, python, Ac t ionSc r ip t...)

● ウェブとかよくわかんない

● アマチュア JavaSc r ip t VM 評論家 ↑ これでよばれた。

Page 4: devsummit2009js

4

参考:アマチュア野球評論家(イメージ)↓

Page 5: devsummit2009js

5

疑問が3つ。

Page 6: devsummit2009js

6

疑問 1/3

ほんとに最終決戦とやらがホットなのか?

Page 7: devsummit2009js

7

02/0

2/0

1

02/0

9/0

1

03/0

4/0

1

03/1

1/0

1

04/0

6/0

1

05/0

1/0

1

05/0

8/0

1

06/0

3/0

1

06/1

0/0

1

07/0

5/0

1

07/1

2/0

1

08/0

7/0

1

0

5

10

15

20

25

30

35

40

45

50

Safa r i リリース “Ajax” 登場Safa r i JS 刷新

Goog le Chrome 登場

/speedup|optimiz/の回数@Saf ar i JSエンジンのChangeLog

Page 8: devsummit2009js

8

疑問 2/3

ほんとに速くなるとうれしいのか?

Page 9: devsummit2009js

9

gmail

gdocs

0% 20% 40% 60% 80% 100%

●システムの10−20%•プロセスの3割

私家版手動ベンチマークwith oprofile

libxul libmozjs

Page 10: devsummit2009js

10

疑問 3/3

この話は皆様にとって何の役に立つのか?

Page 11: devsummit2009js

11

答え3:蘊蓄の足しになる

蘊蓄を愛でるのは日本人として当然の嗜み

Page 12: devsummit2009js

12

ブラウザ J a v a Sc r ip t 高速化J IT バトル最終決戦観戦ガイド

森田創Com m unit y Eng ine, Inc

om o@d od gson.or g

Page 13: devsummit2009js

13

これまでのあらすじ

登場人物 VM x3● V8

f rom Goog le Chrome ● SquirrelFish Extreme

f rom App le Sa fa r i/WebKit● TraceMonkey

f rom Mozilla Fire fox

が、血みどろの戦いを繰り広げていた!!!

Page 14: devsummit2009js

14

V8の系譜● Sun から引き抜かれた

Lars Bak がエース● Strong ta lk VM● Java Hots pot VM

● コードにもStrong ta lk の残骸● GC● アセンブラ

Page 15: devsummit2009js

15

SquirrelFish Extremeの系譜

● JavaSc r ip tCore 高速化バージョン● バイトコード化 (“Squirre lFis h”)

● JIT 追加 (“Extreme”)

● KHTML/KJS から派生● まだあるよ↑

Page 16: devsummit2009js

16

TraceMonkeyの系譜● Sp ide rMonkey

+ Tamarin Trac ing

● Andreas Ga l の研究成果 ● コードも書いてる

Page 17: devsummit2009js

17

決戦の舞台

● V8b ranches /b leed ing _edg e r1198

● Squirre lFis h Extremetrunk r40447

● TraceMonkeyhg .mozilla .org /tracemonkey 24328

スライド作成時の最新版

※ すべて未リリース

Page 18: devsummit2009js

18

本題 :JavaScript VMの高速化JavaSc r ip t は遅い。なぜなら・・・● 昔は実装をさぼっていた。

● うざいポップアップを出せればよかった。● g mailも zoho もなかった。

● 言語仕様も割と面倒。● でも最近はがんばっている。

● 最終決戦ですから !今日のテーマ

Page 19: devsummit2009js

19

● JS 言語固有の面倒をとりのぞき、● 既存 VM の高速化技法をとりいれる

● Java , Smallta lk, Lis p , …

● ウェブアプリのツボをチューンする

JSVM高速化の戦略

Page 20: devsummit2009js

20

● JS 言語固有の面倒をとりのぞき、● 既存VM の高速化技法をとりいれる

● Java , Smallta lk, Lis p , …

● ウェブアプリのツボをチューンする

JSVM高速化の見所3つ

Page 21: devsummit2009js

21

● JS 言語固有の面倒をとりのぞき、● 既存 VM の高速化技法をとりいれる

● Java , Smallta lk, Lis p , …

● ウェブアプリのツボをチューンする● 競合VM への追従

● オープンソース+宣伝熱心∴ノーガードぱくり殴り合い

JSVM高速化の見所3つ+1

Page 22: devsummit2009js

22

● JS 言語固有の面倒 :クラスがないのをどうにかする話

● 既存 VM 技術の応用 :メソッド呼び出しの高速化の話

● ウェブアプリのツボ :正規表現の高速化の話

● どれも三大VM すべてが実装している激戦区。

ここからのあらすじ

Page 23: devsummit2009js

23

ここからのあらすじ

● JS 言語固有の面倒 :クラスがないのをどうにかする話

● 既存 VM 技術の応用 :メソッド呼び出しの高速化の話

● ウェブアプリ固有のホットスポット :正規表現の高速化の話

∴〠ヘ

Page 24: devsummit2009js

24

復習:クラスがあるとどう嬉しい?

class Point {public: int x; int y;};

Point *p = new Point();p->x = 10;p->y = 20;

クラスの例 :C++

Page 25: devsummit2009js

25

● クラス=配列(C+ + , Java 処理系にとって。 )

● 構造=メンバ変数の並び

● メンバ変数アクセスが高速!

こたえ:クラスがあれば構造がわかる

Page 26: devsummit2009js

26

class Point {public: int x; int y; int z;};....Point *p = new Point();p->x = 10;p->y = 20;

// movl $10, (%eax) // movl $20, 4(%eax)

....int *q = new int[3];q[0] = 10;q[1] = 20;

// movl $10, (%eax) // movl $20, 4(%eax)

Page 27: devsummit2009js

27

Point @ JavaScript

function Point(x0, y0) { this.x = x0; this.y = y0;}…var p = new Point(10, 20);

Page 28: devsummit2009js

28

Point @ JavaScriptfunction Point(x0, has_y, y_or_z) { this.x = x0; if (has_y) { // 実行時にプロパティが変わる! this.y = y_or_z; // y プロパティができる } else { this.z = y_or_z; // z プロパティができる }}

● オブジェクトにクラスがない=プロパティに制限がない=構造が事前に決まらない

● 配列にはできそうもない・・・

Page 29: devsummit2009js

29

素朴なJSのオブジェクトはハッシュ表

● プロパティアクセス=ハッシュ表検索● ハッシュ表は遅いらしい

∴〠ヘ

Page 30: devsummit2009js

30

ベンチマーク : 配列 vs.ハッシュ表 in C++int benchmark(bool use_hash) { const int NELEM = 100; if (use_hash) { std::tr1::unordered_map<int, int> obj; for (int i=0; i<NELEM; ++i) { obj[i] = i; } for (int i=0; i<NELEM*NELEM*NELEM; ++i) { int x = obj[i%NELEM]; touch(x); } } else { int obj[NELEM]; for (int i=0; i<NELEM; ++i) { obj[i] = i; } for (int i=0; i<NELEM*NELEM*NELEM; ++i) { int x = obj[i%NELEM]; touch(x); } }}

int touch(int x) { volatile int y = x; }

Page 31: devsummit2009js

31

ベンチマーク結果

● 7倍遅い!● クラスがないオブジェクトはハッシュ表

→なんとかしたい。

hash

array

0 5 10 15 20 25 30 35 40

短い方が高速(横軸は実行時間)

Page 32: devsummit2009js

32

洞察:大半はいつも同じだよね?

function Point(x0, has_y, y_or_z) { this.x = x0; if (has_y) { // 実行時にプロパティが変わる! this.y = y_or_z; } else { this.z = y_or_z; }}

↑ こんなことしないよね?

Page 33: devsummit2009js

33

アイデア:仮のクラスを割り振ってみる

var p = new Point();p.x = 10;p.y = 20;

※ クラスが構造を知っている

Page 34: devsummit2009js

34

別の構造には別のクラスを・・・

var q = new Fullname();q.first = “Jose”;q.middle = “Luis”;q.last = “Carol”

Page 35: devsummit2009js

35

同じ構造には同じクラスを・・・var p1 = new Point();p1.x = 10;p1.y = 20;

var p2 = new Point();p2.x = 10;p2.y = 20;

Page 36: devsummit2009js

36

途中で構造が変わったら?

… var p1 = new Point(); p1.x = 10; p1.y = 20; var p2 = new Point(); p2.x = 10; p2.y = 20; p2.z = 30; // 仮クラスの構造にあわない!

Page 37: devsummit2009js

37

あとから新しいクラスが派生

Page 38: devsummit2009js

38

JSVMsには仮のクラスを類推するしくみがある

プログラム実行時に● 同じ構造のオブジェクトに同じクラスを● 違う構造のオブジェクトに違うクラスを

割り振る。→オブジェクトの構造がわかる

● 構造が変わるとクラスも変わる。● JS プログラマからクラスはみえない。

● 高速化のトリック。

Page 39: devsummit2009js

39

各VM上での名前● V8 では

Hidden Class● TraceMonkey では

Shape Inference● Squirre lFis h Extreme では

Structure Chain

● どれもだいたい同じもの。● 歴史の長さは TM > V8 > SFX

Page 40: devsummit2009js

40

VM実装毎のこまかな違い

● コンストラクタは違うけどプロパティが同じときは?

function A(x0, y0) { this.x = x0; this.y = y0; }function B(x0, y0) { this.x = x0; this.y = y0; }

● オブジェクトリテラルの扱いは?

function make_a(x0, y0) { return { x:x0, y:y0 }; }

Page 41: devsummit2009js

41

クラスができた。次は・・・

Page 42: devsummit2009js

42

ここからのあらすじ

● JS 言語固有の面倒 :クラスがないのをどうにかする話 (済 )

● 既存 VM 技術の応用 :メソッド呼び出しの高速化の話

● ウェブアプリ固有のホットスポット :正規表現の高速化の話

∴〠ヘ

Page 43: devsummit2009js

43

ここからのあらすじ

● JS 言語固有の面倒 :クラスがないのをどうにかする話 (済 )

● 既存 VM 技術の応用 :メソッド呼び出しコードの高速化の話

● ウェブアプリ固有のホットスポット :正規表現の高速化の話

∴〠ヘJIT が生成する

Page 44: devsummit2009js

44

復習 (1/3): JITて何だっけ?● 高級言語のプログラムを実行時に機械語へ変換する高速化● 実行時= J us t In T ime

● 具体的にはJavaSc r ip t VM のバイトコードを機械語に変換すること

Page 45: devsummit2009js

45

JIT前(インタプリタ)

while (true) { switch(instructions[pc++]) { case OP_XXX: do_xxx(); // ほんとはインライン break; case OP_YYY: do_yyy(); break; .... }}

ここがバイトコード

Page 46: devsummit2009js

46

JIT後(生成された機械語)

do_xxx();do_yyy();do_zzz();....

生成される機械語の疑似コード

Page 47: devsummit2009js

47

JITの利点と制限インタプリタのオーバーヘッドがなくなる

● while(true)

● instructions[pc++]

● break;

個々のバイトコード実装は速くならない。● 遅い命令を速くする工夫が必要。● 実行時にコードを出力できる

● 実行結果を利用した高速化ができる

例:メソッド呼び出し

Page 48: devsummit2009js

48

復習 (2/3):JSのメソッド呼び出しfunction Greeting() { this.hello = function() { alert("hello!"); };}

var g = new Greeting();g.hello();

Page 49: devsummit2009js

49

復習 (2/3):JSのメソッド呼び出しfunction Greeting() { this.hello = function() { alert("hello!"); };}

var g = new Greeting();g.hello();

/ / プロパティをとりだして/ / t h is つきで呼ぶのと同じ。var gh = g.hello;gh.apply(g);

Page 50: devsummit2009js

50

復習 (2/3):JSのメソッド呼び出し

var gh = g.hello;gh.apply(g);

↓ プロパティ取得を速くしたい

Page 51: devsummit2009js

51

復習 (3/3)JSは動的型付けの言語● 動的型付けの言語では

プロパティの取得が遅いらしい● でも構造がわかれば速いんでしょ?

● 仮クラスによってオブジェクトにはクラスができた

Page 52: devsummit2009js

52

復習 (3/3)の前に・・・C++は静的型付けの言語

int lengthSquared(Point p) { int x = p.x; int y = p.y; int xx = x*x; int yy = y*y; int ret = xx + xx; return ret; }

● 変数にクラスがある。

Page 53: devsummit2009js

53

int lengthSquared(Point p) { // pushl %ebp // movl %esp, %ebp int x = p.x; // movl 12(%ebp), %edx int y = p.y; // movl 8(%ebp), %eax // leave int xx = x*x; // imull %edx, %edx int yy = y*y; // imull %eax, %eax int ret = xx + xx; // addl %edx, %eax return ret; // ret}

←構造がわかるから1命令でアクセスできる

クラスがある=構造がわかる

Page 54: devsummit2009js

54

復習 (3/3)JSは動的型付けの言語

// x と y がプロパティfunction Point(x0, y0) { this.x = x0; this.y = y0;}

// name と x と y がプロパティfunction Item(name, x0, y0) { this.name = name; this.x = x0; this.y = y0;}

構造の違う二つのコンストラクタがあって・・・

Page 55: devsummit2009js

55

復習 (3/3)JSは動的型付けの言語

// p のクラスはなに?function lengthSquared(p) { return p.x*p.x + p.y*p.y;}

var pt = new Point(10, 20);var ptlensq = lengthSquared(pt);

var it = new Item("yakusou", 10, 20);var itlensq = lengthSquared(it);

Page 56: devsummit2009js

56

動的型付けの言語は変数にクラス指定がない● オブジェクトごとのクラスはわかっても

変数のクラスは (調べるまで ) わからない● 構造を仮定した高速化ができない

● 動的型付け言語一般の問題● JavaSc r ip t だけじゃない。

Page 57: devsummit2009js

57

素朴なJSのプロパティアクセスは結局ハッシュ表検索 (+配列アクセス)

// JSvar x = p.x;

// 等価な C++ 風コードObject* x = p->getProperty(X_NAME_ID);...Object* Object::getProperty(int id){ Hash* h = this->klass->m_propHash; int index = h->lookup(id); Property* p = this->prop_array; return p[index];}

Page 58: devsummit2009js

58

● クラスのハッシュ表から構造を検索して

● 検索結果を使いプロパティ配列にアクセス

Page 59: devsummit2009js

59

洞察 :だいたい同じクラスを使うよね?

function lengthSquared(p) { // 長さの二乗 return p.x*p.x + p.y*p.y; } var pt = new Point(10, 20); var it = new Item("yakusou", 10, 20); // こっちが大半で for (var i=0; i<100; i++) { var point_len = lengthSquared(pt); } // こっちはたまにだよね? var item_len = lengthSquared(it);

Page 60: devsummit2009js

60

アイデア :よく使うクラスに特化してみる// JSvar x = p.x;

// 等価な C++ 風コードif (p->klass == PointClass) { // クラスを調べて、 // よく使うクラスなら速く return Point_GetX((int*)p);} else { return p->getProperty(...);}…Object*Point_GetX(int* p) { // 生成する return p[1]; // 構造を知っている!}

Page 61: devsummit2009js

61

コード...

if (p ->k lass = Po in t Class)

Poin t _Get X ((in t *)p )

p ->Get Pr op er t y ()

コード...

Page 62: devsummit2009js

62

コード...

よく使う型か?

型を仮定した速いコード !型を仮定しない遅いコード

...

コード...

Page 63: devsummit2009js

63

予想が間違っていたら?

var pt = new Point(10, 20); var it = new Item("yakusou", 10, 20); for (var i=0; i<100; ++i) { var p0lensq = lengthSquared(pt); }

// こっちはたまにだよね?こっちがメインだった・・・ for (var i=0; i<100000; ++i) { var p1lensq = lengthSquared(it); ... }

Page 64: devsummit2009js

64

元のコードを書き換える !

// JSvar x = p.x;

// 等価な C++ 風コードif (p->klass == PointClass ItemClass) { return Point_GetX((int*)p); return Item_GetX((int*)p);} else { return p->getProperty(...);}…Object*Item_GetX(int* p) {// 新たに生成 return p[2];}

Page 65: devsummit2009js

65

コード...

よく使う型か?

型を仮定した速いコード !型を仮定しない遅いコード

...

コード...

よく使う型 'か?

型 'を仮定した速コード !

Page 66: devsummit2009js

66

JSVMsは投機的な型付きコードを生成する● ある型専用のコードを生成しておき、● オブジェクトの型をチェックしてから、● そのコードを呼び出す仕組み

適用箇所● プロパティアクセス● そのあとのメソッド呼び出し● 四則演算

Page 67: devsummit2009js

67

四則演算の例

// JSa + b;

// 等価な C++ 風コードif (ClassOf(a) == IntClass && ClassOf(b) == IntClass) { return ((int)a) + ((int)b);} else if (ClassOf(a) == StringClass && ClassOf(b) == StringClass) return ((String*)a)->concat((String*)b);} else { return SlowAdd(a, b);}

Page 68: devsummit2009js

68

各VM上での名前● V8 , Squirre lFis h Extreme では

Polym or p h ic In line Cach ing● SELF 言語由来● Java Hotspot VM でも利用

● TraceMonkey ではTr ace Tr ee● Java Hotpath Re s ea rch VM 由来● Trac ing 自体は Dynamo VM 由来

● コアのアイデアはよく似ている● 実現方法はちょっと違う

Page 69: devsummit2009js

69

Polymorphic Inline Caching● コードの中 (Inline ) に比較用のクラスを保存する (Caching )

● 一つ以上のクラス (Polymorphic ) を保存する

if (p->klass == ItemClass) { return Item_GetX((int*)p);} else if (p->klass == PointClass) { return Point_GetX((int*)p);} else { return p->getProperty(...);}

Page 70: devsummit2009js

70

Tracing Tree● バイトコードを実行しながら (Trac ing )ひとつながりのネイティブコードを生成● コードがインライン化される

( メソッド呼び出しもインライン化 )● 生成されたコード片は合流しない (Tree )

if (p->klass == ItemClass) { return [(int*)p][2]; // inlined! } else { ... }

Page 71: devsummit2009js

71

ベンチマーク// 設定切替var mono = false; var n = 10000;var arr = [];

for (var i=0; i<n; ++i) { if (mono) { arr.push(new Point(i, i)); } else { arr.push((i%2) ? new Point(i, i) : new Item("item", i, i)); }}

for (var i=0; i<n; ++i) { for (var j=0; j<n; ++j) { lengthSquared(arr[j]); }}

mono

poly

Page 72: devsummit2009js

72

ベンチマーク結果

V8

SFX

TM

0 10 20 30 40 50 60 70 80

monopoly

短い方が高速(横軸は実行時間)

Page 73: devsummit2009js

73

世間並みになった。次は・・・

Page 74: devsummit2009js

74

ここからのあらすじ

● JS 言語固有の面倒 :クラスがないのをどうにかする話 (済 )

● 既存 VM 技術の応用 :メソッド呼び出しの高速化の話(済)

● ウェブアプリのツボ :正規表現の高速化の話

∴〠ヘ

Page 75: devsummit2009js

75

復習 (1/2):正規表現ってどう実装するの?二つのアプローチ● 状態遷移表を作る路線

● DFA, NFA, ...● g r ep , lex , …

● インタプリタを作る路線● 構文木、バイトコード、…● ライブラリではこっちが主流

p cr e , 鬼車 , ORO, JDK, …● JSもこっちが多かった (PCRE 採用など )

Page 76: devsummit2009js

76

復習 (2/2):インタプリタの高速化といえば?これまでさんざん話してきましたが ...

Page 77: devsummit2009js

77

アイデア :正規表現も JITしたら?最終決戦だし。

● パース→バイトコード→機械語生成

Page 78: devsummit2009js

78

JSVMsには正規表現の JITコンパイラがあるただし● すべてが JIT されるわけではない。

がんばり具合は各社各様。● V8 : regexp2000ブランチがマージされた● SFX: 最初から搭載● TM: サポート弱め

● 各プロジェクト絶賛開発中

Page 79: devsummit2009js

79

ベンチマーク結果sunspider/regexp-dna.js

V8(JIT)

V8(NOJIT)

SFX

TM

0 0.5 1 1.5 2 2.5 3 3.5

短い方が高速(横軸は実行時間)

Page 80: devsummit2009js

80

3つの見所まとめ

● クラスがない JS の制限を乗り越える仮クラス割り振りの仕組み

● 既存の VM 技術を適用した投機的な型付きコード生成の仕組み

● ウェブアプリのホットスポット正規表現の J IT化

は、3大VM すべてが実装。でも● 実装のがんばりには差がある。

Page 81: devsummit2009js

81

今日でてこなかった話

● VM っぽい高速化● GC (コピーなんとか、世代なんとか・・・)● 命令セット(粒度、レジスタ vs スタック)● ネイティブコード呼び出し

● コンパイラっぽい高速化● レジスタ割当● 共通部分式の除去● ・・・

Page 82: devsummit2009js

82

今後の見所

● ブラウザを含めたアプリの総合性能● FF11 ベンチ → g mailベンチ

● モバイル機器での性能● 消費メモリ● ARM CPU向けの高速化

● Inte rne t Exp lore r の動向● 今は遅すぎて話題にすらならず。

Page 83: devsummit2009js

83

観戦HOWTO● とりあえずコードを読んでみよう

● コア部分は 1 -2 万行程度● 元ねたの論文を探して読もう

● 大抵コードよりわかりやすい● プロジェクトのブログを読もう

● Mozilla , Webkit , Chrome すべてあり● 開発者自身のブログもあり

● 開発記録を読もう

● ChangeLog 、 ML 、バグトラッカー● 新機能もバグトラックで管理するのが定石

Page 84: devsummit2009js

84

ブラウザ J a v a Sc r ip t 高速化J IT バトル最終決戦観戦ガイド

ご清聴ありがとうございました。

Page 85: devsummit2009js

85

写真たち● http ://fl ic kr.com/photos /dc john/2 4 4 0 1 78 80 1 /

● http ://fl ic kr.com/photos /s r i-h/2 8 6 6 4 5 87 4 7 /

● http ://fl ic kr.com/photos /7 1 5 0 2 6 4 6@N0 0 /2 7 7 6 9 0 1 8 2 2 /

● http ://fl ic kr.com/photos /a llis onjenning s /2 5 0 3 4 1 0 2 1 3 /

● http ://fl ic kr.com/photos /g reenmys t/1 4 6 8 7 6 0 7 9 1 /