プロファイラツールを使用して、JavaScript コードのボトルネックを見つけましょう。プロファイラは周期的に、サンプルについて現在の JavaScript コールスタックやコンパイルの統計情報を抽出します。
"Web 開発" メニューから "プロファイラ" を選択することで、プロファイラを起動できます。"Web 開発" メニューは、Linux や OS X では "ツール" メニューの配下に、Windows では "Firefox" メニューの直下にあります。
ツールボックスが開いて、プロファイラが選択されます。
サンプリング型プロファイラ
JavaScript プロファイラは、サンプリング型のプロファイラです。これは JavaScript エンジンの状態を定期的にサンプリングして、その時点のコード実行のスタックを記録します。統計的に、個々の関数を実行しているときに取得したサンプル数はブラウザが実行にかけた時間に対応しますので、コード内のボトルネックを発見できます。
例えば、以下のようなプログラムについて考えてみましょう:
function doSomething() { var x = getTheValue(); x = x + 1; // -> サンプル A logTheValue(x); } function getTheValue() { return 5; } function logTheValue(x) { console.log(x); // -> サンプル B、サンプル C } doSomething();
プロファイラをアクティブにしてこのプログラムを実行したら、実行時にプロファイラは上記のインラインコメントで示したように 3 つのサンプルを取得します。
これらはすべて doSomething()
の内部から取得されますが、2 番目の 2 つは doSomething()
から呼び出された logTheValue()
関数の内部です。よってプロファイルは、以下のように 3 つのスタックトレースで構成されます:
サンプル A: doSomething() サンプル B: doSomething() > logTheValue() サンプル C: doSomething() > logTheValue()
これは私たちに何も伝えられない不十分なデータであることが明らかですが、より多くのサンプルにより、logTheValue()
がコード内のボトルネックであると断定できるかもしれません。
プロファイルの作成
プロファイラでストップウォッチのボタンをクリックして、サンプルの記録を始めます。プロファイラが記録を行っている間は、ストップウォッチのボタンがハイライトされます。ボタンを再度押すと記録を停止して、新たなプロファイルを保存します:
"終了" をクリックすると、新しいプロファイルが自動的に開きます:
このペインは 2 つのパーツに分かれています:
- 左側は取得したすべてのプロファイルを一覧表示しており、それぞれのプロファイルを読み込むことができます。また、リストの上にボタンが 2 つあります。ストップウォッチのボタンは新たなプロファイルの記録を、インポート... ボタンは以前に保存したデータのインポートを行います。プロファイルを選択しているときは、保存ボタンをクリックするとデータを JSON ファイルとして保存できます。
- 右側は現在読み込んでいるプロファイルを表示します。
プロファイルの分析
プロファイルは 2 つのパーツに分かれています:
プロファイルのタイムライン
プロファイルのタイムラインは、プロファイル表示の上部を占めています:
横軸は時間、縦軸はサンプルにおけるコールスタックのサイズを表します。コールスタックは、サンプルを取得したときにアクティブであった関数の量を表します。
チャートで赤色のサンプルは、そのときにブラウザが応答していなかったことを示しており、ユーザはアニメーションや応答性が止まったことに気づいたかもしれません。プロファイルに赤色のサンプルがある場合は、そのコードをいくつかのイベントに分解することを検討したり、requestAnimationFrame や Worker の使用について調べたりしましょう。
タイムラインでクリックアンドドラッグすることで、プロファイル内の特定の範囲を調査できます:
タイムラインの上に "サンプリング範囲 [AAA, BBB]" というラベルがついた、新たなボタンが現れます。そのボタンを押すとプロファイルがズームされて、そのタイムスライスの詳細なビューが下部に表示されます:
プロファイルの詳細
プロファイルの詳細は、プロファイル表示の下部を占めています:
始めに新しいサンプルを開くと、サンプルペインには上のスクリーンショットのように "(total)" という名前の行があります。"(total)" の隣にある三角印をクリックすると、サンプル内にあるすべてのトップレベル関数がリストアップされます。
実行時間は当該関数が現れたサンプルの総数を示し1、その後ろにプロファイル内で当該関数が現れた全サンプルのパーセント値があります。最上段の行はプロファイル全体で 2021 のサンプルがあることを表し、また 2 行目は 1914 サンプルすなわち全体の 94.7% が、detectImage()
関数内にいたことを表します。
滞在 は当該関数そのものを実行する間に取得したサンプル数を示しており、関数を呼び出しているときではありません。前出のシンプルな例では、doSomething()
は実行時間が 3 (サンプル A、B、C) ですが、滞在の値は 1 (サンプル A) になるでしょう。
3 列目は関数名およびファイル名と行数 (ローカルの関数) またはベースネームとドメイン名を表示します。灰色の関数はブラウザ組み込みの関数です。黒色の関数がページで読み込んだ JavaScript を表します。行にマウスポインタを乗せると、関数の識別名の右側に矢印が現れます: 矢印をクリックすると関数のソースを表示します。
コールツリーの展開
ある行で、この関数から呼び出された関数に滞在している間のサンプルが存在する場合 (すなわち、実行時間がその行の滞在より大きい場合) は、関数名の左側に三角印が表示され、コールツリーを展開できます。
前出のシンプルな例では、完全に展開したコールツリーは以下のようになります:
実行時間 | 滞在 | |
3 100% | 1 | doSomething() |
2 67% | 2 | logTheValue() |
より実際的な例を見ましょう: 前出のスクリーンショットで、上から 2 行目を見ると detectImage()
関数の内部で 1914 サンプルかかっていることがわかります。しかし、そのすべては detectImage()
から呼び出された関数でかかっています (滞在 セルが 0 です)。コールツリーを展開して、ほとんどのサンプルがかかっていたとき実際に実行していた関数は何かを明らかにできます:
これは、detectAtScale()
を実際に実行しているときに 6 サンプル、getRect()
の実行に 12 サンプルかかっていたことなどを表します。