Medir el tiempo transcurrido en Java

1. Información general

En este artículo, veremos cómo medir el tiempo transcurrido en Java. Si bien esto puede parecer fácil, hay algunas trampas que debemos tener en cuenta.

Exploraremos las clases estándar de Java y los paquetes externos que brindan funcionalidad para medir el tiempo transcurrido.

2. Medidas simples

2.1. currentTimeMillis ()

Cuando encontramos un requisito para medir el tiempo transcurrido en Java, podemos intentar hacerlo como:

long start = System.currentTimeMillis(); // ... long finish = System.currentTimeMillis(); long timeElapsed = finish - start;

Si miramos el código, tiene mucho sentido. Obtenemos una marca de tiempo al principio y obtenemos otra marca de tiempo cuando finaliza el código. El tiempo transcurrido es la diferencia entre estos dos valores.

Sin embargo, el resultado puede ser inexacto ya que System.currentTimeMillis () mide el tiempo del reloj de pared . La hora del reloj de pared puede cambiar por muchas razones, por ejemplo, cambiar la hora del sistema puede afectar los resultados o un segundo intercalar afectará el resultado.

2.2. nanoTime ()

Otro método en la clase java.lang.System es nanoTime () . Si miramos la documentación de Java, encontraremos la siguiente declaración:

"Este método solo se puede utilizar para medir el tiempo transcurrido y no está relacionado con ninguna otra noción de sistema o tiempo de reloj de pared".

Vamos a usarlo:

long start = System.nanoTime(); // ... long finish = System.nanoTime(); long timeElapsed = finish - start;

El código es básicamente el mismo que antes. La única diferencia es el método utilizado para obtener marcas de tiempo: nanoTime () en lugar de currentTimeMillis () .

Observemos también que nanoTime () , obviamente, devuelve el tiempo en nanosegundos. Por lo tanto, si el tiempo transcurrido se mide en una unidad de tiempo diferente, debemos convertirlo en consecuencia.

Por ejemplo, para convertir a milisegundos debemos dividir el resultado en nanosegundos por 1.000.000.

Otro error con nanoTime () es que , aunque proporciona una precisión de nanosegundos, no garantiza una resolución de nanosegundos (es decir, la frecuencia con la que se actualiza el valor).

Sin embargo, garantiza que la resolución sea al menos tan buena como la de currentTimeMillis () .

3. Java 8

Si usamos Java 8, podemos probar las nuevas clases java.time.Instant y java.time.Duration . Ambos son inmutables, seguros para subprocesos y usan su propia escala de tiempo, la escala de tiempo de Java, al igual que todas las clases dentro de la nueva API java.time .

3.1. Escala de tiempo de Java

La forma tradicional de medir el tiempo es dividir un día en 24 horas de 60 minutos de 60 segundos, lo que da 86.400 segundos por día. Sin embargo, los días solares no siempre son igualmente largos.

La escala de tiempo UTC permite que un día tenga 86,399 o 86,401 segundos SI. Un segundo SI es un "segundo estándar internacional" científico y se define por períodos de radiación del átomo de cesio 133). Esto es necesario para mantener el día alineado con el sol.

La escala de tiempo de Java divide cada día calendario en exactamente 86.400 subdivisiones, conocidas como segundos . No hay segundos intercalares.

3.2. Clase instantánea

La clase Instant representa un instante en la línea de tiempo. Básicamente, es una marca de tiempo numérica desde la época estándar de Java de 1970-01-01T00: 00: 00Z .

Para obtener la marca de tiempo actual, podemos usar el método estático Instant.now () . Este método permite pasar un parámetro de reloj opcional . Si se omite, utiliza el reloj del sistema en la zona horaria predeterminada.

Podemos almacenar las horas de inicio y finalización en dos variables, como en los ejemplos anteriores. A continuación, podemos calcular el tiempo transcurrido entre ambos instantes.

También podemos usar la clase Duration y su método between () para obtener la duración entre dos objetos Instant . Finalmente, necesitamos convertir Duración a milisegundos:

Instant start = Instant.now(); // CODE HERE Instant finish = Instant.now(); long timeElapsed = Duration.between(start, finish).toMillis();

4. Cronómetro

Pasando a las bibliotecas, Apache Commons Lang proporciona la clase StopWatch que se puede usar para medir el tiempo transcurrido.

4.1. Dependencia de Maven

Podemos obtener la última versión actualizando el pom.xml:

 org.apache.commons commons-lang3 3.7 

La última versión de la dependencia se puede consultar aquí.

4.2. Medición del tiempo transcurrido con cronómetro

En primer lugar, necesitamos obtener una instancia de la clase y luego podemos simplemente medir el tiempo transcurrido:

StopWatch watch = new StopWatch(); watch.start();

Una vez que tenemos un reloj en ejecución, podemos ejecutar el código que queremos comparar y luego, al final, simplemente llamamos al método stop () . Finalmente, para obtener el resultado real, llamamos a getTime () :

watch.stop(); System.out.println("Time Elapsed: " + watch.getTime()); // Prints: Time Elapsed: 2501

StopWatch tiene algunos métodos auxiliares adicionales que podemos usar para pausar o reanudar nuestra medición. Esto puede resultar útil si necesitamos hacer nuestro punto de referencia más complejo.

Finalmente, observemos que la clase no es segura para subprocesos.

5. Conclusión

Hay muchas formas de medir el tiempo en Java. Hemos cubierto una forma muy "tradicional" (e inexacta) mediante el uso de currentTimeMillis () . Además, verificamos el StopWatch de Apache Common y examinamos las nuevas clases disponibles en Java 8.

En general, para mediciones simples y correctas del tiempo transcurrido, el método nanoTime () es suficiente. También es más corto de escribir que currentTimeMillis () .

Sin embargo, observemos que para una evaluación comparativa adecuada, en lugar de medir el tiempo manualmente, podemos usar un marco como Java Microbenchmark Harness (JMH). Este tema va más allá del alcance de este artículo, pero lo exploramos aquí.

Finalmente, como siempre, el código utilizado durante la discusión se puede encontrar en GitHub.