System.currentTimeMillis versus System.nanoTime

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 serios de resolución de tiempo entre diferentes sistemas operativos (a saber, 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?

Si solo está buscando mediciones extremadamente precisas del tiempo transcurrido , use System.nanoTime() . System.currentTimeMillis() le proporcionará el tiempo transcurrido más preciso posible en milisegundos desde la época, pero System.nanoTime() le proporciona un tiempo preciso en nanosegundos, relativo a algún punto arbitrario.

De la documentación de Java:

 public static long nanoTime() 

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

Este método solo se puede usar para medir el tiempo transcurrido y no está relacionado con ninguna otra noción de sistema o tiempo de reloj de pared. El valor devuelto representa nanosegundos desde algún tiempo fijo pero arbitrario (tal vez 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 hacen garantías sobre la frecuencia con la que cambian los valores. Las diferencias en las llamadas sucesivas que abarcan más de aproximadamente 292 años (2 63 nanosegundos) no calcularán con precisión el tiempo transcurrido debido al desbordamiento numérico.

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

 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.

Seguridad de subprocesos

Como nadie más ha mencionado esto …

No es seguro

No es seguro comparar los resultados de las System.nanoTime() de System.nanoTime() entre diferentes subprocesos. 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 usar entre subprocesos.

Actualización por Arkadiy : he observado un comportamiento más correcto de System.currentTimeMillis() en Windows 7 en Oracle Java 8. La hora fue devuelta con una precisión de 1 milisegundo. El código fuente en OpenJDK no ha cambiado, así 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 tiene una System.nanoTime() muy detallada de las API de temporización de Java (en particular, System.currentTimeMillis() y System.nanoTime() ), en el que desearía usar qué, y cómo trabajar internamente

Dentro de la máquina virtual de Hotspot: relojes, temporizadores y eventos de progtwigció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 temporizado es que la resolución del temporizador puede cambiar dependiendo de qué otras llamadas API se hayan realizado: todo el sistema (no solo en el proceso particular) . Muestra un ejemplo en el que usar Thread.sleep() hará que cambie esta resolución.

System.nanoTime() no es compatible con las JVM antiguas. Si eso es una preocupación, currentTimeMillis con currentTimeMillis

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

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

Como han dicho otros, currentTimeMillis es la hora del reloj, que cambia debido al horario de verano, los usuarios cambian la configuración de la hora, los segundos intercalares y la sincronización de la hora de Internet. Si su aplicación depende de valores de tiempo transcurridos que aumentan monótonamente, es posible que prefiera nanoTime.

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 debida a la sincronización de la hora de Internet, o quizás 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 discontinuidades debido a la sincronización de la hora de Internet, puede considerar un cliente NTP como Meinberg, que “sintoniza” la velocidad del reloj para ponerlo en cero, en lugar de simplemente restablecer el reloj periódicamente.

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

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

En mis sistemas XP, veo el tiempo del sistema reportado en al menos 100 microsegundos 278 nanosegundos usando 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)); } } 

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

Para gráficos de juegos y actualizaciones de posición sin problemas, 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.

Mientras que 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 debajo de la resolución del reloj de pared
  • capacidad de anti-alias entre los píxeles, si quieres
  • Inexactitud de Windows en el reloj de pared
  • fluctuación de 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; lo mejor sería llamarlo una vez por cuadro y usar el mismo valor para calcular todo el marco.

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

Sobre el método nanoTime:

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

Si usa System.currentTimeMillis() su tiempo transcurrido puede ser negativo (Back <- para el futuro)

Una cosa aquí es la inconsistencia del método nanoTime. No da valores muy consistentes para la misma entrada. 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 menor , y por lo tanto más precisión en su valor. por lo tanto, le sugiero que use currentTimeMillis