Upload
kazuho-oku
View
17.073
Download
6
Embed Size (px)
DESCRIPTION
JavaScript で高速なコードを書こうとする際に、はまりがちな罠と、JSX のコンパイラでどのように対処しているのかを紹介
Citation preview
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
1
JSX 速さの秘密〜高速な JavaScript を書く方法〜
DeNA Co., Ltd.
Kazuho Oku
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
2
JSX をご存知ですか ?
Java っぽいプログラミング言語です JavaScript にコンパイルされます JavaScript より JSX で書いたほうが高速に動作します
JSX 速さの秘密 - 高速な JavaScript を書く方法
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
3
Q. 高速な JavaScript を書く方法を教えてください
JSX 速さの秘密 - 高速な JavaScript を書く方法
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
4
A. JSX を使いましょう
JSX 速さの秘密 - 高速な JavaScript を書く方法
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
5
…
JSX 速さの秘密 - 高速な JavaScript を書く方法
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
6
これだけでは芸がないので、 JSX が行っている様々なJavaScript 最適化技法を紹介します。
JSX 速さの秘密 - 高速な JavaScript を書く方法
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
7
※ これから説明することは、 JSX を使っていれば気にする必要のないことです
(自動で最適化されますから)
JSX 速さの秘密 - 高速な JavaScript を書く方法
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
8
目次
高速な JavaScript を書くための3原則 更なる高速化のために 〜 JSX の最適化コンパイラの仕
事
JSX 速さの秘密 - 高速な JavaScript を書く方法
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
9
高速な JavaScript を書くための 3 原則
JSX 速さの秘密 - 高速な JavaScript を書く方法
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
10
高速な JavaScript を書くための 3 原則
オブジェクトを避ける⁃ 例 : arguments を使わない
Hidden Class を意識したコードを書く⁃ Inline Cache / 配列の要素の型
組み込み関数が速いとは限らない⁃ 遅くなることがある• 例 : Function#apply, Array#forEach
JSX 速さの秘密 - 高速な JavaScript を書く方法
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
11
オブジェクト生成を最小限に
オブジェクト生成を避ける理由⁃ メモリ確保に時間がかかる⁃ GC の原因になる
オブジェクトが生成されるパターン⁃ new, {...}, [...]
⁃ arguments の使用• 使った瞬間にオブジェクトが生成されます
オブジェクトが生成されそうで、されないパターン⁃ ”abc”.foo()• foo 内での this は string? (ES5 以降 )
JSX 速さの秘密 - 高速な JavaScript を書く方法
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 を書く方法
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
13
プロパティアクセスを最小限に
プロパティアクセス = オブジェクトの要素へのアクセス⁃ 変数の要素へのアクセスなので、変数へのアクセス
より遅い 最適化前 :
Foo.bar
foo.func()
最適化後 :Foo$bar // プロパティアクセス → 変数アクセス
Foo$func(foo) // メソッド呼出 → 関数呼出
// ( 呼び出されるメソッドも変換 )
JSX 速さの秘密 - 高速な JavaScript を書く方法
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 を書く方法
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 を書く方法
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 を書く方法
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 を書く方法
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
18
Hidden Class を意識したコードを書く
http://v8-io12.appspot.com/
JSX 速さの秘密 - 高速な JavaScript を書く方法
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 を書く方法
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
20
Hidden Class を意識したコードを書く (3)
未初期化の要素にアクセスしない⁃ JSX では未初期化の要素にアクセスするとエラー• 注 : デバッグビルドのみ
⁃ リリースビルドではエラーチェック省略 Polymorphic なコードを書かない
⁃ JSX では polymorphic なコードは書けない• クラスベースの型指定が「必須」な処理系だから• テンプレートを使った場合は、引数の型ごとに個別に
コード生成
JSX 速さの秘密 - 高速な JavaScript を書く方法
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 を書く方法
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/1339489758
JSX 速さの秘密 - 高速な JavaScript を書く方法
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 を書く方法
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
24
組み込み関数が速いとは限らない (4)
JSX の場合 :
⁃ Array#forEach は独自に実装⁃ 言語仕様が硬い分、 JavaScript よりも処理が単純に⁃ 参照 :
http://d.hatena.ne.jp/kazuhooku/20120612/1339489758
JSX 速さの秘密 - 高速な JavaScript を書く方法
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
25
更なる高速化のために 〜 JSX の最適化コンパイラの仕事
JSX 速さの秘密 - 高速な JavaScript を書く方法
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
26
インライン展開
JavaScript VM は、インライン展開を行う⁃ だが、最速ではない• 理由 : 毎回、呼び出される関数が差し替えられていない
か確認する必要があるため
⁃ JSX の場合 :• コンパイル後に関数の差し替え不可能• 最適化コンパイル時に、あらかじめインライン展開され
た JavaScript を生成⁃ → JavaScript VM によるインライン展開より高速⁃ → 他の最適化も適用可能に
JSX 速さの秘密 - 高速な JavaScript を書く方法
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 を書く方法
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 を書く方法
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 を書く方法
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 を書く方法
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 を書く方法
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
32
まとめ
JSX 速さの秘密 - 高速な JavaScript を書く方法
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
33
高速な JavaScript を書ける自信はありますか ? JavaScript には速度が遅くなる罠が色々
⁃ 速くできるところが速くなった結果、罠に落ちた時の速度の落ち込みが大きくなった
モジュールをまたぐ最適化で高速になるケースも⁃ 例 : アフィン変換
高速かつメンテナンスが容易な JavaScript コードを書くのは難しすぎる⁃ グループ開発では教育コストが大きくなりすぎる⁃ 問題が顕在化してから対処するのでは間に合わない
JSX 速さの秘密 - 高速な JavaScript を書く方法
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
34
JSX を使えば問題解決するよ !!!
JSX 速さの秘密 - 高速な JavaScript を書く方法