2016-02-24 30 views
19

Ho già lavorato con -XX:+PrintCompilation e conosco le tecniche di base del compilatore JIT e perché viene utilizzata la compilazione JIT.In che modo JVM ha deciso di compilare JIT un metodo (categorizzare un metodo come "caldo")?

Eppure non ho ancora scoperto come JVM decida di JIT-compilare un metodo, ad esempio "quando è giunto il momento giusto per compilare un metodo JIT".

Ho ragione nell'assumere che ogni metodo inizi a essere interpretato e finché non viene classificato come "metodo caldo" non verrà compilato? Ho qualcosa nella parte posteriore della mia testa che leggo che un metodo è considerato "caldo" quando è stato eseguito almeno 10.000 volte (dopo aver interpretato il metodo 10.000 volte, sarà compilato), ma devo ammettere che sono non sono sicuro di questo o dove ho letto questo.

Quindi, per riassumere la mia domanda:

(1) è ogni metodo interpretato come fino a quando non è stato classificato come metodo di "caldo" (e quindi è stato compilato) oppure ci sono ragioni per i metodi per essere compilato anche se non sono "caldi"?

(2) In che modo la JVM classifica i metodi in metodi "non caldi" e "caldi"? Numero di esecuzione? Qualunque altra cosa?

(3) Se ci sono alcune soglie (come il numero di esecuzioni) per i metodi "hot", ci sono flag Java (-XX:...) per impostare queste soglie?

+0

Date un'occhiata all'uscita del '-XX: + PrintFlagsFinal', ci sono un sacco di bandiere relativi ai compilatori JIT e le loro livelli, inline, metodi dimensioni, le discussioni del compilatore ecc – the8472

risposta

36

La politica di compilazione di HotSpot è piuttosto complessa, in particolare per la compilazione a livelli, che è attiva per impostazione predefinita in Java 8. Non è un numero di esecuzioni, né una questione di parametro CompileThreshold.

La spiegazione migliore (apparentemente, l'unica spiegazione ragionevole) può essere trovata nei sorgenti HotSpot, vedere advancedThresholdPolicy.hpp.

cercherò di riassumere i punti principali di questa politica di compilazione avanzate:

  • esecuzione inizia a livello 0 (interprete).
  • I principali fattori scatenanti per la compilazione sono
    1. metodo contatore invocazione i;
    2. counter counter b. I rami a rovescio tipicamente denotano un ciclo nel codice.
  • Ogni volta che i contatori raggiungono certo valore di frequenza (TierXInvokeNotifyFreqLog, TierXBackedgeNotifyFreqLog), una politica di raccolta è chiamato a decidere cosa fare dopo con il metodo attualmente in esecuzione. A seconda dei valori di i, b e carico di corrente di C1 e C2 fili compilatore può decidere di

    • continuare l'esecuzione in interprete;
    • avviare la creazione di profili nell'interprete;
    • metodo di compilazione con C1 al livello 3 con dati di profilo completi necessari per la ricompilazione futura;
    • metodo di compilazione con C1 al livello 2 senza profilo ma con possibilità di ricompilazione (improbabile);
    • infine compilare il metodo con C1 al livello 1 senza profilo o contatori (anche improbabile).

    I parametri chiave qui sono TierXInvocationThreshold e TierXBackEdgeThreshold. Le soglie possono essere regolate dinamicamente per un determinato metodo in base alla lunghezza della coda di compilazione.

  • La coda di compilazione non è FIFO, ma piuttosto una coda di priorità.

  • Il codice compilato da C1 con i dati del profilo (livello 3) si comporta in modo simile, tranne che le soglie per il passaggio al livello successivo (C2, livello 4) sono molto più grandi. Per esempio. un metodo interpretato può essere compilato al livello 3 dopo circa 200 invocazioni, mentre il metodo C1-compilato è soggetto per la ricompilazione al livello 4 dopo 5000+ invocazioni.

  • Per l'allineamento dei metodi viene utilizzata una politica speciale. Piccoli metodi possono essere inseriti nel chiamante anche se non sono "caldi". I metodi un po 'più grandi possono essere sottolineati solo se vengono richiamati frequentemente (InlineFrequencyRatio, InlineFrequencyCount).
+0

Questo link è davvero utile e contiene tutto ciò che volevo sapere. Hai ragione, è difficile trovare informazioni _reasonable là fuori, non ho ancora letto nulla di così dettagliato sulla compilazione a più livelli, molto probabilmente non lo farò mai. Grazie! –

+2

L'intuizione chiave per me è che il percorso normale è 0 (interpretato) -> 3 (C1, profilo completo) -> 4 (C2). Su quel percorso, C1 esiste davvero solo per raccogliere dati di profilo per C2 con cui lavorare. –

+3

Esistono quindi tre percorsi alternativi minori. (1) Se la compilazione C1 trova che il metodo è banale, è compilato a 1 (C1, senza profilazione) perché 4 (C2) non sarebbe più veloce. (2) Se il compilatore C2 è occupato, il metodo viene compilato a 2 (C1, profilazione leggera) finché C2 è meno occupato, a quel punto viene ricompilato a 3 C1, profilazione completa), quindi può proseguire fino a 4 (C2). (3) Se C1 è occupato ma C2 no, la profilazione viene eseguita nell'interprete, quindi il metodo può andare direttamente a C2 senza passare per C1. –

6

Il parametro principale per controllare questo è -XX:CompileThreshold=10000

Hotspot per Java 8 ora utilizza una compilation su più livelli di default utilizzando una serie di fasi di compilazione dal livello 1 a 4. Credo 1 è alcuna ottimizzazione. Livello 3 è C1 (basato sul client client) e Livello 4 è C2 (basato sul compilatore del server)

Ciò significa che una piccola ottimizzazione può avvenire prima di quanto ci si potrebbe aspettare e può continuare a ottimizzarsi a lungo dopo aver raggiunto la soglia 10K. Il più alto che ho visto è l'analisi di fuga che elimina uno StringBuilder dopo un milione di chiamate.

Nota: un ciclo ripetuto più volte può attivare il compilatore. per esempio. un loop di 10K volte può essere sufficiente.

1) Fino a quando un metodo è considerato abbastanza caldo, viene interpretato. Tuttavia alcune JVM (ad es. Azul Zing) possono compilare metodi all'avvio e si può forzare la JVM di Hotspot a compilare un metodo tramite un'API interna. Java 9 può anche avere un compilatore AOT (Ahead Of Time) ma è ancora in fase di ricerca AFAIK

2) Numero di chiamate o numero di iterazioni.

3) Sì -XX:CompileThreshold= è il principale.

+0

Avete ulteriore materiale in cui Potrei leggere su questo? Conosco C1 e C2, tuttavia non ho sentito parlare dei livelli di compilazione 1-4 (Interpeter, C1, C2 sarebbero 3 livelli). E, in pratica, dici che un metodo può essere considerato molto caldo sotto il "CompilerThreshold". Qual è questa soglia per allora? Posso excpect che un metodo è considerato hot _at l'ultimo_ quando è stato chiamato 'CompilerThreshold' volte? –

+0

Ci scusiamo per la domanda di follow-up, ma solo per ottenere la mia terminologia corretta: Posso fare riferimento a un metodo come "hot" se è stato ottimizzato almeno una volta, o che è esattamente la definizione di "metodo caldo"? –

+1

@MarkusWeninger Questo è qualcosa che cambia da aggiornamento ad aggiornamento. Se chiami lo stesso set di codice molte volte (ma ognuna una volta) puoi vedere che non tutte vengono compilate contemporaneamente. L'algo reale è molto più complesso.Il posto migliore per ulteriori informazioni Sospetto sia la fonte. Tutto il resto scritto potrebbe non essere aggiornato per l'attuale JVM. –