Interpreted vs Compiled: Eine nützliche Unterscheidung?

29

Hier werden viele Fragen zu interpretierten und kompilierten Sprachwerkzeugen gestellt. Ich frage mich, ob die Unterscheidung überhaupt Sinn macht. (Eigentlich handelt es sich bei den Fragen in der Regel um Sprachen, aber sie denken wirklich über die beliebtesten Implementierungen dieser Sprachen nach.)

Heute wird fast keine Implementierung streng interpretiert. Das heißt, so gut wie niemand parst und führt den Code zeilenweise aus. Darüber hinaus werden Implementierungen, die zu Maschinencode kompiliert werden, immer seltener. Compiler zielen zunehmend auf eine Art virtuelle Maschine.

Tatsächlich laufen die meisten Implementierungen auf der gleichen Grundstrategie zusammen. Der Compiler erzeugt Bytecode, der über eine JIT interpretiert oder zu nativem Code kompiliert wird. Es ist wirklich eine Mischung aus den traditionellen Ideen der Zusammenstellung und Interpretation.

Daher frage ich: Gibt es heutzutage einen nützlichen Unterschied zwischen interpretierten Implementierungen und kompilierten Implementierungen?

Winston Ewert
quelle
7
@DeadMG Nicht so neu, wie Sie vielleicht denken: Eine kurze Geschichte von Just-in-Time ...
Yannis
4
@DeadMG Angesichts der Tatsache, dass die meisten neuen Sprachen, die in den letzten 10 Jahren eingeführt wurden, hauptsächlich auf einer Art VM laufen, würde ich sagen, dass er Recht hat. Natürlich gibt es immer noch (und wird es noch für Jahrzehnte sein) Sprachen, die mit nativem Code kompiliert wurden, und ein JIT wird Luxus bleiben (oder auch nicht, wenn die PyPy-Typen es so wollen). Also ja, mögliche Übertreibung, aber ich stimme zu, dass der Mainstream (vorerst und in absehbarer Zukunft) Bytecode-Compiler + möglicherweise JIT zu sein scheint.
4
@DeadMG, du musst einen langen weißen Bart haben, wenn das VM-Modell für dich "neu" ist. P-codewar 1966 erstmals eingeführt worden. IBM Aix gibt es seit 1986.
SK-logic
6
Dinge wie Unix-Shells, Tcl und ähnliches würden immer rein interpretiert, so dass die Unterscheidung zumindest in einem akademischen CS Sinn macht. Aber es ist wahr, dass Codierer, die über Interpreter und Compiler murmeln, in den meisten Fällen keinen Sinn ergeben.
SK-logic
3
@ SK-Logik, ich denke, Ihr Kommentar ist eine bessere Antwort als jede der tatsächlich geposteten Antworten
Winston Ewert

Antworten:

23

Es ist wichtig, sich daran zu erinnern, dass Dolmetschen und Kompilieren nicht nur Alternativen zueinander sind. Am Ende wird jedes von Ihnen geschriebene Programm (einschließlich eines zu Maschinencode kompilierten) interpretiert. Das Interpretieren von Code bedeutet einfach, eine Reihe von Anweisungen zu übernehmen und eine Antwort zurückzugeben.

Kompilieren bedeutet andererseits, ein Programm in einer Sprache in eine andere Sprache zu konvertieren. Normalerweise wird angenommen, dass der Code bei der Kompilierung in eine "niedrigere" Sprache kompiliert wird (z. B. Maschinencode, eine Art VM-Bytecode usw.). Dieser kompilierte Code wird später noch interpretiert.

In Bezug auf Ihre Frage, ob es eine nützliche Unterscheidung zwischen interpretierten und kompilierten Sprachen gibt, ist meine persönliche Meinung, dass jeder ein grundlegendes Verständnis darüber haben sollte, was mit dem Code passiert, den er während der Interpretation schreibt. Wenn also ihr Code JIT-kompiliert oder Bytecode-zwischengespeichert wird usw., sollte der Programmierer zumindest ein grundlegendes Verständnis dessen haben, was dies bedeutet.

Zach Smith
quelle
3
Ja, der Programmierer sollte ein Grundverständnis haben. Aber ich frage mich, ob die kompilierte / interpretierte Terminologie dem nicht im Wege steht.
Winston Ewert
2
Vielen Dank!! Interpretiert ist nur ein Synonym für "ausgeführt", und so werden alle Programme ausgeführt.
Gardenhead
9

Die Unterscheidung ist von großer Bedeutung, da kompilierte Sprachen die Semantik in einer Weise einschränken, wie dies bei interpretierten Sprachen nicht unbedingt der Fall ist. Einige Interpretationstechniken sind sehr schwer (praktisch unmöglich) zu kompilieren.

Interpretierter Code kann beispielsweise zur Laufzeit Code generieren und diesen Code für lexikalische Bindungen eines vorhandenen Bereichs sichtbar machen. Das ist ein Beispiel. Zum anderen können Interpreter mit interpretiertem Code erweitert werden, der steuern kann, wie Code ausgewertet wird. Dies ist die Basis für alte Lisp-"fexprs": Funktionen, die mit nicht bewerteten Argumenten aufgerufen werden und entscheiden, was mit ihnen zu tun ist (uneingeschränkter Zugriff auf die erforderliche Umgebung, um den Code zu durchsuchen und Variablen auszuwerten usw.). In kompilierten Sprachen können Sie diese Technik nicht wirklich anwenden. Sie verwenden stattdessen Makros: Funktionen, die zur Kompilierungszeit mit nicht bewerteten Argumenten aufgerufen werden, und übersetzen den Code, anstatt ihn zu interpretieren.

Einige Sprachimplementierungen basieren auf diesen Techniken. Ihre Autoren lehnen das Kompilieren als wichtiges Ziel ab und begrüßen diese Art der Flexibilität.

Das Interpretieren ist immer nützlich, um einen Compiler zu booten. Ein konkretes Beispiel finden Sie in CLISP (eine beliebte Implementierung von Common Lisp). CLISP hat einen Compiler, der in sich selbst geschrieben ist. Wenn Sie CLISP erstellen, wird dieser Compiler während der ersten Erstellungsschritte interpretiert. Es wird verwendet, um sich selbst zu kompilieren. Sobald es kompiliert ist, erfolgt das Kompilieren mit dem kompilierten Compiler.

Ohne einen Interpreter-Kernel müssten Sie mit einem vorhandenen Lisp booten, wie es SBCL tut.

Mit der Interpretation können Sie eine Sprache von Grund auf neu entwickeln, beginnend mit der Assemblersprache. Entwickeln Sie die grundlegenden I / O- und Core-Routinen und schreiben Sie dann eine eval noch Maschinensprache. Sobald Sie eval haben, schreiben Sie in der Hochsprache; Der Maschinencode-Kernel führt die Auswertung durch. Verwenden Sie diese Funktion, um die Bibliothek um viele weitere Routinen zu erweitern und auch einen Compiler zu schreiben. Verwenden Sie den Compiler, um diese Routinen und den Compiler selbst zu kompilieren.

Interpretation: ein wichtiger Schritt auf dem Weg zur Zusammenstellung!

Kaz
quelle
1
IMO, das ist die beste Antwort. Ich arbeite an meiner eigenen Spielzeugsprache und der letzte Absatz beschreibt, wie ich sie entwickle. Es macht die Arbeit an neuen Ideen sehr viel einfacher. Auch +1 für die Erwähnung des CLISP-Bootstrap-Prozesses.
Sinan
Theoretisch kann jede "interpretierte" Sprache zu einer "kompilierten" gemacht werden, indem eine EXE-Datei generiert wird, die aus dem Interpreter plus dem Quellcode oder Bytecode für das interpretierte Programm besteht. Könnte aber nicht sehr effizient sein.
Dan04
Lesen Sie, wie Wirth et al P-Code erfunden haben, um die Portierung von PASCAL auf neue Maschinenarchitekturen zu vereinfachen. Das war in den frühen 1970er Jahren.
John R. Strohm
1
Ich vermute, dass Ihr erster Absatz die Kompilierung und Interpretation mit statischem und dynamischem Verhalten verwechselt, aber ich gebe Ihnen den Vorteil des Zweifels und frage Sie nur nach einem Beispiel für eine Sprache mit Semantik, die "praktisch unmöglich" zu kompilieren ist. In Bezug auf das Bootstrapping eines Compilers muss die erste Implementierung zwar in einer anderen Sprache als der von Ihnen implementierten geschrieben sein, es muss sich jedoch nicht um einen Interpreter handeln, sondern es kann sich um einen Compiler handeln, der in einer anderen Sprache geschrieben ist.
8bittree
1

Tatsächlich werden viele Implementierungen von Sprachen immer noch streng interpretiert. Sie kennen sie möglicherweise nicht. Um nur einige zu nennen: die UNIX-Shellsprachen, die Windows-Cmd- und PowerScript-Shells, Perl, awk, sed, MATLAB, Mathematica usw.

Charles E. Grant
quelle
3
Ich glaube, Perl wird intern zu Bytecode kompiliert, und zumindest Mathematica kann kompiliert werden. Und nichts schreibt die Implementierung von awk und sed vor (ich glaube, einige der GNU-Coreutils kompilieren reguläre Ausdrücke zu endlichen Automaten vor der Ausführung, was sie wohl in die Kategorie "Kompilieren zu Zwischendarstellung, Interpretieren dieser" bringen würde).
1
Eigentlich bin ich mir ziemlich sicher, dass Perl, MATLAB und Mathematica alle zu Bytecode kompilieren. Ich kenne PowerScript nicht, meinst du Powershell? In diesem Fall wird die CLR verwendet, und Bytecode wird verwendet.
Winston Ewert
@ WinstonEwert, sorry ich meinte PowerShell. Ich verstehe, dass die Übersetzung in eine Zwischenform nicht bedeutet, dass etwas nicht interpretiert wird. Heck, der ursprüngliche Dartmouth BASIC-Interpreter übersetzte die Quelle vor dem Dolmetschen in Token. Jedes der von mir erwähnten Tools hat einen Modus, in dem es 1) eine Quelltextzeile liest, 2) diese Zeile in eine ausführbare Form übersetzt (möglicherweise einen Zwischencode anstelle von nativem Code), 3) den Code für diese Zeile ausführt, 4) Schleife zurück zu 1). Das entspricht meinem Verständnis eines Dolmetschers.
Charles E. Grant
2
Bytecode impliziert kompiliert. Ein Bytecode-Compiler ist einfach ein Programm, das den Quellcode in Bytecode konvertiert. Daher muss jede Verwendung von Bytecode einen Bytecode-Compiler beinhalten. Bytecode muss aber auch interpretiert (oder JITted) werden. Alles, was Bytecode verwendet, ist also ein Interpreter / Compiler-Hybrid.
Winston Ewert
4
Wirklich, mein Ding ist, dass die Leute Anweisungen wie "Python wird interpretiert" und "Java wird kompiliert" auslassen, ohne die Implementierungen zu verstehen. Ich frage mich, ob es überhaupt nützlich ist, eine Implementierung in diesen Begriffen zu beschreiben. Die Wahrheit ist normalerweise komplizierter und der Versuch, sie auf interpretiert / kompiliert zu reduzieren, ist nicht sinnvoll.
Winston Ewert
1

Ich denke: Auf jeden Fall ja .

Tatsächlich laufen die meisten Implementierungen auf der gleichen Grundstrategie zusammen

Tatsächlich zielt C ++ darauf ab, ein Konzept auf hoher Ebene, das normalerweise den Interpreten überlassen wird, auf die Compilerdomäne zu portieren, aber es bleibt auf der Minderheitenseite ...

CapelliC
quelle
2
Warten Sie, bis Clang + LLVM zur beliebtesten Compiler-Toolchain wird.
SK-logic
@ SK-logic, trotz des Namens glaube ich, dass Clang + LLVM nativen Code erzeugt.
Winston Ewert
1
@ Winston Ewert, nur wenn du willst. Sie können auf der LLVM-IR-Ebene anhalten und dann alles tun, was Sie wollen - interpretieren, JIT-kompilieren, instrumentieren, wie Sie möchten. Sie können es sogar in Javascript übersetzen und dann einen Dolmetscher durchgehen: github.com/kripken/emscripten/wiki
SK-logic
@ SK-Logik, ordentliches Zeug! Ich wusste nicht, dass LLVM das kann.
Winston Ewert
1
Das Schöne an llvm ist die bewusste Trennung von Frontend und Backend. Und die Werkzeuge zum Manipulieren der Mitte, bevor der Befehlssatz ausgewählt wird. Sie können Ihr gesamtes Projekt in Bytecode zusammenführen und dann über das Ganze hinweg optimieren. Bei anderen Compilern benötigen Sie eine einzelne Dateiquelle oder mindestens eine, die den Weg durch den Quellbaum einschließt, damit der Compiler auf eine kombinierte Quelle angewendet wird. Außerdem ist ein Satz von Tools unter llvm für alle Ziele generisch, Sie müssen nicht für jedes Ziel erstellen, ein Compiler passt für alle (zumindest für den Asm des Ziels).
old_timer
-1

Nützliche Unterscheidung: interpretierte Programme können sich ändern, indem sie zur Laufzeit Funktionen hinzufügen oder ändern.

Diego Pacheco
quelle
8
Unsinn. Selbstmodifizierender (Maschinen-) Code ist der älteste Trick im Buch. Andererseits argumentieren einige, dass selbst nativer Code letztendlich von einem Interpreter interpretiert wird, der in Silizium (die CPU) umgewandelt wurde. Wenn wir jedoch davon ausgehen, dass der gesamte Code interpretiert wird, ist kein Unterschied zu machen.
2
@delnan ist richtig. Ich möchte nur hinzufügen, dass moderne Sprachen sich selbst ändern können, indem sie dynamisch neue Klassen erstellen und Bibliotheken (oder "Assemblys" in .NET zum Beispiel) laden / entladen
Jalayn
5
Common Lisp ist kompiliert, aber Sie können Funktionsdefinitionen trotzdem problemlos zur Laufzeit ersetzen.
SK-logic
Das ist ein wirklich interessantes und notwendiges Merkmal der Interpretation (zum Beispiel in Prolog).
CapelliC