JSX 速さの秘密 - 高速なJavaScriptを書く方法
- 3. Q. 高速な JavaScript を書く方法を教えてください
JSX 速さの秘密 - 高速なJavaScriptを書く方法
3
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 4. A. JSX を使いましょう
JSX 速さの秘密 - 高速なJavaScriptを書く方法
4
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 5. …
JSX 速さの秘密 - 高速なJavaScriptを書く方法
5
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 10. 高速なJavaScriptを書くための3原則
オブジェクトを避ける
⁃ 例: arguments を使わない
Hidden Classを意識したコードを書く
⁃ Inline Cache / 配列の要素の型
組み込み関数が速いとは限らない
⁃ 遅くなることがある
•
例: Function#apply, Array#forEach
JSX 速さの秘密 - 高速なJavaScriptを書く方法
10
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 11. オブジェクト生成を最小限に
オブジェクト生成を避ける理由
⁃ メモリ確保に時間がかかる
⁃ GCの原因になる
オブジェクトが生成されるパターン
⁃ new, {...}, [...]
⁃ arguments の使用
•
使った瞬間にオブジェクトが生成されます
オブジェクトが生成されそうで、されないパターン
⁃ ”abc”.foo()
•
foo 内での this は string? (ES5以降)
JSX 速さの秘密 - 高速なJavaScriptを書く方法
11
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 12. Unboxing in JSX
オブジェクトへのアクセスを、複数のローカル変数への
アクセスに変換する最適化
最適化前:
var pt = new Point(x, y);
…
最適化後:
var pt$x = x;
var pt$y = y;
…
注意点:
⁃ オブジェクトを return したり他クラスのオブジェ
クトのプロパティにセットしている場合は使えない
JSX 速さの秘密 - 高速なJavaScriptを書く方法
12
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 14. newの呼出を避ける
new Type(...) より { ... } の方が高速
⁃ Safari で有効な最適化。V8 だと遅くなる
最適化前:
var pt = new Point(x, y);
最適化後:
var pt = { x: x, y: y };
注意点:
⁃ メソッド呼出や instanceof が不可能になる
•
メソッド呼出については、全頁のメソッドから関数への
変換を適用
JSX 速さの秘密 - 高速なJavaScriptを書く方法
14
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 15. argumentsを使わない
遅いJavaScriptの例:
Point.prototype.set = function (a1, a2) {
if (arguments.length == 1) {
this.x = a1.x;
this.y = a1.y;
} else {
this.x = a1;
this.y = a2;
}
};
高速に書くには、Point型を引数にとる setByPoint メ
ソッドと、座標の組を引数にとる setByXY メソッドを
別個に用意すべき
JSX 速さの秘密 - 高速なJavaScriptを書く方法
15
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 16. argumentsを使わない – JSXの場合
そもそも arguments がない
関数のオーバーロードが可能
⁃ コンパイル時に別名になる
// コンパイル後の名前: set$Lpoint
function set(pt : Point) : void {
this.x = pt.x;
this.y = pt.y;
}
// コンパイル後の名前: set$NN
function set(x : number, y : number) : void {
this.x = x;
this.y = y;
}
JSX 速さの秘密 - 高速なJavaScriptを書く方法
16
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 17. コンストラクタのオーバーロード
通常の関数は名前を変えることでオーバーロード可能
Q. コンストラクタの場合、どうするか?
A. こんなコードを書く (JSXの内部実装より抜粋)
function $__jsx_extend(derivations, base) {
var ctor = function () {};
ctor.prototype = base.prototype;
var proto = new ctor();
for (var i in derivations)
derivations[i].prototype = proto;
}
function Point1(pt) { this.x = pt.x; this.y = pt.y; }
function Point2(x, y) { this.x = x; this.y = y; }
// new Point1 しても new Point2 しても同じ型のオブジェクトが生成するおまじない
$__jsx_extend([ Point1, Point2 ], Object);
JSX 速さの秘密 - 高速なJavaScriptを書く方法
17
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 19. Hidden Classを意識したコードを書く (2)
プロパティのセット順を一意に
⁃ JSXではプロパティ初期化時は条件分岐不可能
•
→ セット順が一意になる
•
Java と同様
配列の要素型を一定に
⁃ JSXでは要素型毎に配列型を用意
•
number[], string[], Object[], Point[], …
配列の要素をdeleteしない
⁃ JSXでは禁止
•
nullを代入することで対処(こちらのほうが高速)
JSX 速さの秘密 - 高速なJavaScriptを書く方法
19
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 20. Hidden Classを意識したコードを書く (3)
未初期化の要素にアクセスしない
⁃ JSXでは未初期化の要素にアクセスするとエラー
•
注: デバッグビルドのみ
⁃ リリースビルドではエラーチェック省略
Polymorphicなコードを書かない
⁃ JSXではpolymorphicなコードは書けない
•
クラスベースの型指定が「必須」な処理系だから
•
テンプレートを使った場合は、引数の型ごとに個別にコ
ード生成
JSX 速さの秘密 - 高速なJavaScriptを書く方法
20
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 21. 組み込み関数が速いとは限らない
JavaScript VMの仕事
⁃ JavaScriptを機械語にJust-In-Time compile
•
使われるテクニック:
⁃ Inline Caching
⁃ インライン展開
JavaScriptからC++コードを呼ぶのは遅い(逆も同様)
⁃ C++側で様々なチェックが必要
•
引数の数や型の確認等
•
言語をまたいだInline Cachingやインライン展開は無理
⁃ C++コードは静的にコンパイルされているため
JSX 速さの秘密 - 高速なJavaScriptを書く方法
21
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 22. 組み込み関数が速いとは限らない (2)
汎用的なAPIとして定義されているため遅いケースも
⁃ 数十倍遅いケースも
避けるべき組み込み関数の代表例:
⁃ Function.prototype.call
⁃ Function.prototype.apply
⁃ Function.prototype.bind
⁃ Array.prototype.forEach
ベンチマーク:
⁃ http://jsperf.com/f-p-bind-vs-closure
⁃ http://d.hatena.ne.jp/kazuhooku/20120612/133948
9758
JSX 速さの秘密 - 高速なJavaScriptを書く方法
22
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 23. 組み込み関数が速いとは限らない (3)
JSXの場合:
⁃ Function.prototype 系は存在しない
⁃ クロージャがthisを引き継げるような言語仕様
•
→ bindの必要性が低い
•
JSXのソースコード:
var f = function () : void {
this.n++;
};
•
コンパイル結果 (JavaScript):
var $this = this;
var f = function (){
$this.n++;
};
JSX 速さの秘密 - 高速なJavaScriptを書く方法
23
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 24. 組み込み関数が速いとは限らない (4)
JSXの場合:
⁃ Array#forEachは独自に実装
⁃ 言語仕様が硬い分、JavaScriptよりも処理が単純に
⁃ 参照:
http://d.hatena.ne.jp/kazuhooku/20120612/133948
9758
JSX 速さの秘密 - 高速なJavaScriptを書く方法
24
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 26. インライン展開
JavaScript VMは、インライン展開を行う
⁃ だが、最速ではない
•
理由: 毎回、呼び出される関数が差し替えられていない
か確認する必要があるため
⁃ JSXの場合:
•
コンパイル後に関数の差し替え不可能
•
最適化コンパイル時に、あらかじめインライン展開され
たJavaScriptを生成
⁃ → JavaScript VMによるインライン展開より高速
⁃ → 他の最適化も適用可能に
JSX 速さの秘密 - 高速なJavaScriptを書く方法
26
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 27. 事前計算
定数畳み込み
⁃ JSXのソースコード:
const name = ”John”;
…
console.log(”Hello, ” + N);
⁃ コンパイル結果 (JavaScript):
console.log(”Hello, John”);
JSX 速さの秘密 - 高速なJavaScriptを書く方法
27
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 28. 事前計算 (その2)
Dead-code Elimination
⁃ JSXのソースコード:
const DEBUG = 0;
…
if (DEBUG) console.log(”in debug mode”);
⁃ コンパイル結果 (JavaScript):
// からっぽ
JSX 速さの秘密 - 高速なJavaScriptを書く方法
28
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 29. LTO (リンク時最適化)
以下のコードで、mの型は何?
function transform(m: Matrix, pt : Point) : Point {
return m.rotate(pt);
}
mの型はMatrix型かもしれないし、Matrixの派生型かも
⁃ これでは、rotateの実装を特定できない
•
→ rotateをインライン展開できない
そこでLTO!
⁃ LTO: プログラムが使用する全てのコードに関する情
報を使って(つまり、リンク時に)最適化
⁃ Matrixを継承した型がなければ、mの型はMatrix型
JSX 速さの秘密 - 高速なJavaScriptを書く方法
29
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 30. アフィン変換 (クラス定義)
class Matrix {
var m11 : number; var m21 : number; var m31 : number;
var m12 : number; var m22 : number; var m32 : number;
...
function transform(pt : Point): Point {
return new Point(
this.m11 * pt.x + this.m21 * pt.y + this.m31,
this.m12 * pt.x + this.m22 * pt.y + this.m32);
}
}
class Point {
var x : number; var y : number;
function constructor(x : number, y : number) {
this.x = x;
this.y = y;
}
...
JSX 速さの秘密 - 高速なJavaScriptを書く方法
30
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 31. アフィン変換 (コンパイル例)
JSXのソースコード:
var pt = new Matrix(1, 0, 0, 0, 2, 0).transform(new Point(x, y));
x = pt.x;
y = pt.y;
最適化コンパイル後 (JavaScript):
var pt$x = x + 0 * y;
y = 0 * x + 2 * y;
x = pt$x;
適用された最適化手法:
⁃ LTO (transformの実装を確定)
⁃ インライン展開
⁃ Unboxing
⁃ 定数畳み込みとDCE
JSX 速さの秘密 - 高速なJavaScriptを書く方法
31
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
- 32. まとめ
JSX 速さの秘密 - 高速なJavaScriptを書く方法
32
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.