Wir schaffen Klarheit: Warum interpretieren und kompilieren sich nicht ausschließen.
Es gibt tatsächlich im Bezug auf die Möglichkeiten der eingesetzten Sprache kaum einen Unterschied, denn “kompilierte Programmiersprache” und “interpretierte Programmiersprache” sind an und für sich keine sinnvollen Wortkonstrukte. Jede Programmiersprache, und wirklich jede, kann prinzipiell interpretiert oder kompiliert werden. Interpretation und Kompilierung sind also eher Implementierungstechniken als Charakteristika von Sprachen als solchen.
Was bedeutet Interpretation während der Laufzeit?
Interpretation ist eine Herangehensweise, bei der während der Laufzeit ein anderes Programm, der Interpreter, Operationen für das zu interpretierende Programm ausführt. Stell Dir vor, ein Programm zu lesen und dabei verwendest Du ein Notizblatt um Schritt für Schritt die Anweisungen des Quellcodes zu erfassen und in detailliertere Operationen umzusetzen. Diese Operationen werden dann letztendlich außerhalb Deines Sichtfelds umgesetzt und deren Ergebnis an Dich zurückgemeldet. Das entspricht in etwa der Funktionsweise eines Interpreters. Dementsprechend ist ein Interpreter grundsätzlich auch in der Lage, Programme während ihrer Laufzeit sozusagen “von außen” zu überwachen oder die Einhaltung übergeordneter Sicherheitsrichtlinien zu erzwingen.
Kompilieren: das Übersetzen in eine “Zielsprache”
Kompilieren ist eine Technik, bei der ein in einer Quellsprache verfasstes Programm (die “Ausgangssprache”) in ein in vieler Hinsicht gleichwertiges Programm in einer anderen Sprache übersetzt wird (die “Zielsprache”) und optimalerweise in allen relevanten Fällen zwangsläufig zu den gleichen erwarteten Ergebnissen führt wie das Quellprogramm. Während der Übersetzung ist es üblich, dass der Compiler auch versucht, das Programm so zu transformieren, dass der Code in der Zielsprache möglichst effizient ausgeführt werden kann. Das kann bedeuten, dass etwa objektorientierter Quellcode im Hinblick auf seine Ausführungsgeschwindigkeit optimiert wird – ohne allerdings dessen Bedeutung zu ändern. In einem objektorientierten Kontext lassen sich hier deutliche Vorteile erzielen, da das OOP-Konzept viel implizite Funktionalität beinhaltet, die zur Entwurfszeit unverzichtbar ist aber nach Abschluss der Entwicklung stark komprimiert werden kann.
Interpretierte und kompilierete Programmierung ergänzt sich
Aufgrund der obigen Definitionen ist es nicht schwer zu erraten, dass diese beiden Implementierungstechniken sich nicht gegenseitig ausschließen – und sogar komplementär sein können. Traditionell war die Zielsprache eines Compilers Maschinencode oder etwas vergleichbares. Maschinensprache hat sich als eine Art Sammelbegriff für eine beliebige Anzahl von maschinennahen Programmiersprachen etabliert, die von bestimmten CPU-Typen ausgeführt werden können. Der Maschinencode würde dann “auf der Hardware” laufen – obwohl man bei genauer Betrachtung sehen könnte, dass die “Hardware” in vielerlei Hinsicht auch wie ein Dolmetscher funktioniert. Eine zunehmend etablierte Vorgehensweise besteht darin, einen Compiler zu verwenden, um Programme in eine Zielsprache zu übersetzen, in der sie anschließend interpretiert werden sollen – Java zum Beispiel kannte bis vor nicht allzu langer Zeit nur dieses Konzept.
Statische und Dynamische Programmiersprachen
Es gibt zwei Arten von Sprachen, statische und dynamische. Alle Sprachen können prinzipiell entweder interpretiert oder kompiliert werden obwohl im Arbeitsalltag oft ungenau von “interpretierter Sprache” oder “kompilierter Sprache” die Rede ist. Kompilierung kann nativen Binär- oder IL-Bytecode erzeugen. IL steht für Intermediate Language, was etwa soviel wie “Zwischensprache” bedeutet, die eine VM zur Laufzeit in nativen Code kompiliert. Dieses “kompilieren während der Laufzeit” wird als “Just-in-Time”(jit) Compiling bezeichnet, im Gegensatz zu AOT(Ahead-of-Time) Kompilierung, die vor der Ausführung des Programms erfolgt. JITing ist, bezogen auf den Übersetzungsprozess, theoretisch schneller als AOT (das in native binary konvertiert), da mehr Optimierungen noch während der Laufzeit durchgeführt werden können. Voraussetzung dafür ist allerdings Bytecode und kein einfacher Text (wie js, was Zeile für Zeile interpretiert werden muss, da es dynamisch und nicht stark typisiert ist (und kann daher nicht gut optimiert werden könnte, selbst wenn es kompiliert wäre).
JIT-Kompilierung (Dynamische Übersetzung)
Bei der Just-in-Time (JIT) -Kompilierung, die auch als dynamische Übersetzung bezeichnet wird, erfolgt die Kompilierung während der Ausführung eines Programms – zur Laufzeit – und nicht vor der Ausführung. In vielen Fällen erfolgt diese Übersetzung in eine Maschinencode-Zielsprache, die direkt ausgeführt werden kann. Ein System, das einen JIT-Compiler implementiert, analysiert typischerweise den ausgeführten Code kontinuierlich und identifiziert Teile des Codes, bei denen die aus der Kompilierung gewonnene Beschleunigung den Aufwand beim Kompilieren dieses Codes überwiegen würde.
JIT-Kompilierung ist eine Kombination der beiden traditionellen Ansätze zur Übersetzung in Maschinen-Code – nämlich AOT (Ahead Compilation) und Interpretation – und kombiniert einige Vor- und Nachteile von beiden. Grob gesagt kombiniert die JIT-Kompilierung den Geschwindigkeitsvorteil bei der Ausführung kompilierten Codes mit der grundsätzlichen Flexibilität der Interpretation – und konsquenterweise mit dem kombinierten Overhead eines Interpreters und des Compilers. Die JIT-Kompilierung ist eine Form der dynamischen Kompilierung und ermöglicht eine adaptive Optimierung, beispielsweise eine dynamische Neukompilierung. Daher kann die JIT-Kompilierung theoretisch eine schnellere Ausführung als die statische Kompilierung erzielen. Die Interpretation und die JIT-Kompilierung sind besonders für dynamische Programmiersprachen geeignet, da das Laufzeitsystem spät gebundene Datentypen verarbeiten und Sicherheitsgarantien durchsetzen kann.
Was ist ein Interpreter?
Unter einem Interpreter versteht man in der Informatik ein Computerprogramm, das Anweisungen, die in einer Programmier- oder Skriptsprache geschrieben sind, direkt ausführt, d.h. ausführt, ohne sie zuvor in ein Maschinensprachenprogramm zu kompilieren. Ein Interpreter verwendet im Allgemeinen eine der folgenden Strategien für die Programmausführung:
- Quellcode parsen und direkt ausführen
- Quellcode in eine effiziente Zwischensprache übersetzen und in dieser sofort ausführen.
- Explizit vorkompilierten und gespeicherten Code ausführen, der zuvor von einem Compiler erzeugt wurde, der Teil des Interpretersystems ist.
Frühe Versionen der Programmiersprachen Lsp und Dartmouth BASIC wären Beispiele für den zuerst genannten Typ. Perl, Python, MATLAB und Ruby sind Beispiele für den zweiten, während UCSD Pascal ein Beispiel für den dritten Typ ist. Quellprogramme werden vorab kompiliert und als maschinenunabhängiger Code gespeichert, der dann zur Laufzeit verknüpft und von einem Interpreter und / oder Compiler (für JIT-Systeme) ausgeführt wird.
Beim Entwurf einer Anwendung musst Du möglicherweise entscheiden, ob Du eine kompilierte Sprache oder eine interpretierte Sprache für den Quellcode Deiner Software verwenden möchtest.
Beide Arten von Sprachen haben jeweils ihre eigenen Stärken und Schwächen. In der Regel basiert die Entscheidung, eine interpretierte Sprache zu verwenden, auf zeitlichen Einschränkungen bei der Entwicklung oder um zukünftige Änderungen am Programm leichter durchführen zu können. Bei der Verwendung einer interpretierten Sprache müssen Kompromisse eingegangen werden. Einer höheren Entwicklungsgeschwindigkeit und Wartungseffizienz stehen höhere Ausführungskosten entgegen. Da jede Anweisung eines interpretierten Programms bei jeder Ausführung übersetzt werden muss, ist der Aufwand und damit der Ressourcenhunger höher als bei einem kompilierten Programm.
Vorteile von kompilierten Sprachen
Viele gängige Programmiersprachen wie Assembler, COBOL, PL / I, C / C ++ – um nur einige zu nennen – werden übersetzt, indem der Quellcode über einen Compiler ausgeführt wird. Dies führt zu sehr effizientem, übersetztem Code, der beliebig oft ausgeführt werden kann. Der Overhead für die Übersetzung entsteht nur einmal, wenn die Quelle kompiliert wird; danach muss das Programm nur geladen und ausgeführt werden.
Interpretierte Sprachen müssen dagegen bei jeder Ausführung des Programms geparst, interpretiert und ausgeführt werden, wodurch sich der Aufwand für die Ausführung des Programms erheblich erhöht. Aus diesem Grund sind interpretierte Programme in der Regel weniger effizient als kompilierte Programme.
Einige Programmiersprachen wie REXX (TM) und Java (TM) können sowohl entweder interpretiert als auch kompiliert werden.
Vorteile von interpretierten Sprachen
Es kann sowohl Gründe für die Verwendung kompilierter Sprachen als auch Gründe für die Verwendung interpretierter Sprachen geben. Es gibt keine einfache oder gar universelle Antwort auf die Frage, welche Art Sprache “besser” ist – das hängt im Wesentlichen von der Anwendung ab. Selbst innerhalb einer Anwendung oder eines Software-Projekts könnten wir viele verschiedene Sprachen unterschiedlicher Art verwenden. Eine der Stärken einer Sprache wie zum Beispiel CLIST ist es, dass es wirklich einfach ist damit zu programmieren, zu testen und Veränderungen zu implementieren. Das Ergebnis wird allerdings zur Laufzeit oft wenig effizient sein. Ein Kompromiss bedeutet hier, die Beanspruchung von Maschinenressourcen gegen die Entwicklungszeit abzuwägen.
In Anbetracht dessen erscheint es möglicherweise sinnvoll, eine kompilierte Sprache für die ressourcenintensiven Teile einer Anwendung zu verwenden, während Schnittstellen (die Anwendung aufrufen) und weniger ressourcenlastige Teile in einer interpretierten Sprache geschrieben werden könnten. Eine interpretierte Sprache könnte auch für Ad-hoc-Anfragen oder gegebenenfalls sogar für das Prototyping einer Anwendung geeignet sein.
Eine der Aufgaben eines Entwicklers besteht darin, die Stärken und Schwächen jeder in Frage kommenden Sprache abzuwägen und dann zu entscheiden, welcher Teil einer Anwendung am besten von einer bestimmten Sprache bedient werden kann.
Ich hoffe, dass Dir die Unterschiede sowie Vor- und Nachteile von kompilierten und interpretierten Programmiersprachen nun etwas klarer geworden sind 🙂