115

Are there any timing functions in JavaScript with microsecond resolution?

I am aware of timer.js for 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 including IE are welcome.

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

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

mwcz
  • 8,949
  • 10
  • 42
  • 63
  • 2
    What are you trying to do in a browser that requires microsecond accuracy? In general the performance guarantees of behavior of browsers are just not that precise. – Yuliy Jun 04 '11 at 00:56
  • 5
    Not going to happen. You can't trust micro second accuracy at all even if it existed. The only solid use case I can imagine is native clients in chrome but then you don't care about the JS API. Also loving treating "Epiphany" as a first class browser and ignoring IE. – Raynos Jun 04 '11 at 00:57
  • @Yuliy There's no reason this /shouldn't/ be available in a browser. Here's are some examples where µs timings would be useful: real-time web applications, HTML5 games, and emulators. – mwcz Jun 04 '11 at 01:29
  • @mwc the problem is that anything like that which a "nice" website could use to provide something of value could be used by thousands of "not nice" websites to attack people. – Pointy Jun 04 '11 at 02:10
  • Like anything more accurate than milliseconds would be needed for gaming. Since a computer screen typically has a 60Hz refresh rate, any fps of more than 60 would be a waste. That is a delay of about 18ms. In which circumstances would you need anything less than a millisecond? – GolezTrol Jun 04 '11 at 02:13
  • 6
    'Getting' the time in javascript takes some time, as does returning it- and the latency increases if you are on a webpage that is redrawing or handling events. I wouldn't even count on the nearest 10 milliseconds accuracy. – kennebec Jun 04 '11 at 02:28
  • @Pointy In what way could microsecond resolution be used in an attack? @GolezTrol Fair enough, re: games. Why is there such a negative reaction to this question? – mwcz Jun 04 '11 at 02:33
  • 1
    Like, say, throwing up popups at super-high speed? Basically, the problem is that giving external parties too much access to user machines merely by dint of the fact that a person visits a website is a serious problem. – Pointy Jun 04 '11 at 03:20
  • 1
    It's not any more "vulnerable" than setInterval( popup, 0 ), which is fast enough that the problem is basically equivalent. Should millisecond accuracy be removed too? kennebec: your comment makes sense, thank you. – mwcz Jun 04 '11 at 04:07
  • 1
    looking for same thing...I time my code at certain intervals..and typically get...1-3 msec...I need more resolution to get a better feel for relative performance of my code and what is most efficient....each processor has a high precision clock on it...not too sure why it doens't filter up to closer to the application level. –  Jul 13 '12 at 21:56
  • 1
    Some of these comments crack me up. Reminds me of this: “...[T]hey talk about the 640 K limit...It was ten times what we had before. But to my surprise, we ran out of that address space for applications within—oh five or six years people were complaining.” — Bill Gates, 1993. – danorton Nov 07 '14 at 22:10
  • @danorton You and me both. :) – mwcz Nov 10 '14 at 15:07
  • "640K ought to be enough for anybody." - Bill Gates in 1981, 7 Famous Quotes About the Future That Are Actually Fake Not only does Gates deny saying this, nobody can seem to find independent evidence that he did. Back in the mid-2000s, the editor of the Yale Book of Quotations tried to track down where the quote came from to no avail. "I've said some stupid things and some wrong things, but not that," Gates himself wrote in 1997. "No one involved in computers would ever say that a certain amount of memory is enough for all time." – Stijn de Witt Jun 15 '16 at 19:09
  • 1
    /facepalm this comment thread. microsecond resolution is useful when trying to performance optimize a 60fps browser game :) – dlm Apr 04 '22 at 22:31
  • @dlm Same, but we should cut them some slack, it was over 10 years ago. :) – mwcz Apr 05 '22 at 02:46

4 Answers4

154

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().

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

  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 possibly jump 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...).

now() can be used in almost every place that new Date.getTime(), + new Date and Date.now() are. The exception is that Date and now() times don't mix, as Date is 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() is supported in Chrome stable, Firefox 15+, and IE10. There are also several polyfills available.

Note: When using Web Workers, the window variable isn't available, but you can still use performance.now().

Pikamander2
  • 7,332
  • 3
  • 48
  • 69
NicJ
  • 4,070
  • 1
  • 25
  • 18
  • 1
    the polyfills will be most probably using Date.now(), so this is still the best option considering the IE9 and it's millions of users, why mixing third-party library then – Vitaliy Terziev Aug 28 '16 at 13:54
  • 4
    My wall clock *is* atomic. –  Feb 21 '17 at 18:16
  • 4
    `new Date.getTime()` isn't a thing. `new Date().getTime()` is. – The Qodesmith Jun 19 '17 at 00:22
  • I really liked this response. I ran a few tests and came up with an example you can drop into your console to see that this still will have dramatic collisions when using this. (note I was getting 10% collisions on a good machine even with doing something as expensive as a `console.log` on each run) Hard to make out but copy all the highlighted code here: `last=-11; same=0; runs=100; for(let i=0;i – bladnman Oct 21 '18 at 02:11
  • 6
    Revisiting my **year 2012** comment. performance.now() is now kinda fuzzied up again slightly by Meltdown/Spectre workarounds. Some browsers have seriously degraded performance.now() due to security reasons. I think my technique has probably regained some relevance again for a huge deal of many legitimate benchmarking use-cases, subject to timer-fuzz limitations. That said, some browsers now has some developer performance-profiling features/extensions that did not exist in 2012. – Mark Rejhon Jun 23 '19 at 18:18
21

There's now a new method of measuring microseconds in 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:

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.

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).

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.

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.

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:

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)

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!!!!!!!!

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.

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).

In fact, I witnessed precision approaching microseconds on certain systems, 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.

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 some cases in a web browser.

This method is only useful in very, very niche cases. 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.

Mark Rejhon
  • 869
  • 7
  • 14
  • 3
    It used to be complicated to do timing with superior precision because all we had was `Date.now()` or `+new Date()`. But now we have `performance.now()`. While it's clear you have found some cool ways to hack out more capabilities, this answer is essentially obsolete. Also, don't recommend anything related to busy loops. Just don't do that. We don't need more of that. – Steven Lu Nov 27 '14 at 02:01
  • 3
    Most browsers reduced the precision of their performance.now() implementation to temporarily mitigate the cache timing attack. I wonder whether this answer still has significance in security research. – Qi Fan Jan 10 '18 at 22:17
  • 2
    Revisiting my own comment. Wow, I posted the above in **year 2012** long before performance.now(). But now that's kinda fuzzied up again slightly by Meltdown/Spectre workarounds. Some browsers have seriously degraded performance.now() due to security reasons. I think the above technique has probably regained some relevance again for a huge deal of many legitimate benchmarking use-cases, subject to timer-fuzz limitations. – Mark Rejhon Jun 23 '19 at 18:14
4

Here is an example showing my high-resolution timer for 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.

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

agm1984
  • 15,500
  • 6
  • 89
  • 113
3

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.

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 predicting performance.

editing again — For a while we had performance.now(), but at present (2022 now) browsers have degraded the accuracy of that API for security reasons.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • something happened to security bcoz of precision? – Dee Feb 05 '23 at 17:03
  • 1
    @Dee yes, some timing-based attacks only work when very accurate time is available. I am not a trained hacker so I can't offer details, but you can find information by searching for "timing attack" – Pointy Feb 05 '23 at 17:29