System.currentTimeMillis vs System.nanoTime
Asked Answered
D

10

416

Accuracy Vs. Precision

What I would like to know is whether I should use System.currentTimeMillis() or System.nanoTime() when updating my object's positions in my game? Their change in movement is directly proportional to the elapsed time since the last call and I want to be as precise as possible.

I've read that there are some serious time-resolution issues between different operating systems (namely that Mac / Linux have an almost 1 ms resolution while Windows has a 50ms resolution??). I'm primarly running my apps on windows and 50ms resolution seems pretty inaccurate.

Are there better options than the two I listed?

Any suggestions / comments?

Dockery answered 9/12, 2008 at 1:58 Comment(6)
nanoTime is significantly usually more accurate than currentTimeMillis but it's a relatively expensive call as well.currentTimeMillis() runs in a few (5-6) cpu clocks, nanoTime depends on the underlying architecture and can be 100+ cpu clocks.Massasauga
You do all realise that Windows generally has a timeslice granularity of 1000ms / 64, right? Which is 15.625ms, or 15625000nanoseconds!Palawan
I don't think a hundred extra clock cycles are going to impact your game, and the trade off would probably be worth it. You should only be calling the method once per game update then saving the value in mem, so it won't add a lot of overhead. As for the granularity of different platforms, I have no idea.Shrub
Windows has a DEFAULT timeslice granularity of 1000ms/64. You can increase this through the native timeBeginPeriod API. Modern PCs also have high-resolution timers in addition to the basic timer. High-resolution timers are accessible via the QueryPerformanceCounter call.Guildhall
@Massasauga how to find out the cpu clocks usage?Roanne
@Roanne - This article goes into detail about the inner workings of System.currentTimeMillis(): pzemtsov.github.io/2017/07/23/the-slow-currenttimemillis.htmlCompete
C
346

If you're just looking for extremely precise measurements of elapsed time, use System.nanoTime(). System.currentTimeMillis() will give you the most accurate possible elapsed time in milliseconds since the epoch, but System.nanoTime() gives you a nanosecond-precise time, relative to some arbitrary point.

From the Java Documentation:

public static long nanoTime()

Returns the current value of the most precise available system timer, in nanoseconds.

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 origin 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 years (263 nanoseconds) will not accurately compute elapsed time due to numerical overflow.

For example, to measure how long some code takes to execute:

long startTime = System.nanoTime();    
// ... the code being measured ...    
long estimatedTime = System.nanoTime() - startTime;

See also: JavaDoc System.nanoTime() and JavaDoc System.currentTimeMillis() for more info.

Cashandcarry answered 9/12, 2008 at 2:6 Comment(9)
Are you sure you know the difference between accuracy and precision? There is no way it is accurate to a nanosecond precision.Dockery
Sorry, I meant precise. I was using the term loosely, but I agree it was confusing (and an improper use of the word).Cashandcarry
And since you want elapsed time, you don't care about accuracy, you care about precision. System.nanoTime() will give you the most precise time interval possible.Cashandcarry
@dancavallaro, thanks for the information. If you don't mind, I edited your answer to include a quote from the docs and fixed up the linksDockery
successive calls that span greater than approximately 292 years .... my new fav quote!Nigel
This answer is technically correct in choosing nanoTime() but completely glosses over an extremely important point. nanoTime(), as the doc says, is a precision timer. currentTimeMillis() is NOT A TIMER, it is the "wall clock". nanoTime() will always produce positive elapsed time, currentTimeMillis will not (e.g. if you change the date, hit a leap second, etc.) This is an extremely important distinction for some types of systems.Bodiless
User changing time and NTP sync sure, but why would currentTimeMillis change due to DST? DST switch doesn't change the number of seconds past the epoch. It may be a "wall clock" but it is one based on UTC. You must determine based on your timezone and DST settings what that translates to for your local time (or use other Java utilities to do that for you).Komara
System.nanoTime() is not always monotonic steveloughran.blogspot.com/2015/09/…Pronghorn
@Bodiless read the docs again: "The value returned represents nanoseconds since some fixed but arbitrary origin time (perhaps in the future, so values may be negative)." Also, notice that "This method provides nanosecond precision, but not necessarily nanosecond resolution (that is, how frequently the value changes)"; Could someone please point out which platforms this is true for?Kordula
E
123

Since no one else has mentioned this…

It is not safe to compare the results of System.nanoTime() calls between different JVMs, each JVM may have an independent 'origin' time.

System.currentTimeMillis() will return the (approximate) same value between JVMs, because it is tied to the system wall clock time.

If you want to compute the amount of time that has elapsed between two events, like a stopwatch, use nanoTime(); changes in the system wall-clock make currentTimeMillis() incorrect for this use case.

Exit answered 16/2, 2012 at 16:3 Comment(9)
On Windows that only holds until SP2 according to: stackoverflow.com/questions/510462/…Ossification
Well, you learn something new every day. I suspect, though, that given it was unsafe in the past (definitely returns nonsense readings across threads), such usage is probably still outside the spec and so should probably be avoided.Exit
@jgubby: Very interesting... any reference to support that is not safe to compare the results of System.nanoTime() calls between different Threads? The following links are worth to see: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6519418 docs.oracle.com/javase/7/docs/api/java/lang/…Clothes
Looking at the description mentioned here: docs.oracle.com/javase/7/docs/api/java/lang/…, it seems like the difference between the values returned from nanoTime are valid for comparison as long as they were from the same JVM - The values returned by this method become meaningful only when the difference between two such values, obtained within the same instance of a Java virtual machine, is computed.Garrow
This is not entirely correct, nanoTime() returns consistently across threads unless your system is buggy, this number will increment between calls as expected. On the other hand System.currentTimeMillis() can jump backwards even on the same thread because it represents wall time, which can be adjusted by outside factors, such as NTP updates.Wehrle
I don't see any reason to measure time elapsed between different threads in the first place. But either way, I would like to add a "citation needed" on your statement that System.nanoTime() is not thread safe.Halfsole
The JavaDoc for nanoTime() says: 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. which means that it does return the same across threads.Halfsole
This answer is wrong. Exactly the opposite is the case: currentTimeMillis() is not thread-safe (in the assumed meaning, i. e. monotonically non-decreasing results), even within a single thread. nanoTime() is thread-safe.Wapiti
@leventov, I don't think the purpose of editing answers includes essentially a full re-write. You're correct in that nanoTime is safe within all threads of any given JVM, but it's probably better to create a different answer and downvote this one.Anhedral
B
60

Update by Arkadiy: I've observed more correct behavior of System.currentTimeMillis() on Windows 7 in Oracle Java 8. The time was returned with 1 millisecond precision. The source code in OpenJDK has not changed, so I do not know what causes the better behavior.


David Holmes of Sun posted a blog article a couple years ago that has a very detailed look at the Java timing APIs (in particular System.currentTimeMillis() and System.nanoTime()), when you would want to use which, and how they work internally.

Inside the Hotspot VM: Clocks, Timers and Scheduling Events - Part I - Windows

One very interesting aspect of the timer used by Java on Windows for APIs that have a timed wait parameter is that the resolution of the timer can change depending on what other API calls may have been made - system wide (not just in the particular process). He shows an example where using Thread.sleep() will cause this resolution change.

Bugaboo answered 9/12, 2008 at 6:6 Comment(4)
@Arkadiy: Do you have a source for the statement in the update?Mission
@Mission - Unfortunately, no. It comes from me running the code in this question:stackoverflow.com/questions/34090914/… . The code produces 15ms precision with Java 7 and 1 ms precision with Java 8Oklahoma
I traced the currentTimeMillis to os::javaTimeMillis in hotspot/src/os/windows/vm/os_windows.cpp in OpenJDK (hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/os/…). Looks like it's still GetSystemTimeAsFileTime, so I do not know where change comes from. Or if it's even valid. Test before using.Oklahoma
the change in behaviour is the result of changing how GetSystemTimeAsFileTime worked in XP vs 7. See here for more details (tl;dr it got more precise since the entire system introduced some more precise timing methods).Keshiakesia
S
12

As others have said, currentTimeMillis is clock time, which changes due to daylight saving time (not: daylight saving & time zone are unrelated to currentTimeMillis, the rest is true), users changing the time settings, leap seconds, and internet time sync. If your app depends on monotonically increasing elapsed time values, you might prefer nanoTime instead.

You might think that the players won't be fiddling with the time settings during game play, and maybe you'd be right. But don't underestimate the disruption due to internet time sync, or perhaps remote desktop users. The nanoTime API is immune to this kind of disruption.

If you want to use clock time, but avoid discontinuities due to internet time sync, you might consider an NTP client such as Meinberg, which "tunes" the clock rate to zero it in, instead of just resetting the clock periodically.

I speak from personal experience. In a weather application that I developed, I was getting randomly occurring wind speed spikes. It took a while for me to realize that my timebase was being disrupted by the behavior of clock time on a typical PC. All my problems disappeared when I started using nanoTime. Consistency (monotonicity) was more important to my application than raw precision or absolute accuracy.

Susceptible answered 25/9, 2013 at 0:28 Comment(1)
"currentTimeMillis is clock time, which changes due to daylight saving time" ... pretty sure that this statement is false. System.currentTimeMillis() reports elapsed time (in milliseconds) since the Unix/Posix Epoch which is Midnight, Jan 1, 1970 UTC. Because UTC is never adjusted for daylight savings, this value will not be offset when local time zones add or subject offsets to local times for daylight savings. Moreover, the Java Time Scale smooths out leap seconds so that days continue to appear to have 86,400 seconds.Maybellemayberry
P
11

System.nanoTime() isn't supported in older JVMs. If that is a concern, stick with currentTimeMillis

Regarding accuracy, you are almost correct. On SOME Windows machines, currentTimeMillis() has a resolution of about 10ms (not 50ms). I'm not sure why, but some Windows machines are just as accurate as Linux machines.

I have used GAGETimer in the past with moderate success.

Popovich answered 9/12, 2008 at 2:6 Comment(2)
"older JVMs" as in which ones? Java 1.2 or something?Halfsole
System.nanoTime() was introduced in Java 1.5 in 2004. Java 1.4 extended support in 2013, so it's pretty safe to say System.nanoTime() can now be relied on and this answer is now out of date.Thermomagnetic
I
5

Yes, if such precision is required use System.nanoTime(), but be aware that you are then requiring a Java 5+ JVM.

On my XP systems, I see system time reported to at least 100 microseconds 278 nanoseconds using the following code:

private void test() {
    System.out.println("currentTimeMillis: "+System.currentTimeMillis());
    System.out.println("nanoTime         : "+System.nanoTime());
    System.out.println();

    testNano(false);                                                            // to sync with currentTimeMillis() timer tick
    for(int xa=0; xa<10; xa++) {
        testNano(true);
        }
    }

private void testNano(boolean shw) {
    long strMS=System.currentTimeMillis();
    long strNS=System.nanoTime();
    long curMS;
    while((curMS=System.currentTimeMillis()) == strMS) {
        if(shw) { System.out.println("Nano: "+(System.nanoTime()-strNS)); }
        }
    if(shw) { System.out.println("Nano: "+(System.nanoTime()-strNS)+", Milli: "+(curMS-strMS)); }
    }
Imbrue answered 9/12, 2008 at 2:24 Comment(0)
A
5

For game graphics & smooth position updates, use System.nanoTime() rather than System.currentTimeMillis(). I switched from currentTimeMillis() to nanoTime() in a game and got a major visual improvement in smoothness of motion.

While one millisecond may seem as though it should already be precise, visually it is not. The factors nanoTime() can improve include:

  • accurate pixel positioning below wall-clock resolution
  • ability to anti-alias between pixels, if you want
  • Windows wall-clock inaccuracy
  • clock jitter (inconsistency of when wall-clock actually ticks forward)

As other answers suggest, nanoTime does have a performance cost if called repeatedly -- it would be best to call it just once per frame, and use the same value to calculate the entire frame.

Abiogenesis answered 17/8, 2017 at 2:29 Comment(0)
A
3

System.currentTimeMillis() is not safe for elapsed time because this method is sensitive to the system realtime clock changes of the system. You should use System.nanoTime. Please refer to Java System help:

About nanoTime method:

.. This method provides nanosecond precision, but not necessarily nanosecond resolution (that is, how frequently the value changes) - no guarantees are made except that the resolution is at least as good as that of currentTimeMillis()..

If you use System.currentTimeMillis() your elapsed time can be negative (Back <-- to the future)

Apart answered 28/11, 2017 at 15:48 Comment(0)
C
1

I've had good experience with nanotime. It provides wall-clock time as two longs (seconds since the epoch and nanoseconds within that second), using a JNI library. It's available with the JNI part precompiled for both Windows and Linux.

Cartomancy answered 21/4, 2011 at 11:1 Comment(0)
D
-3

one thing here is the inconsistency of the nanoTime method.it does not give very consistent values for the same input.currentTimeMillis does much better in terms of performance and consistency,and also ,though not as precise as nanoTime,has a lower margin of error,and therefore more accuracy in its value. i would therefore suggest that you use currentTimeMillis

Dual answered 24/4, 2012 at 4:52 Comment(1)
As noted in other answers and comments, currentTimeMillis is subject to system clock changes and therefore a poor choice for calculating elapsed time since some earlier event in the JVM.Lebaron

© 2022 - 2024 — McMap. All rights reserved.