まくまくJavaScriptノート
精度の高いタイムスタンプを取得する (performance.now())
2018-03-07
パフォーマンス計測などの用途で経過時間を計りたいときは、Date.now() ではなく、performance.now() を使用すると、より精度の高い計測を行うことができます。

旧来の Date.now() を使用して得られるタイムスタンプは、ミリ秒単位の精度しかないため、より精度の高い計測を行いたい場合には使用できません。 W3C の High Resolution Time で定義されている Performance インタフェース を使用すると、マイクロ秒単位の経過時間を取得することができます。

[Exposed=(Window,Worker)]
interface Performance : EventTarget {
    DOMHighResTimeStamp now();
    readonly attribute DOMHighResTimeStamp timeOrigin;
    [Default] object toJSON();
};

performance.now() 関数は、そのコンテキストが生成された時点からの経過時間を返します(典型的には、ブラウザであるページを表示した時点からの経過時間です)。

console.log(performance.now())  //=> 12.499999997089617

整数部分がミリ秒、小数点以下がマイクロ秒を表しています。 上記の例では、ブラウザでページ表示を行った時点から、約 12.4999 ミリ秒経過していることを示しています。 ユーザエージェント(ブラウザ環境など)が対応していれば、最大 5 マイクロ秒の精度で情報を得られるとされています。 戻り値の型は、正確には DOMHighResTimeStamp 型として定義されていますが、doubletypedef であり、一般的な浮動小数点数型として使用すれば問題ありません。

典型的な使用例としては、下記のようなパフォーマンス計測があります。

var t0 = performance.now();
doSomething();
var t1 = performance.now();
console.log('doSomething took %f ms', (t1 - t0));

逆に、このような用途で Date.now() を使用すると、精度が悪いだけでなく、思わぬ結果を招くことがあります。 Date.now() は OS のシステムクロックに基いたタイムスタンプを返すため、数値が遡る(あとから呼び出した方の値が小さくなる)ことがあるためです。 performance.now() を使用しておけば、同一コンテキスト内においては単調増加 (monotonic increase) な値を返すことが保証されているため、安心してパフォーマンス計測などの用途に使用することができます。

単調増加と言っても、同一コンテキスト内の処理であればという条件はあります。 例えば、Worker の中から performance.now() を呼び出して得られる値は、Worker が作成された時間が基準になるため、メインコンテキストで performance.now() を呼び出して得られる値より小さくなります。
2018-03-07