JavaScript 中是否有任何具有微秒分辨率的计时函数?
我知道Chrome的timer.js,并希望有其他友好浏览器的解决方案,如 Firefox、Safari、Opera、Epiphany、Konqueror 等。我对支持任何 IE 不感兴趣,但包括IE在内的答案受欢迎的。
(鉴于 JS 中毫秒计时的准确性很差,我对这个没有屏住呼吸!)
更新:timer.js 宣传微秒分辨率,但它只是将毫秒读数乘以 1,000。通过测试和代码检查进行验证。失望的。:[
JavaScript 中是否有任何具有微秒分辨率的计时函数?
我知道Chrome的timer.js,并希望有其他友好浏览器的解决方案,如 Firefox、Safari、Opera、Epiphany、Konqueror 等。我对支持任何 IE 不感兴趣,但包括IE在内的答案受欢迎的。
(鉴于 JS 中毫秒计时的准确性很差,我对这个没有屏住呼吸!)
更新:timer.js 宣传微秒分辨率,但它只是将毫秒读数乘以 1,000。通过测试和代码检查进行验证。失望的。:[
正如 Mark Rejhon 的回答中所提到的,现代浏览器中有一个 API 可以将亚毫秒分辨率的计时数据公开给脚本:W3C 高分辨率计时器,又名window.performance.now()
。
now()
Date.getTime()
在两个重要方面优于传统:
now()
是一个具有亚毫秒分辨率的双精度值,表示自页面导航开始以来的毫秒数。它以小数形式返回微秒数(例如,1000.123 的值是 1 秒和 123 微秒)。
now()
是单调递增的。这很重要,因为Date.getTime()
可能会在后续调用中向前甚至向后跳转。值得注意的是,如果操作系统的系统时间更新(例如原子钟同步),Date.getTime()
也会更新。 now()
保证总是单调递增,所以它不受操作系统系统时间的影响——它总是挂钟时间(假设你的挂钟不是原子的......)。
now()
可几乎每一个地方,用在new Date.getTime()
,+ new Date
和Date.now()
是。例外是Date
和now()
时间不混合,因为Date
基于unix-epoch(自 1970 年以来的毫秒数),而now()
是自页面导航开始以来的毫秒数(因此它会比 小得多Date
)。
now()
支持 Chrome stable、Firefox 15+ 和 IE10。还有几种可用的polyfill。
注意:使用Web Workers 时,该window
变量不可用,但您仍然可以使用performance.now()
.
现在有一种在 javascript 中测量微秒的新方法:http : //gent.ilcore.com/2012/06/better-timer-for-javascript.html
但是,在过去,我发现了一种粗略的方法,可以通过毫秒计时器在 JavaScript 中获得 0.1 毫秒的精度。不可能的?不。继续阅读:
我正在做一些需要自检计时器精度的高精度实验,并发现我能够在某些系统上的某些浏览器上可靠地获得 0.1 毫秒的精度。
我发现在快速系统上的现代 GPU 加速 Web 浏览器中(例如 i7 四核,其中几个核空闲,只有浏览器窗口)——我现在可以相信计时器是毫秒级的。事实上,它在空闲的 i7 系统上变得如此准确,我已经能够可靠地获得完全相同的毫秒数,超过 1,000 次尝试。只有当我尝试执行诸如加载额外的网页或其他操作时,毫秒精度才会降低(并且我能够通过前后时间检查来成功捕获自己降低的精度,以查看是否我的处理时间突然延长到 1 毫秒或更多毫秒——这有助于我使可能受到 CPU 波动不利影响的结果无效)。
它在 i7 四核系统上的某些 GPU 加速浏览器中变得如此准确(当浏览器窗口是唯一的窗口时),我发现我希望我可以在 JavaScript 中访问一个 0.1 毫秒的精确计时器,因为现在终于准确了在一些高端浏览系统上,对于某些类型的需要高精度的利基应用程序,以及应用程序能够自我验证精度偏差的情况,这种计时器精度是值得的。
显然,如果您要进行多次传递,您可以简单地运行多次传递(例如 10 次传递),然后除以 10 以获得 0.1 毫秒的精度。这是获得更好精度的常用方法 - 进行多次传递并将总时间除以传递次数。
但是......如果由于异常独特的情况我只能对特定测试进行一次基准测试,我发现通过执行以下操作可以获得 0.1(有时为 0.01ms)的精度:
初始化/校准:
将一次通过基准测试到亚毫秒级精度:
警告:不建议在 Web 浏览器中使用繁忙循环,但幸运的是,这些繁忙循环每次运行的时间不到 1 毫秒,并且只运行了很少的次数。
诸如 JIT 编译和 CPU 波动之类的变量会增加大量的不准确度,但如果您运行多次初始化,您将获得完整的动态重新编译,最终计数器会稳定到非常准确的值。确保所有繁忙循环在所有情况下都具有完全相同的功能,以便繁忙循环的差异不会导致差异。在开始信任结果之前,确保所有代码行都执行了多次,以使 JIT 编译器已经稳定到完全动态重新编译 (dynarec)。
事实上,我在某些系统上目睹了接近微秒的精度,但我还不相信它。但是 0.1 毫秒的精度在我是唯一浏览器页面的空闲四核系统上似乎非常可靠。我来到了一个科学测试案例,我只能进行一次性传递(由于发生了独特的变量),并且需要精确计时每次传递,而不是平均多次重复传递,所以这就是我这样做的原因。
我做了几次预传递和虚拟传递(也是为了解决 dynarec),以验证 0.1ms 精度的可靠性(保持稳定几秒钟),然后让我的手离开键盘/鼠标,同时进行基准测试,然后做了几次后通过以验证 0.1 毫秒精度的可靠性(再次保持稳定)。这也验证了诸如电源状态更改或其他事情之类的事情在前后之间没有发生,从而干扰了结果。在每一次基准测试之间重复前测和后测。在此基础上,我几乎可以肯定两者之间的结果是准确的。当然,不能保证,但它表明在某些情况下在 Web 浏览器中可以达到 <0.1ms 的精确精度。
这种方法只在非常非常小众的情况下有用。即便如此,它实际上也不会 100% 无限地保证,当与多层内部和外部验证相结合时,您可以获得非常值得信赖的准确性,甚至是科学的准确性。
这是一个示例,显示了我的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
一般来说,答案是“不”。如果您在某些服务器端环境中(即,不在浏览器中)使用 JavaScript,那么一切都将结束,您可以尝试做任何您想做的事情。
编辑- 这个答案是旧的;标准不断进步,更新的设施可以作为解决准确时间问题的方法。尽管如此,应该记住,在真正的实时操作系统的域之外,普通的非特权代码对其对计算资源的访问的控制是有限的。衡量绩效与预测绩效不同(必然)。