Prompt

Date.now()でミリ秒単位の時間を取得しても、なんだかその値にはムラがあるよね?
完全にランダムなタイミングでDate.now()を実行しても、取得することができないミリ秒の周期的な穴がある。

あはは、そこに気づくとは!それはまさに現代のブラウザとCPUが抱える 「セキュリティとプライバシーの防壁」 という名のスイスチーズの穴を見つけちゃったね。

結論から言うと、そのムラや周期的な穴はバグではなく、「サイドチャネル攻撃(特に Spectre や Meltdown)」への対策として、ブラウザ側があえて導入した「時間分解能の制限(タイマーの粒度下げ)」 が原因だよ。

なぜ「正確すぎる時間」が危険なのか、その工学的な背景を解剖してみよう。


1. 犯人は「タイマーの粒度(Timer Resolution)」

本来、コンピュータの時計はナノ秒単位の精度を持っているけれど、JavaScript の Date.now()performance.now() は、意図的にその精度を落とされている(丸められている)んだ。

  • 昔: performance.now() は 5マイクロ秒(0.005ms)単位で取得できた。

  • 今: 多くのブラウザで 100マイクロ秒(0.1ms)〜 1ミリ秒単位 に制限されている。さらに、ブラウザによっては数ミリ秒のランダムな「ジッター(揺らぎ)」をわざと加えていることもあるよ。


2. なぜ「正確なミリ秒」が牙を向くのか?

「たかだかミリ秒で何ができるんだ」と思うかもしれないけれど、情報の「一意な漏洩」を防ぐために、この制限は必須なんだ。

  • Spectre 攻撃の恐怖:

    CPU の予測実行の仕組みを悪用して、他人のパスワードなどを盗み出す攻撃がある。このとき、メモリへのアクセス速度(ナノ秒単位の差)を極めて正確に測る必要があるんだ。

  • フィンガープリント:

    極微細な時間のズレを測ることで、ユーザーの CPU の個体差(個体識別)ができてしまう。これを防ぐために、ブラウザは「わざと時間を曖昧に」しているんだよ。


3. 「取得できない周期的な穴」の正体

キミが感じた「周期的な穴」は、おそらく OS のクロック割り込み(Clock Interrupt)ブラウザのメインループ(Event Loop) の周期と関係しているね。

  • 量子化誤差: タイマーが 1ms 単位でしか更新されない環境で、0.1ms ごとに Date.now() を叩いても、同じ値が 10回並んだ後に突然 1 増える、という階段状のデータになる。

  • ブラウザのサスペンド: タブがバックグラウンドに回ると、リソース節約のためにタイマーの更新頻度がさらに落とされる(例:1秒に1回など)。これが大きな「穴」として観測されることもあるよ。


4. もっと正確な時間を知るには?

もしキミが「正方形の敷き詰めアニメーションのフレームレートを正確に測りたい」なら、Date.now() は避けるべきだね。

  • performance.now() を使う:

    Date.now() よりも高精度で、システムの起動時からの経過時間を返す。ただし、これも前述のセキュリティ対策で丸められているけれど、Date よりは遥かに信頼できる。

  • requestAnimationFrame の引数を使う:

    ブラウザが画面を更新するタイミングの「一意なタイムスタンプ」を引数として受け取れる。描画ロジックにはこれが一番「正解」に近いよ。


見落とされがちな視点: SharedArrayBuffer の壁

実は、SharedArrayBuffer という機能を使うと、マルチスレッド(Web Workers)間でメモリを共有して「自作の高精度タイマー」が作れてしまうことがあった。

でも、これも Spectre 対策でデフォルトでは無効化されていたり、特定の HTTP ヘッダー(COOP/COEP)を設定しないと使えないようになっているんだ。ブラウザは「時間を測らせない」ことに必死なんだね。


結論

キミが観測した「周期的な穴」は、ブラウザがキミのプライバシーを守るためにわざと作った「情報の断絶」 なんだ。

  1. セキュリティ: ナノ秒単位の計測を禁止して攻撃を防ぐ。

  2. プライバシー: 個体識別を難しくする。

  3. 効率: メインループの負荷を下げる。

次は、performance.now() を使って、ブラウザごとのタイマーの粒度(何マイクロ秒ごとに値が変わるか)を実測して可視化するスクリプト」 を書いて、その「穴」の正体をグラフにしてみる?

Would you like to code a jitter-measuring script to visualize exactly how your browser is rounding the time values?