System.currentTimeMillis vs System.nanoTime


379

Precisione vs. Precisione

Quello che vorrei sapere è se dovrei usare System.currentTimeMillis () o System.nanoTime () quando aggiorno le posizioni dei miei oggetti nel mio gioco? Il loro cambio di movimento è direttamente proporzionale al tempo trascorso dall'ultima chiamata e voglio essere il più preciso possibile.

Ho letto che ci sono alcuni seri problemi di risoluzione temporale tra diversi sistemi operativi (vale a dire che Mac / Linux hanno una risoluzione di quasi 1 ms mentre Windows ha una risoluzione di 50ms ??). Sto eseguendo principalmente le mie app su Windows e la risoluzione di 50ms sembra piuttosto imprecisa.

Ci sono opzioni migliori rispetto alle due che ho elencato?

Qualche suggerimento / commento?


81
nanoTimeè in genere significativamente più accurato di currentTimeMillis ma è anche una chiamata relativamente costosa. currentTimeMillis()funziona in pochi (5-6) orologi cpu, nanoTime dipende dall'architettura sottostante e può essere 100+ orologi cpu.
bestsss

10
Ti rendi conto che Windows ha in genere una granularità a intervalli di 1000ms / 64, giusto? Che è 15,625 ms, o 15625000nanosecondi!

7
Non credo che un centinaio di cicli di clock extra avranno un impatto sul tuo gioco e il compromesso probabilmente varrebbe la pena. Dovresti chiamare il metodo solo una volta per aggiornamento del gioco, quindi salvare il valore in mem, in modo che non aggiunga molto overhead. Per quanto riguarda la granularità di piattaforme diverse, non ne ho idea.
aglassman,

9
Windows ha una granularità predefinita di timeslice di 1000ms / 64. Puoi aumentarlo attraverso l'API nativa timeBeginPeriod. I PC moderni hanno anche timer ad alta risoluzione oltre al timer di base. I timer ad alta risoluzione sono accessibili tramite la chiamata QueryPerformanceCounter.
Robin Davies,

2
@Gohan - Questo articolo approfondisce il funzionamento interno di System.currentTimeMillis(): pzemtsov.github.io/2017/07/23/the-slow-currenttimemillis.html
Attila Tanyi,

Risposte:


320

Se stai solo cercando misure estremamente precise del tempo trascorso , usa System.nanoTime(). System.currentTimeMillis()ti darà il tempo trascorso più preciso possibile in millisecondi dall'epoca, ma System.nanoTime()ti darà un tempo di nanosecondi preciso, rispetto ad un punto arbitrario.

Dalla documentazione Java:

public static long nanoTime()

Restituisce il valore corrente del timer di sistema disponibile più preciso, in nanosecondi.

Questo metodo può essere utilizzato solo per misurare il tempo trascorso e non è correlato ad alcuna altra nozione di tempo di sistema o di orologio da parete. Il valore restituito rappresenta i nanosecondi da un tempo di origine fisso ma arbitrario (forse in futuro, quindi i valori possono essere negativi). Questo metodo fornisce precisione dei nanosecondi, ma non necessariamente precisione dei nanosecondi. Non vengono fornite garanzie sulla frequenza con cui i valori cambiano. Le differenze nelle chiamate successive che si estendono per più di circa 292 anni (2 63 nanosecondi) non calcoleranno accuratamente il tempo trascorso a causa dell'overflow numerico.

Ad esempio, per misurare quanto tempo impiega il codice per eseguire:

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

Vedi anche: JavaDoc System.nanoTime () e JavaDoc System.currentTimeMillis () per maggiori informazioni.


20
Sei sicuro di conoscere la differenza tra accuratezza e precisione? Non c'è modo che sia preciso con una precisione in nanosecondi.
mmcdole,

6
Scusa, intendevo preciso. Stavo usando il termine in modo approssimativo, ma sono d'accordo che era confuso (e un uso improprio della parola).
dancavallaro,

4
@dancavallaro, grazie per l'informazione. Se non ti dispiace, ho modificato la tua risposta per includere una citazione dai documenti e risolto i collegamenti
mmcdole,

135
Questa risposta è tecnicamente corretta nella scelta di nanoTime () ma sorprende completamente un punto estremamente importante. nanoTime (), come dice il documento, è un timer di precisione. currentTimeMillis () NON È UN TIMER, è "l'orologio da parete". nanoTime () produrrà sempre un tempo trascorso positivo, currentTimeMillis non lo farà (ad es. se si modifica la data, si preme un secondo bisestile, ecc.) Questa è una distinzione estremamente importante per alcuni tipi di sistemi.
Charstar,

11
L'utente cambia l'ora e la sincronizzazione NTP è sicuro, ma perché dovrebbe currentTimeMilliscambiare a causa dell'ora legale? L'opzione DST non modifica il numero di secondi trascorsi oltre l'epoca. Potrebbe essere un "orologio da parete" ma è basato su UTC. È necessario determinare in base al fuso orario e alle impostazioni dell'ora legale ciò che si traduce per l'ora locale (o utilizzare altre utility Java per farlo per te).
Shadow Man,

100

Dal momento che nessun altro ha menzionato questo ...

Non è sicuro confrontare i risultati delle System.nanoTime()chiamate tra thread diversi. Anche se gli eventi dei thread si verificano in un ordine prevedibile, la differenza in nanosecondi può essere positiva o negativa.

System.currentTimeMillis() è sicuro per l'uso tra i thread.


4
Su Windows che vale solo fino a SP2 secondo: stackoverflow.com/questions/510462/…
Peter Schmitz,

3
Bene, impari qualcosa di nuovo ogni giorno. Sospetto, tuttavia, che dato che non era sicuro in passato (restituisce sicuramente letture senza senso tra i thread), tale utilizzo è probabilmente ancora al di fuori delle specifiche e quindi dovrebbe probabilmente essere evitato.
paffuto

4
@jgubby: Molto interessante ... qualche riferimento al supporto che non è sicuro per confrontare i risultati delle chiamate System.nanoTime () tra thread diversi ? I seguenti link meritano una visita: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6519418 docs.oracle.com/javase/7/docs/api/java/lang/…
user454322

15
Guardando la descrizione menzionata qui: docs.oracle.com/javase/7/docs/api/java/lang/… , sembra che la differenza tra i valori restituiti da nanoTime sia valida per il confronto purché appartenessero allo stesso 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
JavaDoc pernanoTime() dice: La stessa origine viene utilizzata da tutte le invocazioni di questo metodo in un'istanza di una macchina virtuale Java; è probabile che altre istanze di macchine virtuali utilizzino un'origine diversa. il che significa che non restituire lo stesso attraverso i thread.
Simon Forsberg,

58

Aggiornamento di Arkadiy : ho osservato un comportamento più corretto System.currentTimeMillis()su Windows 7 in Oracle Java 8. Il tempo è stato restituito con precisione di 1 millisecondo. Il codice sorgente in OpenJDK non è cambiato, quindi non so quale sia la causa del comportamento migliore.


David Holmes di Sun ha pubblicato un articolo di blog un paio di anni fa che ha uno sguardo molto dettagliato alle API di timing Java (in particolare System.currentTimeMillis()e System.nanoTime()), quando si desidera utilizzare quali e come funzionano internamente.

All'interno della VM Hotspot: orologi, timer ed eventi di pianificazione - Parte I - Windows

Un aspetto molto interessante del timer utilizzato da Java su Windows per le API che hanno un parametro di attesa a tempo è che la risoluzione del timer può cambiare a seconda di quali altre chiamate API potrebbero essere state effettuate - a livello di sistema (non solo nel particolare processo) . Mostra un esempio in cui l'utilizzo Thread.sleep()causerà questo cambiamento di risoluzione.


1
@Arkadiy: hai una fonte per la dichiarazione nell'aggiornamento?
Lii,

@Lii - Purtroppo no. Viene da me che esegue il codice in questa domanda: stackoverflow.com/questions/34090914/… . Il codice produce una precisione di 15 ms con Java 7 e una precisione di 1 ms con Java 8

2
Ho tracciato currentTimeMillis su os :: javaTimeMillis in hotspot / src / os / windows / vm / os_windows.cpp in OpenJDK ( hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/os/… ) . Sembra che sia ancora GetSystemTimeAsFileTime, quindi non so da dove provenga il cambiamento. O se è anche valido. Test prima dell'uso.

il cambiamento nel comportamento è il risultato del cambiamento GetSystemTimeAsFileTimenel funzionamento in XP vs 7. Vedi qui per maggiori dettagli (tl; dr è diventato più preciso da quando l'intero sistema ha introdotto alcuni metodi di timing più precisi).

11

System.nanoTime()non è supportato nelle JVM precedenti. Se questo è un problema, resta concurrentTimeMillis

Per quanto riguarda la precisione, hai quasi ragione. Su alcuni computer Windows, currentTimeMillis()ha una risoluzione di circa 10 ms (non 50 ms). Non sono sicuro del perché, ma alcune macchine Windows sono altrettanto precise delle macchine Linux.

Ho usato GAGETimer in passato con discreto successo.


3
"JVM più vecchie" come in quali? Java 1.2 o qualcosa del genere?
Simon Forsberg,

1
System.nanoTime () è stato introdotto in Java 1.5 nel 2004. Java 1.4 ha esteso il supporto nel 2013, quindi è abbastanza sicuro dire che System.nanoTime () ora può essere invocato e questa risposta non è più aggiornata.
Dave L.,

9

Come altri hanno già detto, currentTimeMillis è l'ora dell'orologio, che cambia a causa dell'ora legale, degli utenti che cambiano le impostazioni dell'ora, dei secondi intercalari e della sincronizzazione dell'ora di Internet. Se la tua app dipende dall'aumento monotonico dei valori di tempo trascorso, potresti preferire nanoTime.

Potresti pensare che i giocatori non giocheranno con le impostazioni del tempo durante il gioco, e forse avresti ragione. Ma non sottovalutare l'interruzione dovuta alla sincronizzazione dell'ora di Internet o agli utenti desktop remoti. L'API nanoTime è immune da questo tipo di interruzione.

Se si desidera utilizzare l'ora, ma evitare discontinuità dovute alla sincronizzazione dell'ora di Internet, si potrebbe prendere in considerazione un client NTP come Meinberg, che "sintonizza" la frequenza di clock per azzerarla, invece di reimpostare periodicamente l'orologio.

Parlo per esperienza personale. In un'applicazione meteorologica che ho sviluppato, stavo ottenendo picchi di velocità del vento casuali. Mi ci è voluto un po 'per capire che la mia base dei tempi era stata interrotta dal comportamento dell'orologio su un tipico PC. Tutti i miei problemi sono scomparsi quando ho iniziato a utilizzare nanoTime. La coerenza (monotonia) era più importante per la mia applicazione della precisione pura o assoluta.


19
"currentTimeMillis è l'ora, che cambia a causa dell'ora legale" ... abbastanza sicuro che questa affermazione sia falsa. System.currentTimeMillis()riporta il tempo trascorso (in millisecondi) dall'epoca Unix / Posix che è mezzanotte, 1 gennaio 1970 UTC. Poiché UTC non viene mai regolato per l'ora legale, questo valore non verrà compensato quando i fusi orari locali aggiungono o sottopongono gli offset agli orari locali per l'ora legale. Inoltre, la Java Time Scale attenua i secondi da saltare in modo che i giorni continuino ad avere 86.400 secondi.
Scott

5

Sì, se è richiesta una tale precisione, è necessario System.nanoTime()tenere presente che è necessario un JVM Java 5+.

Sui miei sistemi XP, vedo l'ora di sistema riportata ad almeno 100 microsecondi 278 nanosecondi utilizzando il seguente codice:

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)); }
    }

4

Per gioco e grafica aggiornamenti di posizione ottimale, usare System.nanoTime()piuttosto che System.currentTimeMillis(). Sono passato da currentTimeMillis () a nanoTime () in un gioco e ho ottenuto un notevole miglioramento visivo della scorrevolezza dei movimenti.

Mentre un millisecondo può sembrare che dovrebbe essere già preciso, visivamente non lo è. I fattori che nanoTime()possono migliorare includono:

  • posizionamento preciso dei pixel al di sotto della risoluzione dell'orologio da parete
  • possibilità di anti-alias tra i pixel, se lo si desidera
  • Inesattezza dell'orologio da parete di Windows
  • clock jitter (incoerenza del momento in cui l'orologio da parete scorre in avanti)

Come suggeriscono altre risposte, nanoTime ha un costo in termini di prestazioni se chiamato ripetutamente: sarebbe meglio chiamarlo una sola volta per frame e utilizzare lo stesso valore per calcolare l'intero frame.


1

Ho avuto una buona esperienza con il nanotime . Fornisce il tempo dell'orologio da parete come due lunghi (secondi dall'epoca e nanosecondi entro quel secondo), usando una libreria JNI. È disponibile con la parte JNI precompilata sia per Windows che per Linux.


1

System.currentTimeMillis()non è sicuro per il tempo trascorso perché questo metodo è sensibile alle modifiche dell'orologio in tempo reale del sistema. Si dovrebbe usare System.nanoTime. Fare riferimento alla guida di Java System:

Informazioni sul metodo nanoTime:

.. Questo metodo fornisce una precisione di nanosecondi, ma non necessariamente una risoluzione di nanosecondi (ovvero, con che frequenza cambia il valore) - non viene fornita alcuna garanzia se non che la risoluzione è almeno pari a quella di currentTimeMillis () ..

Se si utilizza System.currentTimeMillis()il tempo trascorso può essere negativo (Indietro <- al futuro)


-3

una cosa qui è l'incoerenza del metodo nanoTime.it non fornisce valori molto coerenti per lo stesso input.currentTimeMillis fa molto meglio in termini di prestazioni e coerenza e anche, sebbene non così preciso come nanoTime, ha un margine di errore inferiore e quindi una maggiore precisione nel suo valore. quindi suggerirei di usare currentTimeMillis


6
Come notato in altre risposte e commenti, currentTimeMillis è soggetto a modifiche dell'orologio di sistema e quindi una scelta sbagliata per il calcolo del tempo trascorso da alcuni eventi precedenti nella JVM.
marcatori di duelli
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.