7

Il java compiler (il javac predefinito che viene fornito in JDK1.6.0_21) ottimizza il codice per impedire che lo stesso metodo venga chiamato con gli stessi argomenti più e più volte? Se ho scritto questo codice:Ottimizzazione del compilatore Java per chiamate di metodo ripetute?

public class FooBar { 
    public static void main(String[] args) { 
     foo(bar); 
     foo(bar); 
     foo(bar); 
    } 
} 

Sarebbe il metodo foo(bar) eseguito solo una volta? In tal caso, c'è un modo per prevenire questa ottimizzazione? (Sto cercando di confrontare il runtime per due algos, uno iterativo e uno comparativo, e voglio chiamarli un po 'di volte per ottenere un campione rappresentativo)

Qualsiasi intuizione sarebbe molto apprezzata; Ho preso questo problema fino alla follia (ho pensato che il mio computer fosse follemente veloce per un po ', quindi ho continuato ad aggiungere chiamate di metodo fino a quando non ho ottenuto l'errore code too large a 43671 linee).

+0

Non vedo davvero perché il compilatore rimuova le chiamate di metodo come suggerite. Non conosco la risposta effettiva ma c'è un limite di 64k sul codice nei metodi. Quindi il compilatore non sta rimuovendo le chiamate, sta solo raggiungendo il limite della dimensione del codice all'interno del tuo metodo. Lo farebbe anche se avessi altre linee di codice all'interno di quel metodo. –

risposta

6

L'ottimizzazione che si sta osservando probabilmente non ha nulla a che fare con le chiamate ripetute ... perché sarebbe un'ottimizzazione non valida. Più probabilmente, l'ottimizzatore ha capito che le chiamate al metodo non hanno alcun effetto osservabile sul calcolo.

La cura è di cambiare il metodo in modo che non influisce sul risultato del calcolo ...

+0

Se le chiamate "non hanno alcun effetto osservabile sul calcolo", allora non c'è modo di vedere se nessuna di queste chiamate è stata fatta :) In entrambi i casi, non ho mai sentito parlare di tale ottimizzazione in java. –

+0

Grazie, che sia vero o no, questo aiuta ... Rendendo l'input per ogni metodo casuale, sono stato in grado di farli avere * qualche * runtime. Devo ancora chiamare molto i metodi in modo che la legge delle medie possa avere effetto e rendere i runtime dei due metodi comparabili. –

+0

@Nikita - in realtà, ci sono due modi. 1) tempo il codice - e vedere se zero chiamate e una chiamata per ciclo prendono lo stesso tempo. 2) ottenere il JIT per scaricare il codice nativo. (Ho dimenticato come lo fai ... ma c'è un modo.) –

4

Non funziona; questo potrebbe causare un grosso problema se foo non è puro (cambia lo stato globale del programma). Ad esempio:

+0

E se "foo" non ha cambiato gli stati globali del programma? Inoltre, penso che 'i' dovrebbe essere statico per essere modificato dal metodo' static' 'foo' –

+0

Può se può provare che foo() è puro, o che nessuno dei suoi effetti collaterali è importante; e l'hotspot JVM prova a dimostrare che foo() è puro e può essere saltato. Rimuovere println() e hotspot è in grado di ridurre l'intero programma a un nop e molto probabilmente lo farà (eventualmente). – Recurse

3

Non hai fornito informazioni sufficienti per consentire eventuali risposte definitive, ma l'ottimizzatore JVM runtime è estremamente potente e esegue tutti i tipi di inlining, flusso di dati di runtime e analisi di escape e tutti i tipi di trucchi della cache.

Il risultato finale è quello di rendere il tipo di micro-benchmark che si sta tentando di eseguire quasi inutile nella pratica; ed estremamente difficile da ottenere anche quando sono potenzialmente utili.

Leggere decisamente http://www.ibm.com/developerworks/java/library/j-benchmark1.html per una discussione più completa sui problemi che si devono affrontare. Per lo meno è necessario garantire:

  1. foo è chiamata in un ciclo che viene eseguito migliaia di volte
  2. foo() restituisce un risultato, e
  3. che risultato viene utilizzato

Quello che segue è il punto di partenza minimo, assumendo che foo() non sia banale e quindi è improbabile che sia in linea. Nota: è ancora necessario prevedere lo svolgimento del ciclo e altre ottimizzazioni del livello di cache. Fai attenzione anche al punto di interruzione della compilazione di hotspot (credo che si tratti di ~ 5000 chiamate su server IIRC), che può riempire completamente le tue misurazioni se provi a rieseguire le misurazioni nella stessa JVM.

public class FooBar { 
    public static void main(String[] args) { 
     int sum = 0; 
     int ITERATIONS = 10000; 
     for (int i = 0; i < ITERATIONS; i++) { 
      sum += foo(i); 
     } 

     System.out.println("%d iterations returned %d sum", ITERATIONS, sum); 
    } 
} 

Scherzi a parte, è necessario fare qualche lettura prima di poter fare alcun progresso significativo verso la scrittura di parametri di riferimento su una JVM moderna. Le stesse ottimizzazioni che consentono al codice Java moderno di eguagliarsi o persino a volte battere C++ rendono davvero difficile il benchmarking.

+0

Grazie, molto utile. Ho qualche lettura da fare, però ... –

0

Il compilatore Java è non consentito per eseguire tali ottimizzazioni perché le chiamate al metodo molto probabilmente causano effetti collaterali, ad esempio azioni IO o modifiche a tutti i campi che possono raggiungere o chiamare altri metodi che lo fanno.

In linguaggi funzionali in cui ogni chiamata di funzione è garantito per restituire lo stesso risultato se chiamato con gli stessi argomenti (passa allo stato sono vietate), un compilatore potrebbe infatti ottimizzare via più chiamate da memorizzare il risultato.

Se ritenete che i vostri algoritmi siano troppo veloci, provate a dargli qualche grande o set di problemi complicati. Ci sono solo pochi algoritmi che sono sempre abbastanza veloci.

Problemi correlati