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

34
Copyright (C) 2013 DeNA Co.,Ltd. All Rights Reserved. JSX 速速速速速 速速速速 JavaScript 速速速速速速 DeNA Co., Ltd. Kazuho Oku 1

description

JavaScript で高速なコードを書こうとする際に、はまりがちな罠と、JSX のコンパイラでどのように対処しているのかを紹介

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

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

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

1

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

DeNA Co., Ltd.

Kazuho Oku

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

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

2

JSX をご存知ですか ?

Java っぽいプログラミング言語です JavaScript にコンパイルされます JavaScript より JSX で書いたほうが高速に動作します

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

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

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

3

Q. 高速な JavaScript を書く方法を教えてください

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

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

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

4

A. JSX を使いましょう

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

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

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

5

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

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

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

6

これだけでは芸がないので、 JSX が行っている様々なJavaScript 最適化技法を紹介します。

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

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

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

7

※ これから説明することは、 JSX を使っていれば気にする必要のないことです

(自動で最適化されますから)

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

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

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

8

目次

高速な JavaScript を書くための3原則 更なる高速化のために 〜 JSX の最適化コンパイラの仕

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

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

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

9

高速な JavaScript を書くための 3 原則

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

Page 10: 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 を書く方法

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

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

11

オブジェクト生成を最小限に

オブジェクト生成を避ける理由⁃ メモリ確保に時間がかかる⁃ GC の原因になる

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

⁃ arguments の使用• 使った瞬間にオブジェクトが生成されます

オブジェクトが生成されそうで、されないパターン⁃ ”abc”.foo()• foo 内での this は string? (ES5 以降 )

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

Page 12: 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 を書く方法

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

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

13

プロパティアクセスを最小限に

プロパティアクセス = オブジェクトの要素へのアクセス⁃ 変数の要素へのアクセスなので、変数へのアクセス

より遅い 最適化前 :

Foo.bar

foo.func()

最適化後 :Foo$bar // プロパティアクセス → 変数アクセス

Foo$func(foo) // メソッド呼出 → 関数呼出

// ( 呼び出されるメソッドも変換 )

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

Page 14: 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 を書く方法

Page 15: 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 を書く方法

Page 16: 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 を書く方法

Page 17: 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 を書く方法

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

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

18

Hidden Class を意識したコードを書く

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

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

Page 19: 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 を書く方法

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

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

20

Hidden Class を意識したコードを書く (3)

未初期化の要素にアクセスしない⁃ JSX では未初期化の要素にアクセスするとエラー• 注 : デバッグビルドのみ

⁃ リリースビルドではエラーチェック省略 Polymorphic なコードを書かない

⁃ JSX では polymorphic なコードは書けない• クラスベースの型指定が「必須」な処理系だから• テンプレートを使った場合は、引数の型ごとに個別に

コード生成

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

Page 21: 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 を書く方法

Page 22: 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 を書く方法

Page 23: 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 を書く方法

Page 24: 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 を書く方法

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

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

25

更なる高速化のために 〜 JSX の最適化コンパイラの仕事

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

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

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

26

インライン展開

JavaScript VM は、インライン展開を行う⁃ だが、最速ではない• 理由 : 毎回、呼び出される関数が差し替えられていない

か確認する必要があるため

⁃ JSX の場合 :• コンパイル後に関数の差し替え不可能• 最適化コンパイル時に、あらかじめインライン展開され

た JavaScript を生成⁃ → JavaScript VM によるインライン展開より高速⁃ → 他の最適化も適用可能に

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

Page 27: 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 を書く方法

Page 28: 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 を書く方法

Page 29: 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 を書く方法

Page 30: 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 を書く方法

Page 31: 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 を書く方法

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

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

32

まとめ

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

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

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

33

高速な JavaScript を書ける自信はありますか ? JavaScript には速度が遅くなる罠が色々

⁃ 速くできるところが速くなった結果、罠に落ちた時の速度の落ち込みが大きくなった

モジュールをまたぐ最適化で高速になるケースも⁃ 例 : アフィン変換

高速かつメンテナンスが容易な JavaScript コードを書くのは難しすぎる⁃ グループ開発では教育コストが大きくなりすぎる⁃ 問題が顕在化してから対処するのでは間に合わない

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

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

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

34

JSX を使えば問題解決するよ !!!

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