2012-01-23 14 views
6

Ok, ho letto diverse discussioni riguardanti le differenze tra gli interpreti abilitati JIT e non JIT e perché JIT di solito aumenta le prestazioni.Do not Both Gli interpreti abilitati JIT e non JIT producono in modo definitivo il codice macchina

Tuttavia, la mia domanda è:

In ultima analisi, non un non-JIT abilitato interprete deve girare bytecode (riga per riga) in codice macchina/nativo da eseguire, proprio come un compilatore JIT farà ? Ho visto post e libri di testo che dicono di sì, e post che dicono di no. L'ultimo argomento è che l'interprete/JVM esegue questo bytecode direttamente senza alcuna interazione con codice macchina/nativo.

Se interpreti non JIT fanno girare ogni riga in codice macchina, sembra che i principali vantaggi di JIT sono ...

  1. L'intelligenza di caching o tutti (JIT normale) o frequente riscontro (hotspot/ottimizzazione adattiva) parti del bytecode in modo che la fase di compilazione del codice macchina non sia necessaria ogni volta.

  2. Qualsiasi compilatore JIT di ottimizzazione può eseguire nella conversione di codice in codice macchina.

È preciso? Sembra che ci sia poca differenza (diversa dall'ottimizzazione possibile, o blocchi JITtari o linea per linea) tra la traduzione del bytecode e il codice macchina tramite interpreti abilitati JIT e JIT.

Grazie in anticipo.

risposta

8

Un interprete non JIT non converte codice bytecode in codice macchina. Potete immaginare il funzionamento di un qualcosa di non-JIT bytecode interprete come questo (userò un pseudocodice Java-like):

int[] bytecodes = { ... }; 
int ip  = 0; // instruction pointer 
while(true) { 
    int code = bytecodes[ip]; 
    switch(code) { 
    case 0; 
     // do something 
     ip += 1; break; 
    case 1: 
     // do something else 
     ip += 1; break; 
    // and so on... 
    } 
} 

Quindi per ogni bytecode eseguito, l'interprete deve recuperare il codice, accendere il suo valore per decidere cosa fare e incrementare il suo "puntatore di istruzioni" prima di passare alla successiva iterazione.

Con un JIT, tutto ciò che è in testa sarebbe ridotto a nulla. Prenderebbe semplicemente il contenuto degli appositi rami dell'interruttore (le parti che dicono "// fai qualcosa"), li stringa insieme in memoria ed eseguirà un salto all'inizio del primo. Non sarebbe richiesto alcun "puntatore di istruzioni" del software - solo il puntatore dell'istruzione hardware della CPU. Nessun recupero di bytecode dalla memoria e accensione dei loro valori.

Scrivere una macchina virtuale non è difficile (se non deve essere estremamente performante) e può essere un esercizio interessante. Ne ho fatto uno una volta per un progetto embedded in cui il codice del programma doveva essere molto compatto.

+0

Grazie per la risposta! Stavo leggendo il libro "Partire con i giochi e la grafica in C++" e si menziona "L'interprete traduce ogni istruzione di alto livello nelle sue equivalenti istruzioni in linguaggio macchina e la esegue immediatamente". È qui che è iniziata la confusione. Tuttavia, si trattava di interpreti in generale. –

+0

Un interprete "traduce" i comandi di alto livello sul linguaggio macchina passando/saltando su una sezione del linguaggio macchina che implementa l'operazione rappresentata dal comando di alto livello. –

0

Decenni fa, sembrava esserci una convinzione diffusa che i compilatori avrebbero trasformato un intero programma in codice macchina, mentre gli interpreti avrebbero tradotto un'istruzione in codice macchina, l'esecuzione, l'eliminazione, la traduzione del successivo, ecc. era errato al 99%, ma c'erano due minuscoli gherigli di verità. Su alcuni microprocessori, alcune istruzioni richiedevano l'uso di indirizzi che erano specificati nel codice. Ad esempio, su 8080, c'era un'istruzione per leggere o scrivere un indirizzo I/O specificato 0x00-0xFF, ma non c'era alcuna istruzione per leggere o scrivere un indirizzo I/O specificato in un registro. Era comune per gli interpreti di lingue, se il codice utente faceva qualcosa come "out 123,45", per memorizzare in tre byte di memoria le istruzioni "out 7Bh/ret", carica l'accumulatore con 2Dh, ed effettua una chiamata al primo di quelle istruzioni.In tale situazione, l'interprete produrrebbe effettivamente un'istruzione di codice macchina per eseguire l'istruzione interpretata. Tale generazione di codice, tuttavia, era per lo più limitata a cose come le istruzioni IN e OUT.

Molti interpreti Microsoft BASIC comuni per il 6502 (e forse anche l'8080) hanno fatto un uso un po 'più esteso del codice memorizzato nella RAM, ma il codice che è stato memorizzato nella RAM non dipende in modo significativo dal programma in esecuzione; la maggior parte della routine RAM non cambiava durante l'esecuzione del programma, ma l'indirizzo dell'istruzione successiva veniva tenuto in linea come parte della routine consentendo l'uso di un'istruzione "LDA" in modalità assoluta, che salvava almeno un ciclo di spegnimento ogni byte va a prendere.