プロファイラを使う
クライアント側のメモリを多く消費するページは、ユーザビリティに大きな影響を与えます。例えば、1GBのメモリを消費するWebページは、4GBのメモリを積んでいる開発マシンでは快適に動いても、2GBのメモリのマシンではかなり重くなります。モバイル端末になるとさらにメモリの制限が出てくるため、メモリを使い過ぎるとブラウザ自体が終了してしまうこともあります。クライアント側の開発の最も困難なところは、あらゆるクライアントで快適に動くものを作ることです。サーバ側のプログラムならばそのサーバで問題なく動けばよいのですが、クライアント側のプログラムはユーザーが使用しているあらゆるクライアント上で快適に動く必要があります。Webページを作る時、最初に対応機種をある程度想定しますが、対応機種の中で一番スペックの低い端末でもそれほどストレスなく動くことを確認しながら開発を進めるようにします。
JavaScriptの実行を高速化したい時に一番効率の良い方法は、重さの原因となっているボトルネックを見つけてそこを重点的に改善することです。あまりチューニングされていないプログラムの場合、実行時間のうちのほとんどの部分をソースコードのごく一部の処理が占めていることが多いです。実行に時間がかかっていない部分を細々と高速化するよりも、最も時間のかかっている部分から順に高速化していく方が格段に効率が上がります。
プログラムのボトルネックを見つけるために有用なツールがプロファイラです。ChromeではデベロッパーツールのProfilesパネルに、FirefoxではFirebugの中にそれぞれプロファイラが搭載されています。ここではChromeのデベロッパーツールを例にとって解説します。
Chromeのブラウザのメニューから「デベロッパーツール」をクリックしてウインドウを開きます。WindowsではCtrl+Shift+I、MacではCmd+Alt+I(※注1)というショートカットキーで開くこともできます。
このショートカットキーではデベロッパーツールが開くのみだが、コンソールを直接開くにはWindowsではCtrl+Shift+J、MacではCmd+Alt+Jを使う。
ボトルネックを見つける
調査したいページを通常通りブラウザで開き、デベロッパーツールを起動します。デベロッパーツールのProfilesパネルを開き、「Collect JavaScript CPU Profile」を選択してStartボタンを押します(図4)。プロファイルの収集が始まるとボタンの表示がStopに変わります(図5)。そして、調査対象のページをリロードします。JavaScriptの実行が終わったらStopボタンをクリックします。しばらく解析処理が行われた後に結果が表示されます(図6)。
解析結果はボトムアップで表示されています。図6の例では、montReduce
という関数がmontSqrTo
関数から呼び出されたものであり、さらにmontSqrTo
はbnModPow
から呼び出されたものであることがわかります。Selfはその関数自身が実行した時間を表し、Totalはその関数からぶら下がっているものをすべて足した時間を表します。ウインドウの最下部にある%のアイコンをクリックすると、実際にかかった時間(ミリ秒)での表示に切り替わります。
Functionに(program)と表示されている部分はJavaScriptの実行以外の待機時間などの合計値で、これ以上の情報は得られません。(garbage collector)はJavaScriptのガベージコレクタの実行時間です。
下の「Heavy(Bottom Up)」と表示されている箇所をクリックして、「Tree(Top Down)」を選択すると、逆のトップダウン表示に切り替わります。トップダウン表示ではTotalの降順でソートすると見やすくなります。図7の例では、RunStep
関数から呼び出された一連の処理が55.16%で最も時間がかかっていたことを表しています。ただし、純粋にRunStep
関数自身の処理は0%、つまりほとんど時間はかかっていません。RunNextBenchmark
関数以下の実行には54.10%かかっていますが、そのうちの30.19%はRunNextBenchmark
に、残りの23.90%はBenchmarkSuite.RunSingleBenchmark
にかかったことがわかります。しかし、いずれもSelfは0%なのでパフォーマンスにはほとんど影響していません。右三角のマークをクリックして階層を深くまでたどっていくと、やがて末端にたどり着きます。図7の例では、Measure
関数から呼び出されたいくつかの関数で多くの時間がかかっており、この辺りがボトルネックとなっていることがわかります。