2011-09-01 11 views
6

Sto provando a verificare la velocità di autoboxing e unboxing in Java, ma quando provo a confrontarlo con un ciclo vuoto su una primitiva, ho notato una cosa curiosa. Questo frammento di codice:Java: quanto tempo utilizza un loop vuoto?

for (int j = 0; j < 10; j++) { 
    long t = System.currentTimeMillis(); 
    for (int i = 0; i < 10000000; i++) 
     ; 
    t = System.currentTimeMillis() - t; 
    System.out.print(t + " "); 
} 

Ogni volta che corro questo, restituisce lo stesso risultato:

6 7 0 0 0 0 0 0 0 0

Perché i primi due cicli prendono sempre un po 'di tempo, poi il resto solo sembrano essere saltati dal sistema?

In this answer to this post, si dice che la compilazione Just-In-Time sarà in grado di ottimizzarlo. Ma se è così, perché i primi due loop hanno richiesto un po 'di tempo?

+3

Penso che 'System.nanoTime()' sia più indicato per questo test (client/server VM dovrebbe anche fare qualche differenza) –

+0

Sì, giusto, i millisecondi sono spesso a grana troppo grossa. –

risposta

20

Attivatori JIT DOPO che un determinato pezzo di codice è stato eseguito molte volte.

HotSpot JVM proverà a identificare "punti caldi" nel codice. Gli hot spot sono parti del codice che vengono eseguite molte volte. Per fare ciò, la JVM "conta" le esecuzioni di varie istruzioni e, quando determina che un determinato pezzo viene eseguito frequentemente, attiverà il JIT. (questa è un'approssimazione, ma è facile da capire spiegata in questo modo).

Il JIT (Just-In-Time) prende quel pezzo di codice e cerca di renderlo più veloce.

Le tecniche utilizzate dal JIT per rendere il vostro codice più veloce sono molto, ma quello che crea più comunemente la confusione sono:

  1. cercherà di determinare se quel pezzo di codice utilizza variabili che sono non usato da nessun'altra parte (variabili inutili) e rimuoverli.
  2. Se si acquisisce e rilascia lo stesso blocco più volte (ad esempio chiamando i metodi sincronizzati dello stesso oggetto), può acquisire il blocco una sola volta e fare tutte le chiamate in un unico blocco sincronizzato
  3. Se si accede ai membri di un oggetto che non si dichiarano volatili, può decidere di ottimizzarlo (posizionando valori in registri e simili), creando strani risultati nel codice multi-thread.
  4. Sarà in linea metodi, per evitare il costo della chiamata.
  5. Tradurrà bytecode su codice macchina.
  6. Se il loop è completamente inutile, potrebbe essere completamente rimosso.

Quindi, la risposta corretta alla tua domanda è che un ciclo vuoto, dopo essere stato JIT, non richiede tempo per l'esecuzione .. molto probabilmente non c'è più.

Ancora una volta, ci sono molte altre ottimizzazioni, ma nella mia esperienza queste sono tra quelle che hanno creato la maggior parte dei grattacapi.

Inoltre, JIT viene migliorato in qualsiasi nuova versione di Java e talvolta è anche leggermente diverso a seconda della piattaforma (poiché è in una certa misura specifica della piattaforma). Le ottimizzazioni fatte dalla JIT sono difficili da capire, perché di solito non le trovi usando javap e ispezionando il bytecode, anche se nelle versioni recenti di Java alcune di queste ottimizzazioni sono state spostate direttamente nel compilatore (ad esempio, dal momento che Java 6 il compilatore è in grado di rilevare e avvertire su variabili locali non utilizzate e metodi privati).

Se si stanno scrivendo alcuni loop per testare qualcosa, di solito è buona pratica avere il loop all'interno di un metodo, chiamare il metodo alcune volte PRIMA di cronometrarlo, per dargli un giro di "accelerazione" e poi eseguire il ciclo temporizzato.

Generalmente questo attiva il JIT in un programma semplice come il tuo, anche se non vi è alcuna garanzia che si inneschi effettivamente (o che esista anche su una determinata piattaforma).

Se si desidera ottenere paranoico sui tempi JIT o non JIT (l'ho fatto): fare un primo giro, cronometrare ogni esecuzione del ciclo, e attendere che i tempi si stabilizzino (ad esempio, differenza dalla media inferiore a 10 %), quindi inizia con il tuo "reale" tempismo.

7

Il JIT non entra in una parte del codice finché non determina che vi è un certo vantaggio nel farlo. Ciò significa che i primi passaggi attraverso un codice non saranno JIT.

+0

Grazie! Capisco, ma cosa può fare il ciclo vuoto? O come ha detto @Simone Gianni, JIT (è garantito per) non verrà attivato a meno che il ciclo non sia stato eseguito per qualche tempo? EDIT: Grazie ancora! Ho ricevuto la risposta dalla risposta di Simone :) –