Langues compilées et langues interprétées


284

J'essaie de mieux comprendre la différence. J'ai trouvé beaucoup d'explications en ligne, mais elles tendent vers les différences abstraites plutôt que vers les implications pratiques.

La plupart de mes expériences de programmation ont été avec CPython (dynamique, interprété) et Java (statique, compilé). Cependant, je comprends qu'il existe d'autres types de langues interprétées et compilées. Mis à part le fait que les fichiers exécutables peuvent être distribués à partir de programmes écrits dans des langages compilés, y a-t-il des avantages / inconvénients pour chaque type? Souvent, j'entends des gens dire que les langages interprétés peuvent être utilisés de manière interactive, mais je crois que les langages compilés peuvent également avoir des implémentations interactives, n'est-ce pas?


32
Vous avez choisi exactement les pires langues pour cette comparaison. Les deux sont compilés en bytec. La seule vraie différence entre eux est le JITer, et même Python en a un partiel (psyco).
Ignacio Vazquez-Abrams

1
Un bon exemple de langage compilé interactif est Clojure - tout est entièrement compilé (d'abord sur la JVM puis sur le code natif via le JIT). Cependant, une grande partie de la recompilation se produit de manière dynamique et le développement se fait souvent dans un shell REPL interactif où vous pouvez évaluer n'importe quelle fonction de votre choix dans l'environnement en cours d'exécution.
mikera

Standard ML est un autre langage compilé interactif; le compilateur intégré émet également du vrai code machine natif.
Donal Fellows


Réponses:


460

Un langage compilé est un langage où le programme, une fois compilé, est exprimé dans les instructions de la machine cible. Par exemple, une opération d'ajout "+" dans votre code source peut être traduite directement en instruction "ADD" dans le code machine.

Un langage interprété est un langage dans lequel les instructions ne sont pas directement exécutées par la machine cible, mais lues et exécutées à la place par un autre programme (qui est normalement écrit dans la langue de la machine native). Par exemple, la même opération "+" serait reconnue par l'interpréteur au moment de l'exécution, qui appellerait alors sa propre fonction "add (a, b)" avec les arguments appropriés, qui exécuterait alors l'instruction "ADD" du code machine .

Vous pouvez faire tout ce que vous pouvez faire dans un langage interprété dans un langage compilé et vice versa - ils sont tous les deux Turing complets. Cependant, les deux présentent des avantages et des inconvénients pour la mise en œuvre et l'utilisation.

Je vais généraliser complètement (les puristes me pardonnent!) Mais, grosso modo, voici les avantages des langages compilés:

  • Performances plus rapides en utilisant directement le code natif de la machine cible
  • Possibilité d'appliquer des optimisations assez puissantes pendant la phase de compilation

Et voici les avantages des langages interprétés:

  • Plus facile à implémenter (écrire de bons compilateurs est très difficile !!)
  • Pas besoin d'exécuter une étape de compilation: peut exécuter du code directement "à la volée"
  • Peut être plus pratique pour les langues dynamiques

Notez que les techniques modernes telles que la compilation de bytecode ajoutent une complexité supplémentaire - ce qui se passe ici est que le compilateur cible une "machine virtuelle" qui n'est pas la même que le matériel sous-jacent. Ces instructions de machine virtuelle peuvent ensuite être compilées à nouveau à un stade ultérieur pour obtenir le code natif (par exemple, comme le fait le compilateur JVM JIT Java).


1
Toutes les langues compilées n'ont pas besoin d'une étape de compilation lente. Les implémentations sérieuses de Common Lisp sont des compilateurs, et elles ne se soucient souvent pas d'un interprète, préférant simplement compiler très rapidement à la volée. D'un autre côté, Java a besoin d'une étape de compilation, et elle est généralement visible.
David Thornley

2
@Kareem: le compilateur JIT ne fait que 1) et 2) une fois - après cela, c'est du code natif tout le long. L'interprète doit faire à la fois 1) et 2) à chaque fois que le code est appelé (ce qui peut être plusieurs, plusieurs fois ...). Ainsi, au fil du temps, le compilateur JIT gagne par une longue marge.
mikera

3
Oui, le bytecode est traduit en code machine à un moment donné pendant l'exécution globale du programme (par opposition à avant l'exécution du programme, comme c'est le cas avec un compilateur traditionnel). Mais un morceau de code donné peut être exécuté plus de 10 millions de fois pendant l'exécution globale du programme. Il (probablement) n'est compilé qu'une seule fois du bytecode au code machine. Par conséquent, la surcharge d'exécution de JIT est faible et peut être ignorée pour les programmes de longue durée. Une fois que le compilateur JIT aura terminé son travail, vous exécuterez efficacement du code machine pur tout le long.
mikera

2
Il s'agit en fait d'une fausse dichotomie. Il n'y a rien d'intrinsèque à un langage qui le rende compilé notre interprété. Ce n'est rien de plus qu'une idée fausse largement répandue. De nombreuses langues ont les deux implémentations et toutes les langues peuvent avoir l'une ou l'autre.
mmachenry

2
@mmachenry ce n'est pas une fausse dichotomie. "langage de programmation" comprend à la fois la conception et la mise en œuvre. Alors que dans un sens théorique une définition de langage donnée peut être à la fois compilée et interprétée, dans la pratique du monde réel, il existe des différences considérables dans la mise en œuvre. Personne n'a encore résolu comment compiler efficacement certaines constructions de langage, par exemple - c'est un problème de recherche ouvert.
mikera

99

Un langage lui-même n'est ni compilé ni interprété, seule une implémentation spécifique d'un langage l'est. Java en est un parfait exemple. Il existe une plate-forme basée sur le bytecode (la JVM), un compilateur natif (gcj) et un interpètre pour un sur-ensemble de Java (bsh). Alors qu'est-ce que Java maintenant? Compilé en Bytecode, compilé en natif ou interprété?

D'autres langues, qui sont compilées et interprétées, sont Scala, Haskell ou Ocaml. Chacune de ces langues possède un interpréteur interactif, ainsi qu'un compilateur de code octet ou de code machine natif.

Donc, classer les langues en "compilé" et "interprété" n'a généralement pas beaucoup de sens.


3
Je suis d'accord. Ou disons: il y a des compilateurs natifs (créant du code machine pour le CPU à manger), et des compilateurs pas si natifs (créant des trucs à jetons, c'est-à-dire du code intermédiaire, que certains compilateurs juste à temps compilent avant de coder le code machine ( ou pendant) l'exécution UNE FOIS), et il existe de "vrais" non-compilateurs qui ne produisent jamais de code machine et ne laissent jamais le CPU exécuter le code. Ces derniers sont des interprètes. Aujourd'hui, les compilateurs natifs qui produisent directement du code machine (CPU) au moment de la compilation deviennent de plus en plus rares. Delphi / Codegear est l'un des meilleurs survivants.
TheBlastOne

57

Commencez à penser en termes de: explosion du passé

Il était une fois, il y a très longtemps, vivait au pays des interprètes et des compilateurs informatiques. Toutes sortes d'agitation s'ensuivirent sur les mérites de l'un sur l'autre. L'opinion générale à l'époque était quelque chose du genre:

  • Interprète: rapide à développer (éditer et exécuter). Lent à exécuter car chaque instruction devait être interprétée en code machine à chaque exécution (pensez à ce que cela signifiait pour une boucle exécutée des milliers de fois).
  • Compilateur: lent à développer (éditer, compiler, lier et exécuter. Les étapes de compilation / lien peuvent prendre du temps). Rapide à exécuter. L'ensemble du programme était déjà en code machine natif.

Une différence d'un ou deux ordres de grandeur dans les performances d'exécution existait entre un programme interprété et un programme compilé. D'autres points distinctifs, la mutabilité au moment de l'exécution du code par exemple, présentaient également un certain intérêt, mais la principale distinction tournait autour des problèmes de performances au moment de l'exécution.

Aujourd'hui, le paysage a évolué à un point tel que la distinction compilée / interprétée est à peu près hors de propos. De nombreux langages compilés font appel à des services d'exécution qui ne sont pas entièrement basés sur le code machine. De plus, la plupart des langages interprétés sont "compilés" en code octet avant exécution. Les interprètes de code octet peuvent être très efficaces et rivaliser avec du code généré par le compilateur du point de vue de la vitesse d'exécution.

La différence classique est que les compilateurs ont généré du code machine natif, les interprètes lisent le code source et le code machine généré à la volée en utilisant une sorte de système d'exécution. Aujourd'hui, il ne reste que très peu d'interprètes classiques - presque tous compilés en octets (ou dans un autre état semi-compilé) qui s'exécute ensuite sur une "machine" virtuelle.


1
Nice - grand résumé dans le dernier paragraphe - merci!
ckib16

26

Les cas extrêmes et simples:

  • Un compilateur produira un exécutable binaire au format exécutable natif de la machine cible. Ce fichier binaire contient toutes les ressources requises à l'exception des bibliothèques système; il est prêt à fonctionner sans autre préparation et traitement et il fonctionne comme un éclair car le code est le code natif du CPU sur la machine cible.

  • Un interprète présentera à l'utilisateur une invite dans une boucle où il peut entrer des instructions ou du code, et en frappant RUNou l'équivalent, l'interprète examinera, analysera, analysera et exécutera interprétativement chaque ligne jusqu'à ce que le programme s'exécute jusqu'à un point d'arrêt ou une erreur . Parce que chaque ligne est traitée seule et que l'interprète n'apprend rien après avoir vu la ligne auparavant, l'effort de convertir un langage lisible par l'homme en instructions machine est engagé à chaque fois pour chaque ligne, donc c'est lent. Du côté positif, l'utilisateur peut inspecter et autrement interagir avec son programme de toutes sortes de façons: modification des variables, changement de code, exécution en mode trace ou débogage ... peu importe.

Avec ceux-là à l'écart, permettez-moi d'expliquer que la vie n'est plus aussi simple. Par exemple,

  • De nombreux interprètes précompileront le code qui leur est fourni, de sorte que l'étape de traduction n'a pas à être répétée encore et encore.
  • Certains compilateurs compilent non pas en fonction des instructions machine spécifiques au CPU mais en bytecode, une sorte de code machine artificiel pour une machine fictive. Cela rend le programme compilé un peu plus portable, mais nécessite un interpréteur de bytecode sur chaque système cible.
  • Les interprètes de bytecode (je regarde Java ici) ont récemment tendance à recompiler le bytecode qu'ils obtiennent pour le CPU de la section cible juste avant l'exécution (appelé JIT). Pour gagner du temps, cela n'est souvent fait que pour le code qui s'exécute souvent (hotspots).
  • Certains systèmes qui ressemblent et agissent comme des interprètes (Clojure, par exemple) compilent immédiatement tout le code qu'ils obtiennent, mais permettent un accès interactif à l'environnement du programme. C'est essentiellement la commodité des interprètes avec la vitesse de compilation binaire.
  • Certains compilateurs ne compilent pas vraiment, ils ne font que pré-digérer et compresser le code. J'ai entendu il y a quelque temps comment Perl fonctionne. Donc, parfois, le compilateur fait juste un peu de travail et la plupart d'entre eux sont encore de l'interprétation.

En fin de compte, de nos jours, interpréter et compiler est un compromis, le temps passé (une fois) à compiler étant souvent récompensé par de meilleures performances d'exécution, mais un environnement interprétatif offrant plus de possibilités d'interaction. Compiler ou interpréter dépend principalement de la façon dont le travail de «compréhension» du programme est réparti entre différents processus, et la ligne est un peu floue de nos jours alors que les langues et les produits essaient d'offrir le meilleur des deux mondes.


23

Depuis http://www.quora.com/What-is-the-difference-between-compiled-and-interpreted-programming-languages

Il n'y a pas de différence, car «langage de programmation compilé» et «langage de programmation interprété» ne sont pas des concepts significatifs. Tout langage de programmation, et je veux dire vraiment n'importe quel, peut être interprété ou compilé. Ainsi, l'interprétation et la compilation sont des techniques de mise en œuvre, pas des attributs de langues.

L'interprétation est une technique par laquelle un autre programme, l'interpréteur, effectue des opérations au nom du programme en cours d'interprétation afin de l'exécuter. Si vous pouvez imaginer lire un programme et faire ce qu'il dit de faire étape par étape, par exemple sur une feuille de papier à gratter, c'est exactement ce que fait également un interprète. Une raison courante d'interpréter un programme est que les interprètes sont relativement faciles à écrire. Une autre raison est qu'un interprète peut surveiller ce qu'un programme essaie de faire pendant son exécution, pour appliquer une politique, par exemple, de sécurité.

La compilation est une technique par laquelle un programme écrit dans une langue (la «langue source») est traduit dans un programme dans une autre langue (le «langage objet»), ce qui, espérons-le, signifie la même chose que le programme d'origine. Pendant la traduction, il est courant que le compilateur essaie également de transformer le programme de manière à rendre le programme objet plus rapide (sans changer sa signification!). Une raison courante pour compiler un programme est qu'il existe un bon moyen d'exécuter des programmes dans le langage objet rapidement et sans surcharger l'interprétation du langage source en cours de route.

Vous avez peut-être deviné, sur la base des définitions ci-dessus, que ces deux techniques de mise en œuvre ne s'excluent pas mutuellement et peuvent même être complémentaires. Traditionnellement, le langage objet d'un compilateur était un code machine ou quelque chose de similaire, qui fait référence à un certain nombre de langages de programmation compris par des processeurs informatiques particuliers. Le code machine s'exécuterait alors «sur le métal» (bien que l'on puisse voir, si l'on y regarde de près, que le «métal» fonctionne beaucoup comme un interprète). Aujourd'hui, cependant, il est très courant d'utiliser un compilateur pour générer du code objet destiné à être interprété - par exemple, c'est ainsi que Java fonctionnait (et parfois fonctionne toujours). Il existe des compilateurs qui traduisent d'autres langues en JavaScript, qui est ensuite souvent exécuté dans un navigateur Web, qui pourrait interpréter le JavaScript, ou compilez-le une machine virtuelle ou un code natif. Nous avons également des interprètes pour le code machine, qui peuvent être utilisés pour émuler un type de matériel sur un autre. Ou, on peut utiliser un compilateur pour générer du code objet qui est alors le code source d'un autre compilateur, qui pourrait même compiler du code en mémoire juste à temps pour qu'il s'exécute, ce qui à son tour. . . vous avez eu l'idée. Il existe de nombreuses façons de combiner ces concepts.


Pouvez-vous corriger cette phrase: "Il existe des compilateurs qui traduisent d'autres langues en JavaScript, qui est ensuite souvent exécuté dans un navigateur Web, qui peut interpréter le JavaScript ou le compiler une machine virtuelle ou un code natif."
Koray Tugay

J'y suis arrivé. Une autre erreur courante consiste à attribuer l'utilité d'un langage à ses API existantes.
Little Endian

10

Le plus grand avantage du code source interprété par rapport au code source compilé est la PORTABILITÉ .

Si votre code source est compilé, vous devez compiler un exécutable différent pour chaque type de processeur et / ou de plate-forme sur lequel vous souhaitez que votre programme s'exécute (par exemple, un pour Windows x86, un pour Windows x64, un pour Linux x64, etc.) sur). De plus, à moins que votre code ne soit entièrement conforme aux normes et n'utilise aucune fonction / bibliothèque spécifique à la plate-forme, vous devrez réellement écrire et maintenir plusieurs bases de code!

Si votre code source est interprété, vous n'avez besoin de l'écrire qu'une seule fois et il peut être interprété et exécuté par un interprète approprié sur n'importe quelle plateforme! C'est portable ! Notez qu'un interpréteur lui-même est un programme exécutable qui est écrit et compilé pour une plate-forme spécifique.

Un avantage du code compilé est qu'il cache le code source de l'utilisateur final (qui peut être une propriété intellectuelle ) car au lieu de déployer le code source lisible par l'homme d'origine, vous déployez un fichier exécutable binaire obscur.


1
dans ces conditions, java ne peut pas être considéré comme un "langage compilé", mais sa phase de compilation offre les avantages de la compilation (vérification de type, détection précoce des erreurs, etc.), et produit un bytecode qui peut être exécuté sur chaque système d'exploitation, avec un Java machine virtuelle fournie.
Rogelio Triviño

7

Un compilateur et un interprète font le même travail: traduire un langage de programmation en un autre langage de pgoramming, généralement plus proche du matériel, dirige souvent le code machine exécutable.

Traditionnellement, «compilé» signifie que cette traduction se produit en une seule fois, est effectuée par un développeur, et l'exécutable résultant est distribué aux utilisateurs. Exemple pur: C ++. La compilation prend généralement assez de temps et essaie de faire beaucoup d'optimisation coûteuse afin que l'exécutable résultant s'exécute plus rapidement. Les utilisateurs finaux n'ont pas les outils et les connaissances pour compiler eux-mêmes les choses, et l'exécutable doit souvent s'exécuter sur une variété de matériel, vous ne pouvez donc pas faire beaucoup d'optimisations spécifiques au matériel. Pendant le développement, l'étape de compilation séparée signifie un cycle de rétroaction plus long.

Traditionnellement, «interprété» signifie que la traduction a lieu «à la volée», lorsque l'utilisateur souhaite exécuter le programme. Exemple pur: PHP vanille. Un interprète naïf doit analyser et traduire chaque morceau de code à chaque exécution, ce qui le rend très lent. Il ne peut pas effectuer des optimisations complexes et coûteuses car elles prendraient plus de temps que le temps gagné en exécution. Mais il peut utiliser pleinement les capacités du matériel sur lequel il fonctionne. L'absence d'une étape de compilation séparée réduit le temps de rétroaction pendant le développement.

Mais de nos jours, «compilé vs interprété» n'est pas un problème en noir ou blanc, il y a des nuances entre les deux. Des interprètes naïfs et simples sont à peu près éteints. De nombreuses langues utilisent un processus en deux étapes où le code de haut niveau est traduit en un bytecode indépendant de la plateforme (qui est beaucoup plus rapide à interpréter). Ensuite, vous avez des «compilateurs juste à temps» qui compilent le code au plus une fois par exécution de programme, mettent parfois en cache les résultats et décident même intelligemment d'interpréter le code qui est rarement exécuté et effectuent de puissantes optimisations pour le code qui s'exécute beaucoup. Pendant le développement, les débogueurs sont capables de changer de code dans un programme en cours d'exécution même pour les langages compilés traditionnellement.


1
Cependant, le modèle de compilation de C ++ est hérité de C et a été conçu sans tenir compte des fonctionnalités telles que les modèles. Cette maladresse contribue aux longs temps de compilation de C ++ beaucoup plus que tout autre facteur - et en fait un mauvais exemple.

4

Tout d'abord, une clarification, Java n'est pas entièrement compilé statiquement et lié de la manière C ++. Il est compilé en bytecode, qui est ensuite interprété par une JVM. La machine virtuelle Java peut effectuer une compilation juste à temps dans le langage machine natif, mais n'a pas à le faire.

Plus précisément: je pense que l'interactivité est la principale différence pratique. Puisque tout est interprété, vous pouvez prendre un petit extrait de code, l'analyser et l'exécuter par rapport à l'état actuel de l'environnement. Ainsi, si vous aviez déjà exécuté du code qui initialisait une variable, vous auriez accès à cette variable, etc. Cela se prête vraiment à des choses comme le style fonctionnel.

L'interprétation, cependant, coûte cher, surtout lorsque vous avez un grand système avec beaucoup de références et de contexte. Par définition, c'est du gaspillage car un code identique peut devoir être interprété et optimisé deux fois (bien que la plupart des runtimes aient une mise en cache et des optimisations pour cela). Pourtant, vous payez un coût d'exécution et avez souvent besoin d'un environnement d'exécution. Vous êtes également moins susceptible de voir des optimisations interprocédurales complexes, car à l'heure actuelle, leurs performances ne sont pas suffisamment interactives.

Par conséquent, pour les grands systèmes qui ne changeront pas beaucoup, et pour certaines langues, il est plus logique de tout précompiler et de pré-lier tout, de faire toutes les optimisations que vous pouvez faire. Cela se termine par un runtime très léger qui est déjà optimisé pour la machine cible.

Quant à la génération d'exécutables, cela n'a pas grand-chose à voir avec ça, à mon humble avis. Vous pouvez souvent créer un exécutable à partir d'un langage compilé. Mais vous pouvez également créer un exécutable à partir d'un langage interprété, sauf que l'interpréteur et le runtime sont déjà emballés dans l'exectuable et cachés pour vous. Cela signifie que vous payez généralement les coûts d'exécution (même si je suis sûr que pour certains langages, il existe des moyens de tout traduire en un exécutable d'arbre).

Je ne suis pas d'accord pour dire que toutes les langues pourraient être rendues interactives. Certains langages, comme C, sont tellement liés à la machine et à la structure de liens complète que je ne suis pas sûr que vous puissiez créer une version interactive complète à part entière


C n'est pas vraiment lié à une "machine". La syntaxe et la sémantique de C sont assez simples. Il ne devrait pas être particulièrement difficile d'implémenter un interpréteur C, ce qui prend beaucoup de temps (car la bibliothèque standard doit également être implémentée). Et btw, Java peut être compilé en code machine natif (en utilisant gcj).
lunaryorn

@lunaryorn: Je ne suis pas d'accord avec GCJ. GCJ vous donne simplement un environnement basé sur un exécutable. "Les applications compilées sont liées au runtime GCJ, libgcj, qui fournit les bibliothèques de classe de base, un garbage collector et un interprète de bytecode"
Uri

2
GCJ ne produit code machine natif, et non seulement un environnement exécutable avec interpréteur intégré et bytecode. libgcj fournit un interpréteur de bytecode pour prendre en charge les appels du code natif vers le bytecode Java, pas pour interpréter le programme compilé. Si libgcj ne fournissait pas d'interprète de bytecode, GCJ ne serait pas conforme à la spécification Java.
lunaryorn

@lunaryorn: Ah. D'accord, j'apprécie la clarification et je me corrige. Nous utilisons principalement Java dans un environnement Windows, donc je n'ai pas essayé GCJ depuis des années.
Uri


2

Il est assez difficile de donner une réponse pratique car la différence concerne la définition du langage lui-même. Il est possible de construire un interpréteur pour chaque langue compilée, mais il n'est pas possible de construire un compilateur pour chaque langue interprétée. Il s'agit beaucoup de la définition formelle d'une langue. Pour que les trucs d'informatique théorique que noboby aime à l'université.


1
Vous pouvez sûrement construire un compilateur pour un langage interprété, mais le code machine compilé est lui-même un miroir de l'exécution.
Aiden Bell

2

Le livre Python © 2015 Imagine Publishing Ltd, fait simplement la différence par l'indication suivante mentionnée à la page 10:

Un langage interprété tel que Python est un langage dans lequel le code source est converti en code machine, puis exécuté à chaque exécution du programme. Ceci est différent d'un langage compilé tel que C, où le code source n'est converti en code machine qu'une seule fois - le code machine résultant est ensuite exécuté à chaque exécution du programme.


1

Compiler est le processus de création d'un programme exécutable à partir de code écrit dans un langage de programmation compilé. La compilation permet à l'ordinateur d'exécuter et de comprendre le programme sans avoir besoin du logiciel de programmation utilisé pour le créer. Lorsqu'un programme est compilé, il est souvent compilé pour une plate-forme spécifique (par exemple la plate-forme IBM) qui fonctionne avec des ordinateurs compatibles IBM, mais pas d'autres plates-formes (par exemple la plate-forme Apple). Le premier compilateur a été développé par Grace Hopper alors qu'il travaillait sur l'ordinateur Harvard Mark I. Aujourd'hui, la plupart des langages de haut niveau incluront leur propre compilateur ou disposeront de boîtes à outils pouvant être utilisées pour compiler le programme. Un bon exemple de compilateur utilisé avec Java est Eclipse et un exemple de compilateur utilisé avec C et C ++ est la commande gcc.


0

Définition courte (imprécise):

Langage compilé: le programme entier est traduit en code machine à la fois, puis le code machine est exécuté par la CPU.

Langage interprété: le programme est lu ligne par ligne et dès qu'une ligne est lue, les instructions machine de cette ligne sont exécutées par la CPU.

Mais vraiment, peu de langues de nos jours sont purement compilées ou purement interprétées, c'est souvent un mix. Pour une description plus détaillée avec des images, voir ce fil:

Quelle est la différence entre compilation et interprétation?

Ou mon article de blog ultérieur:

https://orangejuiceliberationfront.com/the-difference-between-compiler-and-interpreter/

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.