Estoy ejecutando el siguiente código Java en una computadora portátil con Intel Core i7 a 2.7 GHz. Tenía la intención de dejar que mida cuánto tiempo lleva terminar un ciclo con 2 ^ 32 iteraciones, que esperaba que fueran aproximadamente 1,48 segundos (4 / 2,7 = 1,48).
Pero en realidad solo toma 2 milisegundos, en lugar de 1,48 s. Me pregunto si esto es el resultado de alguna optimización de JVM debajo.
public static void main(String[] args)
{
long start = System.nanoTime();
for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++){
}
long finish = System.nanoTime();
long d = (finish - start) / 1000000;
System.out.println("Used " + d);
}
javap -v
para ver.javac
realiza muy poca optimización real y deja la mayor parte al compilador JIT.Respuestas:
Hay una de dos posibilidades aquí:
El compilador se dio cuenta de que el bucle es redundante y no hace nada, por lo que lo optimizó.
El JIT (compilador just-in-time) se dio cuenta de que el bucle es redundante y no hace nada, por lo que lo optimizó.
Los compiladores modernos son muy inteligentes; pueden ver cuando el código es inútil. Intente poner un bucle vacío en GodBolt y observe la salida, luego active las
-O2
optimizaciones, verá que la salida es algo parecido aMe gustaría aclarar algo, en Java la mayoría de las optimizaciones las realiza el JIT. En algunos otros lenguajes (como C / C ++), la mayoría de las optimizaciones las realiza el primer compilador.
fuente
Parece que fue optimizado por el compilador JIT. Cuando lo apago (
-Djava.compiler=NONE
), el código se ejecuta mucho más lento:Puse el código de OP dentro de
class MyClass
.fuente
Solo diré lo obvio: que esta es una optimización de JVM que ocurre, el bucle simplemente se eliminará. Aquí hay una pequeña prueba que muestra la gran diferencia que
JIT
tiene cuando está habilitado / habilitado solo paraC1 Compiler
y deshabilitado en absoluto.Descargo de responsabilidad: no escriba pruebas como esta, esto es solo para demostrar que la "eliminación" del bucle real ocurre en
C2 Compiler
:Los resultados muestran que, dependiendo de qué parte del
JIT
método esté habilitado, el método se vuelve más rápido (tanto más rápido que parece que no hace "nada" - eliminación de bucle, que parece estar sucediendo en elC2 Compiler
- que es el nivel máximo):fuente
Como ya se señaló, el compilador JIT (just-in-time) puede optimizar un ciclo vacío para eliminar iteraciones innecesarias. ¿Pero cómo?
En realidad, hay dos compiladores JIT: C1 y C2 . Primero, el código se compila con el C1. C1 recopila las estadísticas y ayuda a la JVM a descubrir que en el 100% de los casos, nuestro bucle vacío no cambia nada y es inútil. En esta situación C2 entra en escena. Cuando se llama al código con mucha frecuencia, se puede optimizar y compilar con el C2 utilizando estadísticas recopiladas.
Como ejemplo, probaré el siguiente fragmento de código (mi JDK está configurado para slowdebug build 9-internal ):
Con las siguientes opciones de línea de comando:
Y hay diferentes versiones de mi método de ejecución , compiladas adecuadamente con C1 y C2. Para mí, la variante final (C2) se parece a esto:
Es un poco complicado, pero si miras de cerca, es posible que notes que no hay un bucle largo aquí. Hay 3 bloques: B1, B2 y B3 y los pasos de ejecución pueden ser
B1 -> B2 -> B3
oB1 -> B3
. DondeFreq: 1
: frecuencia estimada normalizada de la ejecución de un bloque.fuente
Está midiendo el tiempo que lleva detectar que el bucle no hace nada, compila el código en un hilo de fondo y elimina el código.
Si ejecuta esto con
-XX:+PrintCompilation
, puede ver que el código se ha compilado en segundo plano al compilador de nivel 3 o C1 y, después de algunos bucles, al nivel 4 de C4.Si cambia el bucle para usar un
long
, no se optimiza tanto.en su lugar obtienes
fuente
long
contador evitaría que ocurra la misma optimización?int
nota char y short son efectivamente lo mismo en el nivel de código de bytes.Considera el tiempo de inicio y finalización en nanosegundos y lo divide por 10 ^ 6 para calcular la latencia
debería ser
10^9
porque1
segundo =10^9
nanosegundo.fuente