2011-02-02 9 views
7

Ho una classe che esegue calcoli dispendiosi in termini di tempo. Sto cercando di test delle prestazioni è:Come posso essere sicuro che il compilatore non ottimizzi il mio test delle prestazioni?

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

che sto utilizzando valori casuali in modo che il compilatore non ottimizzare i calcoli per essere un milione di volte lo stesso. Ma per quanto riguarda i risultati? Il compilatore vede che non è più utilizzato e lascia la chiamata (ma poi, può vedere eventuali effetti collaterali che la chiamata al metodo potrebbe avere?)

Non voglio mettere i risultati da qualche parte (in un file, array o System.out), perché penso che questo rallenterà il test con un lavoro che non voglio misurare. O produrre un OutOfMemoryError.

Grazie in anticipo.

EDIT: cambiato il titolo un po '

+1

possibile duplicato di [Come faccio a scrivere un corretto micro-punto di riferimento in Java?] (Http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in -java) –

+1

Per quanto riguarda il test non so come consumo è 'doSomeTimeConsumingCalculationsOn' ma se è * non così a lungo * probabilmente il benchmarking sia il metodo e' random.nextDouble() '. – gabuzo

risposta

5

Ma per quanto riguarda i risultati? Il compilatore vede che non è più utilizzato e lascia la chiamata (ma poi, può vedere eventuali effetti collaterali che la chiamata al metodo potrebbe avere?)

Dipende. Se il compilatore JIT può rilevare che la chiamata al metodo non ha effetti collaterali, ha il diritto di ottimizzarla. Soprattutto perché il valore del risultato non viene utilizzato. In questo caso, si potrebbe semplicemente essere misurando le chiamate a random.nextDouble() ... o, eventualmente, un loop vuoto.

Per essere sicuri si dovrebbe non può essere ottimizzato via, probabilmente si dovrebbe scrivere in questo modo:

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. 

(sto supponendo che il tempo di calcolo fa dipende l'argomento ...


È inoltre necessario tenere conto del riscaldamento JVM; Esegui quel codice di riferimento più volte nella JVM fino a quando il tempo misurato si stabilizza.


Dire che il compilatore è "sovra-ottimizzazione" è un po 'sbagliato. Il compilatore sta effettivamente facendo il suo lavoro correttamente. Se non altro, l'errore è nel tuo codice; cioè "nulla di utile".

+0

Ho cambiato il titolo. La ringrazio per la risposta! – nokul

0

In caso di dubbio - hanno uno sguardo al codice di byte di questa classe di test. Se il compilatore ha "ottimizzato" quella chiamata, allora non troverai una chiamata a quel metodo lì.

thod è un piccolo disassemblatore fornito con il jdk. Scarica l'output in un file, apri quel file con l'editor standard e prova a trovare il methodname. Non è necessario comprendere l'intero codice byte se si desidera verificare se un determinato metodo viene chiamato o utilizzato da qualche parte.


ha effettuato un veloce test di:

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; 
} 

Per questa classe, il codice byte contiene le linee

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

che racconta sicuramente, che il metodo viene richiamato e il risultato viene memorizzato alla variabile locale.

Ma è comunque possibile che la macchina virtuale rilevi la variabile locale non utilizzata e che il compilatore just-in-time elimini la chiamata durante la compilazione del codice macchina reale.

+3

Le ottimizzazioni hanno più probabilità di essere fatto dal compilatore JIT. Non è possibile determinare se ciò accadrà dai bytecode. Il tuo "test rapido" non prova nulla. –

1

Il compilatore in realtà non esegue molta ottimizzazione (eccetto il calcolo dei valori delle espressioni costanti), poiché è stato rilevato che la JVM svolge un lavoro di ottimizzazione molto migliore.

Sono le moderne JVM che scoprono che il codice può essere inserito (cioè inserito direttamente nel codice chiamante invece di eseguire una chiamata al metodo) e che si applica particolarmente bene al codice vuoto, rimuovendo la chiamata al metodo e sostituendolo con - tada - nessun codice. Funziona molto velocemente, ma non misura bene.

Oltre a ciò, la JVM non è in grado di ottimizzare le chiamate, pertanto non è necessario preoccuparsi di passare argomenti diversi per applicare la valutazione. Se il tuo metodo non è banale verrà chiamato.

Ma la tua preoccupazione per i micro-benchmark che mostrano la cosa sbagliata è valida. Un approccio molto migliore per ottenere una visione approfondita della performance è quello di fare una vera corsa con un profiler allegato (ce n'è uno semplice in jvisualvm nel JDK 6).

Che cosa è necessario sapere?

3

Assicurarsi che il risultato sia utilizzato in qualche modo, ad esempio sommandolo e stampandolo alla fine. Summing è una buona scelta, perché oltre è un'operazione molto a buon mercato.

+0

Riassumendo i valori è una buona idea! +1 – nokul

Problemi correlati