JavaScript によるパフォーマンス計測

JavaScript によるパフォーマンス計測とは

JavaScript によるパフォーマンス計測とは、ユーザが読み込む JavaScript の中にパフォーマンス計測用のコードを埋め込み計測することです。

Web ページのパフォーマンスを計測する方法として Chrome の DevTools を利用する方法が一般的ですが、任意の箇所やタイミングで計測できない点や、自動化できない短所があります。JavaScript によるパフォーマンス計測は、埋め込み用のコードが必要になりますが、それらの短所を補うことができます。

JavaScript による計測の中でも、特定のライブラリやフレームワークに依存しない方法として、以下があります。

  • Navigation Timing API
  • User Timing API
  • Resource Timing API
  • Performance Observer
  • High Resolution Time

Navigation Timing API

Navigation Timing API では、ブラウザのナビゲーション時の詳細なパフォーマンス情報を取得できます。

サポートブラウザ
ie
IE
edge
Edge
firefox
Firefox
chrome
Chrome
safari
Safari
opera
Opera
safari
iOS Safari
android
Android
976.081584.0
○:Support ×:Not Support ?:未定義 n:以降の Version で Support

Navigation Timing API を利用することで、ブラウザの各段階の処理時間を計測できます。例えば、ドメインの名前解決、TCP ハンドシェイク、TLS トンネルの確立、HTTP リクエストの送信、HTTP レスポンスの受信、DOM ツリーの構築、スタイル情報の解決などです。

Navigation Timing API では window.performance.timing オブジェクトと window.performance.navigation オブジェクトを利用します。

window.performance.timing オブジェクトからは、各段階の処理時間のタイムスタンプが得られます。これらのプロパティには、アクションが完了したときのミリ秒単位のタイムスタンプが格納されています。

Navigation Timing API のタイムスタンプダイアグラム
Navigation Timing API のタイムスタンプダイアグラム
var timing = window.performance.timing;
console.log(timing.navigationStart);            // ナビゲーション開始時
console.log(timing.unloadEventStart);           // unload イベントの発火開始時
console.log(timing.unloadEventEnd);             // unload イベントの発火終了時
console.log(timing.redirectStart);              // リダイレクト開始時
console.log(timing.redirectEnd);                // リダイレクト終了時
console.log(timing.fetchStart);                 // AppCache からの fetch 開始時
console.log(timing.domainLookupStart);          // ドメイン名解決の開始時
console.log(timing.domainLookupEnd);            // ドメイン名解決の終了時
console.log(timing.connectStart);               // TCP 接続処理の開始時
console.log(timing.secureConnectionStart);      // TLS トンネルの接続開始時
console.log(timing.connectEnd);                 // 接続確立の終了時
console.log(timing.requestStart);               // HTTP リクエスト送信の開始時
console.log(timing.responseStart);              // HTTP レスポンス受信の開始時
console.log(timing.responseEnd);                // HTTP レスポンス受信の終了時
console.log(timing.domLoading);                 // HTML パース開始時
console.log(timing.domInteractive);             // DOM ツリーの構築終了時
console.log(timing.domContentLoadedEventStart); // DOMContentLoaded 発火開始時
console.log(timing.domContentLoadedEventEnd);   // DOMContentLoaded 発火終了時
console.log(timing.domComplete);                // DOM の構築が完了時
console.log(timing.loadEventStart);             // onLoad イベントの発火開始時
console.log(timing.loadEventEnd);               // onLoad イベントの発火終了時
Navigation Timing API

Navigation Timing API を利用する場合、2つの項目の差に着目することになります。

var timing = window.performance.timing;
console.log("リダイレクト: " + (timing.redirectEnd - timing.redirectStart));
console.log("APキャッシュ: " + (timing.domainLookupStart - timing.fetchStart));
console.log("DNS取得時間: " + (timing.domainLookupEnd - timing.domainLookupStart));
console.log("TCP接続時間: " + (timing.connectEnd - timing.connectStart));
console.log("リクエスト時間: " + (timing.responseStart - timing.requestStart));
console.log("レスポンス時間: " + (timing.responseEnd - timing.responseStart));
console.log("DOMの構築時間: " + (timing.domComplete - timing.domLoading));
console.log("onLoadイベント: " + (timing.loadEventEnd - timing.loadEventStart));
Navigation Timing API の項目差から各種時間を算出する例

window.performance.navigation オブジェクトは、プロパティに typeredirectCount を持ちます。

type プロパティは、ドキュメントを読み込んだ際のナビゲーションの種別を表します。type の値は 0, 1, 2, 255 のいずれか1つを取ります。ただし 255 は将来的な予約語として確保しているだけなので、実際には 0, 1, 2 のいずれかになります。

  • 0: TYPE_NAVIGATE(クリック、または URLを入力してページに移動してきた)
  • 1: TYPE_RELOAD(リロードしてページを表示した)
  • 2: TYPE_BACK_FORWARD(ブラウザの「進む」、または「戻る」ボタンでページを移動した)
  • 255: TYPE_RESERVED(上記以外の操作)

redirectCount プロパティは、最終的なドキュメントにたどり着くまでに何回リダイレクトされたかを表します。

User Timing API

User Timing API は、開発者が任意の処理にかかる時間を計測することができます。これは、インタラクションなどの必ずしも計測する地点を機械的に宣言できない処理のパフォーマンスを計測するのに有効です。

サポートブラウザ
ie
IE
edge
Edge
firefox
Firefox
chrome
Chrome
safari
Safari
opera
Opera
safari
iOS Safari
android
Android
10124128113311
○:Support ×:Not Support ?:未定義 n:以降の Version で Support

User Timing API でパフォーマンスを計測するには、performance.mark() メソッドを用いて、処理の計測地点を宣言します。

performance.mark('test-start');

// 計測したい処理

performance.mark('test-end');
User Timing API

上記の例では、開始地点に test-start、終了地点に test-end の引数を渡して宣言しています。この2つの計測地点を測定するためには performance.measure() メソッドを用います。

performance.mark('test-start');

// 計測したい処理

performance.mark('test-end');
performance.measure('test', 'test-start', 'test-end');
User Timing API

performance.measure() メソッドの第一引数には test という文字列を渡していますが、これは test-starttest-end 間の測定に対して名前を割り当てています。この名前は、計測時間を取り出す際に利用します。

計測時間を取り出すためには、performance.getEntriesByType() メソッドを用います。

performance.mark('test-start');

// 計測したい処理

performance.mark('test-end');
performance.measure('test', 'test-start', 'test-end');

console.log(performance.getEntriesByType('mark'));
console.log(performance.getEntriesByType('measure'));
User Timing API

Resource Timing API

Resource Timing API は、リソースの取得にかかっている時間の統計情報を取得することができます。

サポートブラウザ
ie
IE
edge
Edge
firefox
Firefox
chrome
Chrome
safari
Safari
opera
Opera
safari
iOS Safari
android
Android
1228111511
○:Support ×:Not Support ?:未定義 n:以降の Version で Support

Navigation Timing API では、個別のリソースに対して計測することはできませんが、Resource Timing API ではリソース単位に計測することができます。

Resource Timing API のプロパティ一覧
プロパティ説明
nameリクエストされたリソースの URL。
entryTypeここでは resource の文字列が入ります。
startTime開始時点でのタイムスタンプ。
durationresponseEndstartTime との差分の時間。
initiatorTypeリソース取得タイプ(img, request, css など)。
redirectStartHTTP リダイレクトの開始時点。
redirectEndHTTP リダイレクトの終了時点。
fetchStartリソースの取得開始時点。
domainLookupStartドメイン名解決の開始時点。
domainLookupEndドメイン名解決の終了時点。
connectStartTCP 接続を開始する前の時点。
connectEnd接続の確立終了時点。
secureConnectionStartオプション。HTTPS 接続を開始する前の時点。
requestStartリクエストの送信の開始時点。
responseStartリソースの最初のバイトを受け取った直後の時間。
responseEndリソースの最後のバイトを受け取った直後の時間。

Resource Timing API で情報を取得するには、performance.getEntriesByType('resource') メソッドから PerformanceResourcesTiming オブジェクトの配列を得ます。

for (let resource of performance.getEntriesByType('resource')) {
  console.log(
    'Name: '       + resource.name      + '\n' +
    'Entry Type: ' + resource.entryType + '\n' +
    'Start Time: ' + resource.startTime + '\n' +
    'Duration: '   + resource.duration  + '\n' +
    'Redirect: '   + (resource.redirectEnd - resource.redirectStart)         + 'n' +
    'App Cache: '  + (resource.domainLookupStart - resource.fetchStart)      + 'n' +
    'DNS: '        + (resource.domainLookupEnd - resource.domainLookupStart) + 'n' +
    'TCP: '        + (resource.connectEnd - resource.connectStart)           + 'n' +
    'Request: '    + (resource.responseStart - resource.requestStart)        + 'n' +
    'Response: '   + (resource.responseEnd - resource.responseStart)         + 'n' +
    'Processing: ' + (resource.loadEventStart - resource.responseEnd)        + 'n'
  );
}
Resource Timing API
注意アイコン
performance.getEntriesByType('resource') メソッドの引数について
Resource Timing practical tips | High Performance Web Sites で書かれているように、performance.getEntries() でも performance.getEntriesByType('resource') と同様の結果が得られます。しかし、getEntries() は将来的に resourcenavigationmarkmeasure の4種類のオブジェクトを返すようになるので、getEntriesByType('resource') で取得するようにしてください。

Performance Observer

Performance Observer は、パフォーマンス情報を効率的に監視することができる API です。

サポートブラウザ
ie
IE
edge
Edge
firefox
Firefox
chrome
Chrome
safari
Safari
opera
Opera
safari
iOS Safari
android
Android
×575211391152
○:Support ×:Not Support ?:未定義 n:以降の Version で Support

Performance Observer 以外のパフォーマンス情報を取得する API は、過去のある時点と別のある時点の差異としてしかパフォーマンス情報を取得することしかできません。現在の Web ページでこれから起こる処理のパフォーマンス情報を指定して監視するには、Performance Observer を利用します。

var observer = new PerformanceObserver(function(list, obj) {
  var entries = list.getEntries();
  for (var i=0; i < entries.length; i++) {
    // Process "mark" and "frame" events
  }
});
observer.observe({entryTypes: ["mark", "frame"]});

function perf_observer(list, observer) {
  // Process the "measure" event 
}
var observer2 = new PerformanceObserver(perf_observer);
observer2.observe({entryTypes: ["measure"]});
Performance Observer

new PerformanceObserver() のコンストラクタに指定した関数が、パフォーマンス情報を得られたときに呼び出されます。PerformanceObserver オブジェクトの observe() メソッドを使って監視する項目を指定して監視を開始します。

observe() メソッドの entryTypes には、監視する項目を指定します。ここで指定できる文字列は、Performance.getEntriesByType() で指定できる値と同じです。

監視を中止するには disconnect() メソッドを呼び出します。

observer.disconnect();
Performance Observer

High Resolution Time

High Resolution Time は、高精度なタイムスタンプです。performance オブジェクトには、高精度なタイムスタンプの DOMHighResolutionTimeStamp 型を得るための now() メソッドがあります。performance.now() メソッドは、W3C の High Resolution Time で定義されています。

サポートブラウザ
ie
IE
edge
Edge
firefox
Firefox
chrome
Chrome
safari
Safari
opera
Opera
safari
iOS Safari
android
Android
101215248159
○:Support ×:Not Support ?:未定義 n:以降の Version で Support

performance.now() メソッドの使用方法は以下のとおりです。

var startTime = performance.now();

// 計測したい処理

var endTime = performance.now();

console.log(endTime - startTime);
High Resolution Time

performance.now() が返すタイムスタンプは、そのマシンのシステム時刻に依存しません。performance.now() が返すタイムスタンプの時間軸は、ドキュメントのナビゲーションの開始時点となります。

performance.now() が返すタイムスタンプの時間は、Navigation Timing API の performance.timing.navigationStart からの経過時間です。連続的に呼び出した場合、タイムスタンプが必ず増えることが保証されています。そのため、従来使用されてきた Date.now() に比べて信頼性が高いと言えます。

performance.now() が返すタイムスタンプの時間は、浮動小数点型で表現されます。整数部ではミリ秒、小数点以下はマイクロ秒単位の値を得ることができます。このような高精度なタイムスタンプは、アニメーションの FPS を計測する場合などミリ秒では精度が不足しているときに有効です。

Category:
パフォーマンス
公開日:
更新日:
Pageviews:
257
Shares:
9
Tag:
JavaScript