JSX のプロファイラが素晴らしい!

個人的にとても便利だと思っているけどあまり注目されていない JSX の機能にプロファイラがあります。

PC ブラウザであれば今時のブラウザはプロファイラがあるので、使うことはあまりないかもしれませんが、iOS5 の MobileSafari とかでプロファイルを取りたい場合には重宝すると思います。1
今までは独自のプロファイラを使って、処理の重そうな関数に対してコール回数と処理時間(関数全体を処理するのにかかった時間と関数内で呼ばれた関数の処理時間を除いた時間)を測定していましたが、JSX のプロファイラを使うようになって全ての関数に対して詳細な情報を得られるようになったので劇的に作業効率が上がりました。

というわけで、Box2D.jsx のサンプルを使ってプロファイラの使い方を解説したいと思います。

Box2D.jsx のプロファイルを取ってみる

box2djsx のリポジトリを clone してプロファイラを有効にしてコンパイルするまでの流れは次のとおりです。コンパイルオプションには –profile を付けます。
ここで注意したいのは、必ず –release オプションも一緒に付けることです。–release オプションを付けないとインライン展開がされない上に、余計な処理が入るのでプロファイル結果としてはあまり参考にならない結果になります。

$ git clone https://github.com/tkihira/box2djsx.git
$ cd box2djsx/
$ git checkout kazuho/optimize
$ sed -i '' 's/kazuho/jsx/' .gitmodules  # JSX の URL を変更
$ git submodule update --init
$ cd JSX
$ git checkout master
$ cd ..
$ JSX/bin/jsx --add-search-path src --release --profile src/box2d.jsx > sample/box2d.js

プロファイラ用のサーバを起動します。自前でプロファイル結果を受け取るサーバを用意しなくて良いので楽ですね。

$ cd JSX
$ make server
node web/server.js
Open http://localhost:5000/

sample/box2djsx.html を次のように書き換えて、タッチイベントによってプロファイル結果をサーバに対して送信するようにします。サーバは make server を実行したサーバを指定するので、HTML ファイルのあるサーバと make server を実行したサーバが違えば JSX.postProfileResults の引数は変える必要があります。

<!doctype html>
<html><head><title>box2d sample</title>
<meta name="viewport" content="width=320px" />
<script src="box2d.js"></script>
<script>
	window.addEventListener("load", function(e) {
		var sample = JSX.require("src/box2d.jsx");
		sample._Main.main$();
	});

	window.addEventListener("touchstart", function(e) {
		if (JSX.profilerIsRunning()) {
			JSX.postProfileResults(location.origin + ":5000/post-profile");
		}
	});
</script>
</head>
<body style="margin:0;position:relative;"><canvas id="canvas" width="320" height="400"></canvas>
<div id="fps" style="position:absolute;top:0;left:0;color:#f00">test</div>
</body>
</html>

sample ディレクトリを移動するなりシンボリックリンクを張るなりしてブラウザからアクセスできるようにします。
MobileSafari から sample/box2djsx.html にアクセスして、プロファイリングを終了したいところでタッチすれば結果がサーバに送信されます。
結果が送信されたら、http://localhost:5000/web/profiler.html にアクセスして、Results から結果を選択します。
私が iPhone 4S (iOS 5.1.1) の MobileSafari で測定したら次のようになりました。
画像じゃないのでいろいろクリックしてみるとおもしろいと思います。

結果の見方ですが、Count はその関数が呼ばれた回数、Inclusive はその関数全体の処理にかかった時間、Exclusive は Inclusive から他の関数の処理時間を除いた時間です。
例えば、処理単位でボトルネックを調べる場合は Mode を “Call Tree” にして Inclusive に注目すれば良いでしょう。
関数単位でボトルネックを調べる場合は Mode を “Functions” にして Exclusive でソート(「Exclusive(ms)」をクリック)すれば簡単に調べられます。

ボトルネックがわかったら、あの手この手でチューニングして高速化することになります。

プロファイル結果のリセットの仕方

–profile オプションを付けると最初からプロファイルが開始されますが、特定の箇所に対してプロファイルを取りたい場合もあるかと思います。そんな時は JSX.resetProfileResults を使います。
例えば次のように書くと、1回目のタッチでプロファイラをリセットして、2回目のタッチでプロファイル結果を送信することができます。

(function() {
	var profilerIsRunning = false;
	window.addEventListener("touchstart", function(e) {
		if (JSX.profilerIsRunning()) {
			if (profilerIsRunning) {
				JSX.postProfileResults(location.origin + ":5000/post-profile");
				console.log("profile end");
			} else {
				console.log("profile start");
				JSX.resetProfileResults();
			}
			profilerIsRunning = !profilerIsRunning;
		}
	});
})();

実は JSX.resetProfileResults は私の要望がきっかけでできた機能なんですが、この機能がなかったらいろいろ破綻してただろうなぁと思うぐらい個人的に重宝している機能です。

以上、JSX のプロファイラの利用方法でした!

  1. iOS6 からは Web インスペクタを利用することでプロファイルを取ることができるようになりましたが使い勝手はあまり良くない気がします 

広告
Emacs から PlantUML を使ってみた R のデータ構造とメモリ管理
※このエントリーははてなダイアリーから移行したものです。過去のコメントなどはそちらを参照してください