2011-03-09 8 views
14

que tienen el código que está probando Calendar.getInstance().getTimeInMillis() vs System.currentTimeMilli():cómo asegurarse de que ninguna JVM y optimización del compilador se produce

long before = getTimeInMilli(); 
for (int i = 0; i < TIMES_TO_ITERATE; i++) 
{ 
    long before1 = getTimeInMilli(); 
    doSomeReallyHardWork(); 
    long after1 = getTimeInMilli(); 
} 
long after = getTimeInMilli(); 
System.out.println(getClass().getSimpleName() + " total is " + (after - before)); 

quiero para asegurarse de que ninguna JVM o compilador optimización sucede, por lo que la prueba será válida y realmente mostrará la diferencia.

¿Cómo estar seguro?

EDIT: Cambié el ejemplo de código por lo que será más claro. Lo que estoy comprobando aquí es cuánto tiempo se necesita para llamar a getTimeInMilli() en diferentes implementaciones: Calendario vs Sistema.

Respuesta

24

Creo que necesita deshabilitar JIT. Agregue a su comando de ejecución la siguiente opción:

-Djava.compiler=NONE 
+6

Gracias; eso era justo lo que necesitaba para que Java muestre "con precisión" la diferencia de eficiencia entre los algoritmos O (1), O (n) y O (n^2) para una demostración en el aula :-) En Eclipse, puede poner eso en la configuración Ejecutar/Argumentos/VM. –

4

Lo sentimos, pero lo que está tratando de hacer tiene poco sentido.

Si desactiva la compilación JIT, solo medirá el tiempo que lleva llamar a ese método con la compilación JIT desactivada. Esta no es información útil ... porque te dice poco o nada sobre lo que sucederá cuando la compilación JIT esté activada.

Los tiempos entre JIT y encendido pueden ser diferentes por un factor enorme. Es poco probable que desee ejecutar nada en producción con JIT desactivado.

Un mejor enfoque sería hacer esto:

long before1 = getTimeInMilli(); 
for (int i = 0; i < TIMES_TO_ITERATE; i++) { 
    doSomeReallyHardWork(); 
} 
long after1 = getTimeInMilli(); 

... y/o usar el reloj de nanosegundo.


Si usted está tratando de medir el tiempo necesario para llamar a las dos versiones de getTimeInMillis(), entonces no entiendo el punto de que su llamada a doSomeReallyHardWork(). Un punto de referencia más senible sería la siguiente:

public long test() { 
    long before1 = getTimeInMilli(); 
    long sum = 0; 
    for (int i = 0; i < TIMES_TO_ITERATE; i++) { 
     sum += getTimeInMilli(); 
    } 
    long after1 = getTimeInMilli(); 
    System.out.println("Took " + (after - before) + " milliseconds"); 
    return sum; 
} 

... y llamar a que un número de veces, hasta que los tiempos impresos estabilice.

De cualquier manera, mi punto principal sigue en pie, el giro de la compilación JIT y/o optimización significaría que estaba midiendo algo que no es útil saber, y no lo que realmente está tratando de averiguar. (A menos, es decir, tiene la intención de ejecutar su aplicación en producción con JIT desactivado ... lo que me cuesta trabajo creer ...)

+0

Es probable que la señorita me haya entendido. Lo que intento probar es cuánto toma la llamada a getTimeInMilli(). una vez que use el Calendario y una vez que use el Sistema. – oshai

11

quiere optimización para que suceda, porque lo hará en la vida real - la prueba no sería válida si la JVM no se optimizó de la misma manera que lo haría en la situación real en la que está interesado.

Sin embargo, si desea asegurarse de que la JVM no lo haga eliminar llamadas que potencialmente podría considerar no-operaciones; de lo contrario, una opción es usar el resultado, por lo que si llama al System.currentTimeMillis() repetidamente, puede sumar todos los valores de retorno y luego mostrar la suma al final.

Tenga en cuenta que aún puede tener algunos sesgos, por ejemplo, puede haber algo de optimización si la JVM puede determinar económicamente que solo ha pasado una pequeña cantidad de tiempo desde la última llamada al System.currentTimeMillis(), por lo que puede usar un caché valor.No estoy diciendo que es en realidad el caso aquí, pero es el tipo de cosas que necesita pensar. En última instancia, los puntos de referencia solo pueden realmente probar las cargas que les das.

Otra cosa a tener en cuenta: suponiendo que se desea modelar una situación real donde el código se ejecuta una gran cantidad, se debe ejecutar el código mucho antes de tomar cualquier momento - porque el Hotspot JVM optimizará cada vez más difícil, y presumiblemente usted se preocupa por la versión altamente optimizada y no desea desea medir el tiempo para JITting y las versiones "lentas" del código.

Como se mencionó Stephen, debe casi seguro que tomar el tiempo fuera del bucle ... y no se olvide que en realidad uso los resultados ...

2

Lo que está haciendo se parece a la evaluación comparativa, Puede leer Robust Java benchmarking para obtener información acerca de cómo hacerlo bien. En pocas palabras, no es necesario que lo desactive, ya que no será lo que ocurra en el servidor de producción. En su lugar, debe conocer el tiempo aproximado de estimación/rendimiento "real". Antes de optimización que necesita para 'calentar' el código, que parece:

// warm up 
for (int j = 0; j < 1000; j++) { 
    for (int i = 0; i < TIMES_TO_ITERATE; i++) 
    { 
     long before1 = getTimeInMilli(); 
     doSomeReallyHardWork(); 
     long after1 = getTimeInMilli(); 
    } 
} 

// measure time 
long before = getTimeInMilli(); 
for (int j = 0; j < 1000; j++) { 
    for (int i = 0; i < TIMES_TO_ITERATE; i++) 
    { 
     long before1 = getTimeInMilli(); 
     doSomeReallyHardWork(); 
     long after1 = getTimeInMilli(); 
    } 
} 
long after = getTimeInMilli(); 

System.out.prinltn("What to expect? " + (after - before)/1000); // average time 

Cuando medimos el rendimiento de nuestro código se utiliza este enfoque, nos dará tiempo más menos real nuestro código tiene que trabajar. Mejor aún para medir el código en métodos separados:

public void doIt() { 
    for (int i = 0; i < TIMES_TO_ITERATE; i++) 
    { 
     long before1 = getTimeInMilli(); 
     doSomeReallyHardWork(); 
     long after1 = getTimeInMilli(); 
    } 
} 

// warm up 
for (int j = 0; j < 1000; j++) { 
    doIt() 
} 

// measure time 
long before = getTimeInMilli(); 
for (int j = 0; j < 1000; j++) { 
    doIt(); 
} 
long after = getTimeInMilli(); 

System.out.prinltn("What to expect? " + (after - before)/1000); // average time 

El segundo enfoque es más preciso, pero también depende de la máquina virtual. P.ej. HotSpot puede realizar "on-stack replacement", significa que si alguna parte del método se ejecuta con mucha frecuencia, se optimizará mediante VM y la versión anterior del código se intercambiará con una optimizada mientras se ejecuta el método. Por supuesto, requiere acciones adicionales del lado de VM. JRockit no lo hace, la versión optimizada del código se usará solo cuando este método se ejecute de nuevo (por lo que no hay optimización de "tiempo de ejecución" ... quiero decir, en mi primer código de muestra todo el código antiguo se ejecutará ... excepto para doSomeReallyHardWork internos: no pertenecen a este método, por lo que la optimización funcionará bien).

ACTUALIZADO: el código en cuestión se editó mientras respondía;)

Cuestiones relacionadas