Angenommen, ein Programm wurde in zwei verschiedenen Sprachen geschrieben: Sprache X und Sprache Y. Wenn die Compiler denselben Bytecode generieren, warum sollte ich Sprache X anstelle von Sprache Y verwenden? Was macht aus, dass eine Sprache schneller ist als die andere?
Ich frage dies, weil oft Leute sagen: "C ist die schnellste Sprache, ATS ist eine Sprache, die so schnell ist wie C". Ich wollte die Definition von "schnell" für Programmiersprachen verstehen.
programming-languages
compilers
Rodrigo Valente
quelle
quelle
Antworten:
Es gibt viele Gründe, die für die Wahl einer Sprache X gegenüber einer Sprache Y in Betracht gezogen werden können. Programmlesbarkeit, einfache Programmierung, Portabilität auf viele Plattformen und das Vorhandensein guter Programmierumgebungen können solche Gründe sein. Ich werde jedoch nur die in der Anfrage geforderte Ausführungsgeschwindigkeit berücksichtigen. Die Frage scheint zum Beispiel die Geschwindigkeit der Entwicklung nicht zu berücksichtigen.
Zwei Sprachen können zu demselben Bytecode kompiliert werden, dies bedeutet jedoch nicht, dass derselbe Code erzeugt wird.
Eigentlich ist Bytecode nur Code für eine bestimmte virtuelle Maschine. Es hat technische Vorteile, führt jedoch keine grundlegenden Unterschiede zum direkten Kompilieren für eine bestimmte Hardware ein. Sie können also auch zwei Sprachen vergleichen, die für die direkte Ausführung auf demselben Computer kompiliert wurden.
Das Problem der relativen Geschwindigkeit von Sprachen ist jedoch alt und geht auf die ersten Compiler zurück.
In jenen frühen Zeiten war der Fachmann jahrelang der Ansicht, dass handgeschriebener Code schneller war als kompilierter Code. Mit anderen Worten, die Maschinensprache galt als schneller als Hochsprachen wie Cobol oder Fortran. Und es war sowohl schneller als auch normalerweise kleiner. Hochrangige Sprachen entwickelten sich immer noch, weil sie für viele Menschen, die keine Informatiker waren, viel einfacher zu benutzen waren. Die Kosten für die Verwendung von Hochsprachen hatten sogar einen Namen: das Erweiterungsverhältnis, das die Größe des generierten Codes (ein sehr wichtiges Problem in diesen Zeiten) oder die Anzahl der tatsächlich ausgeführten Anweisungen betreffen könnte. Das Konzept war hauptsächlich experimentell, aber das Verhältnis war anfangs größer als 1, da die Compiler nach heutigen Maßstäben eine relativ einfache Aufgabe erledigten.
So war die Maschinensprache schneller als etwa Fortran.
Das änderte sich natürlich im Laufe der Jahre, als die Compiler immer ausgefeilter wurden und das Programmieren in Assemblersprache heutzutage sehr selten ist. In den meisten Anwendungen können Assembler-Programme nur schlecht mit Code mithalten, der durch die Optimierung von Compilern generiert wurde.
Dies zeigt, dass ein Hauptproblem die Qualität der für die jeweilige Sprache verfügbaren Compiler, ihre Fähigkeit, den Quellcode zu analysieren und entsprechend zu optimieren, ist.
Diese Fähigkeit kann in gewissem Umfang von den Merkmalen der Sprache abhängen, um die strukturellen und mathematischen Eigenschaften der Quelle hervorzuheben und dem Compiler die Arbeit zu erleichtern. Beispielsweise könnte eine Sprache die Aufnahme von Aussagen über die algebraischen Eigenschaften von benutzerdefinierten Funktionen ermöglichen, damit der Compiler diese Eigenschaften für Optimierungszwecke verwenden kann.
Der Kompilierungsprozess kann einfacher sein, wodurch ein besserer Code erzeugt wird, wenn das Programmierparadigma der Sprache näher an den Merkmalen der Maschinen liegt, die den Code interpretieren, egal ob es sich um eine reale oder eine virtuelle Maschine handelt.
Ein weiterer Punkt ist, ob die in der Sprache implementierten Paradigmen auf die Art des zu programmierenden Problems beschränkt sind. Es ist zu erwarten, dass eine Programmiersprache, die auf bestimmte Programmierparadigmen spezialisiert ist, Merkmale, die mit diesem Paradigma zusammenhängen, sehr effizient kompiliert. Daher kann die Wahl einer Programmiersprache aus Gründen der Klarheit und Geschwindigkeit von der Wahl einer Programmiersprache abhängen, die an die Art des zu programmierenden Problems angepaßt ist.
Die Popularität von C für die Systemprogrammierung ist wahrscheinlich darauf zurückzuführen, dass C der Maschinenarchitektur nahe kommt und dass die Systemprogrammierung auch direkt mit dieser Architektur zusammenhängt.
Einige andere Probleme lassen sich einfacher programmieren, da sie mithilfe von Logikprogrammierung und Sprachen für die Einschränkungsauflösung schneller ausgeführt werden können .
Komplexe reaktive Systeme können sehr effizient mit speziellen synchronen Programmiersprachen wie Esterel programmiert werden , die sehr spezielle Kenntnisse über solche Systeme enthalten und sehr schnellen Code generieren.
Um ein extremes Beispiel zu nennen: Einige Sprachen sind hochspezialisiert, z. B. Syntaxbeschreibungssprachen, die zum Programmieren von Parsern verwendet werden. Ein Parser-Generator ist nichts anderes als ein Compiler für solche Sprachen. Natürlich ist Turing nicht vollständig, aber diese Compiler sind für ihre Spezialität außerordentlich gut: Sie produzieren effiziente Parsing-Programme. Da der Wissensbereich eingeschränkt ist, können die Optimierungstechniken sehr spezialisiert und sehr fein abgestimmt werden. Diese Parser-Generatoren sind normalerweise viel besser als das, was man durch das Schreiben von Code in einer anderen Sprache erzielen kann. Es gibt viele hochspezialisierte Sprachen mit Compilern, die exzellenten und schnellen Code für eine eingeschränkte Klasse von Problemen produzieren.
Daher kann es beim Schreiben eines großen Systems ratsam sein, sich nicht auf eine einzelne Sprache zu verlassen, sondern die beste Sprache für verschiedene Komponenten des Systems zu wählen. Dies wirft natürlich Kompatibilitätsprobleme auf.
Ein weiterer häufig wichtiger Punkt ist einfach die Existenz effizienter Bibliotheken für die zu programmierenden Themen.
Schließlich ist die Geschwindigkeit nicht das einzige Kriterium und kann im Widerspruch zu anderen Kriterien stehen, z. B. zur Codesicherheit (z. B. in Bezug auf fehlerhafte Eingaben oder Ausfallsicherheit bei Systemfehlern), zur Speichernutzung und zur Erleichterung der Programmierung (obwohl die Paradigmenkompatibilität tatsächlich hilfreich sein kann) ), Objektcodegröße, Programmwartbarkeit usw.
Geschwindigkeit ist nicht immer der wichtigste Parameter. Es kann auch verschiedene Erscheinungsformen annehmen, wie Komplexität, die durchschnittliche Komplexität oder Komplexität im schlimmsten Fall sein kann. Außerdem gibt es in einem großen System wie in einem kleineren Programm Teile, in denen die Geschwindigkeit entscheidend ist, und andere, in denen es wenig darauf ankommt. Und es ist nicht immer einfach, das im Voraus zu bestimmen.
quelle
Während letztendlich alles auf der CPU * läuft , gibt es verschiedene Unterschiede zwischen verschiedenen Sprachen. Hier sind einige Beispiele.
Interpretierte Sprachen Einige Sprachen werden eher interpretiert als kompiliert , zum Beispiel Python, Ruby und Matlab. Das bedeutet, dass Python- und Ruby-Code nicht zu Maschinencode kompiliert, sondern im laufenden Betrieb interpretiert wird. Es ist möglich, Python und Ruby zu einer virtuellen Maschine zu kompilieren (siehe nächster Punkt). Siehe auch diese Frage . Interpretiert wird in der Regel aus verschiedenen Gründen langsamer als kompilierter Code. Die Interpretation selbst kann nicht nur langsam sein, sondern es ist auch schwieriger, Optimierungen vorzunehmen. Wenn Ihr Code jedoch die meiste Zeit für Bibliotheksfunktionen verwendet (im Fall von Matlab), leidet die Leistung nicht.
Virtuelle Maschine Einige Sprachen werden in Bytecode kompiliert , einem erfundenen "Maschinencode", der dann interpretiert wird. Die wesentlichen Beispiele sind Java und C #. Während Bytecode im laufenden Betrieb in Maschinencode konvertiert werden kann, wird der Code wahrscheinlich immer noch langsamer ausgeführt. Im Falle von Java wird eine virtuelle Maschine für die Portabilität verwendet. Im Fall von C # können andere Probleme auftreten, z. B. die Sicherheit.
Overhead Einige Sprachen tauschen Effizienz gegen Sicherheit. Einige Versionen von Pascal überprüfen beispielsweise, ob Sie außerhalb der Grenzen auf ein Array zugreifen. C # -Code wird "verwaltet", und dies ist kostenpflichtig. Ein weiteres bekanntes Beispiel ist die Speicherbereinigung, die dem Programmierer Zeit spart, aber nicht so effizient ist wie die praktische Speicherverwaltung. Es gibt andere Overhead-Quellen, z. B. eine Infrastruktur zur Ausnahmebehandlung oder zur Unterstützung der objektorientierten Programmierung.
* Tatsächlich führen Hochleistungssysteme heutzutage auch Code auf GPUs und sogar auf FPGAs aus.
quelle
Es gibt verschiedene Faktoren, um X anstelle von Y zu wählen
Einige Sprachen eignen sich für die Entwicklung von Geschäftsprojekten wie C # oder Python, andere eignen sich für die Systemprogrammierung wie C ++.
Sie müssen festlegen, unter welcher Plattform Sie arbeiten und welche Anwendung Sie erstellen möchten.
quelle
Die "schnellste" Programmiersprache, die Sie mit jeder Plattform bekommen können, ist die Assemblersprache für den Chipsatz, mit dem Sie es zu tun haben. Auf dieser Ebene gibt es keine Übersetzungen. Es müssen jedoch einige Kenntnisse darüber vorhanden sein, wie der Chipsatz Befehle ausführt, insbesondere solche, die parallel arbeiten können.
Die Konvertierung von C nach Assembly ist sehr "flach", da sie nahe eins zu eins liegt, aber besser lesbar ist. Aufgrund der Standardbibliotheken befinden sich jedoch viele Ebenen darüber, um die Portabilität zu verbessern. Es gibt nicht so viele Dinge, die der Compiler tun müsste, um zum Assembly-Code zu gelangen, und die stärkeren Optimierungen sind im Allgemeinen vorhanden, um maschinenspezifische Änderungen vorzunehmen.
C ++ fügt eine reichhaltigere Sprache hinzu. Da die Sprache jedoch zu viel Komplexität beiträgt, wird es für den Compiler schwieriger, optimalen Code für die Plattform zu erstellen.
Dann gehen wir auf die andere Seite der Skala. Interpretierte Sprachen. Diese sind in der Regel am langsamsten, da neben der Arbeit einige Zeit aufgewendet wird, um den Code zu analysieren und in Maschinenaufrufe umzuwandeln.
Dann haben wir die dazwischen. Im Allgemeinen verfügen sie über eine virtuelle Maschinenschicht, die für die Plattform optimiert ist. Der Compiler erstellt dann Code für die Ausführung der virtuellen Maschine. Manchmal passiert das auf einmal wie Perl oder Pascal oder Ruby oder Python. Oder in mehreren Stufen wie Java.
Einige dieser virtuellen Maschinen enthalten den Begriff eines JIT-Compilers, der auch die Laufzeit beschleunigt, indem er Code auf Computerebene erstellt und keinen Zwischen-Byte-Code übersetzt.
Einige virtuelle Maschinen sind auf niedrigem Niveau, was eine geringere Übersetzung von Bytecode zu Maschinencode ermöglicht. Was die Dinge beschleunigt und gleichzeitig die Portabilität beibehält.
quelle
*p++=*q++;
Vergangenheit war beispielsweise der Maschinencode, der dem entspricht , auf vielen Maschinen schneller alsarray1[i]=array2[i];
bei vielen Prozessoren, häufig umgekehrt, und daher konvertieren Compiler möglicherweise den ersteren Codestil in den letzteren - kaum eine "flache" Konvertierung.-O0
werden dadurch keine Optimierungen vorgenommen. Optimierungen sind ein Bonus, den Sie mit dem Compiler erhalten, aber die Sprache selbst kann nahezu eins zu eins in Assembler übersetzt werden.Ein Punkt, der noch nicht erwähnt wurde, ist, dass in einigen Sprachen immer die gleiche Abfolge von Aktionen ausgeführt wird, wenn derselbe Code mehrmals ausgeführt wird. Der Computer muss also nur einmal festlegen, was der Codeabschnitt tun soll. Einer der Hauptvorteile des "use strict" -Dialekts von JavaScript besteht darin, dass die JavaScript-Engine, sobald sie herausgefunden hat, was ein Teil des Codes tut, diese Informationen beim nächsten Ausführen verwenden kann. ohne "use strict" geht das nicht.
Wenn zum Beispiel "use strict" fehlt, kann ein Stück Code wie:
kann eine Variable X im unmittelbaren aufrufenden Kontext, falls vorhanden, oder eine Variable X aus einem äußeren aufrufenden Kontext zurückgeben oder sie kann zurückgeben
Undefined
. Schlimmer noch, in einer Schleife wie:Es gibt keine Möglichkeit für die JavaScript-Engine zu wissen, was
g()
miti
[oder für sichg
selbst in dieser Angelegenheit tun könnte . Dag
oderi
könnte durchaus legitim Änderungi
in einen String, kann der JavaScript - Engine einfach nicht eine numerische Addition und numerischen Vergleich in der Schleife verwenden, sondern muss bei jedem Durchlauf durch die Schleife geprüft, ob eine der beiden Funktionsaufrufe etwas getan habeni
oderg
. Im Gegensatz dazu kann die JavaScipt-Engine im Dialekt "use strict" den obigen Code untersuchen und wissen, dass jeder Durchlauf durch die Schleife dieselbe numerische Variable verwendet und dieselbe Funktion aufruft. Es muss sich also nur identifiziereni
und funktioniereng
einmal, anstatt sie bei jedem Durchgang durch die Schleife nachschlagen zu müssen - eine große Zeitersparnis.quelle
Hier gibt es einige recht professionelle Antworten, diese sind ihnen nicht nah, könnten aber für Sie intuitiv sein.
Möglicherweise haben Sie schon oft gehört, dass Sie, wenn Sie eine Aufgabe so schnell wie möglich ausführen möchten, den Code schreiben möchten, der sie in der Assembly ausführt. Das liegt daran, dass Sie nur Befehle ausführen, die Sie zum Ausführen der Aufgabe tatsächlich benötigen, und nicht mehr. Während Sie diese Aufgabe in einer höheren Sprache in mehreren Zeilen implementieren können, muss der Compiler sie dennoch in die Maschinensprache übersetzen. Diese Übersetzung ist nicht immer minimalistisch, da Sie sie direkt schreiben könnten. Das bedeutet, dass die Maschine viele Stunden damit verbringt, Befehle auszuführen, die Sie sparen könnten.
Obwohl Compiler heutzutage sehr hoch entwickelt sind, sind sie immer noch nicht so effektiv, wie es die besten Assembler-Programmierer können.
Wenn Sie in diese Richtung fortfahren, werden diese nicht benötigten Befehle (normalerweise) umso umfangreicher, je höher die Sprachstufe ist. (Dies gilt nicht zu 100% für alle Hochsprachen.)
Für mich ist die Sprache X schneller als die Sprache Y (zur Laufzeit), wenn für bestimmte Codeteile der Maschinencode von X kürzer als der von Y ist.
quelle
Es ist schwierig, diese Frage definitiv zu beantworten, da sie so komplex und mehrdimensional ist (es ist fast so, als würde man beispielsweise Automarken anhand verschiedener Kriterien vergleichen), aber es gibt neue wissenschaftliche Studien, darunter ein ausgezeichnetes Code-Repository, das als Rosetta-Code bekannt ist ( Wikipedia-Übersicht ). Diese Umfrage von Nanz und Furia aus dem Jahr 2014 untersucht diese Frage ziemlich eindeutig und wissenschaftlich auf der Grundlage der folgenden typischen Kriterien und einer seltenen quantitativen Analyse der typisch subjektiven Codequalitäten. Das Abstract enthält einige objektiv begründete Befunde und Verallgemeinerungen. (Hoffentlich können in Zukunft weitere Studien durchgeführt werden, die auf diesen Ergebnissen aufbauen.)
RQ1. Welche Programmiersprachen sorgen für präziseren Code?
RQ2. Welche Programmiersprachen werden in kleinere ausführbare Dateien kompiliert?
RQ3. Welche Programmiersprachen haben eine bessere Laufzeitleistung?
RQ4. Welche Programmiersprachen nutzen den Speicher effizienter?
RQ5. Welche Programmiersprachen sind weniger störanfällig?
quelle
Computersprachen sind nur eine Abstraktion von Befehlen, um dem Computer zu erklären, was zu tun ist.
Sie können sogar in der Computersprache schreiben
Python
und mit einem C-Compiler (Cython) kompilieren.Vor diesem Hintergrund ist die Geschwindigkeit von Computersprachen nicht zu vergleichen.
Sie können jedoch bis zu einem gewissen Grad Compiler für dieselbe Sprache vergleichen. Zum Beispiel
GNU C
Compiler versusIntel C
Compiler. (Suche nach Compiler-Benchmark)quelle