System.currentTimeMillis vs System.nanoTime

379

Genauigkeit Vs. Präzision

Was würde Ich mag es , wissen , ob ich verwenden sollte System.currentTimeMillis () oder System.nanoTime () , wenn mein Objekt der Positionen in meinem Spiel zu aktualisieren? Ihre Bewegungsänderung ist direkt proportional zur seit dem letzten Anruf verstrichenen Zeit, und ich möchte so genau wie möglich sein.

Ich habe gelesen, dass es einige schwerwiegende Probleme mit der Zeitauflösung zwischen verschiedenen Betriebssystemen gibt (nämlich, dass Mac / Linux eine Auflösung von fast 1 ms hat, während Windows eine Auflösung von 50 ms hat ??). Ich verwende meine Apps hauptsächlich unter Windows und eine Auflösung von 50 ms scheint ziemlich ungenau zu sein.

Gibt es bessere Optionen als die beiden, die ich aufgelistet habe?

Anregungen / Kommentare?

mmcdole
quelle
81
nanoTimeist in der Regel wesentlich genauer als currentTimeMillis, aber es ist auch ein relativ teurer Anruf. currentTimeMillis()läuft in wenigen (5-6) CPU-Uhren, nanoTime hängt von der zugrunde liegenden Architektur ab und kann über 100 CPU-Uhren sein.
Bests
10
Sie alle wissen, dass Windows im Allgemeinen eine Zeitscheibengranularität von 1000 ms / 64 hat, oder? Welches ist 15.625ms oder 15625000nanosekunden!
7
Ich denke nicht, dass hundert zusätzliche Taktzyklen Ihr Spiel beeinflussen werden, und der Kompromiss wäre es wahrscheinlich wert. Sie sollten die Methode nur einmal pro Spielaktualisierung aufrufen und dann den Wert in mem speichern, damit nicht viel Aufwand entsteht. Was die Granularität verschiedener Plattformen betrifft, habe ich keine Ahnung.
Aglassman
9
Windows hat eine DEFAULT-Zeitscheibengranularität von 1000 ms / 64. Sie können dies über die native timeBeginPeriod-API erhöhen. Moderne PCs verfügen neben dem Basis-Timer auch über hochauflösende Timer. Auf hochauflösende Timer kann über den QueryPerformanceCounter-Aufruf zugegriffen werden.
Robin Davies
2
@ Gohan - Dieser Artikel geht detailliert auf das Innenleben von System.currentTimeMillis(): pzemtsov.github.io/2017/07/23/the-slow-currenttimemillis.html
Attila Tanyi

Antworten:

320

Wenn Sie nur nach äußerst präzisen Messungen der verstrichenen Zeit suchen , verwenden Sie System.nanoTime(). System.currentTimeMillis()gibt Ihnen die genauestmögliche verstrichene Zeit in Millisekunden seit der Epoche, System.nanoTime()gibt Ihnen jedoch eine nanosekundengenaue Zeit relativ zu einem beliebigen Punkt.

Aus der Java-Dokumentation:

public static long nanoTime()

Gibt den aktuellen Wert des genauesten verfügbaren Systemzeitgebers in Nanosekunden zurück.

Diese Methode kann nur zur Messung der verstrichenen Zeit verwendet werden und steht in keinem Zusammenhang mit einem anderen Begriff der System- oder Wanduhrzeit. Der zurückgegebene Wert repräsentiert Nanosekunden seit einer festen, aber willkürlichen Ursprungszeit (möglicherweise in der Zukunft, daher können die Werte negativ sein). Diese Methode bietet eine Nanosekundengenauigkeit, jedoch nicht unbedingt eine Nanosekundengenauigkeit. Es wird keine Garantie dafür übernommen, wie häufig sich Werte ändern. Unterschiede bei aufeinanderfolgenden Aufrufen, die länger als ungefähr 292 Jahre (2 63 Nanosekunden) dauern, berechnen die verstrichene Zeit aufgrund des numerischen Überlaufs nicht genau.

Um beispielsweise zu messen, wie lange die Ausführung von Code dauert:

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

Siehe auch: JavaDoc System.nanoTime () und JavaDoc System.currentTimeMillis () für weitere Informationen.

Dancavallaro
quelle
20
Sind Sie sicher, dass Sie den Unterschied zwischen Genauigkeit und Präzision kennen? Es gibt keine Möglichkeit, dass es auf eine Nanosekundengenauigkeit genau ist.
mmcdole
6
Entschuldigung, ich meinte genau. Ich habe den Begriff locker verwendet, aber ich stimme zu, dass er verwirrend war (und eine missbräuchliche Verwendung des Wortes).
Dancavallaro
4
@dancavallaro, danke für die Information. Wenn es Ihnen nichts ausmacht, habe ich Ihre Antwort so bearbeitet, dass sie ein Zitat aus den Dokumenten enthält, und die Links
korrigiert
135
Diese Antwort ist bei der Auswahl von nanoTime () technisch korrekt, beschönigt jedoch einen äußerst wichtigen Punkt vollständig. nanoTime () ist, wie das Dokument sagt, ein Präzisions-Timer. currentTimeMillis () ist KEIN TIMER, sondern die "Wanduhr". nanoTime () erzeugt immer eine positive verstrichene Zeit, currentTimeMillis nicht (z. B. wenn Sie das Datum ändern, eine Schaltsekunde erreichen usw.). Dies ist eine äußerst wichtige Unterscheidung für einige Systemtypen.
Charstar
11
Benutzerwechselzeit und NTP-Synchronisierung sicher, aber warum sollte currentTimeMillissich dies aufgrund der Sommerzeit ändern? Der DST-Schalter ändert nicht die Anzahl der Sekunden nach der Epoche. Es kann eine "Wanduhr" sein, aber sie basiert auf UTC. Sie müssen anhand Ihrer Zeitzonen- und Sommerzeiteinstellungen bestimmen, was dies für Ihre Ortszeit bedeutet (oder andere Java-Dienstprogramme verwenden, um dies für Sie zu tun).
Shadow Man
100

Da hat das sonst niemand erwähnt ...

Es ist nicht sicher, die Ergebnisse von System.nanoTime()Aufrufen zwischen verschiedenen Threads zu vergleichen . Selbst wenn die Ereignisse der Threads in einer vorhersagbaren Reihenfolge auftreten, kann der Unterschied in Nanosekunden positiv oder negativ sein.

System.currentTimeMillis() ist sicher für die Verwendung zwischen Threads.

mollig
quelle
4
Unter Windows gilt das nur bis SP2 laut: stackoverflow.com/questions/510462/…
Peter Schmitz
3
Nun, Sie lernen jeden Tag etwas Neues. Ich vermute jedoch, dass eine solche Verwendung, da sie in der Vergangenheit unsicher war (definitiv unsinnige Messwerte über Threads hinweg zurückgibt), wahrscheinlich immer noch außerhalb der Spezifikation liegt und daher wahrscheinlich vermieden werden sollte.
Gubby
4
@jgubby: Sehr interessant ... Gibt es einen Hinweis auf Unterstützung, der nicht sicher ist, um die Ergebnisse von System.nanoTime () -Aufrufen zwischen verschiedenen Threads zu vergleichen ? Die folgenden Links sind sehenswert: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6519418 docs.oracle.com/javase/7/docs/api/java/lang/…
user454322
15
Wenn man sich die hier erwähnte Beschreibung ansieht : docs.oracle.com/javase/7/docs/api/java/lang/… , scheint der Unterschied zwischen den von nanoTime zurückgegebenen Werten zum Vergleich gültig zu sein, solange sie von denselben stammen 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.
Tuxdude
7
Das JavaDoc fürnanoTime() sagt: Der gleiche Ursprung wird von allen Aufrufen dieser Methode in einer Instanz einer virtuellen Java-Maschine verwendet; Andere Instanzen virtueller Maschinen verwenden wahrscheinlich einen anderen Ursprung. was bedeutet , dass sie nicht das gleiche über Threads zurück.
Simon Forsberg
58

Update von Arkadiy : Ich habe unter System.currentTimeMillis()Oracle 7 in Windows Java 8 ein korrekteres Verhalten beobachtet . Die Zeit wurde mit einer Genauigkeit von 1 Millisekunde zurückgegeben. Der Quellcode in OpenJDK hat sich nicht geändert, daher weiß ich nicht, was das bessere Verhalten verursacht.


David Holmes von Sun hat vor ein paar Jahren einen Blog-Artikel veröffentlicht, der einen sehr detaillierten Überblick über die Java-Timing-APIs (insbesondere System.currentTimeMillis()und System.nanoTime()) gibt, wann Sie welche verwenden möchten und wie sie intern funktionieren.

Innerhalb der Hotspot-VM: Uhren, Timer und Planungsereignisse - Teil I - Windows

Ein sehr interessanter Aspekt des von Java unter Windows für APIs mit einem zeitgesteuerten Warteparameter verwendeten Timers ist, dass sich die Auflösung des Timers abhängig davon ändern kann, welche anderen API-Aufrufe möglicherweise getätigt wurden - systemweit (nicht nur im jeweiligen Prozess) . Er zeigt ein Beispiel, in dem die Verwendung Thread.sleep()diese Auflösungsänderung verursacht.

Michael Burr
quelle
1
@Arkadiy: Haben Sie eine Quelle für die Anweisung im Update?
Lii
@Lii - Leider nein. Ich habe den Code in dieser Frage ausgeführt: stackoverflow.com/questions/34090914/… . Der Code erzeugt eine Genauigkeit von
2
Ich habe die currentTimeMillis auf os :: javaTimeMillis in hotspot / src / os / windows / vm / os_windows.cpp in OpenJDK ( hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/os/… ) zurückgeführt . Sieht so aus, als wäre es immer noch GetSystemTimeAsFileTime, daher weiß ich nicht, woher die Änderung kommt. Oder ob es überhaupt gültig ist. Vor Gebrauch testen.
die Änderung im Verhalten ist das Ergebnis zu verändern , wie GetSystemTimeAsFileTimein XP vs 7. Siehe arbeitete hier für weitere Einzelheiten (tl; dr es genauer bekam , da das gesamte System einige genauere Timing Methoden eingeführt).
11

System.nanoTime()wird in älteren JVMs nicht unterstützt. Wenn das ein Problem ist, bleiben Sie beicurrentTimeMillis

In Bezug auf die Genauigkeit sind Sie fast richtig. Hat auf einigen Windows-Computern currentTimeMillis()eine Auflösung von ca. 10 ms (nicht 50 ms). Ich bin mir nicht sicher warum, aber einige Windows-Computer sind genauso genau wie Linux-Computer.

Ich habe GAGETimer in der Vergangenheit mit mäßigem Erfolg verwendet.

Paul Morel
quelle
3
"ältere JVMs" wie in welchen? Java 1.2 oder so?
Simon Forsberg
1
System.nanoTime () wurde 2004 in Java 1.5 eingeführt. Die erweiterte Unterstützung für Java 1.4 wurde 2013 erweitert. Man kann also mit ziemlicher Sicherheit sagen, dass man sich jetzt auf System.nanoTime () verlassen kann und diese Antwort veraltet ist.
Dave L.
9

Wie bereits erwähnt, handelt es sich bei currentTimeMillis um die Uhrzeit, die sich aufgrund der Sommerzeit, der Änderung der Zeiteinstellungen, der Schaltsekunden und der Internetzeitsynchronisierung ändert. Wenn Ihre App von monoton ansteigenden Werten für die verstrichene Zeit abhängt, bevorzugen Sie möglicherweise stattdessen nanoTime.

Sie könnten denken, dass die Spieler während des Spiels nicht an den Zeiteinstellungen herumspielen, und vielleicht haben Sie Recht. Unterschätzen Sie jedoch nicht die Störung aufgrund der Internet-Zeitsynchronisierung oder möglicherweise von Remotedesktopbenutzern. Die nanoTime-API ist gegen diese Art von Störung immun.

Wenn Sie die Uhrzeit verwenden möchten, aber Diskontinuitäten aufgrund der Internet-Zeitsynchronisierung vermeiden möchten, sollten Sie einen NTP-Client wie Meinberg in Betracht ziehen, der die Taktrate so einstellt, dass sie auf Null gesetzt wird, anstatt nur die Uhr regelmäßig zurückzusetzen.

Ich spreche aus persönlicher Erfahrung. In einer von mir entwickelten Wetteranwendung bekam ich zufällig auftretende Windgeschwindigkeitsspitzen. Es dauerte eine Weile, bis mir klar wurde, dass meine Zeitbasis durch das Verhalten der Uhrzeit auf einem typischen PC gestört wurde. Alle meine Probleme verschwanden, als ich anfing, nanoTime zu verwenden. Konsistenz (Monotonie) war für meine Anwendung wichtiger als rohe Präzision oder absolute Genauigkeit.

KarlU
quelle
19
"currentTimeMillis ist die Uhrzeit, die sich aufgrund der Sommerzeit ändert" ... ziemlich sicher, dass diese Aussage falsch ist. System.currentTimeMillis()meldet die verstrichene Zeit (in Millisekunden) seit der Unix / Posix-Epoche, die Mitternacht, 1. Januar 1970 UTC, ist. Da UTC niemals an die Sommerzeit angepasst wird, wird dieser Wert nicht ausgeglichen, wenn lokale Zeitzonen die lokalen Zeiten für die Sommerzeit hinzufügen oder versetzen. Darüber hinaus glättet die Java-Zeitskala Schaltsekunden, sodass die Tage weiterhin 86.400 Sekunden zu haben scheinen.
Scottb
5

Ja, wenn eine solche Genauigkeit erforderlich ist, verwenden Sie System.nanoTime()diese. Beachten Sie jedoch, dass Sie eine Java 5+ JVM benötigen.

Auf meinen XP-Systemen wird die Systemzeit mit dem folgenden Code auf mindestens 100 Mikrosekunden und 278 Nanosekunden gemeldet :

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)); }
    }
Lawrence Dol
quelle
4

Verwenden Sie für Spielgrafiken und reibungslose Positionsaktualisierungen System.nanoTime()anstelle von System.currentTimeMillis(). Ich habe in einem Spiel von currentTimeMillis () zu nanoTime () gewechselt und eine deutliche visuelle Verbesserung der Bewegungsruhe erzielt.

Während eine Millisekunde so aussieht, als ob sie bereits präzise sein sollte, ist dies visuell nicht der Fall. Folgende Faktoren nanoTime()können sich verbessern:

  • genaue Pixelpositionierung unterhalb der Wanduhrauflösung
  • Fähigkeit zum Anti-Alias ​​zwischen Pixeln, wenn Sie möchten
  • Ungenauigkeit der Windows-Wanduhr
  • Clock Jitter (Inkonsistenz, wenn die Wanduhr tatsächlich vorwärts tickt)

Wie andere Antworten vermuten lassen, verursacht nanoTime bei wiederholtem Aufruf Leistungskosten. Am besten rufen Sie es nur einmal pro Frame auf und verwenden denselben Wert, um den gesamten Frame zu berechnen.

Thomas W.
quelle
1

Ich habe gute Erfahrungen mit Nanotime gemacht . Es liefert eine Wanduhrzeit in zwei Längen (Sekunden seit der Epoche und Nanosekunden innerhalb dieser Sekunde) unter Verwendung einer JNI-Bibliothek. Es ist mit dem vorkompilierten JNI-Teil für Windows und Linux verfügbar.

Jon Bright
quelle
1

System.currentTimeMillis()ist für die verstrichene Zeit nicht sicher, da diese Methode empfindlich auf Änderungen der System-Echtzeituhr des Systems reagiert. Du solltest benutzenSystem.nanoTime . Weitere Informationen finden Sie in der Hilfe zum Java-System:

Informationen zur nanoTime-Methode:

.. Diese Methode bietet Nanosekundengenauigkeit, aber nicht unbedingt Nanosekundenauflösung (dh wie häufig sich der Wert ändert). Es werden keine Garantien gegeben, außer dass die Auflösung mindestens so gut ist wie die von currentTimeMillis ().

Wenn Sie System.currentTimeMillis()Ihre verstrichene Zeit nutzen, kann dies negativ sein (Zurück <- in die Zukunft)

Ricardo Gasca
quelle
-3

Eine Sache hier ist die Inkonsistenz der nanoTime-Methode. Sie liefert keine sehr konsistenten Werte für dieselbe Eingabe. currentTimeMillis ist in Bezug auf Leistung und Konsistenz viel besser und hat, obwohl nicht so genau wie nanoTime, eine geringere Fehlerquote und damit mehr Genauigkeit in seinem Wert. Ich würde daher vorschlagen, dass Sie currentTimeMillis verwenden

Sarvesh
quelle
6
Wie in anderen Antworten und Kommentaren erwähnt, unterliegt currentTimeMillis Änderungen der Systemuhr und ist daher eine schlechte Wahl für die Berechnung der seit einem früheren Ereignis in der JVM verstrichenen Zeit.
Duelin Marker