Como se documenta en la publicación del blog Cuidado con System.nanoTime () en Java , en sistemas x86, Java System.nanoTime () devuelve el valor de tiempo usando un contador específico de la CPU . Ahora considere el siguiente caso que uso para medir el tiempo de una llamada:
long time1= System.nanoTime();
foo();
long time2 = System.nanoTime();
long timeSpent = time2-time1;
Ahora, en un sistema multinúcleo, podría ser que después de medir el tiempo1, el subproceso esté programado para un procesador diferente cuyo contador sea menor que el de la CPU anterior. Por lo tanto, podríamos obtener un valor en time2 que sea menor que time1. Por lo tanto, obtendríamos un valor negativo en timeSpent.
Considerando este caso, ¿no es que System.nanotime es bastante inútil por ahora?
Sé que cambiar la hora del sistema no afecta el nanotime. Ese no es el problema que describo anteriormente. El problema es que cada CPU mantendrá un contador diferente desde que se encendió. Este contador puede ser inferior en la segunda CPU en comparación con la primera CPU. Dado que el sistema operativo puede programar el subproceso en la segunda CPU después de obtener time1, el valor de timeSpent puede ser incorrecto e incluso negativo.
Respuestas:
Esta respuesta fue escrita en 2011 desde el punto de vista de lo que realmente hizo el Sun JDK de la época en los sistemas operativos de la época. ¡Eso fue hace mucho tiempo! respuesta de leventov ofrece una perspectiva más actualizada.
Esa publicación está equivocada y
nanoTime
es segura. Hay un comentario en la publicación que enlaza con una publicación de blog de David Holmes , un tipo en tiempo real y de concurrencia en Sun. Dice:Entonces, en Windows, esto era un problema hasta WinXP SP2, pero no lo es ahora.
No puedo encontrar una parte II (o más) que habla sobre otras plataformas, pero ese artículo incluye una observación de que Linux ha encontrado y resuelto el mismo problema de la misma manera, con un enlace a las Preguntas frecuentes para clock_gettime (CLOCK_REALTIME) , que dice:
Entonces, si se puede leer que el enlace de Holmes implica
nanoTime
llamadasclock_gettime(CLOCK_REALTIME)
, entonces es seguro desde el kernel 2.6.18 en x86, y siempre en PowerPC (porque IBM y Motorola, a diferencia de Intel, realmente saben cómo diseñar microprocesadores).No hay mención de SPARC o Solaris, lamentablemente. Y, por supuesto, no tenemos idea de lo que hacen las JVM de IBM. Pero las JVM de Sun en Windows y Linux modernos hacen esto bien.
EDITAR: esta respuesta se basa en las fuentes que cita. Pero todavía me preocupa que pueda estar completamente equivocado. Una información más actualizada sería realmente valiosa. Acabo de encontrar un enlace a un artículo nuevo de cuatro años sobre los relojes de Linux que podría ser útil.
fuente
void foo() { Thread.sleep(40); }
obtuve un tiempo negativo (-380 ms!) Usando un soloAthlon 64 X2 4200+
procesadorHice un poco de búsqueda y descubrí que si uno es pedante, sí, podría considerarse inútil ... en situaciones particulares ... depende de cuán sensibles sean sus requisitos ...
Consulte esta cita del sitio Java Sun:
Java también tiene una advertencia para el método nanoTime () :
Parece que la única conclusión que se puede extraer es que no se puede confiar en nanoTime () como un valor preciso. Como tal, si no necesita medir tiempos que están separados por nano segundos, entonces este método es lo suficientemente bueno incluso si el valor devuelto resultante es negativo. Sin embargo, si necesita una mayor precisión, parecen recomendarle que use JAVA RTS.
Entonces, para responder a su pregunta ... no nanoTime () no es inútil ... simplemente no es el método más prudente para usar en cada situación.
fuente
-100
,-99
,-98
(Valores, evidentemente, mucho más grandes en la práctica). Van en la dirección correcta (aumentando), por lo que no hay ningún problema aquí.No es necesario debatir, solo usa la fuente. Aquí, SE 6 para Linux, saque sus propias conclusiones:
fuente
Desde Java 7,
System.nanoTime()
se garantiza la seguridad de la especificación JDK.System.nanoTime()
'Javadoc deja en claro que todas las invocaciones observadas dentro de una JVM (es decir, en todos los hilos) son monótonas:La implementación de JVM / JDK es responsable de resolver las inconsistencias que podrían observarse cuando se llaman las utilidades subyacentes del sistema operativo (por ejemplo, las mencionadas en la respuesta de Tom Anderson ).
La mayoría de las otras respuestas antiguas a esta pregunta (escritas en 2009-2012) expresan FUD que probablemente fue relevante para Java 5 o Java 6, pero ya no es relevante para las versiones modernas de Java.
Sin embargo, vale la pena mencionar que, a pesar de
nanoTime()
la seguridad de las garantías de JDK , ha habido varios errores en OpenJDK que hacen que no se mantenga esta garantía en ciertas plataformas o bajo ciertas circunstancias (por ejemplo, JDK-8040140 , JDK-8184271 ). No hay errores abiertos (conocidos) en OpenJDK wrtnanoTime()
en este momento, pero el descubrimiento de un nuevo error o una regresión en una versión más reciente de OpenJDK no debería sorprender a nadie.Con eso en mente, el código que se usa
nanoTime()
para el bloqueo temporizado, la espera de intervalos, los tiempos de espera, etc., debe tratar preferiblemente las diferencias de tiempo negativas (tiempos de espera) como ceros en lugar de arrojar excepciones. Esta práctica también es preferible porque es coherente con el comportamiento de todos los métodos de espera cronometrados en todas las clases enjava.util.concurrent.*
, por ejemploSemaphore.tryAcquire()
,Lock.tryLock()
,BlockingQueue.poll()
, etc.No obstante,
nanoTime()
aún se debe preferir para implementar el bloqueo temporizado, el intervalo de espera, los tiempos de espera, etc.,currentTimeMillis()
ya que este último está sujeto al fenómeno del "tiempo que retrocede" (por ejemplo, debido a la corrección de la hora del servidor),currentTimeMillis()
es decir, no es adecuado para medir intervalos de tiempo en absoluto. Ver esta respuesta para más información.En lugar de utilizar
nanoTime()
directamente para mediciones de tiempo de ejecución de código, se deben utilizar preferiblemente marcos de referencia y perfiladores especializados, por ejemplo JMH y async-profiler en modo de perfil de reloj de pared .fuente
Descargo de responsabilidad: soy el desarrollador de esta biblioteca
Puede que te guste más esto:
http://juliusdavies.ca/nanotime/
Pero copia un archivo DLL o Unix .so (objeto compartido) en el directorio de inicio del usuario actual para que pueda llamar a JNI.
Alguna información de fondo está en mi sitio en:
http://juliusdavies.ca/posix_clocks/clock_realtime_linux_faq.html
fuente
Linux corrige las discrepancias entre las CPU, pero Windows no. Le sugiero que asuma que System.nanoTime () solo tiene una precisión de alrededor de 1 micro segundo. Una manera simple de obtener un tiempo más largo es llamar a foo () 1000 o más veces y dividir el tiempo entre 1000.
fuente
Absolutamente no inútil. Los aficionados al tiempo señalan correctamente el problema de múltiples núcleos, pero en aplicaciones de palabras reales a menudo es radicalmente mejor que currentTimeMillis ().
Al calcular las posiciones gráficas en las actualizaciones de cuadros, nanoTime () conduce a un movimiento MUCHO más suave en mi programa.
Y solo pruebo en máquinas multinúcleo.
fuente
He visto un tiempo transcurrido negativo informado al usar System.nanoTime (). Para ser claros, el código en cuestión es:
y la variable 'elapsedNanos' tenía un valor negativo. (Estoy seguro de que la llamada intermedia también tardó menos de 293 años, que es el punto de desbordamiento para los nanos almacenados en largos :)
Esto ocurrió utilizando un IBM v1.5 JRE de 64 bits en el hardware IBM P690 (multi-core) que ejecuta AIX. Solo he visto este error ocurrir una vez, por lo que parece extremadamente raro. No sé la causa, es un problema específico de hardware, un defecto de JVM, no lo sé. Tampoco sé las implicaciones para la precisión de nanoTime () en general.
Para responder a la pregunta original, no creo que nanoTime sea inútil: proporciona un tiempo inferior a milisegundos, pero existe un riesgo real (no solo teórico) de que sea inexacto, lo que debe tener en cuenta.
fuente
Esto no parece ser un problema en un Core 2 Duo con Windows XP y JRE 1.5.0_06.
En una prueba con tres hilos no veo que System.nanoTime () vaya hacia atrás. Los procesadores están ocupados y los hilos se van a dormir ocasionalmente para provocar hilos en movimiento.
[EDITAR] Supongo que solo ocurre en procesadores físicamente separados, es decir, que los contadores están sincronizados para múltiples núcleos en el mismo dado.
fuente
No, no lo es ... Solo depende de tu CPU, verifica el temporizador de eventos de alta precisión cómo / por qué las cosas se tratan de manera diferente según la CPU.
Básicamente, lea la fuente de su Java y verifique qué hace su versión con la función, y si funciona contra la CPU, la estará ejecutando.
IBM incluso sugiere que lo use para la evaluación comparativa del rendimiento (una publicación de 2008, pero actualizada).
fuente
Me estoy vinculando a lo que esencialmente es la misma discusión donde Peter Lawrey está proporcionando una buena respuesta. ¿Por qué obtengo un tiempo transcurrido negativo usando System.nanoTime ()?
Muchas personas mencionaron que en Java System.nanoTime () podría devolver un tiempo negativo. Pido disculpas por repetir lo que otras personas ya dijeron.
Sería genial si System.nanoTime () devolviera coreID donde se ejecutó.
fuente
Java es multiplataforma y nanoTime depende de la plataforma. Si usa Java, cuando no use nanoTime. Encontré errores reales en diferentes implementaciones de jvm con esta función.
fuente
La documentación de Java 5 también recomienda usar este método para el mismo propósito.
Java 5 API Doc
fuente
Además,
System.currentTimeMillies()
cambia cuando cambia el reloj de su sistema, mientrasSystem.nanoTime()
que no lo hace, por lo que este último es más seguro para medir duraciones.fuente
nanoTime
Es extremadamente inseguro para el tiempo. Lo probé en mis algoritmos básicos de prueba de primalidad y dio respuestas que estaban literalmente separadas por un segundo para la misma entrada. No uses ese método ridículo. Necesito algo que sea más preciso y preciso que obtener tiempo millis, pero no tan malo comonanoTime
.fuente