2011-02-02 10 views
7

Tengo una clase que hace algunos cálculos que requieren mucho tiempo. Estoy tratando de prueba de rendimiento que:¿Cómo puedo estar seguro de que el compilador no optimiza mi prueba de rendimiento?

int numValues = 1000000; 
Random random = new Random(); 
startMeasuringTime(); 
double result; 
for (int i = 0; i < numValues; i++) { 
    result = calculatorInstance.doSomeTimeConsumingCalculationsOn(random.nextDouble()); 
} 
stopMeasuringTime(); 

estoy usando valores aleatorios por lo que el compilador no permitirá optimizar los cálculos por ser un millón de veces la misma. Pero ¿y los resultados? ¿El compilador ve que ya no se utiliza y omite la llamada (pero puede ver los efectos secundarios que podría tener la llamada al método?)

No quiero poner los resultados en algún lugar (en un file, array o System.out), porque creo que esto ralentizará la prueba con un trabajo que no quiero medir. O produce un OutOfMemoryError.

Gracias de antemano.

EDIT: cambiado el título un poco

+1

posible duplicado de [¿Cómo puedo escribir una micro-referencia correcta en Java?] (Http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in -java) –

+1

En cuanto a su prueba, no sé cuánto consume 'doSomeTimeConsumingCalculationsOn' pero si es * no mucho tiempo * probablemente comparará su método y' random.nextDouble() '. – gabuzo

Respuesta

5

¿Qué pasa con los resultados? ¿El compilador ve que ya no se utiliza y omite la llamada (pero puede ver los efectos secundarios que podría tener la llamada al método?)

Depende. Si el compilador JIT puede detectar que la llamada al método no tiene efectos secundarios, entonces tiene derecho a optimizarlo. Especialmente porque el valor del resultado no se usa. En este caso, tal vez solo esté midiendo las llamadas al random.nextDouble() ... o posiblemente un bucle vacío.

Para estar seguro de que usted debe no se pueden optimizar de distancia, probablemente debería escribir así:

int numValues = 1000000; 
Random random = new Random(); 
startMeasuringTime(); 
double result; 
for (int i = 0; i < numValues; i++) { 
    result = result + 
     calculatorInstance.doSomeCalculationsOn(random.nextDouble()); 
} 
stopMeasuringTime(); 
System.err.println(result); // Force result to be computed. 

(estoy suponiendo que el tiempo de cálculo qué dependen del argumento ...)


También debe tener en cuenta el calentamiento de JVM; es decir, ejecute ese código de referencia varias veces en la JVM hasta que el tiempo medido se estabilice.


Decir que el compilador está "sobre-optimización" es una especie de mal. El compilador está haciendo su trabajo correctamente. En todo caso, la falla está en tu código; es decir, no hace "nada útil".

+0

Cambié el título. ¡Gracias por su respuesta! – nokul

0

En caso de duda - echar un vistazo al código de bytes de esta clase de prueba. Si el compilador ha "optimizado" esa llamada, entonces no encontrará una llamada a ese método allí.

thod es un pequeño desensamblador que se envía con el jdk. Vuelque la salida a un archivo, abra ese archivo con el editor estándar e intente encontrar el nombre del método. No es necesario que comprenda el código de bytes completo si solo desea verificar si se llama o usa un determinado método en algún lugar.


Hicimos una prueba rápida:

public static main(String[] args) { 
    for (int i = 0; i < 1000000; i++) { 
    double result = doSomething(Math.random()); 
    } 
} 

public double doSomething(double random) { 
    return random * random; 
} 

Para esta clase, el código byte contiene las líneas

invokestatic #22; // Method doSomething:(D)D 
dstore_2 

la que definitivamente le dice, que el método es invocado y el resultado se almacena a la variable local.

Pero aún es posible que la máquina virtual detecte la variable local no utilizada y que el compilador just-in-time elimine la llamada al compilar código de máquina real.

+3

Es muy probable que el compilador JIT realice las optimizaciones. No puede determinar si esto sucederá a partir de los códigos de bytes. Su "prueba rápida" no prueba nada. –

1

El compilador en realidad no hace mucha optimización (excepto calcular valores de expresiones constantes), ya que se ha encontrado que la JVM hace un mejor trabajo de optimización.

Es puede ser inline de la JVM moderna que descubre que el código (es decir, inserta directamente en el código de llamada en lugar de ejecutar una llamada de método) y que se aplica especialmente bien al código vacío, la eliminación de la llamada al método y su sustitución por - tada - sin código. Eso funciona muy rápido, pero no mide bien.

Por encima de eso, la JVM no puede optimizar las llamadas, por lo que no es necesario preocuparse por pasar diferentes argumentos para imponer la evaluación. Si su método no es trivial se llamará.

Pero su preocupación acerca de los micro-puntos de referencia que muestran lo incorrecto es válida. Un enfoque mucho mejor para obtener información sobre el rendimiento es hacer una carrera real con un perfilador adjunto (hay uno simple en jvisualvm en el JDK 6).

¿Qué es lo que necesita saber?

3

Asegúrese de que el resultado se utiliza de alguna manera, por ejemplo, sumándolo e imprimiéndolo al final. Sumar es una buena opción porque además es una operación muy económica.

+0

Resumiendo los valores es una muy buena idea! +1 – nokul

Cuestiones relacionadas