Help us understand the problem. What is going on with this article?

パフォーマンスまわりのAPIについて

More than 3 years have passed since last update.

Navigation Timing とか Resource Timing とか、パフォーマンスまわりのAPIについて自分で整理できていなかったので、これを機会にまとめてみました。

API

API 内容 サポート状況
Navigation Timing API ブラウザがウェブページを表示するのに要する時間の詳細に関するAPI http://caniuse.com/#feat=nav-timing
Resource Timing API ウェブページで読み込まれるリソースの読み込みに要する時間の詳細に関するAPI http://caniuse.com/#feat=resource-timing
High Resolution Time API マイクロ秒で経過時間を取得するAPI http://caniuse.com/#feat=high-resolution-time
User Timing API マイクロ秒の性能計測用API http://caniuse.com/#feat=user-timing

Navigation Timing API

performance.timingで以下のようなプロパティが取得できます。

capture1.png

取得される値は、UTC(1970年1月1日0時0分0秒)を起点としたミリ秒になります。

プロパティ 内容
navigationStart 前回のドキュメントのunloadが完了した直後の時間。前回のドキュメントが存在しない場合、"fetchStart"と同じ値になる。
unloadEventStart 前回のドキュメントのunloadイベントが発火する直前の時間
unloadEventEnd 前回のドキュメントのunloadイベントが発火した直後の時間
redirectStart リダイレクトが開始された時間
redirectEnd リダイレクトが存在する場合、リダイレクトのレスポンスの最後のバイトを受け取った直後の時間。
fetchStart ブラウザがURLを探し始める直前の時間。アプリケーションキャッシュのチェックやキャッシュされてないファイルをサーバに要求することも含んでいる。
domainLookupStart DNSルックアップが発生する直前の時間。DNSルックアップが不要な場合、"fetchStart"と同じ値になる。
domainLookupEnd DNSルックアップの発生直後の時間。DNSルックアップが不要な場合、"fetchStart"と同じ値になる。
connectStart ブラウザがサーバに接続する直前の時間。URLがキャッシュもしくはローカルのリソースの場合、"domainLookupEnd"と同じ値になる。
connectEnd サーバへの接続が確立した時間。URLがキャッシュもしくはローカルのリソースの場合、"domainLookupEnd"と同じ値になる。
secureConnectionStart HTTPSプロトコルが使われているケースで、セキュアに接続するためのハンドシェイク処理の開始する直前の時間
requestStart ブラウザがURLにリクエストを送信する直前の時間
responseStart ブラウザがレスポンスの受け取りを開始した直後の時間
responseEnd ブラウザがレスポンスを全て受け取った直後の時間
domLoading "document.readyState"の値が"loading"になる直前の時間
domInteractive "document.readyState"の値が"interactive"になる直前の時間
domContentLoadedEventStart DOMContentLoadedイベントの発火直前の時間
domContentLoadedEventEnd DOMContentLoadedイベントの発火直後の時間
domComplete "document.readyState"の値が"complete"になる直前の時間
loadEventStart windowのloadイベント発火直前の時間
loadEventEnd windowのloadイベント発火直後の時間

window.onloadイベント中では、loadEventEndの値は常に0が返されるので、
javascript - Why does my attempt at measuring render time with Web Performance API keep resulting in negative values - Stack Overflowのように
setTimeout()で遅延させることで、正常に値を取得できます。

Resource Timing API

performance.getEntriesByType('resource')で、全リソースのPerformanceResourceTimingオブジェクトの配列が取得できます。
PerformanceResourceTimingオブジェクトには以下のようなプロパティがあり、Navigation Timing APIとは違い、ナビゲーション開始時を起点とするミリ秒での値となります。

capture2.png

プロパティ 内容
name リクエストされたリソースのURL
entryType resource
startTime ダウンロードするリソースをキューに溜め込み始める直前の時間
duration responseEndとstartTimeとの差分の時間
initiatorType リソース取得タイプ
redirectStart HTTPリダイレクトが発生している場合、リダイレクトのダウンロード開始の時間
redirectEnd HTTPリダイレクトが発生している場合、最後のリダイレクトの最後のバイトを受け取った直後の時間
fetchStart HTTPリダイレクトが発生しない場合、リソースのダウンロードを開始する直前の時間
domainLookupStart DNSルックアップが発生する直前の時間。DNSルックアップが不要な場合、"fetchStart"と同じ値になる。
domainLookupEnd DNSルックアップの発生直後の時間。DNSルックアップが不要な場合、"fetchStart"と同じ値になる。
connectStart サーバとの接続が確立される直前の時間
connectEnd サーバとの接続が確立された直後の時間
secureConnectionStart オプション。HTTPSプロトコルが使われているケースで、セキュアに接続するためのハンドシェイク処理の開始する直前の時間
requestStart サーバから、もしくはアプリケーションキャッシュかローカルリソースからのリクエストを開始する直前の時間
responseStart リソースの最初のバイトを受け取った直後の時間
responseEnd リソースの最後のバイトを受け取った直後の時間

Resource Timing practical tips | High Performance Web Sitesで書かれているように、
performance.getEntries()でもperformance.getntriesByType('resource')と同様の結果が得られるブラウザが多いけれども、getEntries()は将来的にresourcenavigationmarkmeasureの4種類のオブジェクトを返すようになるので、getEntriesByType('resource')で取得するようにしておいた方がいいようです。

また、生成元が異なるリソース(cross-origin)の場合、セキュリティ上の理由で詳細な値を得ることができないのでdurationの値がリソース取得にかかる時間を計測する値になりますが、SERIOUS CONFUSION with Resource Timing | High Performance Web Sitesによると、durationはブロッキングの時間も含んでいるので、正確なリソース取得に要した時間ではないようです。
つまり、ブラウザのホスト名での同時接続数制限によって、ダウンロード開始を順番待ちしている時間もdurationの時間に含まれているので、順番待ちをしているリソースほどdurationの値が極端に増加していきます。
この点に関して、Steve Souders氏は”networkDuration”の追加を提案しているので、今後どうなっていくか注目したいです。

High Resolution Time API

High Resolution Time APIはナビゲーション開始時を起点として、マイクロ秒で値を取得できるnow()メソッドのみを提供します。

Date.now()で取得したミリ秒の値を比較していた部分を、performance.now()に置き換えることでマイクロ秒での比較が可能になります。
より細かい粒度での測定が可能になることで、ミリ秒の精度では十分でなかったアニメーションのパフォーマンス測定などで有用らしいです。

function hoge () {
    var list = [];
    for (var i = 0, max = 100000; i < max; i++) {
        list.push(i * i);
    }
    console.log(list);
}

var start = Date.now();
var startHR = performance.now();

hoge();

var end = Date.now();
var endHR = performance.now();

console.log(end - start);// -> 658
console.log(endHR - startHR);// -> 657.6000000000022

加えて、performance.now()はナビゲーション開始時から常に一定の間隔で単調に値を増やすので、15分から20分間隔で数ミリ秒の調整をおこなっているUNIX時間Date.now()より高精度の測定ができそうです。

User Timing API

User Timing APIperformanceオブジェクトで以下の4つのメソッドを提供しています。

メソッド 内容
mark(name) name に紐付いたDOMHighResTimeStamp を保持する
clearMarks([name]) 保持しているMarkを削除する
measure(name[, mark1[, mark2]]) 2つのMark間の経過時間を保持する
clearMeasures([name]) 保持しているMeasureを削除する

High Resolution Time APIと同様にマイクロ秒で結果が得られます。
測定したい箇所に任意の名前でmarkをつけて、複数のmarkの間の経過時間をmeasureでとります。

performance.mark('hoge-start');

var list = [];

for (var i = 0, max = 100; i < max; i++) {
    list.push(i * i);
}

performance.mark('hoge-end');

performance.measure('hoge', 'hoge-start', 'hoge-end');

console.log(performance.getEntriesByType('mark'));
console.log(performance.getEntriesByType('measure'));

上記のようなコードで、以下のような結果がコンソールに出力されます。

capture3.png


今回このアドベントカレンダーを機会に、これらのAPIについて多少は整理がついたので、今後積極的に活用していきたいと思います。

Links

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away