【问题标题】:How to get microsecond timings in JavaScript since Spectre and Meltdown自 Spectre 和 Meltdown 以来如何在 JavaScript 中获得微秒计时
【发布时间】:2018-10-11 13:17:27
【问题描述】:

情况

在编写高性能 JavaScript 代码时,Chrome 等人提供的标准分析工具并不总是足够的。它们似乎只提供功能级别的粒度,深入挖掘并找到我需要的信息可能非常耗时。

在 .NET 中,StopWatch 类正是我所需要的:任意代码片段的亚微秒分辨率时间。

对于 JavaScript,performance.now() 曾经是衡量性能的好方法,但为了响应 Spectre 和 Meltdown,所有主要浏览器都将分辨率降低到甚至不到一毫秒。

performance.now()引用MDN:

时间戳实际上不是高分辨率的。减轻安全性 Spectre 等威胁,浏览器目前将结果四舍五入 不同程度。 (Firefox 开始舍入到 2 毫秒 Firefox 59.) 一些浏览器也可能稍微随机化时间戳。 在未来的版本中,精度可能会再次提高;浏览器开发者 仍在调查这些定时攻击以及如何最好地缓解 他们。

问题

我需要微秒级精度的计时。在撰写本文时,浏览器似乎没有提供任何选项或标志来禁用这些安全措施。也许我在谷歌上搜索了错误的术语,但我遇到的唯一文章是对安全问题的解释以及这些缓解措施如何解决这些问题。

我对这里的安全方面不感兴趣 - 我正在我自己的机器上对性能关键的 JavaScript 代码片段进行基准测试,我唯一关心的是我能以尽可能少的努力获得尽可能准确的测量结果可能。

现有的解决方法

想到两个选项:

  1. 安装未实施这些缓解措施的旧版浏览器

例如,我必须将旧版本的 FireFox 用于基准测试,而将新版本的 Chrome 用于浏览。这是不切实际的,因为我需要在所有浏览器中进行测试(最好在所有浏览器中进行基准测试)。此外,新的优化并未在旧浏览器中实现,因此基准测试可能毫无用处。

  1. 使用 WebWorkers 实现自定义计时器

我看过各种较早的博客文章,但似乎都没有达到我需要的高精度(毕竟,曾经有 performance.now() 用于此)。

问题

我如何获得有效的预 Spectre performance.now() 而无需求助于旧版浏览器、虚拟机等?

JavaScript 中有没有可以达到微秒精度的编码技术或库?

上述 3 种浏览器是否有任何选项或标志可以禁用这些安全措施?

我最终正在寻找一种方法来准确衡量不同代码段之间的相对性能 - 因此,如果有一种解决方案可以让我得到滴答声,而不是微秒,那也是可以接受的,只要它准确并且可以跨浏览器工作。

【问题讨论】:

  • 鉴于您所描述的听起来像是相当低级的时序,大概它并不完全依赖于在浏览器中运行(访问 DOM、执行 ajax 等将比任何代码慢几个数量级)运行) - 你可以改为在 node.js 环境中工作吗?你在那里有process.hrtime
  • 我认为你最好的选择是在某个地方寻找一个旋钮,在现代浏览器中重新启用高精度计时;大概它们使启用像您这样的用例成为可能。我不知道怎么做,但我想这是可能的。不过,您可能必须在禁用该选项的情况下从源代码构建自己的 Firefox 和 Chromium。您主要关心的是在您自己的浏览器中在一台或几台机器上使用它,因为这是用于基准测试的。
  • @PeterCordes JavaScript 不做线程,从概念上讲,所有事情都发生在单个线程中的消息循环中——虽然你现在有像服务工作者这样的东西(可以被认为是线程),但你有在他们之间发布消息 - 无法保证看到该消息的速度
  • @Mike'Pomax'Kamermans 我想要进行基准测试的一个例子是解析器中的特定热路径。测试一个有 100 个 case 的 switch 语句是否比只有 20 个 case 和 2-3 个 if-else 的 switch 语句快几个%。 Chrome、FireFox 和 Edge 的优化非常不同。有时在 Chrome 中无法进一步优化,但在 FireFox 中可以。除了这个低级示例之外,我还在寻找更高级别的基准测试,它们仍然是亚毫秒级的。
  • 是的,你会这么说,但我已经用谷歌搜索了所有禁用精度限制 - 完全缺乏结果是导致我问这个问题的原因。我想我在 chromium 中找到了提交:github.com/chromium/chromium/commit/… - 对我来说看起来很硬编码。

标签: javascript performance benchmarking


【解决方案1】:

Firefox 确实有一个名为 privacy.reduceTimerPrecision 的配置设置,它禁用了 Spectre 缓解。您可以使用 Firefox 的 about:config 页面(在地址栏中输入 about:config)将其切换为 false。

通过MDN 上的提示解决了这个问题。

【讨论】:

  • 当我发布这个问题时,我在 FireFox 中读到了这个选项,但当时它似乎还没有实现。它现在在那里。我应该注意到,切换实际上并没有完全禁用 Spectre 缓解,它只是将其限制在 20µs,这就是 v59 中的缓解。仍然比默认钳制到 1-2ms 好很多,但不完全是以前的 5µs..
【解决方案2】:

从 Firefox 79 开始,如果您让服务器随页面发送两个标头,则可以使用高分辨率计时器:

从 Firefox 79 开始,如果您使用 Cross-Origin-Opener-PolicyCross-Origin-Embedder-Policy 标头跨域隔离文档,则可以使用高分辨率计时器:

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

这些标题确保顶级文档不会与跨域文档共享浏览上下文组。 COOP 进程 - 隔离您的文档,如果潜在攻击者在弹出窗口中打开它,他们将无法访问您的全局对象,从而防止了一组称为 XS-Leaks 的跨域攻击。

参考:https://developer.mozilla.org/en-US/docs/Web/API/Performance/now

到目前为止,该页面上没有提到它,但是通过一些实验,我得出结论,在存在这些标头的情况下,计时器的准确度为 20µs,比默认的准确度高 50 倍。

(() => {
  const start = performance.now();
  let diff;
  while ((diff = performance.now() - start) === 0);
  return diff;
})();

这会返回 0.02 或非常接近于 0.02 的值,如果没有这些标题,则返回 1。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-20
    • 1970-01-01
    • 2011-10-04
    • 1970-01-01
    • 2012-06-12
    • 1970-01-01
    相关资源
    最近更新 更多