JavaScript中的微时序

时间:2011-06-04 00:44:39

标签: javascript

JavaScript中是否有任何具有微秒分辨率的计时功能?

我知道Chrome有timer.js,我希望有其他友好浏览器的解决方案,如Firefox,Safari,Opera,Epiphany,Konqueror等。我对支持任何IE都不感兴趣,但欢迎回答包括 IE。

(鉴于JS中毫秒级的准确性很差,我不会屏住呼吸!)

更新:timer.js通告微秒级分辨率,但它只是将毫秒读数乘以1,000。通过测试和代码检查验证。失望。 :

4 个答案:

答案 0 :(得分:112)

正如Mark Rejhon的回答所提到的,现代浏览器中有一个API可以将亚毫秒分辨率的时序数据暴露给脚本:W3C High Resolution Timer,又名window.performance.now()

now()在两个重要方面优于传统Date.getTime()

  1. now()是一个亚毫秒分辨率的双精度,表示自页面导航开始以来的毫秒数。它返回小数中的微秒数(例如,1000.123的值是1秒和123微秒)。

  2. now()单调增加。这很重要,因为Date.getTime()可以可能在后续调用中向前跳转甚至向后跳转。值得注意的是,如果OS的系统时间更新(例如原子钟同步),则Date.getTime()也会更新。 now()保证始终单调递增,因此它不受操作系统系统时间的影响 - 它始终是挂钟时间(假设您的挂钟不是原子的......)。

  3. now()几乎可用于new Date.getTime()+ new DateDate.now()所在的每个地方。例外情况是Datenow()次不混合,因为Date基于unix-epoch(自1970年以来的毫秒数),而now()是自页面导航开始以来的毫秒数(因此它将比Date小得多)。

    Chrome stable,Firefox 15+和IE10支持

    now()。还有几个polyfills可用。

答案 1 :(得分:16)

现在有了一种在javascript中测量微秒的新方法: http://gent.ilcore.com/2012/06/better-timer-for-javascript.html

然而,在过去,我发现了一种粗略的方法,可以在毫秒计时器中获得0.1毫秒的精度。不可能?不。继续阅读:

我正在进行一些需要自检定时器精度的高精度实验,并发现在某些系统上某些浏览器能够可靠地获得0.1毫秒的精度。

我发现在快速系统上的现代GPU加速网络浏览器中(例如i7四核,其中几个内核空闲,只有浏览器窗口) - 我现在可以相信定时器是毫秒准确的。事实上,它在空闲的i7系统上变得如此准确,我已经能够可靠地获得相同的毫秒,超过1000次尝试。只有当我尝试加载额外的网页或其他内容时,毫秒精度才会降低(并且我能够通过进行前后检查来成功捕获自己降低的精度) ,看看我的处理时间是否突然延长到1毫秒或更长 - 这有助于我使可能受CPU波动影响太大的结果无效。

在i7四核系统的某些GPU加速浏览器中(当浏览器窗口是唯一的窗口时),它变得如此准确,我发现我希望能够访问0.1ms的精确定时器在JavaScript中,由于精确度现在终于存在于一些高端浏览系统中,因此对于需要高精度的某些类型的小众应用而言,这种定时器精度是值得的,并且应用程序能够自我验证精度偏差。 / p>

显然,如果你进行多次传球,你可以简单地运行多次传球(例如10次传球)然后除以10以获得0.1毫秒的精度。这是获得更好精度的常用方法 - 进行多次传球并将总时间除以传球次数。

但是......如果由于异常独特的情况我只能进行特定测试的单一基准测试,我发现通过这样做我可以获得0.1(有时0.01ms)的精度:

初​​始化/校准:

  1. 运行一个繁忙的循环,等待计时器递增到下一毫秒(将计时器对齐到下一毫秒间隔的开始)这个繁忙的循环持续时间不到一毫秒。
  2. 运行另一个忙循环以递增计数器,同时等待计时器递增。计数器告诉您在一毫秒内发生了多少计数器增量。这个繁忙的循环持续一整毫秒。
  3. 重复上述步骤,直到数字变得超稳定(加载时间,JIT编译器等)。 4. 注意:数字的稳定性使您可以在空闲系统上获得可达到的精度。如果需要自检精度,可以计算方差。某些浏览器的差异较大,而其他浏览器的差异较小。更快的系统更大,慢速系统更慢。一致性也各不相同。您可以确定哪些浏览器比其他浏览器更一致/更准确。较慢的系统和繁忙的系统将导致初始化过程之间的差异更大。如果浏览器没有给出足够的精度以允许0.1ms或0.01ms的测量,这可以让您有机会显示警告消息。定时器偏斜可能是一个问题,但某些系统上的某些整数毫秒定时器会非常准确地增加(在点上非常正确),这将导致您可以信任的非常一致的校准值。
  4. 保存最终计数器值(或最后几次校准通过的平均值)
  5. 将一遍传递到亚毫秒精度:

    1. 运行忙循环以等待,直到计时器递增到下一毫秒(将计时器对齐到下一毫秒间隔的开始)。这个繁忙的循环持续不到一毫秒。
    2. 执行您想要精确确定时间基准的任务。
    3. 检查计时器。这给你整数毫秒。
    4. 运行最终繁忙循环以在等待计时器递增时递增计数器。这个繁忙的循环持续不到一毫秒。
    5. 将此计数器值除以初始化的原始计数器值。
    6. 现在你得到毫秒的小数部分!!!!!!!!
    7. 警告:不建议在Web浏览器中使用繁忙的循环,但幸运的是,这些繁忙的循环每次运行的时间不到1毫秒,并且只运行了几次。

      JIT编译和CPU波动等变量会增加大量的不准确性,但是如果你运行几个初始化过程,你将完全动态重新编译,最终计数器会达到非常准确的状态。确保所有繁忙循环对于所有情况都是完全相同的功能,因此繁忙循环中的差异不会导致差异。在开始信任结果之前,确保所有代码行都被执行了几次,以允许JIT编译器已经稳定到完全动态重新编译(dynarec)。

      事实上,我目睹某些系统的精确度接近微秒,但我还是不相信它。但是在一个空闲的四核系统中,0.1毫秒的精度似乎非常可靠,我是唯一的浏览器页面。我来到一个科学测试案例,我只能进行一次性通行证(由于发生了独特的变量),并且需要精确计算每次通过的时间,而不是平均多次重复通过,这就是我这样做的原因。

      我做了几次预传和虚拟传球(也用于解决dynarec),以验证0.1ms精度的可靠性(保持稳定几秒),然后让我的手离开键盘/鼠标,同时基准发生,然后做了几次后通过以验证0.1ms精度的可靠性(再次保持稳定)。这也验证了诸如电源状态变化或其他因素之类的事情在前后之间没有发生,从而干扰了结果。在每个基准测试通道之间重复预测试和后测试。在此之后,我几乎可以肯定其间的结果是准确的。当然,并不能保证,但它表明在Web浏览器的某些情况下,精确度可以达到<0.1ms。

      此方法仅适用于非常非常 niche 的情况。即便如此,它实际上不会100%无限保证,当与多层内部和外部验证相结合时,您可以获得非常可靠的准确度,甚至科学准确性。

答案 2 :(得分:3)

总的来说答案是“不”。如果您在某些服务器端环境中使用JavaScript(即不在浏览器中),则所有投注均已关闭,您可以尝试执行任何操作。

编辑 - 这个答案很旧;标准已经取得进展,新的设施可用作解决准确时间问题的方法。即便如此,应该记住,在真正的实时操作系统的领域之外,普通的非特权代码对其对计算资源的访问具有有限的控制。测量性能与预测性能不一样(必然)。

答案 3 :(得分:0)

以下是显示 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