Quali componenti / fasi di un compilatore JIT sono diversi da un tradizionale compilatore in anticipo?

3

Oltre ad un algoritmo di allocazione dei registri più veloce e ad alcuni trade-off nell'analisi di controllo e flusso di dati per scopi di ottimizzazione, quali componenti / fasi di un compilatore JIT sono diversi da un tradizionale compilatore in anticipo?

    
posta Matthias 11.01.2015 - 17:09
fonte

3 risposte

8

Le principali differenze tra un compilatore AOT e un compilatore JIT sono risorse e informazioni .

Un compilatore AOT ha risorse infinite . Può utilizzare tutta la RAM che gli piace e impiegare tutto il tempo che vuole. (Si noti che questo è teoricamente vero.) Pragmaticamente, le persone non amano le compilazioni a lungo termine.Inoltre, i compilatori sono ora tipicamente incorporati in IDE, dove forniscono un feedback istantaneo mentre-you-type, in modo che almeno lexing, analisi, semantica analisi, tipo di inferenza, controllo del tipo, espansione delle macro, ecc., in pratica tutto, eccetto la generazione e l'ottimizzazione del codice reale devono avvenire molto velocemente e con un uso limitato della memoria.)

Un compilatore JIT OTOH deve "rubare" le sue risorse dall'applicazione in esecuzione. (Anche in teoria, pragmaticamente, il compilatore JIT deve lavorare più duramente, quando viene introdotto un sacco di nuovo codice nel sistema, in genere quando inizia l'applicazione, a quel punto il caricamento dei file di configurazione e l'impostazione dei grafici degli oggetti è il collo di bottiglia, non il compilatore JIT.)

Un compilatore JIT ha molte più informazioni disponibili di un compilatore AOT e non deve faticare per ottenerlo. Un compilatore AOT può solo ottenere informazioni statiche sul codice. Gli algoritmi di analisi statica sono solitamente molto costosi (spesso almeno O (n 2 ) in almeno uno spazio e tempo, a volte esponenziale) e non funzionano nemmeno in modo affidabile , perché molti di loro sono equivalenti a risolvere il problema di interruzione (Analisi della gerarchia di classe, Analisi di fuga, Eliminazione del codice morto, ad esempio).

Un compilatore JIT OTOH non si imbatte nel problema di interruzione, perché non esegue analisi statiche. E non è necessario eseguire algoritmi costosi: vuoi sapere se un metodo viene sovrascritto o meno in modo da poterlo potenzialmente integrare? Non è necessario eseguire Class Hierarchy Analysis, basta osservare le classi, sono tutte lì. O ancora meglio ancora: non preoccuparti nemmeno, solo in linea, comunque, e se si scopre che hai sbagliato su di esso non essere sovrascritto, di nuovo in-linea. Vuoi sapere se un riferimento sfugge a un ambito locale o no in modo da poterlo allocare potenzialmente nello stack? Non preoccuparti, basta allocarlo nello stack, taggarlo e quando il tag compare altrove, riassegnalo sull'heap. E poiché si compila il codice solo quando è in esecuzione, l'eliminazione dei codici morti è totalmente banale perché il codice non verrà mai eseguito e quindi non verrà mai compilato.

Quindi, la differenza fondamentale è che l'analisi in un compilatore JIT può essere più semplice, perché ha molte informazioni disponibili che un compilatore AOT non possiede e l'ottimizzazione e la generazione del codice deve essere più semplice, perché ha meno risorse disponibili. Si noti, tuttavia, che un compilatore JIT può comunque eseguire ottimizzazioni molto più aggressive di un compilatore AOT, poiché non deve necessariamente dimostrare che le ottimizzazioni sono corrette. Se risulta che l'ottimizzazione è sbagliata, può sempre de-ottimizzare di nuovo. (Non tutte le JIT lo fanno (il JIT CLR ad esempio, non è in grado di de-ottimizzare), ma ad esempio il JIT HotSpot nel JDK di Oracle.) L'Inlining speculativo è una tale ottimizzazione che è possibile solo in un JIT de-optimizing .

Una cosa peculiare delle JIT è che molto spesso i linguaggi che compilano sono progettati per essere facilmente compilabili da una macchina (ad es. bytecode JVM, bytecode CPython, bytecode Rubinius, IRL LLVM, CIL CLI, bytecode Dalvik), mentre lingue che una tipica compilazione di compilatori AOT è progettata per essere facilmente leggibile dagli esseri umani (es. Ruby, Python, Java). Ma ho capito che la tua domanda riguardava un AOT rispetto a un JIT per la stessa lingua , quindi niente di tutto questo. Ovviamente, se si confrontano i compilatori per lingue diverse, ci saranno molte differenze e molti di questi saranno totalmente estranei alla differenza tra JIT e AOT e più relativi alle differenze tra le due lingue.

    
risposta data 12.01.2015 - 04:33
fonte
1

Poiché si tratta di una domanda molto ampia, mi limiterò alle differenze concettuali piuttosto che ai dettagli di implementazione:

  • Un compilatore JIT può teoricamente eseguire più ottimizzazioni specifiche della piattaforma, poiché non deve preoccuparsi di produrre un binario compatibile con il maggior numero possibile di macchine.

  • Un compilatore JIT ha accesso ad alcune informazioni disponibili solo in fase di runtime, il che significa che può fare cose come fare una versione ottimizzata solo per interi della tua funzione perché ha visto solo argomenti interi finora (questo lo rende lingue dinamiche), o prova a usare meno memoria perché il tuo sistema non ha molto spazio.

  • Un compilatore AOT non deve mai essere installato sui computer degli utenti. Un compilatore JIT (e il runtime o VM in genere viene fornito con) deve essere presente ogni volta che eseguono un programma che lo utilizza.

  • Un compilatore AOT tipicamente guarda l'intero programma, o almeno parti molto grandi di esso, permettendogli di fare cose come controllare gli errori di tipo tra i moduli (che è buono nei linguaggi statici che definiscono rigidamente i tipi ciascuno la funzione accetterà) e rimuoverà il codice che sa non verrà mai chiamato.

  • Poiché la compilazione AOT si verifica solo una volta, le prestazioni dell'eseguibile sono più prevedibili e ripetibili, anche in situazioni in cui un JIT può avere prestazioni migliori in media.

risposta data 11.01.2015 - 17:29
fonte
1

La maggior parte dei compilatori JIT utilizza come lingua di input una lingua intermedia o bytecode. Per questo motivo, la struttura di un compilatore JIT è più vicina a quella di un assemblatore tradizionale rispetto a quella di un compilatore AOT.

L'analisi lessicale e l'analisi sono molto più semplici, perché la lingua di input è molto più semplice del tipico linguaggio di programmazione di alto livello.
L'analisi semantica è probabilmente completamente assente, confidando che il bytecode non provi a fare l'impossibile (come la combinazione di operazioni integer e floating point senza le conversioni richieste).

Ciò che un compilatore AOT e JIT hanno in comune sono le ottimizzazioni e la generazione del codice, anche se un compilatore JIT non può eseguire lunghe ottimizzazioni ma può facilmente fare ottimizzazioni che richiedono informazioni su come viene effettivamente utilizzato il codice.

    
risposta data 11.01.2015 - 18:54
fonte

Leggi altre domande sui tag