Compilatore just-in-time
# Compilatore just-in-time

Un compilatore just-in-time o JIT permette un tipo di compilazione, conosciuta anche come traduzione dinamica, effettuata durante l'esecuzione del programma piuttosto che precedentemente. L'obiettivo finale è combinare i vantaggi della compilazione del bytecode a quelli della compilazione nativa, aumentando le prestazioni quasi al pari di una compilazione direttamente in linguaggio macchina.

## Storia

La traduzione dinamica fu realizzata per la prima volta nei primi anni ottanta in una versione commerciale del linguaggio Smalltalk oggi conosciuta con il nome di VisualWorks. Oggi la traduzione dinamica è presente nella maggior parte delle implementazioni della Java virtual machine.

La macchina virtuale Java HotSpot della Sun Microsystems contiene un esempio di implementazione JIT. In realtà ve ne sono due: una versione "client" che è ottimizzata per essere veloce, ed è più indicata per applicazioni che hanno un tempo di esecuzione breve; e una versione "server" che produce un codice altamente ottimizzato, ed è pensata per applicazioni che devono durare a lungo nel tempo. Gli utenti possono scegliere quale compilatore dinamico usare in base alle caratteristiche della loro applicazione.

Microsoft ha scelto un sistema JIT per l'ambiente .NET, e il processore Crusoe della Transmeta usa tecniche simili a livello hardware. La macchina virtuale Parrot per il linguaggio Perl usa un sistema JIT. PyPy è un compilatore JIT per il linguaggio Python. Nell'ottobre del 2005 Macromedia ha annunciato che, a partire dalla versione 8.5, il plugin di Macromedia Flash userà un compilatore JIT per migliorare le performance dell'esecuzione di Flash.

## Funzionamento

Un compilatore JIT si basa su due concetti recenti negli ambienti run-time: la compilazione del bytecode e la compilazione dinamica.

In un sistema a compilazione di bytecode come il Perl, GNU CLISP o le prime versioni di Java, il codice sorgente è tradotto in una rappresentazione intermedia conosciuta come bytecode. Il bytecode non è il codice macchina di un computer in particolare, e può essere portabile su più architetture. Il bytecode è poi interpretato o eseguito in una macchina virtuale.

In un sistema a compilazione dinamica il compilatore può essere utilizzato in fase di esecuzione. Per esempio, la maggior parte dei sistemi Lisp hanno una funzione compile con cui è possibile compilare nuove funzioni create durante l'esecuzione. Sebbene sia vantaggiosa durante una sessione di debug interattivo, la compilazione dinamica risulta poco utile quando la fase di sviluppo del sistema è terminata.

In un ambiente JIT la prima fase è costituita dalla compilazione del bytecode, in cui si trasforma il codice sorgente in una rappresentazione intermedia portabile e ottimizzabile, detta appunto bytecode. Successivamente il codice bytecode viene installato sul sistema di destinazione. Quando il codice viene eseguito, il compilatore dell'ambiente di esecuzione lo traduce in codice macchina nativo. La traduzione in codice macchina può avvenire per file o per funzione: le funzioni possono essere compilate solo quando stanno per essere eseguite, da qui il nome just-in-time, ovvero "appena in tempo".

La compilazione just-in-time permette di ottenere un buon compromesso tra velocità d'esecuzione e portabilità del codice. Nella fase di compilazione del bytecode è eseguita la maggior parte del "lavoro pesante", ovvero tutte quelle operazioni che richiedono molto tempo per essere eseguite, come l'analisi sintattica e semantica del codice sorgente e una prima fase di ottimizzazione; la compilazione da bytecode a codice nativo è invece molto più veloce. I compilatori da bytecode a codice macchina (tra cui, appunto, i sistemi JIT) sono più semplici da scrivere perché la maggior parte del lavoro è già stata compiuta dal compilatore che ha prodotto il bytecode; questo, inoltre, rende i programmi in bytecode più facilmente portabili su nuove architetture.

