JavaScript 中的微秒计时

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6233927/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 20:52:51  来源:igfitidea点击:

Microsecond timing in JavaScript

javascript

提问by mwcz

Are there any timing functions in JavaScript with microsecond resolution?

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

I am aware of timer.jsfor Chrome, and am hoping there will be a solution for other friendly browsers, like Firefox, Safari, Opera, Epiphany, Konqueror, etc. I'm not interested in supporting any IE, but answers includingIE are welcome.

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

(Given the poor accuracy of millisecond timing in JS, I'm not holding my breath on this one!)

(鉴于 JS 中毫秒计时的准确性很差,我对这个没有屏住呼吸!)

Update: timer.js advertises microsecond resolution, but it simply multiplies the millisecond reading by 1,000. Verified by testing and code inspection. Disappointed. :[

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

回答by NicJ

As alluded to in Mark Rejhon's answer, there is an API available in modern browsers that exposes sub-millisecond resolution timing data to script: the W3C High Resolution Timer, aka window.performance.now().

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

now()is better than the traditional Date.getTime()in two important ways:

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

  1. now()is a double with submillisecond resolution that represents the number of milliseconds since the start of the page's navigation. It returns the number of microseconds in the fractional (e.g. a value of 1000.123 is 1 second and 123 microseconds).

  2. now()is monotonically increasing. This is important as Date.getTime()can possiblyjump forward or even backward on subsequent calls. Notably, if the OS's system time is updated (e.g. atomic clock synchronization), Date.getTime()is also updated. now()is guaranteed to always be monotonically increasing, so it is not affected by the OS's system time -- it will always be wall-clock time (assuming your wall clock is not atomic...).

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

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

now()can be used in almost every place that new Date.getTime(), + new Dateand Date.now()are. The exception is that Dateand now()times don't mix, as Dateis based on unix-epoch(the number of milliseconds since 1970), while now()is the number of milliseconds since your page navigation started (so it will be much smaller than Date).

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

now()is supported in Chrome stable, Firefox 15+, and IE10. There are also several polyfillsavailable.

now()支持 Chrome stable、Firefox 15+ 和 IE10。还有几种可用的polyfill

回答by Mark Rejhon

There's now a new method of measuring microseconds in javascript: http://gent.ilcore.com/2012/06/better-timer-for-javascript.html

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

However, in the past, I found a crude method of getting 0.1 millisecond precision in JavaScript out of a millisecond timer. Impossible? Nope. Keep reading:

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

I'm doing some high-precisio experiments that requires self-checked timer accuracies, and found I was able to reliably get 0.1 millisecond precision with certain browsers on certain systems.

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

I have found that in modern GPU-accelerated web browsers on fast systems (e.g. i7 quad core, where several cores are idle, only browser window) -- I can now trust the timers to be millisecond-accurate. In fact, it's become so accurate on an idle i7 system, I've been able to reliably get the exact same millisecond, over more than 1,000 attempts. Only when I'm trying to do things like load an extra web page, or other, the millisecond accuracy degrades (And I'm able to successfully catch my own degraded accuracy by doing a before-and-after time check, to see if my processing time suddenly lengthened to 1 or more milliseconds -- this helps me invalidate results that has probably been too adversely affected by CPU fluctuations).

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

It's become so accurate in some GPU accelerated browsers on i7 quad-core systems (when the browser window is the only window), that I've found I wished I could access a 0.1ms precision timer in JavaScript, since the accuracy is finally now there on some high-end browsing systems to make such timer precision worthwhile for certain types of niche applications that requires high-precision, and where the applications are able to self-verify for accuracy deviations.

它在 i7 四核系统上的某些 GPU 加速浏览器中变得如此准确(当浏览器窗口是唯一的窗口时),我发现我希望我可以在 JavaScript 中访问一个 0.1 毫秒的精确计时器,因为现在终于准确了在一些高端浏览系统上,对于某些类型的需要高精度的利基应用程序,以及应用程序能够自我验证准确性偏差的应用程序,这种计时器精度是值得的。

Obviously if you are doing several passes, you can simply run multiple passes (e.g. 10 passes) then divide by 10 to get 0.1 millisecond precision. That is a common method of getting better precision -- do multiple passes and divide the total time by number of passes.

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

HOWEVER...If I can only do a single benchmark pass of a specific test due to an unusually unique situation, I found out that I can get 0.1 (And sometimes 0.01ms) precision by doing this:

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

Initialization/Calibration:

初始化/校准:

  1. Run a busy loop to wait until timer increments to the next millisecond (align timer to beginning of next millisecond interval) This busy loop lasts less than a millisecond.
  2. Run another busy loop to increment a counter while waiting for timer to increment. The counter tells you how many counter increments occured in one millisecond. This busy loop lasts one full millisecond.
  3. Repeat the above, until the numbers become ultra-stable (loading time, JIT compiler, etc). 4. NOTE: The stability of the number gives you your attainable precision on an idle system. You can calculate the variance, if you need to self-check the precision. The variances are bigger on some browsers, and smaller on other browsers. Bigger on faster systems and slower on slower systems. Consistency varies too. You can tell which browsers are more consistent/accurate than others. Slower systems and busy systems will lead to bigger variances between initialization passes. This can give you an opportunity to display a warning message if the browser is not giving you enough precision to allow 0.1ms or 0.01ms measurements. Timer skew can be a problem, but some integer millisecond timers on some systems increment quite accurately (quite right on the dot), which will result in very consistent calibration values that you can trust.
  4. Save the final counter value (or average of the last few calibration passes)
  1. 运行一个忙碌循环以等待计时器增加到下一个毫秒(将计时器与下一个毫秒间隔的开始对齐)这个忙碌循环持续不到一毫秒。
  2. 运行另一个繁忙循环以在等待计时器递增的同时递增计数器。计数器告诉您在一毫秒内发生了多少计数器增量。这个繁忙的循环持续一整毫秒。
  3. 重复上述操作,直到数字变得超稳定(加载时间、JIT 编译器等)。4.注意:数字的稳定性为您提供在空闲系统上可达到的精度。如果您需要自检精度,您可以计算方差。差异在某些浏览器上较大,而在其他浏览器上较小。在较快的系统上较大,在较慢的系统上较慢。一致性也各不相同。您可以判断哪些浏览器比其他浏览器更一致/更准确。较慢的系统和繁忙的系统将导致初始化过程之间的差异更大。如果浏览器没有给您足够的精度以允许 0.1ms 或 0.01ms 测量,这可以让您有机会显示警告消息。计时器偏差可能是一个问题,但某些系统上的某些整数毫秒计时器会非常准确地递增(非常正确),这将导致您可以信任的非常一致的校准值。
  4. 保存最终计数器值(或最后几次校准通过的平均值)

Benchmarking one pass to sub-millisecond precision:

将一次通过基准测试到亚毫秒级精度:

  1. Run a busy loop to wait until timer increments to the next millisecond (align timer to beginning of next millisecond interval). This busy loop lasts less than a millisecond.
  2. Execute the task you want to precisely benchmark the time.
  3. Check the timer. This gives you the integer milliseconds.
  4. Run a final busy loop to increment a counter while waiting for timer to increment. This busy loop lasts less than a millisecond.
  5. Divide this counter value, by the original counter value from initialization.
  6. Now you got the decimal part of milliseconds!!!!!!!!
  1. 运行一个忙循环以等待计时器增加到下一个毫秒(将计时器与下一个毫秒间隔的开始对齐)。这个繁忙的循环持续不到一毫秒。
  2. 执行您想要精确基准时间的任务。
  3. 检查计时器。这为您提供整数毫秒。
  4. 运行最后一个忙循环以在等待计时器递增的同时递增计数器。这个繁忙的循环持续不到一毫秒。
  5. 将此计数器值除以初始化时的原始计数器值。
  6. 现在你得到了毫秒的小数部分!!!!!!!!

WARNING: Busy loops are NOT recommended in web browsers, but fortunately, these busy loops run for less than 1 millisecond each, and are only run a very few times.

警告:不建议在 Web 浏览器中使用繁忙循环,但幸运的是,这些繁忙循环每次运行的时间不到 1 毫秒,并且只运行了很少的次数。

Variables such as JIT compilation and CPU fluctuations add massive inaccuracies, but if you run several initialization passes, you'll have full dynamic recompilation, and eventually the counter settles to something very accurate. Make sure that all busy loops is exactly the same function for all cases, so that differences in busy loops do not lead to differences. Make sure all lines of code are executed several times before you begin to trust the results, to allow JIT compilers to have already stabilized to a full dynamic recompilation (dynarec).

诸如 JIT 编译和 CPU 波动之类的变量会增加大量的不准确度,但如果您运行多次初始化,您将获得完整的动态重新编译,最终计数器会稳定到非常准确的值。确保所有繁忙循环在所有情况下都具有完全相同的功能,以便繁忙循环中的差异不会导致差异。在开始信任结果之前,确保所有代码行都执行了多次,以使 JIT 编译器已经稳定到完全动态重新编译 (dynarec)。

In fact, I witnessed precision approaching microseconds on certainsystems, but I wouldn't trust it yet. But the 0.1 millisecond precision appears to work quite reliably, on an idle quad-core system where I'm the only browser page. I came to a scientific test case where I could only do one-off passes (due to unique variables occuring), and needed to precisely time each pass, rather than averaging multiple repeat pass, so that's why I did this.

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

I did several pre-passes and dummy passes (also to settle the dynarec), to verify reliability of 0.1ms precision (stayed solid for several seconds), then kept my hands off the keyboard/mouse, while the benchmark occured, then did several post-passes to verify reliability of 0.1ms precision (stayed solid again). This also verifies that things such as power state changes, or other stuff, didn't occur between the before-and-after, interfering with results. Repeat the pre-test and post-test between every single benchmark pass. Upon this, I was quite virtually certain the results in between were accurate. There is no guarantee, of course, but it goes to show that accurate <0.1ms precision is possible in somecases in a web browser.

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

This method is only useful in very, very nichecases. Even so, it literally won't be 100% infinitely guaranteeable, you can gain quite very trustworthy accuracy, and even scientific accuracy when combined with several layers of internal and external verifications.

这种方法只在非常非常小众的情况下有用。即便如此,它实际上也不是 100% 无限保证的,当结合多层内部和外部验证时,您可以获得非常值得信赖的准确性,甚至是科学的准确性。

回答by Pointy

The answer is "no", in general. If you're using JavaScript in some server-side environment (that is, not in a browser), then all bets are off and you can try to do anything you want.

一般来说,答案是“不”。如果您在某些服务器端环境(即,不在浏览器中)使用 JavaScript,那么一切都将结束,您可以尝试做任何您想做的事情。

edit— this answer is old; the standards have progressed and newer facilities are available as solutions to the problem of accurate time. Even so, it should be remembered that outside the domain of a true real-time operating system, ordinary non-privileged code has limited control over its access to compute resources. Measuring performance is not the same (necessarily) as predictingperformance.

编辑- 这个答案很旧;标准已经取得进步,并且可以使用更新的设施来解决准确时间问题。尽管如此,应该记住,在真正的实时操作系统的域之外,普通的非特权代码对其对计算资源的访问的控制是有限的。衡量绩效与预测绩效不同(必然)。

回答by agm1984

Here is an example showing my high-resolution timer for node.js:

这是一个示例,显示了我的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
 }

Usage:

用法:

 const start = startTimer();

 console.log('test');

 console.log(`Time since start: ${endTimer(start)} ms`);

Normally, you might be able to use:

通常,您可能可以使用:

 console.time('Time since start');

 console.log('test');

 console.timeEnd('Time since start');

If you are timing sections of code that involve looping, you cannot gain access to the value of console.timeEnd()in order to add your timer results together. You can, but it get gets nasty because you have to inject the value of your iterating variable, such as i, and set a condition to detect if the loop is done.

如果您正在对涉及循环的代码段进行计时,则无法访问 的值console.timeEnd()以便将计时器结果加在一起。你可以,但它会变得令人讨厌,因为你必须注入迭代变量的值,例如i,并设置一个条件来检测循环是否完成。

Here is an example because it can be useful:

这是一个示例,因为它很有用:

 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');
 }

Cite: https://nodejs.org/api/process.html#process_process_hrtime_time

引用:https: //nodejs.org/api/process.html#process_process_hrtime_time