JavaScriptでのマイクロ秒のタイミング


99

JavaScriptにマイクロ秒の解像度のタイミング関数はありますか?

私はChrome のtimer.jsを知っており、Firefox、Safari、Opera、Epiphany、Konquerorなど、他のフレンドリーなブラウザー向けのソリューションがあることを期待しています。IEのサポートには興味はありませんが IE を含めて回答します歓迎します。

(JSのミリ秒のタイミングの精度が悪いことを考えると、私はこれに息を止めていません!)

更新:timer.jsはマイクロ秒の解像度をアドバタイズしますが、ミリ秒の読み取り値に1,000を乗算するだけです。テストとコードインスペクションによって検証されています。がっかり。:[


2
マイクロ秒の精度が必要なブラウザで何をしようとしていますか?一般に、ブラウザの動作のパフォーマンス保証はそれほど正確ではありません。
Yuliy

4
起こりません。マイクロ秒の精度が存在していても、まったく信頼できません。私が想像できる唯一の確実な使用例は、クロムのネイティブクライアントですが、それではJS APIは気にしません。「エピファニー」をファーストクラスのブラウザとして扱い、IEを無視することも大好きです。
Raynos

6
JavaScriptで時間を「取得」すると、それを返す場合と同様に時間がかかります。また、イベントを再描画または処理しているWebページにいる場合は、レイテンシが増加します。私は10ミリ秒単位の精度さえ期待していません。
ケネベック2011年

1
たとえば、超高速でポップアップをスローするのですか?基本的に問題は、人がウェブサイトにアクセスするという事実を単に指摘するだけで、外部の当事者にユーザーのマシンへのアクセスを許可しすぎることは深刻な問題であるということです。
とがった

1
これは、問題が基本的に同等であるほど高速であるsetInterval(popup、0)よりも「脆弱」ではありません。ミリ秒の精度も削除する必要がありますか?ケネベック:あなたのコメントは理にかなっています、ありがとう。
mwcz

回答:


134

Mark Rejhonの回答で触れたように、最近のブラウザーでは、サブミリ秒の解像度のタイミングデータをスクリプトに公開するAPIとして、W3C High Resolution Timerがありwindow.performance.now()ます。

now()Date.getTime()2つの重要な点で従来のものより優れています。

  1. now()ページのナビゲーションが開始されてからのミリ秒数を表す、サブミリ秒の解像度を持つdoubleです。マイクロ秒数を小数で返します(たとえば、1000.123の値は1秒で123マイクロ秒です)。

  2. now()単調増加しています。これは、後続の呼び出しで前方または後方にジャンプDate.getTime()する可能性があるため重要です。特に、OSのシステム時刻が更新された場合(原子時計の同期など)Date.getTime()も更新されます。 now()は常に単調に増加することが保証されているため、OSのシステム時間の影響を受けません。常に壁時計時間になります(壁時計がアトミックでないと仮定すると...)。

now()ほぼすべての場所で使用することができるnew Date.getTime()+ new DateDate.now()しています。例外は、Unixエポック(1970年からのミリ秒数)に基づいているようにDatenow()時刻が混在しないことですが、ページナビゲーションが開始されてからのミリ秒数です(したがって、ははるかに小さくなります)。Datenow()Date

now()Chromeの安定版、Firefox 15以降、IE10でサポートされています。いくつかのポリフィルも利用できます。


1
ポリフィルはおそらくDate.now()を使用するため、IE9と数百万のユーザーを考慮した場合でもこれは最良のオプションです。サードパーティのライブラリを混在させる理由
Vitaliy Terziev

4
私の掛け時計原子です。
programmer5000

4
new Date.getTime()事ではありません。new Date().getTime()です。
Qodesmith、2017年

私は本当にこの反応が好きでした。私はいくつかのテストを実行し、コンソールにドロップして、これを使用するときに依然として劇的な衝突があることを確認できる例を思い付きました。(私ものような高価なような何かをやって良いマシン上の10%の衝突を得ていた注意しconsole.logて行うために、ハード実行ごとに)が、ここで、すべてのハイライトされたコードをコピー:last=-11; same=0; runs=100; for(let i=0;i<runs;i++) { let now = performance.now(); console.log('.'); if (now === last) { same++; } last = now; } console.log(same, 'were the same');
bladnman

2
私の2012年のコメントを再訪します。Performance.now()は、Meltdown / Spectreの回避策により、ややあいまいになりました。一部のブラウザでは、セキュリティ上の理由により、performance.now()が大幅に低下しています。私のテクニックは、タイマーファズの制限に従い、多くの正当なベンチマークユースケースの多くに再び関連性を取り戻したと思います。一部のブラウザは、現在、2012年には存在しなかったいくつかの開発者のパフォーマンスプロファイリング機能/拡張あり、言った
マーク・Rejhon

20

JavaScriptでマイクロ秒を測定する新しい方法があります:http : //gent.ilcore.com/2012/06/better-timer-for-javascript.html

ただし、過去に、JavaScriptでミリ秒タイマーから0.1ミリ秒の精度を取得する大まかな方法​​を見つけました。不可能な?いいえ。読み続けます:

セルフチェックのタイマー精度を必要とする高精度の実験を行っていますが、特定のシステムの特定のブラウザーで0.1ミリ秒の精度を確実に得ることができました。

高速システムの最新のGPU高速化Webブラウザー(たとえば、i7クアッドコア、いくつかのコアがアイドル状態、ブラウザーウィンドウのみ)で、タイマーがミリ秒精度であることが信頼できることがわかりました。実際、アイドル状態のi7システムでは非常に正確になり、1,000回を超える試行で正確に同じミリ秒を確実に取得できました。余分なWebページの読み込みなどを実行しようとする場合にのみ、ミリ秒の精度が低下します(前後の時間チェックを実行して、自分自身の低下した精度を正常にキャッチできます。処理時間が突然1ミリ秒以上に長くなりました-これは、CPUの変動による悪影響が多すぎると思われる結果を無効にするのに役立ちます)。

i7クアッドコアシステムの一部のGPUアクセラレートブラウザー(ブラウザーウィンドウが唯一のウィンドウの場合)で非常に正確になり、JavaScriptで0.1ミリ秒の精度タイマーにアクセスできればいいのにと思った。一部のハイエンドブラウジングシステムでは、高精度を必要とする特定の種類のニッチアプリケーションに対してタイマーの精度を価値のあるものにし、アプリケーションが精度の偏差を自己検証できるようにします。

複数のパスを実行している場合は、明らかに複数のパス(例:10パス)を実行し、10で除算して0.1ミリ秒の精度を得ることができます。これは、精度を上げる一般的な方法です。複数のパスを実行し、合計時間をパスの数で割ります。

しかし、非常にユニークな状況のために特定のテストのベンチマークパスを1つしか実行できない場合、これを実行することで0.1(場合によっては0.01ミリ秒)の精度を取得できることがわかりました。

初期化/キャリブレーション:

  1. タイマーが次のミリ秒に増加するまで待機するビジーループを実行します(タイマーを次のミリ秒間隔の開始に合わせます)このビジーループは1ミリ秒未満続きます。
  2. タイマーがインクリメントするのを待っている間に、別のビジーループを実行してカウンターをインクリメントします。カウンターは、1ミリ秒に発生したカウンター増分の数を示します。このビジーループは1ミリ秒続きます。
  3. 数値が非常に安定するまで(読み込み時間、JITコンパイラなど)、上記を繰り返します。4. 注意:数値の安定性により、アイドルシステムで達成可能な精度が得られます。精度をセルフチェックする必要がある場合は、分散を計算できます。一部のブラウザでは差異が大きく、他のブラウザでは差異が小さくなっています。より速いシステムではより大きく、より遅いシステムではより遅くなります。一貫性も異なります。どのブラウザが他のブラウザよりも一貫性/正確性が高いかがわかります。システムが低速でビジーなシステムでは、初期化パス間の差異が大きくなります。これにより、ブラウザが0.1msまたは0.01msの測定を可能にするのに十分な精度を提供していない場合、警告メッセージを表示する機会が与えられます。タイマースキューは問題になる可能性がありますが、一部のシステムの一部の整数ミリ秒タイマーは非常に正確に増分します(ドットのすぐ上)。これにより、信頼できる非常に一貫したキャリブレーション値が得られます。
  4. 最終カウンター値(または最後の数回のキャリブレーションパスの平均)を保存します

1ミリ秒未満の精度で1つのパスをベンチマークします。

  1. タイマーが次のミリ秒に増分するまで待機するために、ビジーループを実行します(タイマーを次のミリ秒間隔の開始に合わせます)。このビジーループは1ミリ秒未満続きます。
  2. 時間を正確にベンチマークするタスクを実行します。
  3. タイマーを確認してください。これにより整数ミリ秒が得られます。
  4. 最後のビジーループを実行して、タイマーのインクリメントを待機しながらカウンターをインクリメントします。このビジーループは1ミリ秒未満続きます。
  5. このカウンター値を、初期化からの元のカウンター値で除算します。
  6. これで、ミリ秒の小数部分を取得しました!!!!!!!!

警告:ビジーループはWebブラウザーでは推奨されませんが、幸い、これらのビジーループはそれぞれ1ミリ秒未満で実行され、ごくわずかしか実行されません。

JITコンパイルやCPUの変動などの変数は非常に不正確になりますが、いくつかの初期化パスを実行すると、完全な動的再コンパイルが行われ、最終的にカウンターは非常に正確なものに落ち着きます。すべてのビジーループがすべてのケースでまったく同じ機能であることを確認し、ビジーループの違いが違いにつながらないようにします。結果を信頼し始める前に、すべてのコード行が数回実行されていることを確認してください。これにより、JITコンパイラーは完全な動的再コンパイル(dynarec)にすでに安定しています。

実際、特定のシステムでは精度がマイクロ秒に近づくのを見ましたが、まだ信頼できません。しかし、0.1ミリ秒の精度は、私が唯一のブラウザーページであるアイドル状態のクアッドコアシステムで、非常に確実に機能するようです。私は(一意の変数が発生したため)1回限りのパスしか実行できず、複数の繰り返しパスを平均化するのではなく、各パスの正確な時間を計測する必要がある科学的テストケースになりました。そのため、これを行いました。

いくつかのプレパスとダミーパス(ダイナレックを安定させるため)を実行し、0.1msの精度(数秒間安定した状態)の信頼性を確認してから、ベンチマークが発生している間、キーボード/マウスから手を離しました。 0.1ms精度の信頼性を検証するためのポストパス(再び安定した状態)。これはまた、電源状態の変更やその他の事柄が、前と後の間に発生せず、結果に干渉しなかったことを確認します。すべてのベンチマークパス間で事前テストと事後テストを繰り返します。これに関して、私はその間の結果が正確であることを事実上確信していた。もちろん、保証はありませんが、場合によってはWebブラウザーで0.1ms未満の正確な精度が可能であることを示しています。

この方法は、非常にニッチなケースでのみ役立ちます。それでも、文字通り100%無限に保証されるわけではありません。内部および外部検証の複数のレイヤーと組み合わせると、非常に信頼できる正確さ、さらには科学的正確さを得ることができます。


3
それは私たちが持っていたすべてだったので、優れた精度でタイミングを行うために複雑になるために使用されますDate.now()+new Date()。しかし今、私たちは持っていperformance.now()ます。あなたがより多くの機能をハックするいくつかのクールな方法を見つけたのは明らかですが、この答えは本質的に時代遅れです。また、ビジーループに関連するものはお勧めしません。それをしないでください。それ以上は必要ありません。
Steven Lu

1
ほとんどのブラウザは、パフォーマンスの低下を抑え、パフォーマンスの一時的な低下を一時的に軽減します。この答えがセキュリティ研究でまだ重要であるかどうか疑問に思います。
Qi Fan

2
私自身のコメントを再訪します。うわー、私はperformance.now()よりずっと前の2012年に上記を投稿しました。しかし、今度は、メルトダウン/スペクターの回避策によって、少しあいまいになりました。一部のブラウザでは、セキュリティ上の理由により、performance.now()が大幅に低下しています。上記の手法は、タイマーファズの制限に従い、多くの正当なベンチマークユースケースの関連性を再び取り戻したと考えられます。
Mark Rejhon、

3

答えは、一般的には「いいえ」です。一部のサーバー側環境(つまり、ブラウザーではない)でJavaScriptを使用している場合は、すべての賭けがオフになっているので、必要なことをすべて実行できます。

編集 —この答えは古いです。標準は進歩し、正確な時間の問題に対する解決策として、新しい施設が利用可能になりました。それでも、真のリアルタイムオペレーティングシステムのドメインの外では、通常の非特権コードでは、計算リソースへのアクセスに対する制御が制限されていることに注意してください。パフォーマンスの測定は、パフォーマンスの予測と(必ずしも)同じではありません。


2

node.jsの高解像度タイマーを示す例を次に示します。

 function startTimer() {
   const time = process.hrtime();
   return time;
 }

 function endTimer(time) {
   function roundTo(decimalPlaces, numberToRound) {
     return +(Math.round(numberToRound + `e+${decimalPlaces}`)  + `e-${decimalPlaces}`);
   }
   const diff = process.hrtime(time);
   const NS_PER_SEC = 1e9;
   const result = (diff[0] * NS_PER_SEC + diff[1]); // Result in Nanoseconds
   const elapsed = result * 0.0000010;
   return roundTo(6, elapsed); // Result in milliseconds
 }

使用法:

 const start = startTimer();

 console.log('test');

 console.log(`Time since start: ${endTimer(start)} ms`);

通常、以下を使用できる場合があります。

 console.time('Time since start');

 console.log('test');

 console.timeEnd('Time since start');

ループを含むコードのセクションのタイミングを計っている場合console.timeEnd()、タイマーの結果を一緒に追加するためにの値にアクセスすることはできません。可能ですが、のような反復変数の値を注入しi、ループが完了したかどうかを検出するための条件を設定する必要があるため、厄介になります。

これは便利な例です。

 const num = 10;

 console.time(`Time til ${num}`);

 for (let i = 0; i < num; i++) {
   console.log('test');
   if ((i+1) === num) { console.timeEnd(`Time til ${num}`); }
   console.log('...additional steps');
 }

引用:https : //nodejs.org/api/process.html#process_process_hrtime_time

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.