ejemplo - ¿Cómo puedo medir el tiempo con microsegundos de precisión en Java?
localdate java (12)
Vi en Internet que se suponía que debía usar System.nanoTime () pero eso no me funciona, me da tiempo con una precisión de milisegundos. Solo necesito los microsegundos antes y después de que se ejecute mi función para saber cuánto tiempo lleva. Estoy usando Windows XP.
Básicamente, tengo este código que, por ejemplo, hace de 1 millón hasta 10 millones de inserciones en una lista vinculada a java. El problema es que no puedo medir la precisión correcta; a veces toma menos tiempo insertar todo en la lista más pequeña.
Aquí hay un ejemplo:
class test
{
public static void main(String args[])
{
for(int k=1000000; k<=10000000; k+=1000000)
{
System.out.println(k);
LinkedList<Integer> aux = new LinkedList<Integer>();
//need something here to see the start time
for(int i=0; i<k; i++)
aux.addFirst(10000);
//need something here to see the end time
//print here the difference between both times
}
}
}
Hice esto muchas veces, hubo un ciclo exterior haciéndolo 20 veces por cada k, pero el resultado no es bueno. A veces toma menos tiempo hacer 10 millones de inserciones que 1 millón, porque no estoy obteniendo el tiempo correcto medido con lo que estoy usando ahora (System.nanoTime ())
Editar 2: Sí, estoy usando Sun JVM.
Edición 3: puedo haber hecho algo mal en el código, veré si al cambiarlo hago lo que quiero.
Edit 4: Mi error, parece que System.nanoTime () funciona. Uf.
Usando java.time
FYI, Java 9 y posteriores tienen una nueva implementación de Clock
que puede capturar el momento actual en hasta una resolución de nanosegundos.
La clase Instant
representa un momento en la línea de tiempo en UTC con una resolución de nanoseconds (hasta nueve (9) dígitos de una fracción decimal).
Llama a Instant.now
para capturar el momento actual.
- En Java 9 y posterior, obtienes una resolución de hasta nanoseconds para ese momento actual.
En Java 8, el momento actual se captura solo hasta una resolución de milliseconds (de hecho puede contener valores con nanosegundos, pero solo capturar el momento actual en milisegundos).
Instant instantáneo = Instant.now ();
Represente un lapso de tiempo no vinculado a la línea de tiempo con la clase de Duration
. Tiene una cantidad de tiempo en términos de segundos y nanosegundos.
Duration d = Duration.between( instantThen , Instant.now() );
Para ser claros, la resolución de microseconds que se pide en la Pregunta se encuentra entre las granularidades de milisegundos y nanosegundos. Número de lugares en una fracción decimal: millis es 3 (0.123), micros es 6 (0.123456), nanos es 9 (0.123456789).
Advertencia
Java depende del reloj de hardware de su computadora. Como otros advirtieron, ese hardware seguramente capturará el tiempo con mucha menos precisión y mucha menos resolución que nanosegundos.
El benchmarking en una granularidad tan fina está plagado de problemas y generalmente no es aconsejable .
Y ten cuidado con la optimización prematura .
Existe una propuesta para agregar una instalación de micro-benchmarking a la plataforma Java en JEP 230: Microbenchmark Suite . Basado en Java Microbenchmark Harness (JMH) .
Acerca de java.time
El marco java.time está integrado en Java 8 y posterior. Estas clases suplantan a las problemáticas antiguas clases legacy fecha y hora como java.util.Date
, Calendar
y SimpleDateFormat
.
El proyecto Joda-Time , ahora en modo de mantenimiento , aconseja la migración a las clases java.time .
Para obtener más información, consulte el Tutorial de Oracle . Y busque para obtener muchos ejemplos y explicaciones. La especificación es JSR 310 .
¿Dónde obtener las clases de java.time?
- Java SE 8 y SE 9 y posteriores
- Incorporado.
- Parte de la API Java estándar con una implementación integrada.
- Java 9 agrega algunas características y correcciones menores.
- Java SE 6 y SE 7
- Gran parte de la funcionalidad de java.time se transfiere a Java 6 y 7 en ThreeTen-Backport .
- Android
- El proyecto ThreeTenABP adapta ThreeTen-Backport (mencionado anteriormente) específicamente para Android.
- Consulte Cómo utilizar ThreeTenABP ....
El proyecto ThreeTen-Extra amplía java.time con clases adicionales. Este proyecto es un terreno de prueba para posibles adiciones futuras a java.time. Puede encontrar algunas clases útiles aquí, como Interval
, YearWeek
, YearQuarter
y more .
Eso es raro. Se supone que System.nanoTime () funciona. ¿Estás usando Sun JVM?
¿Puedes repetir tu operación 1000 veces y dividir el tiempo entre 1000 para saber lo que necesitas saber?
No está claro para mí exactamente cuál es el punto de referencia, pero en general cualquier prueba que requiera tan poco tiempo para ejecutarse, esa precisión inferior a 50 ms es relevante, va a ser muy propensa a otras perturbaciones.
Por lo general, trato de hacer que los puntos de referencia se ejecuten durante al menos 10 segundos. El marco que estoy escribiendo en este momento adivinará cuántas iteraciones ejecutar, de modo que demorará 30 segundos. Eso significa que no obtendrá resultados radicalmente diferentes solo porque algún otro proceso robó la CPU durante unos pocos milisegundos.
Correr por más tiempo es casi siempre un mejor enfoque que tratar de medir con precisión más fina.
Para nuestro perfil reciente, descubrí que ThreadMXBean.getCurrentThreadCpuTime () y la opción -XX: + UseLinuxPosixThreadCPUClocks hicieron lo que necesitábamos.
Ver http://bugs.java.com/view_bug.do?bug_id=6888526 para más detalles
Puede ser que el sistema operativo subyacente no proporcione temporizadores con precisión de nanosegundos.
También hay una publicación más antigua .
Sí, la precisión y precisión de System.nanoTime suele ser mucho mejor que System.currentTimeMillis, pero sin garantía: en el peor de los casos, puede ser igual de mala.
ThreadMXBean.getCurrentThreadCpuTime tiende a rendir tiempos más pequeños, pero su resolución no es clara, y tiene otras desventajas (¿realmente desea el tiempo de CPU ?, ¿semántica dependiente de la plataforma ?, ¿compatible con su plataforma?).
Medir el tiempo con las tres técnicas también tiene algún costo, es decir, requiere tiempo en sí mismo, lo que puede distorsionar las mediciones. Los costos dependen en gran medida de la plataforma, pero a menudo cuestan (System.currentTimeMillis) << cost (System.nanoTime) << cost (ThreadMXBean.getCurrentThreadCpuTime).
Sobre micro benchmarking en general, ver
Si desea un resultado confiable, use un generador de perfiles. Sugiero VisualVM , que es fácil de instalar y se incluye con el JDK a partir de la versión 1.6.0_07. Es una herramienta visual fácil de usar que integra varias herramientas JDK de línea de comandos y capacidades de generación de perfiles livianas.
Supongo que dado que System.nanoTime()
usa el "temporizador de sistema más preciso disponible" que aparentemente solo tiene una precisión de milisegundos en su sistema, no puede obtener nada mejor.
System.nanoTime () usa un contador en la CPU y suele tener una precisión de aproximadamente 1 microsegundo en Windows XP y Linux.
Nota: Windows XP a menudo es menos preciso en máquinas multi-cpu ya que no compensa las diferentes CPU que tienen diferentes contadores. Linux lo hace. Nota 2: Derivará en relación con System.currentTimeMillis () ya que se basa en la precisión del reloj de su CPU (que no necesita ser tan preciso durante un período de tiempo), en lugar del reloj que tiene para obtener el tiempo. (que se desplaza menos por día, pero tiene menos granularidad)
En su punto de referencia, básicamente está probando la velocidad a la que puede crear nuevos objetos. No sorprende que sus resultados varíen dramáticamente en función de la configuración de su GC y de la cantidad de GC que se haya realizado recientemente.
Intenta ejecutar tus pruebas con las siguientes opciones y verás resultados muy diferentes.
-verbosegc -XX: NewSize = 128m -mx256m
Tal punto de referencia que se basa en corto intervalo de tiempo le da resultados poco fiables. Siempre obtendrás resultados diferentes debido a factores externos como E / S, Intercambio, Cambios de proceso, Cachés, Recolección de basuras, etc. Además, la JVM optimiza tus llamadas, por lo que es probable que las primeras cosas medidas vayan más despacio que una llamada posterior. La JVM se inicia cada vez más para optimizar los comandos que ejecuta.
Además, el método como System.nanoTime () depende de los temporizadores del sistema subyacente. Pueden (y muy probablemente) no tener la granularidad para medir con esa precisión. Para citar la System.nanoTime() :
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.
Para medir realmente con alta precisión, necesita acceder a un hardware de sincronización externo con precisión garantizada.
Para que su punto de referencia sea más estable, debe ejecutarlo más de una vez y medir intervalos de tiempo mayores que solo milisegundos.
Tienes que repetir las pruebas miles de veces. Suceden muchas cosas que influirán en sus medidas, como recolección de basura, E / S, intercambio de entrada / salida, el tamaño de los hilos listos para cola, etc.
una solución "rápida y sucia" con la que finalmente fui:
TimeUnit.NANOSECONDS.toMicros(System.nanoTime());
ACTUALIZAR:
Originalmente fui con System.nanoTime, pero luego descubrí que solo debería usarse durante el tiempo transcurrido, finalmente cambié mi código para trabajar con milisegundos o, en algunos lugares, uso:
TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis());
pero esto solo agregará ceros al final del valor (micros = millis * 1000)
Dejó esta respuesta aquí como una "señal de advertencia" en caso de que alguien más piense en nanoTime :)