SlideShare a Scribd company logo
JSX 速さの秘密
〜高速なJavaScriptを書く方法〜
DeNA Co., Ltd.
Kazuho Oku

1
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
JSXをご存知ですか?
 Javaっぽいプログラミング言語です
 JavaScriptにコンパイルされます
 JavaScriptよりJSXで書いたほうが高速に動作します

JSX 速さの秘密 - 高速なJavaScriptを書く方法

2
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
Q. 高速な JavaScript を書く方法を教えてください

JSX 速さの秘密 - 高速なJavaScriptを書く方法

3
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
A. JSX を使いましょう

JSX 速さの秘密 - 高速なJavaScriptを書く方法

4
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
…

JSX 速さの秘密 - 高速なJavaScriptを書く方法

5
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
これだけでは芸がないので、JSX が行っている様々な
JavaScript 最適化技法を紹介します。

JSX 速さの秘密 - 高速なJavaScriptを書く方法

6
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
※これから説明することは、JSXを使って
いれば気にする必要のないことです
(自動で最適化されますから)

JSX 速さの秘密 - 高速なJavaScriptを書く方法

7
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
目次
 高速なJavaScriptを書くための3原則
 更なる高速化のために 〜 JSXの最適化コンパイラの仕
事

JSX 速さの秘密 - 高速なJavaScriptを書く方法

8
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
高速なJavaScriptを書くための3原則

JSX 速さの秘密 - 高速なJavaScriptを書く方法

9
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
高速なJavaScriptを書くための3原則
 オブジェクトを避ける
⁃ 例: arguments を使わない
 Hidden Classを意識したコードを書く

⁃ Inline Cache / 配列の要素の型
 組み込み関数が速いとは限らない
⁃ 遅くなることがある
•

例: Function#apply, Array#forEach

JSX 速さの秘密 - 高速なJavaScriptを書く方法

10
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
オブジェクト生成を最小限に
 オブジェクト生成を避ける理由
⁃ メモリ確保に時間がかかる
⁃ GCの原因になる

 オブジェクトが生成されるパターン
⁃ new, {...}, [...]
⁃ arguments の使用
•

使った瞬間にオブジェクトが生成されます

 オブジェクトが生成されそうで、されないパターン
⁃ ”abc”.foo()
•

foo 内での this は string? (ES5以降)

JSX 速さの秘密 - 高速なJavaScriptを書く方法

11
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
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.
プロパティアクセスを最小限に
 プロパティアクセス=オブジェクトの要素へのアクセス
⁃ 変数の要素へのアクセスなので、変数へのアクセス
より遅い

 最適化前:
Foo.bar
foo.func()

 最適化後:
Foo$bar

// プロパティアクセス → 変数アクセス

Foo$func(foo) // メソッド呼出 → 関数呼出
// (呼び出されるメソッドも変換)

JSX 速さの秘密 - 高速なJavaScriptを書く方法

13
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
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.
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.
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.
コンストラクタのオーバーロード
 通常の関数は名前を変えることでオーバーロード可能
 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.
Hidden Classを意識したコードを書く

http://v8-io12.appspot.com/

JSX 速さの秘密 - 高速なJavaScriptを書く方法

18
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
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.
Hidden Classを意識したコードを書く (3)
 未初期化の要素にアクセスしない
⁃ JSXでは未初期化の要素にアクセスするとエラー
•

注: デバッグビルドのみ
⁃ リリースビルドではエラーチェック省略

 Polymorphicなコードを書かない
⁃ JSXではpolymorphicなコードは書けない
•

クラスベースの型指定が「必須」な処理系だから

•

テンプレートを使った場合は、引数の型ごとに個別にコ
ード生成

JSX 速さの秘密 - 高速なJavaScriptを書く方法

20
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
組み込み関数が速いとは限らない
 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.
組み込み関数が速いとは限らない (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.
組み込み関数が速いとは限らない (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.
組み込み関数が速いとは限らない (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.
更なる高速化のために 〜 JSXの最適化コンパイラの仕事

JSX 速さの秘密 - 高速なJavaScriptを書く方法

25
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
インライン展開
 JavaScript VMは、インライン展開を行う
⁃ だが、最速ではない
•

理由: 毎回、呼び出される関数が差し替えられていない
か確認する必要があるため

⁃ JSXの場合:
•

コンパイル後に関数の差し替え不可能

•

最適化コンパイル時に、あらかじめインライン展開され
たJavaScriptを生成
⁃ → JavaScript VMによるインライン展開より高速
⁃ → 他の最適化も適用可能に

JSX 速さの秘密 - 高速なJavaScriptを書く方法

26
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
事前計算
 定数畳み込み
⁃ 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.
事前計算 (その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.
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.
アフィン変換 (クラス定義)
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.
アフィン変換 (コンパイル例)
 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.
まとめ

JSX 速さの秘密 - 高速なJavaScriptを書く方法

32
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
高速なJavaScriptを書ける自信はありますか?
 JavaScriptには速度が遅くなる罠が色々
⁃ 速くできるところが速くなった結果、罠に落ちた時
の速度の落ち込みが大きくなった

 モジュールをまたぐ最適化で高速になるケースも
⁃ 例: アフィン変換
 高速かつメンテナンスが容易なJavaScriptコードを書く
のは難しすぎる
⁃ グループ開発では教育コストが大きくなりすぎる
⁃ 問題が顕在化してから対処するのでは間に合わない

JSX 速さの秘密 - 高速なJavaScriptを書く方法

33
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.
JSXを使えば問題解決するよ!!!

JSX 速さの秘密 - 高速なJavaScriptを書く方法

34
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved.

More Related Content

JSX 速さの秘密 - 高速なJavaScriptを書く方法