Web パフォーマンス: ミリ秒の精度では十分でない場合

時間を測定するとき、ミリ秒単位の精度では正確性に欠ける場合があります。この問題を解消するため、W3C Web パフォーマンス ワーキング グループでは、業界を先導する組織やコミュニティのリーダーたちと連携して High Resolution Time (英語) 仕様の標準化に取り組んできました。今週、この仕様が勧告案 (PR: Proposed Recommendation) (英語) として公開されました。この API の動作は、Test Drive デモ What Time is it? (英語) でご覧いただけます。

この仕様は、単なるアイデアから 8 か月という短期間で PR に達しました。PR は標準化の最終段階であり、Web 標準が正式な W3C 勧告として公開されるまであと 1 段階を残すだけとなります。また、このインターフェイスは、Internet Explorer 10 と Firefox 15 で完全にサポートされているほか、Chrome 22 ではプレフィックスによってサポートされるなど、既にさまざまなブラウザーに幅広く採用されています。これは、業界とコミュニティが W3C を通じて協調するとどんなことができるかを示す格好の例と言えます。

さて、ミリ秒の精度ではどうして不十分なのでしょうか。長い間 Web プラットフォームでの時間測定には、JavaScript の Date オブジェクトが何らかの形で使われてきました。つまり、Date.now() メソッドや DOMTimeStamp 型を使う方法です。Date オブジェクトは、1970 年 1 月 1 日 (UTC) からの時刻値をミリ秒単位で表します。これは 1970 年 1 月 1 日 (UTC) を基準とする前後 285,616 年間の任意の瞬間を表すことができ、現実的なほとんどの用途には十分な精度でした。

たとえば、このブログの執筆中に IE 10 開発者ツールのコンソールで Date.now() の時刻値を取得したところ、1350509874902 となりました。この 13 桁の数値は、時間軸の起点である 1970 年 1 月 1 日からのミリ秒数を表し、2012 年 10 月 17 日 21:37:54 (UTC) に相当します。

この精度は、現在のカレンダー時刻を取得する分にはそのままで何の問題もありませんが、用途によっては不十分となる場合があります。たとえば、開発中には、アニメーションが 1 秒あたり 60 フレーム (16.667 ミリ秒ごとに 1 フレーム描画) でスムーズに動作しているかどうかの判定が必要になることがあります。しかし、最後にフレーム描画のコールバックが行われた時間を測定するという単純な方法で特定の瞬間の FPS を算出しても、FPS が 58.8 FPS (1/17) か 62.5 FPS (1/16) かを区別することしかできません。

同様に、経過時間を正確に測定する場合 (たとえば、Navigation Timing、Resource Timing、User Timing の API を使ってネットワークやスクリプトのタイミングをインストルメント化する場合)、アニメーション シーンの同期を試みる場合、またはオーディオとアニメーションとの同期を試みる場合にも、ミリ秒以下の精度が求められます。

この問題を解決するため、High Resolution Time 仕様では、少なくともマイクロ秒 (ミリ秒の 1/1000) の精度を持つ新しい時間基準が定義されています。新しい時間基準では、1970 年 1 月 1 日 (UTC) からの時間を測定するのではなく、ドキュメントのナビゲーション開始時点である performance.timing.navigationStart からの時間を測定します。これにより、時間数の表現に使われるビット数が減ると共に、可読性が高まります。

この仕様で定義される performance.now() は Date.now() とよく似たメソッドですが、現在の時刻を高精度で返します。また、DOMTimeStamp と類似する型として、高精度の時刻値を定義する DOMHighResTimeStamp が用意されています。

例として、このブログの執筆中にもう一度 IE10 開発ツールのコンソールで現在の時刻を調べてみました。performance.now() と Date.now() を使ったところ、次の 2 つの値が返されました。

 performance.now():        196.304879519774
Date.now():        1350509874902

どちらの時刻値も同じ瞬間を表していますが、測定の基準となる時点が異なります。performance.now() の時刻値の方がわかりやすいことに異論はないでしょう。

High Resolution Time ではドキュメントのナビゲーション開始時点が基準となるため、サブドキュメントで performance.now() を呼び出すと、ルート ドキュメントではなく、そのサブドキュメントのナビゲーション開始時点からの測定値が返されます。たとえば、1 つのドキュメントに、同一オリジンの iframe A と、クロスオリジンの iframe B が含まれている場合を考えてみてください。iframe A のナビゲーションは、ルート ドキュメントのナビゲーション開始から約 5 ミリ秒後に発生します。一方、iframe B のナビゲーションは、ルート ドキュメントのナビゲーション開始から約 10 ミリ秒後に発生します。ルート ドキュメントのナビゲーション開始から正確に 15 ミリ秒後に時間を取得したとすると、それぞれのコンテキストでは、performance.now() の呼び出しから次の値が返されることになります。

iframe B での performance.now():                               5.123 ミリ秒

iframe A での performance.now():                              10.123 ミリ秒

ルート ドキュメントでの performance.now():                    15.123 ミリ秒

すべてのコンテキストでの Date.now():                         134639846051 ミリ秒

Date.now() による測定と performance.now() の違いを示す図
図: Date.now() は 1970 年 1 月 1 日からの測定時間を表すのに対し、performance.now() はドキュメントのナビゲーション開始時点からの測定時間を表す

このような仕様により、クロスオリジン iframe の親ページの作成時刻にかかわるデータ損失を避けることができるだけでなく、自身の開始時点を基準とした時間測定が可能になります。たとえば、サーバーがリソース要求に応答するまでの時間をサブドキュメント内で取得する場合、High Resolution Time を使う Resource Time インターフェイスなら、ルート ドキュメントにサブドキュメントが追加されるまでの時間を考慮に入れて調整する必要がないということです。

フレーム間の時刻を比較する必要がある場合は、top.performance.now() を呼び出すだけで、ルート ドキュメントのナビゲーション開始時点を基準とした時刻値を取得できます。この戻り値は、同一オリジンであればどの iframe でも同じ値になります。

この API を Date.now() と比較した場合のもう 1 つの重要なメリットとして、performance.now() は単調に増加するため、時計のずれや補正の影響を受けないという点があります。performance.now() を連続で呼び出したとき、その間隔がマイナス値になることはありません。Date.now() にはこのような保証はなく、実際問題として、解析データにマイナスの時間が現れるという報告を耳にしたこともあります。

High Resolution Time は、新しいアイデアが驚くべき速さで相互運用可能な標準になり得るという好例の 1 つです。開発者は、HTML5 に対応する最新のブラウザーで、この標準に基づいた開発を行うことができます。この API の設計にかかわった W3C Web パフォーマンス ワーキング グループのすべてのメンバー、そして相互運用性の重要性を理解し、この API をすばやく実装したブラウザー ベンダーの皆さんに感謝いたします。

お読みいただき、ありがとうございました。
Jatinder Mann
Internet Explorer プログラム マネージャー