Chrome開発者ツールを使用したJavaScriptプロファイリング

サイトの速度は、ページの読み込み速度とページ内のコードの動作速度の2つの部分で構成されています。 ミニファイヤやCDNなどの多くのサービスは読み込みを高速化するのに役立ちますが、コードの速度はユーザー次第です。



コードの小さな変更は、パフォーマンスに大きな変化をもたらす可能性があります。 ほんの数行は、高速サイトと「応答しないスクリプト」ダイアログの違いを意味します。



基準点を設定する



カラーソーターと呼ばれるシンプルなアプリケーションを分解します 。これは、ドラッグできる色のテーブルを表示します。 各ドットはdivで、小さなCSSが追加されて円のように見えます。







ページは十分に速く読み込まれますが、まだ時間がかかり、描画が完了する前にわずかな点滅があります。 プロファイリングを行い、より速くする時が来ました。

ベンチマークを設定して、アプリケーションの現在の速度を確認して、パフォーマンスの加速を常に開始してください。 レポートポイントは、プログラムを本当に高速化し、妥協を支援しているかどうかのアイデアを提供します。



プロファイラーはChrome Developer Toolsの一部であり、Chromeで常に利用可能です。 Ctrl + Shift + Iを押して起動します。 Chromeには、優れたイベント追跡ユーティリティであるSpeed Tracerもあります。



基準点を決定するには、「タイムライン」タブで記録を開始し、ページをロードして記録を停止します(開いているChrome開発者ツールで記録を開始するには、「タイムライン」タブに移動し、左下隅のアイコン-黒丸-「記録」をクリックします)。 Chromeは、ページの読み込みが開始されるまで記録を開始しないほどスマートです。 3回実行し、平均値を計算しました。







私の基準点は、最初のページ要求からブラウザーでのページレンダリングの終了までの時間-1.25秒です。 これは悪いことではありませんが、そのような単純なページにはあまり良くありません。



コードをより速く動作させたいのですが、何がそれを正確に遅くするのかわかりません。 プロファイラーは原因を見つけるのに役立つはずです。



プロファイル作成



タイムラインはコードの時間を示しますが、コードの一部の動作に関する情報は提供しません。 何かを変更してタイムラインを何度も実行できますが、それはスズメを撃つようなものです。 [プロファイル]タブでは、何が起こるかを確認するためのより便利な方法が提供されます。



プロファイラーは、最も時間がかかる機能を示します。 3つのタイプのプロファイリングがある[プロファイル]タブに切り替えて、初期プロファイルを作成しましょう。



  1. JavaScript CPUプロファイル

    JavaScriptにかかるCPU時間を示します。
  2. CSSセレクタープロファイル

    CPUがCSSセレクターを処理するのにかかる時間を示します
  3. ヒープのスナップショット

    JavaScriptで変数が使用するメモリ量を示します




JavaScriptを高速化するため、CPUプロファイリングを使用します。 プロファイラーを起動するには、ページを更新してからプロファイラーを停止します。







多くのことが起こっていることがわかります。 カラーソーターはjQueryとjQuery UIを使用します。これらは、プラグインの管理や正規表現の解析など、多くのことを行います。 リストの一番上にある2つの関数:decimalToHexとmakeColorSorderも見ることができます。 これら2つの関数は、合計でプロセッサ時間の13.2%を占めるため、改善を開始するのに適した場所です。



関数の横にある矢印をクリックして、関数呼び出しの完全なスタックを表示できます。 この場合、decimalToHexはmakeColorSorterから呼び出され、makeColorSorterは$(document).readyから呼び出されます。



コードは次のとおりです。



$(document).ready(function() { makeColorSorter(.05, .05, .05, 0, 2, 4, 128, 127, 121); makeSortable(); });
      
      







それらがどこから呼び出されるかを理解することは、色をドラッグ可能にすることが最大のパフォーマンス問題ではないという洞察を与えます。 この場合、DOM要素をドラッグする機能を提供するよりも、DOM要素を追加するのに時間がかかります。



これらの機能をより高速にしたいので、最初にすべきことは私の変更を分離することです。 ページをロードするときに多くのことが起こりますが、変更を評価するときにこれを取り除きたいと思います。



問題を切り分ける



ドキュメントをロードした後にカラーソーターをロードする代わりに、 2番目のバージョンを作成します。これは、ボタンをクリックするまで待機します。 これにより、ブラウザーが実行する他の処理の影響が軽減され、コードのみをプロファイルできます。 パフォーマンスを高速化した後、元に戻します。



新しい関数にtestColorSorterという名前を付けて、ボタンにバインドしましょう。



 function testColorSorter() { makeColorSorter(.05, .05, .05, 0, 2, 4, 128, 127, 121); makeSortable(); } //<button id="clickMe" onclick="testColorSorter();">Click me</button>
      
      







アプリケーションを変更すると、パフォーマンスに予測できない影響を与える可能性があります。 この変更はかなり安全に思えますが、プロファイラーを再度実行して、他に何か影響があるかどうかを確認したいと思います。 プロファイラーを起動し、アプリケーションのボタンをクリックしてプロファイラーを停止することにより、新しいプロファイルを作成します。







最初に注意することはdecimalToHex関数です。この関数はロード時間の4.23%を要します。 これは、コードで最も長く機能するものです。 このシナリオでコードがどのように改善されるかを確認するために、新しいベンチマークを作成しましょう。







ボタンがクリックされる前にいくつかのイベントが発生しますが、私はボタンをクリックしてから色彩選別機が描画されるまでの時間しか気にしません。 ボタンは390ミリ秒で押され、ペイントイベントは726ミリ秒で発生します。 726-290が基準点-390ミリ秒です。 初めてのように、3回実行して平均を計算しました。



これで、コードの実行場所と参照先がわかりました。 問題を修正する準備ができました。



より速く



プロファイラーは、どの関数が問題につながるのかを伝えるだけなので、その内部を調べて、その機能を理解する必要があります。



 function decimalToHex(d) { var hex = Number(d).toString(16); hex = "00".substr(0, 2 - hex.length) + hex; console.log('converting ' + d + ' to ' + hex); return hex; }
      
      







カラーソーターの各ポイントは、#86F01Bや#2456FEのような16進形式の背景色の値を取ります。 decimalToHex関数は、RGB値をページで使用できる16進数の色に変換します。



この関数は非常に単純ですが、そこにconsole.logを残しましたが、これは削除できます。 また、色の先頭にパディングを追加します。 一部の10進数は1文字の16進数になることがあるため、これは重要です。 たとえば、10進数の「12」は16進数の「C」であり、CSSには2文字が必要です。 この変換を少し一般的にすることができます。



 function decimalToHex(d) { var hex = Number(d).toString(16); return hex.length === 1 ? '0' + hex : hex; }
      
      







カラーソーターのバージョン3は、インデントが必要で、substrを呼び出さない場合にのみ行を変更します。 この新機能により、コードは137ミリ秒で実行されます。 コードを再度プロファイリングすると、decimalToHex関数が合計時間のわずか0.04%を占め、リストのはるか下に移動することが明らかになりました。







また、現在最もリソースを消費する関数はjQueryのe.extend.mergeであることがわかります。 コードが縮小されているため、この関数が何をするのかわかりません。 開発者向けにjQueryバージョンを追加することもできますが、この関数はmakeColorSorterから呼び出されていることがわかりますので、高速化しましょう。



コンテンツの変更を減らす



カラーソーターの虹色は、正弦波に基づいて生成されます。 コードはカラースペクトルの中心点を見て、指定された幅のこの中心点を通る正弦波を作成します。 虹のイメージで色を変換します。 赤、緑、青の周波数を変更することで、虹の色を変更することもできます。



 function makeColorSorter(frequency1, frequency2, frequency3, phase1, phase2, phase3, center, width, len) { for (var i = 0; i < len; ++i) { var red = Math.floor(Math.sin(frequency1 * i + phase1) * width + center); var green = Math.floor(Math.sin(frequency2 * i + phase2) * width + center); var blue = Math.floor(Math.sin(frequency3 * i + phase3) * width + center); console.log('red: ' + decimalToHex(red)); console.log('green: ' + decimalToHex(green)); console.log('blue: ' + decimalToHex(blue)); var div = $('<div class="colorBlock"></div>'); div.css('background-color', '#' + decimalToHex(red) + decimalToHex(green) + decimalToHex(blue)); $('#colors').append(div); } }
      
      







console.logへの呼び出しをさらに削除できます。 これらの呼び出しは、decimalToHex関数を毎回呼び出すため、特に有害です。つまり、decimalToHexが必要以上に2回呼び出されることを意味します。

この関数はDOMを大きく変更します。 ループの本体が実行されるたびに、新しいdivが追加されます。 たぶん、これはe.extend.mergeを呼び出すという事実と関係があるのでしょう。 プロファイラーを使用すると、簡単な実験でこれを検証できます。



ループを実行するたびに新しいdivを追加する代わりに、すべてのdivタグを一度に追加します。 それらを保存する変数を作成し、最後に1回追加します。



 function makeColorSorter(frequency1, frequency2, frequency3, phase1, phase2, phase3, center, width, len) { var colors = ""; for (var i = 0; i < len; ++i) { var red = Math.floor(Math.sin(frequency1 * i + phase1) * width + center); var green = Math.floor(Math.sin(frequency2 * i + phase2) * width + center); var blue = Math.floor(Math.sin(frequency3 * i + phase3) * width + center); colors += '<div class="colorBlock" style="background-color: #' + decimalToHex(red) + decimalToHex(green) + decimalToHex(blue) + '"></div>'; } $('#colors').append(colors); }
      
      







この小さなコードの変更は、DOMが1回変更されることを意味します。 これをタイムラインでテストすると、クリックからPaintイベントまでの実行時間が31ミリ秒になっていることがわかります。 この1つの変更により、 バージョン4ランタイムが87%削減されました。 また、プロファイラーを実行して、e.extend.merge関数の時間がかかりすぎないため、リストの最上部に表示されないこともわかります。



decimalToHex関数を完全に削除することで、コードを少し速くすることができます。 CSSはRGBカラーをサポートしているため、16進数値に変換する必要はありません。 makeColorSorter関数は次のようになります。



 function makeColorSorter(frequency1, frequency2, frequency3, phase1, phase2, phase3, center, width, len) { var colors = ""; for (var i = 0; i < len; ++i) { var red = Math.floor(Math.sin(frequency1 * i + phase1) * width + center); var green = Math.floor(Math.sin(frequency2 * i + phase2) * width + center); var blue = Math.floor(Math.sin(frequency3 * i + phase3) * width + center); colors += '<div class="colorBlock" style="background-color: rgb(' + red + ',' + green + ',' + blue + ')"></div>'; } $('#colors').append(colors); }
      
      







バージョン5はわずか26ミリ秒で実行され( 最終バージョンの加速度 92%を超えています)、28行ではなく18行のコードを使用します。



アプリケーションでのJavaScriptプロファイリング



実際のアプリケーションはこの色選別機よりもはるかに複雑ですが、それらのプロファイリングは同じステップで構成されています。

  1. 出発を把握できるように、参照ポイントを定義します。
  2. 問題をアプリケーション内の他のコードから分離して、問題を特定します。
  3. タイムラインとプロファイリングを頻繁に使用して、制御された環境で高速化します。




コードを高速化する場合、さらにいくつかのルールがあります。




All Articles