Compilation vs interprétation
La synthèse : compilé ou interprété n'est pas noir ou blanc mais un spectre. AOT vs JIT, où se situe vraiment chaque langage (C/Rust, C#/Java, Python, JavaScript), et ce que ça change pour toi.
Compilation vs interprétation
On a vu séparément la compilation (tout traduire avant de lancer) et les langages interprétés (exécuter au fil de l'eau). Cette note fait la synthèse : en pratique, aucun langage moderne n'est tout blanc ou tout noir, et savoir où se situe le tien t'aide à comprendre ses forces et ses limites.
# La vraie question : QUAND traduit-on ?
« Compilé ou interprété ? » est une mauvaise question. La bonne, c'est : à quel moment le code devient-il du code machine, et combien de ce travail est fait à l'avance ?
- À un extrême, tout est traduit avant de lancer : rapide à l'exécution, erreurs attrapées tôt.
- À l'autre, tout est traduit pendant l'exécution : plus souple, démarrage immédiat, erreurs tardives.
La plupart des langages sont entre les deux. C'est un spectre, pas deux camps.
# AOT vs JIT
Deux stratégies pour ce « quand » :
- AOT (Ahead-Of-Time) : toute la traduction en code machine est faite avant le lancement, une bonne fois. Résultat : un binaire natif prêt à courir, sans rien d'autre.
- JIT (Just-In-Time) : la traduction en code machine se fait pendant l'exécution, au dernier moment, souvent guidée par ce qui tourne vraiment (le code « chaud » est optimisé en priorité).
Entre les deux, beaucoup de langages passent par une étape intermédiaire : ils compilent d'abord vers du bytecode (ni humain, ni machine), qu'un runtime finit ensuite en code machine par JIT.
# Où se situe chaque langage
| Langage | Comment il tourne réellement |
|---|---|
| C, C++, Rust | Compilés AOT en binaire natif. Aucun runtime, au plus près de la machine. |
| Go | Compilé AOT en binaire natif autonome (avec un petit runtime embarqué). |
| C#, Java | Compilés en bytecode (IL en .NET, bytecode JVM), puis JIT à l'exécution par le runtime (.NET / JVM). |
| Python, Ruby | Compilés en bytecode en interne, puis interprétés par une machine virtuelle. |
| JavaScript | Interprété puis JIT par le moteur (V8) : il démarre en interprétant, puis compile le code chaud à la volée. |
Même les langages dits « interprétés » (Python) compilent en bytecode avant de l'exécuter. L'interprétation pure, ligne par ligne du texte source, est aujourd'hui très rare. Et l'inverse existe aussi : C# sait produire un binaire natif via Native AOT.
# Ce que ça change pour toi
| Critère | Plutôt AOT / compilé natif | Plutôt JIT / interprété |
|---|---|---|
| Erreurs | beaucoup attrapées avant de lancer (dont les types) | découvertes à l'exécution |
| Démarrage | immédiat | le JIT doit « chauffer » un peu |
| Vitesse de pointe | très rapide, prévisible | le JIT peut optimiser finement le code chaud |
| Portabilité | liée à la plateforme (recompiler par OS/CPU) | le bytecode + runtime tourne partout où le runtime existe |
| Distribution | un binaire autonome | besoin du runtime/interpréteur ; le code source peut être visible |
# À retenir
- La vraie question n'est pas « compilé ou interprété » mais quand la traduction a lieu : c'est un spectre.
- AOT : tout traduit avant, binaire natif (C, Rust, Go). JIT : traduit à l'exécution (C#, Java, JS), souvent via un bytecode.
- Presque tous les langages « interprétés » compilent en bytecode en interne ; l'interprétation pure est rare.
- Ce que ça change concrètement : quand les erreurs surgissent, le démarrage, la vitesse, la portabilité et la distribution.
À lire ensuite : La transpilation, une traduction d'un langage vers un autre, encore différente.