Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6519418

(spec) System.nanoTime() needs substantial clarification

    XMLWordPrintable

Details

    • b15
    • x86
    • windows_xp
    • Not verified

    Backports

      Description

        A DESCRIPTION OF THE REQUEST :
        The System.nanoTime() method added in JDK5 needs to be more clearly specified with respect to scope of use, granularity, and relative relationship to System.currentTimeMillis(). I recommend specific javadoc enhancements to both of these methods in these three areas.

        First, the scope issue.

        In Bug 6380553, Doug Lea writes: "System.nanoTime [...] returns time relative to an arbitrary but fixed offset". The JDK6 javadoc says: "Returns the current value of the most precise available system timer [...] represent[ing] nanoseconds since some fixed but arbitrary time."

        What remains unclear is to what extent a developer can rely on the "arbitrary but fixed offset" changing. It's clear that nanoTime() has a "mutable epoch", but what are the boundaries across which a developer can rely on that epoch NOT changing? The SDN is sometimes ambiguous on this critical issue. For example:

        http://java.sun.com/docs/books/tutorial/essential/environment/sysmisc.html

        suggests that nanoTime() is accurate across the scope of an entire application (which I assume includes all threads, classes and methods).

        The javadoc for java.lang.System.nanoTime() refers to a "system timer", which suggests that the nanoTime() epoch would be the same across all applications running on the same "system". This, of course, begs the question about how the term "system" relates to things like a "JVM", "host machine", "Java Server", etc.

        One other meaningful and authorative SDN reference:

        http://java.sun.com/developer/technicalArticles/Media/timing/index.html

          states that nanoTime() returns "a time value that is useful only when compared to other return values from that function". The wording "that function" is unfortunately ambiguous. Which "function"? The nanoTime() method, or the method calling nanoTime()?

        The times at which this "mutable epoch" may be reset need to be clarified. Otherwise it's unclear from the API if these values, for example, can be persisted. They could, technically, still be used by the same "application" on the same "system", but if the epoch is reset on machine boot, then nanoTime() values in a database or on disk become meaningless.

        Next: granularity. This term, and the related terms "accuracy", "precision" and "resolution" should be clarified as synonymous, or else they should be defined so that nanoTime() and currentTimeMillis() can be clearly understood and differentiated using them.

        I'll use the terms loosely here. Common sense suggests that that nanoTime() would always provide measurements no worse in accuracy than currentTimeMillis(). That is not explicitly stated, but it seems to be the intention. The extent to which that intention is realized on various platforms seems to cause a lot of confusion in the community.

        A few examples for common platforms would be really helpful, so that peoples' expectations can be set properly. There are many threads on the web (SDN and elsewhere) which illustrate the frustration people have when the actual resolution of nanoTime() is far, far less than expected.

        Finally: relative relationship.

        Simple math tells us that 1 millisecond = 1 million nanoseconds. Unfortunately, this forum thread:

        http://forum.java.sun.com/thread.jspa?forumID=426&threadID=730138

        strongly suggests that there may be "drift" between the two native sources of time used by currentTimeMillis() and nanoTime(). My limited testing supports this theory of relative clock drift, which after only 10 minutes was as much as 12 milliseconds.

        If code like the following is not kosher, the API should spell that out:

        long start = System.nanoTime();
        doSomething();
        long millis = (System.nanoTime() - start) / (1000 * 1000);
        Date started = new Date(System.currentTimeMillis() - millis);

        Otherwise the very concept of a millisecond will be ambiguous in Java.


        JUSTIFICATION :
        System.nanoTime() solved two important problems. 1) increased timing accuracy on platforms which provide it, and 2) a timing mechanism immune to changes in the platform O/S clock.

        Since then a number of issues have arisen which confuse developers about the proper use of nanoTime(), its limitations, and its relationship to currentTimeMillis(). These can be easily corrected in the javadoc.

        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        Enhance the javadoc of the java.lang.System class as follows:

        1) clearly document the scope in which a developer may rely on the "epoch" of nanoTime() method: thread, method, program, JVM, boot, etc.

        2) clearly document the terms "granularity", "accuracy", "precision", and "resolution" (others?) as needed, and use them to clarify:

        2a) the intended differences between currentTimeMillis() and nanoTime(), and

        2b) actual accuracy example for both methods on several of the most common platforms, to help set developer expectations.

        3) clearly document the compatibility between the millisecond differentials which are computed using currentTimeMillis() versus nanoTime(), addressing concerns about relative clock drift.
        ACTUAL -
        The existing JDK5 and JDK6 javadoc is unclear on these issues.

        ---------- BEGIN SOURCE ----------
        public class Test { // illustrates clock "drift"

            public static void main(String args[]) throws Exception {
                long startN = System.nanoTime();
                long startM = System.currentTimeMillis();
                while (true) {
                    Thread.sleep(60000);
                    long endM = System.currentTimeMillis();
                    long endN = System.nanoTime();
                    double diffN = endN - startN;
                    diffN /= 1000 * 1000;
                    System.out.println(diffN + "\t" + (endM - startM));
                }
            }
        }

        ---------- END SOURCE ----------

        CUSTOMER SUBMITTED WORKAROUND :
        Spend a lot of time searching the SDN and elsewhere for everyone's best guess about these issues (optional). Then make your own best guess.

        My recommendations at this point in time (see the Comments section below for updates):

        1) Assume the nanoTime() "epoch" is only consistent within a single JVM. Do not persist values or share them via IPC outside a single JVM.

        2) Don't assume you get a lot more timing accuracy with nanoTime(). But switch to it everywhere you're doing "timing" of something in code which should be immune to changes in the PC clock.

        3) Don't mix millisecond values from currentTimeMillis() with milliseconds from (nanoTime() / (1000*1000)). If you do, beware that relative clock drift may skew your results by as much as several milliseconds per minute.

        Attachments

          Issue Links

            Activity

              People

                martin Martin Buchholz
                keshah Ketan Shah (Inactive)
                Votes:
                0 Vote for this issue
                Watchers:
                1 Start watching this issue

                Dates

                  Created:
                  Updated:
                  Resolved:
                  Imported:
                  Indexed: