java timer time-precision

java - System.currentTimeMillis vs System.nanoTime



timer time-precision (10)

Hilo de seguridad

Como nadie más ha mencionado esto ...

No es seguro

No es seguro comparar los resultados de las llamadas a System.nanoTime() entre diferentes hilos. Incluso si los eventos de los hilos ocurren en un orden predecible, la diferencia en nanosegundos puede ser positiva o negativa.

Seguro

System.currentTimeMillis() es seguro para su uso entre subprocesos.

Exactitud vs. Precisión

Lo que me gustaría saber es si debo usar System.currentTimeMillis () o System.nanoTime () al actualizar las posiciones de mi objeto en mi juego. Su cambio de movimiento es directamente proporcional al tiempo transcurrido desde la última llamada y quiero ser lo más preciso posible.

He leído que hay algunos problemas graves de resolución de tiempo entre diferentes sistemas operativos (es decir, que Mac / Linux tiene una resolución de casi 1 ms, mientras que Windows tiene una resolución de 50 ms). Principalmente estoy ejecutando mis aplicaciones en Windows y la resolución de 50 ms parece bastante inexacta.

¿Hay mejores opciones que las dos que enumeré?

¿Alguna sugerencia / comentario?


Actualización de Arkadiy : he observado un comportamiento más correcto de System.currentTimeMillis() en Windows 7 en Oracle Java 8. El tiempo se devolvió con una precisión de 1 milisegundo. El código fuente en OpenJDK no ha cambiado, por lo que no sé qué causa el mejor comportamiento.

David Holmes, de Sun, publicó un artículo de blog hace un par de años que analiza en detalle las API de sincronización de Java (en particular System.currentTimeMillis() y System.nanoTime() ), cuándo querría usar cuáles y cómo trabajar internamente

Dentro de la máquina virtual Hotspot: Relojes, temporizadores y eventos de programación - Parte I - Windows

Un aspecto muy interesante del temporizador utilizado por Java en Windows para las API que tienen un parámetro de espera temporizada es que la resolución del temporizador puede cambiar dependiendo de las demás llamadas a la API, en todo el sistema (no solo en el proceso particular) . Muestra un ejemplo donde el uso de Thread.sleep() causará este cambio de resolución.


Como han dicho otros, currentTimeMillis es la hora del reloj, que cambia debido al horario de verano, a los usuarios que cambian la configuración de la hora, los segundos de salto y la sincronización de la hora de Internet. Si su aplicación depende del aumento monótono de los valores de tiempo transcurrido, es posible que prefiera nanoTime en su lugar.

Podrías pensar que los jugadores no jugarán con la configuración de tiempo durante el juego, y quizás tengas razón. Pero no subestime la interrupción debido a la sincronización de la hora de Internet, o tal vez a los usuarios de escritorio remoto. La API nanoTime es inmune a este tipo de interrupción.

Si desea utilizar la hora del reloj, pero evitar las discontinuidades debidas a la sincronización de la hora de Internet, puede considerar un cliente NTP como Meinberg, que "ajusta" la frecuencia del reloj para ponerla en cero, en lugar de simplemente reiniciar el reloj periódicamente.

Hablo por experiencia personal. En una aplicación de clima que desarrollé, estaba teniendo picos de velocidad del viento que ocurrían al azar. Me tomó un tiempo darme cuenta de que el comportamiento de la hora del reloj en una PC típica estaba interrumpiendo mi base de tiempo. Todos mis problemas desaparecieron cuando comencé a usar nanoTime. La consistencia (monotonicidad) fue más importante para mi aplicación que la precisión en bruto o la precisión absoluta.


He tenido una buena experiencia con nanotime . Proporciona el tiempo de reloj de pared como dos largos (segundos desde la época y los nanosegundos dentro de ese segundo), utilizando una biblioteca JNI. Está disponible con la parte JNI precompilada para Windows y Linux.


Para gráficos de juegos y actualizaciones de posición suave, use System.nanoTime() lugar de System.currentTimeMillis() . Cambié de currentTimeMillis () a nanoTime () en un juego y obtuve una mejora visual importante en la suavidad del movimiento.

Si bien un milisegundo puede parecer que ya debería ser preciso, visualmente no lo es. Los factores que nanoTime() puede mejorar incluyen:

  • posicionamiento preciso de píxeles por debajo de la resolución de reloj de pared
  • Capacidad de anti-alias entre píxeles, si lo desea
  • Windows de reloj de pared inexactitud
  • jitter del reloj (inconsistencia de cuando el reloj de pared realmente avanza)

Como sugieren otras respuestas, nanoTime tiene un costo de rendimiento si se lo llama repetidamente; sería mejor llamarlo solo una vez por fotograma y usar el mismo valor para calcular el fotograma completo.


Sí, si se requiere tal precisión, use System.nanoTime() , pero tenga en cuenta que entonces está requiriendo una JVM Java 5+.

En mis sistemas XP, veo la hora del sistema reportada a al menos 100 microsegundos 278 nanosegundos utilizando el siguiente código:

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


Si solo está buscando medidas extremadamente precisas del tiempo transcurrido , use System.nanoTime() . System.currentTimeMillis() le dará el tiempo transcurrido más exacto posible en milisegundos desde la época, pero System.nanoTime() le da un tiempo nanosegundo de precisión, relativo a algún punto arbitrario.

De la documentación de Java:

public static long nanoTime()

Devuelve el valor actual del temporizador de sistema disponible más preciso, en nanosegundos.

Este método solo se puede utilizar para medir el tiempo transcurrido y no está relacionado con ninguna otra noción del sistema o del reloj de pared. El valor devuelto representa nanosegundos desde un tiempo fijo pero arbitrario (quizás en el futuro, por lo que los valores pueden ser negativos). Este método proporciona una precisión de nanosegundos, pero no necesariamente una precisión de nanosegundos. No se garantiza la frecuencia con la que cambian los valores. Las diferencias en llamadas sucesivas que abarcan más de aproximadamente 292 años (2 63 nanosegundos) no calcularán con precisión el tiempo transcurrido debido a un desbordamiento numérico.

Por ejemplo, para medir cuánto tiempo tarda en ejecutarse un código:

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

Consulte también: JavaDoc System.nanoTime () y JavaDoc System.currentTimeMillis () para obtener más información.


Una cosa aquí es la inconsistencia del método nanoTime. No proporciona valores muy consistentes para el mismo input.currentTimeMillis lo hace mucho mejor en términos de rendimiento y consistencia, y también, aunque no es tan preciso como nanoTime, tiene un margen de error más bajo , y por lo tanto más precisión en su valor. Por lo tanto, sugeriría que use currentTimeMillis


System.currentTimeMillis() no es seguro por el tiempo transcurrido porque este método es sensible a los cambios de reloj en tiempo real del sistema. Debe utilizar System.nanoTime . Consulte la ayuda del sistema Java:

Acerca del método nanoTime:

.. Este método proporciona una precisión de nanosegundos, pero no necesariamente una resolución de nanosegundos (es decir, con qué frecuencia cambia el valor); no se ofrecen garantías, excepto que la resolución es al menos tan buena como la de currentTimeMillis () ..

Si usa System.currentTimeMillis() el tiempo transcurrido puede ser negativo (Atrás <- hacia el futuro)


System.nanoTime() no es compatible con JVM anteriores. Si eso es una preocupación, quédate con currentTimeMillis

En cuanto a la precisión, es casi correcto. En ALGUNAS máquinas Windows, currentTimeMillis() tiene una resolución de aproximadamente 10 ms (no 50 ms). No estoy seguro de por qué, pero algunas máquinas Windows son tan precisas como las máquinas Linux.

He usado GAGETimer en el pasado con un éxito moderado.