高精度计时

Performance API 允许进行基于时间的、可能具有亚毫秒级分辨率的高精度测量,并且具有稳定的单调时钟,不受系统时钟偏差或调整的影响。为了进行精确的基准测试,需要高分辨率计时器,而不是精度较低且非单调的 Date 时间戳。

本文档概述了 Performance API 中的高精度时间工作原理,以及它与 Date 时间戳的对比。

DOMHighResTimeStamp

高精度计时是通过使用 DOMHighResTimeStamp 类型来表示时间值实现的。单位是毫秒,应精确到 5 微秒 (µs)。然而,如果浏览器无法提供精确到 5 微秒的时间值,则可以将该值表示为精确到毫秒的时间。这可能是由于硬件/软件限制、安全和隐私原因造成的。有关更多信息,请参阅下面的 降低精度 部分。

Performance API 中的所有时间戳都使用 DOMHighResTimeStamp 类型。以前,Performance API(以及其他 Web API)使用的是 EpochTimeStamp 类型(以前称为 DOMTimeStamp)。现在不推荐使用这些类型。

Performance.now() vs. Date.now()

JavaScript 将 Date.now() 定义为自 纪元 以来的毫秒数,纪元定义为 1970 年 1 月 1 日午夜(UTC)。另一方面,performance.now() 方法是相对于 Performance.timeOrigin 属性的。有关更多信息,请参阅下面的 时间起点 部分。

JavaScript Date 时间会受到系统时钟偏差或调整的影响。这意味着时间值可能不总是单调递增的。Date 对象的主要目的是向用户显示时间和日期信息,因此许多操作系统会运行一个定期同步时间的守护进程。时钟可能每小时被调整几次,每次几毫秒。

performance.now() 方法(以及所有其他 DOMHighResTimeStamp 值)提供单调递增的时间值,不受时钟调整的影响。这意味着 DOMHighResTimeStamp 值保证至少等于您上次访问它时的时间,甚至大于它。

js
Date.now(); // 1678889977578
performance.now(); // 233936

对于性能测量、精确计算帧率 (FPS)、动画循环等,请使用 Performance.now() 提供的单调递增的高分辨率时间,而不是 JavaScript 的 Date.now()

总结

- Performance.now() Date.now()
分辨率 亚毫秒 milliseconds
Origin Performance.timeOrigin Unix 纪元(1970 年 1 月 1 日 UTC)
使用时钟调整
单调递增

时间起点

Performance API 使用 Performance.timeOrigin 属性来确定与性能相关的计时器的基线。所有 DOMHighResTimeStamp 时间都相对于 timeOrigin 属性。

在 Window 上下文中,此时间起点是导航开始的时间。在 WorkerServiceWorker 上下文中,时间起点是 worker 运行的时间。

在以前的规范版本(Level 1)中,performance.now() 方法过去是相对于导航计时规范中的 performance.timing.navigationStart 属性的。然而,在后来的规范版本(Level 2)中,这一点发生了变化,performance.now() 现在相对于 Performance.timeOrigin,这避免了跨网页比较时间戳时的时钟更改风险。

js
// Level 1 (clock change risks)
currentTime = performance.timing.navigationStart + performance.now();

// Level 2 (no clock change risks)
currentTime = performance.timeOrigin + performance.now();

同步不同上下文的时间起点

为了考虑窗口和 worker 上下文中不同的时间起点,您应该通过 timeOrigin 属性来转换来自 worker 脚本的时间戳,以便为整个应用程序同步计时。有关同步时间的示例代码,请参阅 Performance.timeOrigin 页面的示例部分。

降低精度

为了提供针对时序攻击和 指纹识别 的保护,DOMHighResTimeStamp 类型会根据站点隔离状态进行粗化。

  • 隔离上下文中的分辨率:5 微秒
  • 非隔离上下文中的分辨率:100 微秒

要为您的站点应用跨源隔离,请使用 Cross-Origin-Opener-Policy (COOP) 和 Cross-Origin-Embedder-Policy (COEP) 标头。

http
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

这些标头确保顶级文档不与跨源文档共享浏览上下文组。 Cross-Origin-Opener-Policy 对您的文档进行进程隔离,潜在攻击者无法访问您的全局对象(如果他们在弹出窗口中打开它),从而防止了一系列称为 XS-Leaks 的跨源攻击。

另见