Haxe vs TypeScript でベンチマーク対決

TypeScriptって速度はどうなの?速いの?ってことが気になったので、HaXeや素のJavaScriptと比較してみました。

比較にはAOBenchを使いました。

比較に使ったプログラム

HaXe版(HaXe2.10 @yoshihiro503さん移植https://github.com/yoshihiro503/aobench_haxe)
TypeScript版(https://github.com/shohei909/TypeScript_AOBench)
JSX版(JSX0.0.2 @kioku_systemkさん移植 http://kioku.sys-k.net/aobench_jsx/)
JavaScript版(http://kioku.sys-k.net/aobench_jsx/)

HaXe版はデッドコード削除を使用するにあたりちょっとだけ改変してあります。TypeScript版はHaXe版を元に書き変えて移植しました。

結果

HaXe TypeScript JSX(修正前) JSX(修正後) JavaScript
Google Chrome (Windows) [ms] 974 995 968 1125 1076
Firefox (Windows) 1544 3698 3766 1604 1649
Browser (Android) 63158 64636 61327 57338 70425
Safari (iOS) 54876 59792 57831 43200 70800
JavaScriptのファイルサイズ [Byte] 6010 8163 7963 17591 6968

HaXeは安定して高速ですね。ファイルサイズも小さくなっています。以前のHaXeはもうちょっと大きいファイルを出力してたんですが、HaXe2.10で強力なデッドコード削除最適化が実装されて、未使用なコードがJavaScriptファイルに反映されなくする–dead-code-eliminationオプションが強化されました。

一方、TypeScriptで出力されたJavaScriptは直訳っぽい素直なコードで読みやすいんですが、あんまり速くありません。基本的にはインライン展開が無い分の差だと思うんですが、気になるのは2倍以上の差がついたFirefoxの結果です。インライン展開されてるはずのJSXでも遅い結果になっています。

追記(2012/10/17 18:32)
JSXのコンパイル方法について@__gfx__さんからの指摘(https://twitter.com/__gfx__/status/258467380248854528)があったので、–releaseモードでコンパイルして再計測、表にJSX(修正後)を追加し計測しました。

その際に、iOSの動作が軽くなってたのでSafari(iOS)の結果は全プログラムで再計測を行いました。

FireFoxで大きな差がついた原因

TypeScriptから出力されたコード(一部)

var Vec3 = (function () {
    function Vec3(x, y, z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
    Vec3.vadd = (function (a, b) {
        return new Vec3(a.x + b.x, a.y + b.y, a.z + b.z);
    });
    Vec3.vsub = (function (a, b) {
        return new Vec3(a.x - b.x, a.y - b.y, a.z - b.z);
    });
    Vec3.vcross = (function (a, b) {
        return new Vec3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
    });
    Vec3.vdot = (function (a, b) {
        return (a.x * b.x + a.y * b.y + a.z * b.z);
    });
    Vec3.vnormalize = function vnormalize(a) {
        var len = Math.sqrt(a.x * a.x + a.y * a.y + a.z * a.z);
        var v = new Vec3(a.x, a.y, a.z);
        if(Math.abs(len) > 1e-17) {
            v.x /= len;
            v.y /= len;
            v.z /= len;
        }
        return v;
    }
    return Vec3;
})();

staticな関数がfunction()の外で定義されてます。

HaXeから出力されたコード(一部)

var Vec3 = function(x,y,z) {
	this.x = x;
	this.y = y;
	this.z = z;
};
Vec3.vnormalize = function(a) {
	var len = Math.sqrt(a.x * a.x + a.y * a.y + a.z * a.z);
	var v = new Vec3(a.x,a.y,a.z);
	if(Math.abs(len) > 1.0e-17) {
		v.x /= len;
		v.y /= len;
		v.z /= len;
	}
	return v;
}

staticな関数がfunction()の外で定義されてます。一部の関数はインライン展開で消滅していますがそこはあんまり重要じゃありません。

ここを以下のように変更してみると…

var Vec3 = (function(){
	function Vec3(x,y,z) {
		this.x = x;
		this.y = y;
		this.z = z;
	};
	Vec3.vnormalize = function(a) {
		var len = Math.sqrt(a.x * a.x + a.y * a.y + a.z * a.z);
		var v = new Vec3(a.x,a.y,a.z);
		if(Math.abs(len) > 1.0e-17) {
			v.x /= len;
			v.y /= len;
			v.z /= len;
		}
		return v;
	}
	return Vec3;
})();

これだけで、実行時間が1500ms程度遅くなって3秒台に。
JavaScriptってよく分からないですね。

結論:速さを求めるならHaXeを使うといいんじゃない?

でも、「とりあえずHaXe使っとけば速くて軽いコードが書けるよ」って結論は、ある程度正解である程度間違ってる気がします。というのもHaXeでより速いコードを書くためにはちょっとした設定が必要になります。

inlineキーワードを適切な関数に設定

まず、HaXeのインライン展開はinlineキーワードを自分で設定しなければ機能しません。

こんな感じに。

static inline function vlength(a:Vec3) {
    return Math.sqrt(a.x * a.x + a.y * a.y + a.z * a.z);
}

また、inlineキーワードが機能するための条件もあります。

–dead-code-eliminationと@:keepでデッドコード削除。

Main.main()から参照されなかった関数を自動で取り除くデッドコード削除も、デフォルトでは無効になってます。使いたいときは、コンパイル時に–dead-code-eliminationオプションを使います。

こんな風に。
haxe -main Main -js script.js –dead-code-elimination

しかし、これだけだと必要な関数もデッドコードとみなされて削除されてしまうことがあります。

例えば、こんな場合。

<body onload="Application.start()"></body>

Application.start()がメインの関数で使用されてなければデッドコードとして削除されて、実行エラーが起こります。

そういう場合にメタデータ@:keepを使用して、必要なコードであることをコンパイラに知らせます。

class Application {
    @:keep public static function start(){
         //削除されたら困る処理
    }
}

HaXeなら無茶な書き換えをせずに最適化ができる。

HaXeはインライン展開やデッドコード削除などで、オブジェクト指向のコードを崩すことなく最適化ができたことが今回の勝因になっていると思います。

JavaScript、TypeScriptでも、柔軟な書き方ができるのでまだまだ高速化の余地は残されてます。ですが、それをやると保守のしづらいコードとなってしまいます。

やっぱり高速に動作するJavaScriptを書きたいならHaXeを選択して上記のようなオプションを駆使すると、わかりやすく軽快なコードが書けて良いんじゃないでしょうか?

参考

aobench – Google Project Hosting
HaxeいいよHaxe – GEOQUAKE Backstage

Haxe vs TypeScript でベンチマーク対決」への1件のフィードバック

  1. ピンバック: HaXe vs TypeScript でベンチマーク対決 | 机上のにゅーろん | ちゅどん道中記

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です