Your SlideShare is downloading. ×

devsummit2009js

3,121

Published on

Published in: Technology, Education
0 Comments
6 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
3,121
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
121
Comments
0
Likes
6
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. ブラウザ JavaScr ip t 高速化 JIT バトル最終決戦 12-B-5 森田創 Co m m u n it y En g in e, In c o m o @d o d g so n .o r g
  • 2. 本日の趣旨 J a va S c r ip t VM に使われている 高速化技法を 紹介します。 2
  • 3. 自己紹介 森田創 勤め先 : Co mm unity Eng ine ( 株 ) ●  オンラインゲームなどの開発や開発支援  オンラインゲームのミドルウェア販売 仕事 :↑ らのためにコードを書くこと ● 主に C+ + ( たまに r ub y, p ytho n, Ac t io nS c r ip t...) ● ウェブとかよくわかんない ● アマチュア J a va S c r ip t VM 評論家 ● ↑ これでよばれた。 3
  • 4. 参考: アマチュア野球評論家(イメージ)↓ 4
  • 5. 疑問が3つ。 5
  • 6. 疑問 1/3 ほんとに 最終決戦とやらが ホットなのか? 6
  • 7. /speedup|optimiz/ の回数 @Saf ar i JS エンジンの ChangeLog 50 45 40 35 30 Go o g le Chr o me 登場 25 20 S a f a r i J S 刷新 15 S a f a r i リリース “ x”登場 Aja 10 5 0 02 02 03 03 04 05 05 06 06 07 07 08 /0 /1 /0 /0 /0 /0 /1 /0 /0 /0 /0 /1 6 3 0 7 2 9 4 1 1 8 5 2 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 7 1 1 1 1 1 1 1 1 1 1 1 1
  • 8. 疑問 2/3 ほんとに 速くなると うれしいのか? 8
  • 9. 私家版手動ベンチマーク with oprofile gmail libmozjs libxul gdocs 0% 20% 40% 60% 80% 100% システムの10− 20% ● •プロセスの3割 9
  • 10. 疑問 3/3 この話は 皆様にとって 何の役に立つのか? 10
  • 11. 答え3:蘊蓄の足しになる 蘊蓄を愛でるのは日本人として当然の嗜み 11
  • 12. ブラウザ J a v a Sc r ip t 高速化 J IT バトル最終決戦 観戦ガイド 森田創 Co m m u n it y En g in e, In c o m o @d o d g so n .o r g 12
  • 13. これまでのあらすじ 登場人物 VM x3 V8 ● f r o m Go o g le Chro m e SquirrelFish Extreme ● f r o m Ap p le S a f a ri/We b Kit TraceM onkey ● f r o m Mo zilla Fire f o x が、血みどろの戦いを繰り広げていた!!! 13
  • 14. V8 の系譜 S un から引き抜かれた ● La r s Ba k がエース S t r o ng ta lk VM ● J a va Ho t s p o t VM ● コードにも ● S t r ong t a lk の残骸 GC ● アセンブラ ● 14
  • 15. SquirrelFish Extreme の系譜 J a va S c r ip tCo r e 高速化バージョン ● バイトコード化 (“ q uirr e lFis h” ● S ) J IT 追加 (“ ● Extre m e ” ) KHTML/KJ S から派生 ● まだあるよ↑ ● 15
  • 16. TraceMonkey の系譜 S p id e rMo nke y ● + Ta m a r in Tr a c ing And r e a s Ga l の研究成果 ● コードも書いてる ● 16
  • 17. 決戦の舞台 V8 ● b r a nc he s /b le e d ing _e d g e r1198 S q uir re lFis h Extre me ● t r unk r40447 Tr a c e Mo nke y ● hg .m o zilla .o rg /tr a c e m o nke y 24328 スライド作成時の最新版 ※ すべて未リリース 17
  • 18. 本題 :JavaScript VM の高速化 J a va S c r ip t は遅い。なぜなら・・・ 昔は実装をさぼっていた。 ● うざいポップアップを出せればよかった。 ● g m a il も zo ho もなかった。 ● 言語仕様も割と面倒。 ● でも最近はがんばっている。 ● 最終決戦ですから ! ● 今日のテーマ 18
  • 19. JSVM 高速化の戦略 J S 言語固有の面倒をとりのぞき、 ● 既存 VM の高速化技法をとりいれる ● J a va , S ma llta lk, Lis p , … ● ウェブアプリのツボをチューンする ● 19
  • 20. JSVM 高速化の見所3つ J S 言語固有の面倒をとりのぞき、 ● 既存 VM の高速化技法をとりいれる ● J a va , S ma llta lk, Lis p , … ● ウェブアプリのツボをチューンする ● 20
  • 21. JSVM 高速化の見所3つ+1 J S 言語固有の面倒をとりのぞき、 ● 既存 VM の高速化技法をとりいれる ● J a va , S ma llta lk, Lis p , … ● ウェブアプリのツボをチューンする ● 競合 VM への追従 ● オープンソース+宣伝熱心 ● ∴ノーガードぱくり殴り合い 21
  • 22. ここからのあらすじ J S 言語固有の面倒 : ● クラスがないのをどうにかする話 既存 VM 技術の応用 : ● メソッド呼び出しの高速化の話 ウェブアプリのツボ : ● 正規表現の高速化の話 どれも三大 VM すべてが実装している ● 激戦区。 22
  • 23. ここからのあらすじ J S 言語固有の面倒 : ● クラスがないのをどうにかする話 既存 VM 技術の応用 : ● メソッド呼び出しの高速化の話ヘ ∴〠ウェブアプリ固有のホットスポット : ● 正規表現の高速化の話 23
  • 24. 復習: クラスがあるとどう嬉しい? class Point { public: int x; int y; }; Point *p = new Point(); p->x = 10; p->y = 20; クラスの例 :C+ + 24
  • 25. こたえ: クラスがあれば構造がわかる クラス=配列 ● (C+ + , J a va 処理系にとって。 ) 構造=メンバ変数の並び ● メンバ変数アクセスが ● 高速! 25
  • 26. class Point { public: int x; int y; int z; }; .... Point *p = new Point(); p->x = 10; // movl $10, (%eax) p->y = 20; // movl $20, 4(%eax) .... int *q = new int[3]; // movl $10, (%eax) q[0] = 10; // movl $20, 4(%eax) q[1] = 20; 26
  • 27. Point @ JavaScript function Point(x0, y0) { this.x = x0; this.y = y0; } … var p = new Point(10, 20); 27
  • 28. Point @ JavaScript function 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 プロパティができる } } オブジェクトにクラスがない ● =プロパティに制限がない =構造が事前に決まらない 28 配列にはできそうもない・・・ ●
  • 29. JS のオブジェクトはハッシュ表 素朴な ヘ ∴〠プロパティアクセス=ハッシュ表検索 ● ハッシュ表は遅いらしい ● 29
  • 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; } 30
  • 31. ベンチマーク結果 hash array 0 5 10 15 20 25 30 35 40 短い方が高速(横軸は実行時間) 7倍遅い! ● クラスがないオブジェクトはハッシュ表 ● →なんとかしたい。 31
  • 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; } } 32
  • 33. アイデア: 仮のクラスを割り振ってみる var p = new Point(); p.x = 10; p.y = 20; ※ クラスが構造を知っている 33
  • 34. 別の構造には別のクラスを・・・ var q = new Fullname(); q.first = “Jose”; q.middle = “Luis”; q.last = “Carol” 34
  • 35. 同じ構造には同じクラスを・・・ var p1 = new Point(); p1.x = 10; p1.y = 20; var p2 = new Point(); p2.x = 10; p2.y = 20; 35
  • 36. 途中で構造が変わったら? … var p1 = new Point(); p1.x = 10; p1.y = 20; var p2 = new Point(); p2.x = 10; p2.y = 20; 30; // 仮クラスの構造にあわない! p2.z = 36
  • 37. あとから新しいクラスが派生 37
  • 38. JSVMs には 仮のクラスを類推するしくみがある プログラム実行時に 同じ構造のオブジェクトに同じクラスを ● 違う構造のオブジェクトに違うクラスを ● 割り振る。→オブジェクトの構造がわかる 構造が変わるとクラスも変わる。 ● J S プログラマからクラスはみえない。 ● 高速化のトリック。 ● 38
  • 39. 各 VM 上での名前 V8 では ● Hidden Class Tr a c e Mo nke y では ● Shape Inf erence S q uir re lFis h Extre me では ● Structure Chain どれもだいたい同じもの。 ● 歴史の長さは TM > V8 > S FX ● 39
  • 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 }; } 40
  • 41. クラスができた。次は・・・ 41
  • 42. ここからのあらすじ J S 言語固有の面倒 : ● クラスがないのをどうにかする話 ( 済 ) 既存 VM 技術の応用 : ● メソッド呼び出しの高速化の話ヘ ∴〠ウェブアプリ固有のホットスポット : ● 正規表現の高速化の話 42
  • 43. ここからのあらすじ J S 言語固有の面倒 : ● クラスがないのをどうにかする話 ( 済 ) 既存 VM 技術の応用 : ● メソッド呼び出しコードの高速化の話 ヘ ∴〠JIT が生成する ウェブアプリ固有のホットスポット : ● 正規表現の高速化の話 43
  • 44. 復習 (1/3): JIT て何だっけ? 高級言語のプログラムを ● 実行時に機械語へ変換する高速化 実行時= J us t In Tim e ● 具体的には ● J a va S c r ip t VM のバイトコードを 機械語に変換すること 44
  • 45. JIT 前(インタプリタ) ここがバイトコード while (true) { switch(instructions[pc++]) { case OP_XXX: do_xxx(); // ほんとはインライン break; case OP_YYY: do_yyy(); break; .... } } 45
  • 46. JIT 後(生成された機械語) do_xxx(); do_yyy(); do_zzz(); .... 生成される機械語の疑似コード 46
  • 47. JIT の利点と制限 インタプリタのオーバーヘッドがなくなる ● while(true) instructions[pc++] ● break; ● 個々のバイトコード実装は速くならない。 例:メソッド呼び出し 遅い命令を速くする工夫が必要。 ● 実行時にコードを出力できる ● 実行結果を利用した高速化ができる ● 47
  • 48. 復習 (2/3): JS のメソッド呼び出し function Greeting() { this.hello = function() { alert(quot;hello!quot;); }; } var g = new Greeting(); g.hello(); 48
  • 49. 復習 (2/3): JS のメソッド呼び出し function Greeting() { this.hello = function() { alert(quot;hello!quot;); }; } / / プロパティをとりだして / / t h is つきで呼ぶのと同じ。 var g = new Greeting(); g.hello(); var gh = g.hello; gh.apply(g); 49
  • 50. 復習 (2/3): JS のメソッド呼び出し ↓ プロパティ取得を速くしたい var gh = g.hello; gh.apply(g); 50
  • 51. 復習 (3/3) JS は動的型付けの言語 動的型付けの言語では ● プロパティの取得が遅いらしい でも ● 構造がわかれば速いんでしょ? 仮クラスによって ● オブジェクトにはクラスができた 51
  • 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; } 52
  • 53. クラスがある=構造がわかる int lengthSquared(Point p) { // pushl %ebp // movl %esp, %ebp int x = p.x; // movl 12(%ebp), %edx ← 構造がわかるから 1命令でアクセスできる 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 53 }
  • 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; } 構造の違う二つのコンストラクタがあって・・・ 54
  • 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(quot;yakusouquot;, 10, 20); var itlensq = lengthSquared(it); 55
  • 56. 動的型付けの言語は 変数にクラス指定がない オブジェクトごとのクラスはわかっても ● 変数のクラスは ( 調べるまで ) わからない 構造を仮定した高速化ができない ● 動的型付け言語一般の問題 ● J a va S c r ip t だけじゃない。 ● 56
  • 57. JS のプロパティアクセスは 素朴な 結局ハッシュ表検索 (+ 配列アクセス) // 等価な C++ 風コード // JS var x = p.x; 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]; } 57
  • 58. クラスのハッシュ表から ● 構造を検索して 検索結果を使い ● プロパティ配列にアクセス 58
  • 59. 洞察 : だいたい同じクラスを使うよね? function lengthSquared(p) { // 長さの二乗 return p.x*p.x + p.y*p.y; } var pt = new Point(10, 20); var it = new Item(quot;yakusouquot;, 10, 20); // こっちが大半で for (var i=0; i<100; i++) { var point_len = lengthSquared(pt); } // こっちはたまにだよね? var item_len = lengthSquared(it); 59
  • 60. アイデア : よく使うクラスに特化してみる // 等価な C++ 風コード // JS var x = p.x; if (p->klass == PointClass) { // クラスを調べて、 // よく使うクラスなら速く return Point_GetX((int*)p); } else { return p->getProperty(...); } … Object* Point_GetX(int* p) { // 生成する return p[1]; // 構造を知っている! } 60
  • 61. コード ... if (p ->k lass = Po in t Class) p ->Get Pr o p er t y () Po in t _Get X ((in t *)p ) コード ... 61
  • 62. コード ... よく使う型か? 型を仮定しない 型を仮定した速いコード ! 遅いコード ... コード ... 62
  • 63. 予想が間違っていたら? var pt = new Point(10, 20); var it = new Item(quot;yakusouquot;, 10, 20); for (var i=0; i<100; ++i) { var p0lensq = lengthSquared(pt); } // こっちはたまにだよね?こっちがメインだった・・・ for (var i=0; i<100000; ++i) { var p1lensq = lengthSquared(it); ... } 63
  • 64. 元のコードを書き換える ! // 等価な C++ 風コード // JS var x = p.x; 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]; } 64
  • 65. コード ... よく使う型 ' か? よく使う型か? 型を仮定しない 型を仮定した速いコード !! 型 ' を仮定した速コード 遅いコード ... コード ... 65
  • 66. JSVMs は 投機的な型付きコードを生成する ある型専用のコードを生成しておき、 ● オブジェクトの型をチェックしてから、 ● そのコードを呼び出す仕組み ● 適用箇所 プロパティアクセス ● そのあとのメソッド呼び出し ● 四則演算 ● 66
  • 67. 四則演算の例 // 等価な C++ 風コード // JS a + b; 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); } 67
  • 68. 各 VM 上での名前 V8 , S q uir r e lFis h Ext r e m e では ● Po ly m o r p h ic In lin e Cac h in g SELF 言語由来 ● J a va Hotspot VM でも利用 ● Tr a c e Mo nke y では ● Tr ac e Tr ee J a va Hotpath Re s e a r c h VM 由来 ● Tr a c ing 自体は Dynamo VM 由来 ● コアのアイデアはよく似ている ● 実現方法はちょっと違う 68 ●
  • 69. Polymorphic Inline Caching コードの中 (Inline ) に ● 比較用のクラスを保存する (Ca c hing ) 一つ以上のクラス (Po lym o r p hic ) を ● 保存する if (p->klass == ItemClass) { return Item_GetX((int*)p); } else if (p->klass == PointClass) { return Point_GetX((int*)p); } else { return p->getProperty(...); } 69
  • 70. Tracing Tree バイトコードを実行しながら (Tr a c ing ) ● ひとつながりのネイティブコードを生成 コードがインライン化される ● ( メソッド呼び出しもインライン化 ) 生成されたコード片は合流しない (Tr e e ) ● if (p->klass == ItemClass) { return [(int*)p][2]; // inlined! } else { ... } 70
  • 71. // 設定切替 ベンチマーク var mono = false; var n = 10000; var arr = []; for (var i=0; i<n; ++i) { if (mono) { arr.push(new Point(i, i)); mono } else { poly arr.push((i%2) ? new Point(i, i) : new Item(quot;itemquot;, i, i)); } } for (var i=0; i<n; ++i) { for (var j=0; j<n; ++j) { lengthSquared(arr[j]); } 71 }
  • 72. ベンチマーク結果 V8 SFX mono poly TM 0 10 20 30 40 50 60 70 80 短い方が高速(横軸は実行時間) 72
  • 73. 世間並みになった。次は・・・ 73
  • 74. ここからのあらすじ J S 言語固有の面倒 : ● クラスがないのをどうにかする話 ( 済 ) 既存 VM 技術の応用 : ● メソッド呼び出しの高速化の話(済) ヘ ∴〠ウェブアプリのツボ : ● 正規表現の高速化の話 74
  • 75. 復習 (1/2) : 正規表現ってどう実装するの? 二つのアプローチ 状態遷移表を作る路線 ● DFA, NFA, ... ● g r ep , lex , … ● インタプリタを作る路線 ● 構文木、バイトコード、… ● ライブラリではこっちが主流 ● p c r e, 鬼車 , ORO, JDK , … JS もこっちが多かった (PCRE 採用など ) ● 75
  • 76. 復習 (2/2): インタプリタの高速化といえば? これまでさんざん話してきましたが ... 76
  • 77. アイデア : 正規表現も JIT したら? 最終決戦だし。 パース→バイトコード→機械語生成 ● 77
  • 78. JSVMs には 正規表現の JIT コンパイラがある ただし すべてが J IT されるわけではない。 ● がんばり具合は各社各様。 V8 : regexp2000 ブランチがマージされた ● S FX: 最初から搭載 ● TM: サポート弱め ● 各プロジェクト絶賛開発中 ● 78
  • 79. ベンチマーク結果 sunspider/regexp-dna.js V8(JIT) V8(NOJIT) SFX TM 0 0.5 1 1.5 2 2.5 3 3.5 短い方が高速(横軸は実行時間) 79
  • 80. 3つの見所まとめ クラスがない J S の制限を乗り越える ● 仮クラス割り振りの仕組み 既存の VM 技術を適用した ● 投機的な型付きコード生成の仕組み ウェブアプリのホットスポット ● 正規表現の J IT 化 は、3大 VM すべてが実装。でも 実装のがんばりには差がある。 ● 80
  • 81. 今日でてこなかった話 VM っぽい高速化 ● GC (コピーなんとか、世代なんとか・・・) ● 命令セット(粒度、レジスタ vs スタック) ● ネイティブコード呼び出し ● コンパイラっぽい高速化 ● レジスタ割当 ● 共通部分式の除去 ● ・・・ ● 81
  • 82. 今後の見所 ブラウザを含めたアプリの総合性能 ● FF1 1 ベンチ → g m a il ベンチ ● モバイル機器での性能 ● 消費メモリ ● ARM CPU 向けの高速化 ● Int e rne t Exp lo re r の動向 ● 今は遅すぎて話題にすらならず。 ● 82
  • 83. 観戦 HOWTO とりあえずコードを読んでみよう ● コア部分は 1 -2 万行程度 ● 元ねたの論文を探して読もう ● 大抵コードよりわかりやすい ● プロジェクトのブログを読もう ● Mo zilla , We b kit, Chr o m e すべてあり ● 開発者自身のブログもあり ● 開発記録を読もう ● ChangeLog 、 M L 、バグトラッカー ● 新機能もバグトラックで管理するのが定石 ● 83
  • 84. ブラウザ J a v a Sc r ip t 高速化 J IT バトル最終決戦 観戦ガイド ご清聴ありがとうございました。 84
  • 85. 写真たち htt p ://f ic kr.c o m /p ho to s /d c jo hn/2 4 4 0 1 7 8 8 0 1 / l ● htt p ://f ic kr.c o m /p ho to s /s ri-h/2 8 6 6 4 5 8 7 4 7 / l ● htt p ://f ic kr.c o m /p ho to s /7 1 5 0 2 6 4 6 @ N0 0 /2 7 7 6 9 0 1 8 2 2 / l ● htt p ://f ic kr.c o m /p ho to s /a llis o nje nning s /2 5 0 3 4 1 0 2 1 3 / l ● htt p ://f ic kr.c o m /p ho to s /g r e e nmys t/1 4 6 8 7 6 0 7 9 1 / l ● 85

×