コールツリーは、どの JavaScript 関数がもっともブラウザで時間をかけているかを示します。この結果を分析すると、コードのボトルネック (ブラウザが不相応に多くの時間をかけている場所) を見つけることができます。
これらのボトルネックは、最適化により最大の効果を得られる場所です。
コールツリーは、サンプリングプロファイラです。これは JavaScript エンジンの状態を定期的にサンプリングして、その時点のコード実行のスタックを記録します。統計的に、個々の関数を実行しているときに取得したサンプル数はブラウザが実行にかけた時間に対応しますので、コード内のボトルネックを発見できます。
本記事では、例としてシンプルなプログラムから出力した結果を使用します。自身のプロファイルで実験するためにプログラムを取得したい場合は、こちらをご覧ください。ここで言及するプロファイルはこちらにあります。読み進めるために、インポートしてください。
プログラムの構造を説明するページはこちらです。
なおフレームチャートのドキュメントページでも、同じプログラムおよび同じプロファイルを使用しています。
以下のスクリーンショットはバブルソート、選択ソート、クイックソートの 3 種類のソートアルゴリズムを比較するプログラムの出力です。整数の乱数を埋めた配列をいくつか生成して、それぞれのアルゴリズムで順にソートします。
記録内で長い JavaScript マーカーを示している部分をズームインしました:
コールツリーは、表形式で結果を表示します。それぞれの行は 1 個以上のサンプルを取得した関数を表し、これらの行は関数内で取得したサンプル数の降順で並べられます。
サンプルは、特定の関数を実行しているときに取得したサンプルの数です。
コストは、選択した範囲内の総サンプル数に対する、前出のサンプル数の割合です。
時間は、選択した範囲内の総時間に基いて、前出のサンプル数をミリ秒に変換した値です。
現バージョンのコールツリーでは、これらの列がもっとも重要です。コストが相対的に高い関数は、最適化のよい候補です。これは実行に時間がかかっている、あるいは頻繁に呼び出されているためです。
このスクリーンショットでは、バブルソートはとても効率が低いアルゴリズムであるという、私たちがすでに知っているであろうことを示しています。バブルソートのサンプル数は選択ソートの約 6 倍、クイックソートの約 13 倍です。
コールツリーを渡り歩く
各関数名の隣に、展開用の矢印があります。クリックすると、サンプルを取得した関数からルートに向けて、コールツリーを戻るパスを確認できます。例えば、bubbleSort()
のエントリを展開します:
コールグラフは以下のとおりであるとわかります:
sortAll() -> sort() -> bubbleSort()
ここで sort()
のコスト は 1.45% であり、これはリストの後方にある個別の sort()
の値と同じであることに注意してください。これは、一部のサンプルが呼び出した関数内ではなく sort()
自身で取得されたことを表します。
トップレベルへ戻るパスが複数存在することがあります。swap()
のエントリを展開してみましょう:
swap()
内で 253 個のサンプルを取得しました。しかし swap()
は 2 つの異なるパスで到達しています。bubbleSort()
と selectionSort()
が使用しています。swap()
の 253 サンプルのうち 252 個は bubbleSort()
の枝で、また 1 個だけ selectionSort()
の枝で取得したこともわかります。
これは、私たちが考えていた以上にバブルソートの効率が低いということです! 自身以外に 252 サンプル、または総コストのほぼ 10% を抱えています。
このような探求によりコールグラフ全体を、関連付けられたサンプル数とともに明らかにできます:
sortAll() // 8 -> sort() // 37 -> bubbleSort() // 1345 -> swap() // 252 -> selectionSort() // 190 -> swap() // 1 -> quickSort() // 103 -> partition() // 12
プラットフォームのデータ
Gecko、入力とイベントなどと記載された行がいくつかあるでしょう。これらは内部のブラウザ呼び出しを表します。
これらも役に立つ情報です。あなたのサイトがブラウザを懸命に働かせている場合、あなたのコードではサンプルが記録されないかもしれませんが、問題は残されています。
本記事の例では、679 個のサンプルが Gecko に割り当てられており、bubbleSort()
に次いで 2 番目に大きいグループです。これを展開してみましょう:
これは 614 個のサンプル、または総コストの役 20% が sort()
の呼び出しに由来することを表します。sort()
のコードを見ると、プラットフォームのデータの高いコストの理由は console.log()
を繰り返し呼び出しているためであることが明白でしょう:
function sort(unsorted) { console.log(bubbleSort(unsorted)); console.log(selectionSort(unsorted)); console.log(quickSort(unsorted)); }
より効率がよい実装方法を検討することは、間違いなく有益でしょう。
ここでわかることとして、アイドル時間は Gecko として分類されますので、プロファイル内で JavaScript を実行していない部分は Gecko のサンプルが増えます。これらはサイトのパフォーマンスとは関係がありません。
デフォルトで、コールツリーはプラットフォームのデータを個別の関数に分割しません。これは大量のノイズを加えてしまうことと、Firefox 自体に取り組んでいる人々以外には役立たないと思われるためです。これらの詳細を確認したい場合は、設定で"Gecko プラットフォームのデータを表示" にチェックを入れてください。