Is System.nanoTime() completely useless?
Asked Answered
M

14

160

As documented in the blog post Beware of System.nanoTime() in Java, on x86 systems, Java's System.nanoTime() returns the time value using a CPU specific counter. Now consider the following case I use to measure time of a call:

long time1= System.nanoTime();
foo();
long time2 = System.nanoTime();
long timeSpent = time2-time1;

Now in a multi-core system, it could be that after measuring time1, the thread is scheduled to a different processor whose counter is less than that of the previous CPU. Thus we could get a value in time2 which is less than time1. Thus we would get a negative value in timeSpent.

Considering this case, isn't it that System.nanotime is pretty much useless for now?

I know that changing the system time doesn't affect nanotime. That is not the problem I describe above. The problem is that each CPU will keep a different counter since it was turned on. This counter can be lower on the second CPU compared to the first CPU. Since the thread can be scheduled by the OS to the second CPU after getting time1, the value of timeSpent may be incorrect and even negative.

Makedamakefast answered 4/2, 2009 at 8:3 Comment(3)
I don't have an answer but I do agree with you. Maybe it should be considered a bug in the JVM.Transliterate
that post is incorrect and not using TSC is slow but you have to live with: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6440250 Also TSC can be made useful via hypervisor but then it's slow again.Postgraduate
And of course, you can run in a virtual machine where a CPU can show up mid-way through a session :DKodak
H
213

This answer was written in 2011 from the point of view of what the Sun JDK of the time running on operating systems of the time actually did. That was a long time ago! leventov's answer offers a more up-to-date perspective.

That post is wrong, and nanoTime is safe. There's a comment on the post which links to a blog post by David Holmes, a realtime and concurrency guy at Sun. It says:

System.nanoTime() is implemented using the QueryPerformanceCounter/QueryPerformanceFrequency API [...] The default mechanism used by QPC is determined by the Hardware Abstraction layer(HAL) [...] This default changes not only across hardware but also across OS versions. For example Windows XP Service Pack 2 changed things to use the power management timer (PMTimer) rather than the processor timestamp-counter (TSC) due to problems with the TSC not being synchronized on different processors in SMP systems, and due the fact its frequency can vary (and hence its relationship to elapsed time) based on power-management settings.

So, on Windows, this was a problem up until WinXP SP2, but it isn't now.

I can't find a part II (or more) that talks about other platforms, but that article does include a remark that Linux has encountered and solved the same problem in the same way, with a link to the FAQ for clock_gettime(CLOCK_REALTIME), which says:

  1. Is clock_gettime(CLOCK_REALTIME) consistent across all processors/cores? (Does arch matter? e.g. ppc, arm, x86, amd64, sparc).

It should or it's considered buggy.

However, on x86/x86_64, it is possible to see unsynced or variable freq TSCs cause time inconsistencies. 2.4 kernels really had no protection against this, and early 2.6 kernels didn't do too well here either. As of 2.6.18 and up the logic for detecting this is better and we'll usually fall back to a safe clocksource.

ppc always has a synced timebase, so that shouldn't be an issue.

So, if Holmes's link can be read as implying that nanoTime calls clock_gettime(CLOCK_REALTIME), then it's safe-ish as of kernel 2.6.18 on x86, and always on PowerPC (because IBM and Motorola, unlike Intel, actually know how to design microprocessors).

There's no mention of SPARC or Solaris, sadly. And of course, we have no idea what IBM JVMs do. But Sun JVMs on modern Windows and Linux get this right.

EDIT: This answer is based on the sources it cites. But i still worry that it might actually be completely wrong. Some more up-to-date information would be really valuable. I just came across to a link to a four year newer article about Linux's clocks which could be useful.

Harsho answered 3/1, 2011 at 21:36 Comment(7)
Even WinXP SP2 appears to suffer. Running the original code sample, with void foo() { Thread.sleep(40); } I got a negative time (-380 ms!) using a single Athlon 64 X2 4200+ processorBiotype
I don't suppose there are updated on this, wrt. behavior on Linux, BSD or other platforms?Eppes
Good answer, should add a link to the more recent exploration of this topic: shipilev.net/blog/2014/nanotrusting-nanotimeEiffel
Note: the new link is broken. Can anyone post an abstract here?Herder
@SOFe: Oh, that's a shame. It is in the web archive, fortunately. I will see if i can track down a current version.Harsho
Note: OpenJDK didn't uphold to the spec up until OpenJDK 8u192, see bugs.openjdk.java.net/browse/JDK-8184271. Make sure to use at least as fresh version of OpenJDK 8, or OpenJDK 11+.Dolores
@Dolores I've rolled back your edit, as it's adding significant new content rather than improving what is already there. I encourage you to post a new answer with your observations - i will happily link to and upvote it!Harsho
D
38

Since Java 7, System.nanoTime() is guaranteed to be safe by JDK specification. System.nanoTime()'s Javadoc makes it clear that all observed invocations within a JVM (that is, across all threads) are monotonic:

The value returned represents nanoseconds since some fixed but arbitrary origin time (perhaps in the future, so values may be negative). The same origin is used by all invocations of this method in an instance of a Java virtual machine; other virtual machine instances are likely to use a different origin.

JVM/JDK implementation is responsible for ironing out the inconsistencies that could be observed when underlying OS utilities are called (e. g. those mentioned in Tom Anderson's answer).

The majority of other old answers to this question (written in 2009–2012) express FUD that was probably relevant for Java 5 or Java 6 but is no longer relevant for modern versions of Java.

It's worth mentioning, however, that despite JDK guarantees nanoTime()'s safety, there have been several bugs in OpenJDK making it to not uphold this guarantee on certain platforms or under certain circumstances (e. g. JDK-8040140, JDK-8184271). There are no open (known) bugs in OpenJDK wrt nanoTime() at the moment, but a discovery of a new such bug or a regression in a newer release of OpenJDK shouldn't shock anybody.

With that in mind, code that uses nanoTime() for timed blocking, interval waiting, timeouts, etc. should preferably treat negative time differences (timeouts) as zeros rather than throw exceptions. This practice is also preferable because it is consistent with the behaviour of all timed wait methods in all classes in java.util.concurrent.*, for example Semaphore.tryAcquire(), Lock.tryLock(), BlockingQueue.poll(), etc.

Nonetheless, nanoTime() should still be preferred for implementing timed blocking, interval waiting, timeouts, etc. to currentTimeMillis() because the latter is a subject to the "time going backward" phenomenon (e. g. due to server time correction), i. e. currentTimeMillis() is not suitable for measuring time intervals at all. See this answer for more information.

Instead of using nanoTime() for code execution time measurements directly, specialized benchmarking frameworks and profilers should preferably be used, for example JMH and async-profiler in wall-clock profiling mode.

Dolores answered 7/2, 2019 at 5:35 Comment(0)
S
35

I did a bit of searching and found that if one is being pedantic then yes it might be considered useless...in particular situations...it depends on how time sensitive your requirements are...

Check out this quote from the Java Sun site:

The real-time clock and System.nanoTime() are both based on the same system call and thus the same clock.

With Java RTS, all time-based APIs (for example, Timers, Periodic Threads, Deadline Monitoring, and so forth) are based on the high-resolution timer. And, together with real-time priorities, they can ensure that the appropriate code will be executed at the right time for real-time constraints. In contrast, ordinary Java SE APIs offer just a few methods capable of handling high-resolution times, with no guarantee of execution at a given time. Using System.nanoTime() between various points in the code to perform elapsed time measurements should always be accurate.

Java also has a caveat for the nanoTime() method:

This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time. The value returned represents nanoseconds since some fixed but arbitrary time (perhaps in the future, so values may be negative). This method provides nanosecond precision, but not necessarily nanosecond accuracy. No guarantees are made about how frequently values change. Differences in successive calls that span greater than approximately 292.3 years (263 nanoseconds) will not accurately compute elapsed time due to numerical overflow.

It would seem that the only conclusion that can be drawn is that nanoTime() cannot be relied upon as an accurate value. As such, if you do not need to measure times that are mere nano seconds apart then this method is good enough even if the resulting returned value is negative. However, if you're needing higher precision, they appear to recommend that you use JAVA RTS.

So to answer your question...no nanoTime() is not useless....its just not the most prudent method to use in every situation.

Spirometer answered 4/2, 2009 at 11:21 Comment(9)
> this method is good enough even if the resulting returned value is negative. I dont get this, if the value in timespent is negative, then how is it useful at all in measuring the time taken in foo()?Makedamakefast
its ok because all you are worried about is the absolute value of the difference. ie if your measurement is time t where t = t2 - t1 then you want to know |t|....so what if the value is negative...even with the multi core problem the impact is rarely going to be a few nanoseconds anyway.Spirometer
Also, according to the Java docs, you can still get a negative value even on a single core machine since you have no guarantee of when your code will be executed..."the value represents nanoseconds since some fixed but arbitrary time (perhaps in the future, so values may be negative)"...Spirometer
mezoid: If that was true, i.e. if the "fixed time" could change at any moment, then nanoTime would be completely useless because you have no way to tell. If the fix point is set during startup, it must not change and t2 must always be >= t1. Which means there is a bug.Transliterate
t2 >= t1 is also true if t0 (the start time) is < 0.Transliterate
To backup @Aaron: both t2 and t1 might be negative but (t2-t1) must not be negative.Valerivaleria
aaron: that is exactly what my point is. t2-t1 should never be negative otherwise we have a bug.Makedamakefast
@Makedamakefast - but you are misunderstanding what the doc says. You are raising a non-issue. There is some point in time that is considered "0". The values returned by nanoTime() are accurate relative to that time. Its a monotonically increasing timeline. You just might be getting a series of numbers from the negative portion of that timeline. -100, -99, -98 (obviously much bigger values in practice). They are going in the correct direction (increasing), so there is no issue here.Verdugo
What is missed here is that due to wall clock drift, service adjustments and generally poor resolution, System.currentTimeMills() comparisons (to calculate duration) can commonly result in "0" even though tens or hundreds of milliseconds have actually elapsed. So it is an issue of accuracy when measuring elapsed time of any operation that runs in under a second - such as when logging duration or cumulation calculation of very fast event durations.Harp
C
18

No need to debate, just use the source. Here, SE 6 for Linux, make your own conclusions:

jlong os::javaTimeMillis() {
  timeval time;
  int status = gettimeofday(&time, NULL);
  assert(status != -1, "linux error");
  return jlong(time.tv_sec) * 1000  +  jlong(time.tv_usec / 1000);
}


jlong os::javaTimeNanos() {
  if (Linux::supports_monotonic_clock()) {
    struct timespec tp;
    int status = Linux::clock_gettime(CLOCK_MONOTONIC, &tp);
    assert(status == 0, "gettime error");
    jlong result = jlong(tp.tv_sec) * (1000 * 1000 * 1000) + jlong(tp.tv_nsec);
    return result;
  } else {
    timeval time;
    int status = gettimeofday(&time, NULL);
    assert(status != -1, "linux error");
    jlong usecs = jlong(time.tv_sec) * (1000 * 1000) + jlong(time.tv_usec);
    return 1000 * usecs;
  }
}
Cuspidor answered 1/4, 2011 at 17:7 Comment(1)
That's helpful only if you know what the used API does. The used API is implemented by the operating system; this code is correct wrt. the specification of the used API (clock_gettime/gettimeofday), but as others pointed out, some non-up-to-date operating systems have buggy implementations.Tocology
D
7

Linux corrects for discrepancies between CPUs, but Windows does not. I suggest you assume System.nanoTime() is only accurate to around 1 micro-second. A simple way to get a longer timing is to call foo() 1000 or more times and divide the time by 1000.

Diandiana answered 5/2, 2009 at 23:17 Comment(6)
Could you provide a reference (behaviour on Linux and Windows)?Valerivaleria
Unfortunately the proposed method will commonly be highly imprecise because each event falling into the +/- 100ms wall clock update slot will often return zero for sub-second operations. The sum of 9 operations each with duration of zero is, well, zero, divided by nine is ... zero. Conversely, using System.nanoTime() will provide relatively accurate (non-zero) event durations, which then summed and divided by the number of events will provide a highly precise average.Harp
@DarrellTeague summing 1000 events and adding them up is the same as the end to end time.Diandiana
@DarrellTeague System.nanoTime() is accurate to 1 micro-second or better (not 100,000 microseconds) on most systems. Averaging many operations is only relevant when you get down to a few micro-seconds, and only on certain systems.Diandiana
Apologies as there was some confusion about the language used in "summing" events. Yes, if the time is marked at the beginning of say 1000 sub-second operations, they run and then time is marked again at the end and divided - that would work for some system under development to get an good approximation of duration for a given event.Harp
This is not right according to the System.nanoTime() spec. JVM takes responsibility for ironing out discrepancies on all operating systems. OpenJDK seems to succeed at that since 8u192, where bugs.openjdk.java.net/browse/JDK-8184271 has been fixed.Dolores
F
5

Absolutely not useless. Timing aficionados correctly point out the multi-core problem, but in real-word applications it is often radically better than currentTimeMillis().

When calculating graphics positions in frame refreshes nanoTime() leads to MUCH smoother motion in my program.

And I only test on multi-core machines.

Falco answered 31/3, 2010 at 13:27 Comment(0)
S
5

I have seen a negative elapsed time reported from using System.nanoTime(). To be clear, the code in question is:

    long startNanos = System.nanoTime();

    Object returnValue = joinPoint.proceed();

    long elapsedNanos = System.nanoTime() - startNanos;

and variable 'elapsedNanos' had a negative value. (I'm positive that the intermediate call took less than 293 years as well, which is the overflow point for nanos stored in longs :)

This occurred using an IBM v1.5 JRE 64bit on IBM P690 (multi-core) hardware running AIX. I've only seen this error occur once, so it seems extremely rare. I do not know the cause - is it a hardware-specific issue, a JVM defect - I don't know. I also don't know the implications for the accuracy of nanoTime() in general.

To answer the original question, I don't think nanoTime is useless - it provides sub-millisecond timing, but there is an actual (not just theoretical) risk of it being inaccurate which you need to take into account.

Slam answered 3/1, 2011 at 20:23 Comment(2)
Alas, seems to be some OS/hardware issue. The documentation states that the core values may be negative but (larger negative minus smaller negative) should still be a positive value. Indeed the assumption being that in the same thread, the nanoTime() call should always return a positive or negative value. Have never seen this on various Unix and Windows systems over many years but sounds possible especially if the hardware/OS are splitting this seemingly atomic operation across processors.Harp
@BasilVandegriend it is not a bug anywhere. As per the documentation, rarely the second System.nanoTime() in your example can run on a different CPU and the nanoTime values calculated on that CPU could just happen to be lower than the values calculated on the first CPU. So, a -ve value for elapsedNanos is possibleReglet
K
2

No, it's not... It just depends on your CPU, check High Precision Event Timer for how/why things are differently treated according to CPU.

Basically, read the source of your Java and check what your version does with the function, and if it works against the CPU you will be running it on.

IBM even suggests you use it for performance benchmarking (a 2008 post, but updated).

Kronstadt answered 4/2, 2009 at 8:13 Comment(1)
As all implementation defined bahaviour, "caveat emptor!"Sheers
Q
2

This doesn't seem to be a problem on a Core 2 Duo running Windows XP and JRE 1.5.0_06.

In a test with three threads I don't see System.nanoTime() going backwards. The processors are both busy, and threads go to sleep occasionally to provoke moving threads around.

[EDIT] I would guess that it only happens on physically separate processors, i.e. that the counters are synchronized for multiple cores on the same die.

Queenstown answered 4/2, 2009 at 10:5 Comment(4)
It probably wont happen all the time, but due to the way nanotime() is implemented the possibility is always there.Makedamakefast
I would guess that it only happens on physically separate processors, i.e. that the counters are synchronized for multiple cores on the same die.Queenstown
Even that depends on the specific implementation, IIRC. But that's something that the OS is supposed to take care of.Tocology
The RDTSC counters on multiple cores of the same x86 processor are not necessarily synchronized - some modern systems let different cores run at different speeds.Cocoon
C
2

I am linking to what essentially is the same discussion where Peter Lawrey is providing a good answer. Why I get a negative elapsed time using System.nanoTime()?

Many people mentioned that in Java System.nanoTime() could return negative time. I for apologize for repeating what other people already said.

  1. nanoTime() is not a clock but CPU cycle counter.
  2. Return value is divided by frequency to look like time.
  3. CPU frequency may fluctuate.
  4. When your thread is scheduled on another CPU, there is a chance of getting nanoTime() which results in a negative difference. That's logical. Counters across CPUs are not synchronized.
  5. In many cases, you could get quite misleading results but you wouldn't be able to tell because delta is not negative. Think about it.
  6. (unconfirmed) I think you may get a negative result even on the same CPU if instructions are reordered. To prevent that, you'd have to invoke a memory barrier serializing your instructions.

It'd be cool if System.nanoTime() returned coreID where it executed.

Clown answered 20/2, 2016 at 2:3 Comment(7)
All points except 3. and 5. are wrong. 1. nanoTime() is not a CPU cycle counter, it is nano time. 2. How the nanoTime value is produced is platform-specific. 4. No, the difference can't be negative, according to nanoTime() spec. Assuming OpenJDK doesn't have bugs wrt nanoTime(), and there are no known unresolved bugs at the moment. 6. nanoTime calls can't be reordered within a thread because it's a native method and JVM respects program order. JVM never reorders native method calls because it doesn't know what happens inside them and therefore can't prove that such reorderings would be safe.Dolores
Regarding 5. nanoTime() difference results can be misleading indeed, but not for the reasons presented in other points in this asnwer. But rather for reasons presented here: shipilev.net/blog/2014/nanotrusting-nanotimeDolores
Ironically, regarding 6. there had been a bug in OpenJDK specifically due to reorderings: bugs.openjdk.java.net/browse/JDK-8184271. In OpenJDK, nanoTime() is an intrinsic and it was permitted to be reordered, that was a bug.Dolores
@leventov, so is nanotime() safe to use? meaning, it can't return negative values and is ~accurate as far as time goes. I do not see the point of exposing an API function that is riddled with issues. This post is a proof, started in 2009 and still commented on in 2019. For mission critical stuff, I imagine people rely on timing cards like SymmetricomClown
@leventon - you are wrong about #4. nanoTime() definitely returned negative values in multi-threaded JVM few years ago. that's a fact.Clown
Yes, nanoTime is safe (since JDK 7 released in 2011, modulo bugs in OpenJDK, the last one of which (referenced above) is fixed in 2017).Dolores
Your #4 says about negative difference, not values: "there is a chance of getting nanoTime() which results in a negative difference."Dolores
G
1

Java is crossplatform, and nanoTime is platform-dependent. If you use Java - when don't use nanoTime. I found real bugs across different jvm implementations with this function.

Gerbil answered 21/9, 2011 at 10:30 Comment(0)
K
0

The Java 5 documentation also recommends using this method for the same purpose.

This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time.

Java 5 API Doc

Knowhow answered 4/2, 2009 at 8:31 Comment(0)
F
0

Also, System.currentTimeMillies() changes when you change your systems clock, while System.nanoTime() doesn't, so the latter is safer to measure durations.

Fleurdelis answered 20/8, 2015 at 12:14 Comment(0)
U
-3

nanoTime is extremely insecure for timing. I tried it out on my basic primality testing algorithms and it gave answers which were literally one second apart for the same input. Don't use that ridiculous method. I need something that is more accurate and precise than get time millis, but not as bad as nanoTime.

Unbalanced answered 24/4, 2012 at 4:46 Comment(1)
without a source or a better explanation this comment is uselessRodmur

© 2022 - 2024 — McMap. All rights reserved.