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 | Edge | Firefox | Chrome | Safari | Opera | iOS Safari | Android |
---|---|---|---|---|---|---|---|
9 | ◯ | 7 | 6.0 | 8 | 15 | 8 | 4.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
オブジェクトからは、各段階の処理時間のタイムスタンプが得られます。これらのプロパティには、アクションが完了したときのミリ秒単位のタイムスタンプが格納されています。
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 を利用する場合、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));
window.performance.navigation
オブジェクトは、プロパティに type
と redirectCount
を持ちます。
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 | Edge | Firefox | Chrome | Safari | Opera | iOS Safari | Android |
---|---|---|---|---|---|---|---|
10 | 12 | 41 | 28 | 11 | 33 | 11 | ◯ |
◯:Support ✕:Not Support ?:未定義 n:以降の Version で Support |
User Timing API でパフォーマンスを計測するには、performance.mark()
メソッドを用いて、処理の計測地点を宣言します。
performance.mark('test-start');
// 計測したい処理
performance.mark('test-end');
上記の例では、開始地点に test-start
、終了地点に test-end
の引数を渡して宣言しています。この2つの計測地点を測定するためには performance.measure()
メソッドを用います。
performance.mark('test-start');
// 計測したい処理
performance.mark('test-end');
performance.measure('test', 'test-start', 'test-end');
performance.measure()
メソッドの第一引数には test
という文字列を渡していますが、これは test-start
と test-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'));
Resource Timing API
Resource Timing API は、リソースの取得にかかっている時間の統計情報を取得することができます。
IE | Edge | Firefox | Chrome | Safari | Opera | iOS Safari | Android |
---|---|---|---|---|---|---|---|
◯ | 12 | ◯ | 28 | 11 | 15 | 11 | ◯ |
◯:Support ✕:Not Support ?:未定義 n:以降の Version で Support |
Navigation Timing API では、個別のリソースに対して計測することはできませんが、Resource Timing API ではリソース単位に計測することができます。
プロパティ | 説明 |
---|---|
name | リクエストされたリソースの URL。 |
entryType | ここでは resource の文字列が入ります。 |
startTime | 開始時点でのタイムスタンプ。 |
duration | responseEnd と startTime との差分の時間。 |
initiatorType | リソース取得タイプ(img, request, css など)。 |
redirectStart | HTTP リダイレクトの開始時点。 |
redirectEnd | HTTP リダイレクトの終了時点。 |
fetchStart | リソースの取得開始時点。 |
domainLookupStart | ドメイン名解決の開始時点。 |
domainLookupEnd | ドメイン名解決の終了時点。 |
connectStart | TCP 接続を開始する前の時点。 |
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'
);
}
performance.getEntriesByType('resource')
メソッドの引数についてperformance.getEntries()
でも performance.getEntriesByType('resource')
と同様の結果が得られます。しかし、getEntries()
は将来的に resource
、navigation
、mark
、measure
の4種類のオブジェクトを返すようになるので、getEntriesByType('resource')
で取得するようにしてください。Performance Observer
Performance Observer は、パフォーマンス情報を効率的に監視することができる API です。
IE | Edge | Firefox | Chrome | Safari | Opera | iOS Safari | Android |
---|---|---|---|---|---|---|---|
✕ | ? | 57 | 52 | 11 | 39 | 11 | 52 |
◯: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"]});
new PerformanceObserver()
のコンストラクタに指定した関数が、パフォーマンス情報を得られたときに呼び出されます。PerformanceObserver
オブジェクトの observe()
メソッドを使って監視する項目を指定して監視を開始します。
observe()
メソッドの entryTypes
には、監視する項目を指定します。ここで指定できる文字列は、Performance.getEntriesByType()
で指定できる値と同じです。
監視を中止するには disconnect()
メソッドを呼び出します。
observer.disconnect();
High Resolution Time
High Resolution Time は、高精度なタイムスタンプです。performance オブジェクトには、高精度なタイムスタンプの DOMHighResolutionTimeStamp 型を得るための now()
メソッドがあります。performance.now()
メソッドは、W3C の High Resolution Time で定義されています。
IE | Edge | Firefox | Chrome | Safari | Opera | iOS Safari | Android |
---|---|---|---|---|---|---|---|
10 | 12 | 15 | 24 | 8 | 15 | 9 | ◯ |
◯:Support ✕:Not Support ?:未定義 n:以降の Version で Support |
performance.now()
メソッドの使用方法は以下のとおりです。
var startTime = performance.now();
// 計測したい処理
var endTime = performance.now();
console.log(endTime - startTime);
performance.now()
が返すタイムスタンプは、そのマシンのシステム時刻に依存しません。performance.now()
が返すタイムスタンプの時間軸は、ドキュメントのナビゲーションの開始時点となります。
performance.now()
が返すタイムスタンプの時間は、Navigation Timing API の performance.timing.navigationStart
からの経過時間です。連続的に呼び出した場合、タイムスタンプが必ず増えることが保証されています。そのため、従来使用されてきた Date.now()
に比べて信頼性が高いと言えます。
performance.now()
が返すタイムスタンプの時間は、浮動小数点型で表現されます。整数部ではミリ秒、小数点以下はマイクロ秒単位の値を得ることができます。このような高精度なタイムスタンプは、アニメーションの FPS を計測する場合などミリ秒では精度が不足しているときに有効です。