Wie kann eine Sprache, deren Compiler in C geschrieben ist, jemals schneller sein als C?


175

Wenn Sie sich Julias Webseite ansehen , sehen Sie einige Benchmarks mehrerer Sprachen über mehrere Algorithmen (Timings siehe unten). Wie kann eine Sprache mit einem Compiler, der ursprünglich in C geschrieben wurde, C-Code übertreffen?

Bildbeschreibung hier eingeben Abbildung: Benchmark-Zeiten im Verhältnis zu C (kleiner ist besser, C-Leistung = 1,0).



382
Wie kann sich ein Auto, das ein von Menschen geschaffenes Objekt ist, schneller bewegen als ein Mensch?
Babou

19
Laut Tabelle ist Python langsamer als C. Glauben Sie, dass es unmöglich ist, in Python einen C-Compiler zu schreiben, der denselben Code wie Ihr Lieblings-C-Compiler generiert? Und in welcher Sprache ist das überhaupt geschrieben?
Carsten S

6
babous Kommentar war genau richtig, aber ich glaube nicht, dass wir mehrere Versionen desselben brauchen.
Raphael

14
Ein verwandter Gedanke. Viele Compiler hosten sich selbst , dh sie sind in ihrer eigenen Sprache geschrieben (oft mit einigen Assemblys) und mit der vorherigen Version von sich selbst kompiliert. Dennoch werden Compiler besser und schneller. Geistesblitz .
Schwern

Antworten:


263

Es besteht kein notwendiger Zusammenhang zwischen der Implementierung des Compilers und der Ausgabe des Compilers. Sie könnten einen Compiler in einer Sprache wie Python oder Ruby, deren häufigste Implementierungen sind sehr langsam, schreiben und dass Compiler könnte Ausgang hochMaschinenCode optimiert Lage , übertraf damit C. Der Compiler selbst eine lange Zeit zu laufen dauern würde, weil ihreCode ist in einer langsamen Sprache geschrieben. (Genauer gesagt, in einer Sprache mit langsamer Implementierung geschrieben. Sprachen sind nicht von Natur aus schnell oder langsam, wie Raphael in einem Kommentar ausführt. Ich werde auf diese Idee weiter unten eingehen.) Das kompilierte Programm wäre so schnell wie es ist Eigene Implementierung erlaubt - wir könnten einen Compiler in Python schreiben, der den gleichen Maschinencode wie ein Fortran-Compiler generiert, und unsere kompilierten Programme wären so schnell wie Fortran, auch wenn die Kompilierung viel Zeit in Anspruch nimmt.

Es ist eine andere Geschichte, wenn wir über einen Dolmetscher sprechen. Dolmetscher müssen ausgeführt werden, während das Programm, das sie interpretieren, ausgeführt wird. Daher besteht ein Zusammenhang zwischen der Sprache, in der der Dolmetscher implementiert ist, und der Leistung des interpretierten Codes. Es ist eine clevere Laufzeitoptimierung erforderlich, um eine interpretierte Sprache zu erstellen, die schneller ausgeführt wird als die Sprache, in der der Interpreter implementiert ist. Die endgültige Leistung kann davon abhängen, wie gut ein Codeteil für diese Art der Optimierung geeignet ist. Viele Sprachen, wie Java und C #, verwenden Laufzeiten mit einem Hybridmodell, das einige der Vorteile von Interpreten mit einigen der Vorteile von Compilern kombiniert.

Schauen wir uns Python als konkretes Beispiel genauer an. Python hat mehrere Implementierungen. Am gebräuchlichsten ist CPython, ein in C geschriebener Bytecode-Interpreter. Es gibt auch PyPy, das in einem speziellen Python-Dialekt namens RPython geschrieben ist und ein hybrides Kompilierungsmodell ähnlich der JVM verwendet. PyPy ist in den meisten Benchmarks viel schneller als CPython. Es verwendet alle möglichen erstaunlichen Tricks, um den Code zur Laufzeit zu optimieren. Die Python-Sprache, die PyPy ausführt, entspricht jedoch genau der Python-Sprache , die CPython ausführt, mit Ausnahme einiger Unterschiede, die die Leistung nicht beeinträchtigen.

Angenommen, wir haben einen Compiler in der Python-Sprache für Fortran geschrieben. Unser Compiler erzeugt den gleichen Maschinencode wie GFortran. Jetzt kompilieren wir ein Fortran-Programm. Wir können unseren Compiler auf CPython oder PyPy ausführen, da er in Python geschrieben ist und beide Implementierungen dieselbe Python-Sprache ausführen. Wir werden feststellen, dass, wenn wir unseren Compiler auf CPython ausführen, ihn dann auf PyPy ausführen und dann dieselbe Fortran-Quelle mit GFortran kompilieren, wir alle drei Male genau denselben Maschinencode erhalten, sodass das kompilierte Programm immer ausgeführt wird mit etwa der gleichen Geschwindigkeit. Die für die Erstellung dieses kompilierten Programms erforderliche Zeit ist jedoch unterschiedlich. CPython wird höchstwahrscheinlich länger dauern als PyPy, und PyPy wird höchstwahrscheinlich länger dauern als GFortran, obwohl alle am Ende denselben Maschinencode ausgeben.

Beim Durchsuchen der Benchmark-Tabelle der Julia-Website sieht es so aus, als ob keine der auf Dolmetschern (Python, R, Matlab / Octave, Javascript) ausgeführten Sprachen Benchmarks hat, bei denen sie C schlagen. obwohl ich mir vorstellen könnte, dass Code mit Pythons hochoptimierter Numpy-Bibliothek (geschrieben in C und Fortran) einige mögliche C-Implementierungen von ähnlichem Code übertrifft. Die Sprachen, die gleich oder besser als C sind, werden kompiliert (Fortran, Julia ) oder mit einem Hybridmodell mit teilweiser Kompilierung (Java und wahrscheinlich LuaJIT). PyPy verwendet auch ein Hybridmodell. Wenn wir also auf PyPy anstelle von CPython denselben Python-Code ausführen, ist es durchaus möglich, dass er bei einigen Benchmarks C übertrifft.


9
Das ist eine erstaunliche Antwort. Sehr klar, verständlich und informativ. Vielen Dank, dass Sie sich die Zeit genommen haben, es zu schreiben!
Alex A.

7
Sowohl Javascript als auch Java werden mit einem JIT-Compiler ausgeführt, Java hat jedoch einen Test, bei dem es schneller als C ist. Der Hauptgrund, warum ein Runtime / Compiler schneller ausgeführt werden kann, liegt darin, dass mehr Informationen verfügbar sind. C / C ++ - Compiler können Code (normalerweise) viel besser optimieren als jemand, der Assemblys von Hand schreibt, einfach weil der Compiler mehr Informationen zur Verfügung hat. Sicher, theoretisch könnte die Person besseren Assembler-Code schreiben, aber das erfordert mehr Wissen und Können als die meisten Leute. JIT-Sprachen können dies noch weiter ausbauen, indem sie in der Lage sind, für die exakte Maschine zu optimieren, auf der sie läuft
Programmdude

Welche Optimierungen der Compiler vornimmt, ist eine wichtige Sache. Ein wirklich kluger Compiler würde erkennen, dass das Programm ein synthetischer Benchmark ist, und einfach nahezu den gesamten Code optimieren und einfach die erwartete Ausgabe erstellen.
Ghellquist

@ghellquist Klar, wenn der Benchmark künstlich genug und der Compiler schlau genug ist. Das hängt jedoch nicht direkt mit der Implementierungssprache des Compilers zusammen, deshalb habe ich es hier nicht erwähnt.
Tsleyson

97

Wie kann eine von einem Menschen gebaute Maschine stärker sein als ein Mensch? Das ist genau die gleiche Frage.

Die Antwort ist, dass die Ausgabe des Compilers von den von diesem Compiler implementierten Algorithmen abhängt und nicht von der Sprache, in der er implementiert wurde. Sie könnten einen sehr langsamen, ineffizienten Compiler schreiben, der sehr effizienten Code erzeugt. Ein Compiler hat nichts Besonderes: Er ist nur ein Programm, das Eingaben macht und Ausgaben erzeugt.


33
Wie kann ein Schachprogramm den Menschen schlagen, der es geschrieben hat?
Thorbjørn Ravn Andersen

25
Indem Sie bessere Züge machen! <rimshot>
Tony Ennis

Um Penn Gilettes Antwort darauf zu paraphrasieren, warum es nicht wichtig ist, dass ein Computer einen Mann beim Schach schlagen kann: "Würden Sie erwarten, dass ein von GE entwickelter Roboter gegen einen Mann in einem Boxkampf verliert?"
Dave Kanter

90

Ich möchte einen Punkt gegen eine verbreitete Annahme aussprechen, die meiner Meinung nach so trügerisch ist, dass sie bei der Auswahl der Werkzeuge für einen Job schädlich ist.

Es gibt keine langsame oder schnelle Sprache. ¹

Auf dem Weg zur CPU gibt es tatsächlich einige Schritte².

  1. Mindestens ein Programmierer mit bestimmten Fähigkeiten.
  2. Die (formale) Sprache, in der sie programmieren ("Quellcode").
  3. Die Bibliotheken, die sie verwenden.
  4. Etwas, das Quellcode in Maschinencode übersetzt (Compiler, Interpreter).
  5. Die gesamte Hardwarearchitektur, z. B. Anzahl der Verarbeitungseinheiten und Layout der Speicherhierarchie.
  6. Das Betriebssystem, das die Hardware verwaltet.
  7. On-CPU-Optimierungen.

Jedes einzelne Element trägt zur tatsächlichen Laufzeit bei, die Sie manchmal stark messen können. Unterschiedliche "Sprachen" konzentrieren sich auf unterschiedliche Dinge³.

Nur um einige Beispiele zu nennen.

  • 1 vs 2-4 : Ein durchschnittlicher C-Programmierer erzeugt wahrscheinlich weitaus schlechteren Code als ein durchschnittlicher Java-Programmierer, sowohl in Bezug auf Korrektheit als auch Effizienz. Das liegt daran, dass der Programmierer mehr Verantwortung in C hat.

  • 1/4 vs 7 : In einer einfachen Sprache wie C können Sie möglicherweise bestimmte CPU-Funktionen als Programmierer ausnutzen . In höheren Sprachen kann dies nur der Compiler / Interpreter und nur dann, wenn er die Ziel-CPU kennt .

  • 1/4 vs 5 : Möchten oder müssen Sie das Speicherlayout steuern, um die vorhandene Speicherarchitektur optimal zu nutzen? Einige Sprachen geben Ihnen die Kontrolle darüber, andere nicht.

  • 2/4 vs 3 : Ausgelegt Python selbst ist schrecklich langsam, aber es gibt beliebte Bindungen zu hoch optimierten, nativ kompilierten Bibliotheken für wissenschaftliches Rechnen. Bestimmte Dinge in Python zu tun ist also am Ende schnell , wenn die meiste Arbeit von diesen Bibliotheken erledigt wird.

  • 2 gegen 4 : Der Standard-Ruby-Interpreter ist ziemlich langsam. JRuby hingegen kann sehr schnell sein. Das ist die gleiche Sprache ist schnell mit einem anderen Compiler / Interpreter.

  • 1/2 vs 4 : Mit Hilfe von Compiler-Optimierungen kann einfacher Code in sehr effizienten Maschinencode übersetzt werden.

Die Quintessenz ist, dass der von Ihnen gefundene Benchmark wenig Sinn macht, zumindest nicht, wenn er auf die von Ihnen angegebene Tabelle reduziert wird. Auch wenn Sie nur an der Laufzeit interessiert sind, müssen Sie die gesamte Kette vom Programmierer bis zur CPU angeben . Das Austauschen eines der Elemente kann die Ergebnisse dramatisch verändern.

Um es klar auszudrücken, hiermit wird die Frage beantwortet, da die Sprache, in der der Compiler (Schritt 4) geschrieben ist, nur ein Teil des Puzzles ist und wahrscheinlich überhaupt nicht relevant ist (siehe andere Antworten).


  1. Es sicherlich sind Sprachfunktionen , die teurer sind als andere zu implementieren. Das Vorhandensein von Funktionen bedeutet jedoch nicht, dass Sie sie verwenden müssen, und eine teure Funktion erspart möglicherweise die Verwendung vieler billigerer Funktionen und zahlt sich letztendlich aus. (Von anderen Vorteilen, die in der Laufzeit nicht messbar sind.)
  2. Ich überspringe die algorithmische Ebene, weil sie nicht immer zutrifft und größtenteils unabhängig von der verwendeten Programmiersprache ist. Denken Sie daran, dass sich verschiedene Algorithmen beispielsweise besser für unterschiedliche Hardware eignen.
  3. Ich gehe hier bewusst nicht auf verschiedene Erfolgsmetriken ein: Laufzeiteffizienz, Speichereffizienz, Entwicklerzeit, Sicherheit, (nachweisbare?) Korrektheit, Tool-Unterstützung, Plattformunabhängigkeit, ...

    Der Vergleich von Sprachen mit einer Metrik, obwohl sie für ganz andere Ziele entwickelt wurden, ist ein großer Irrtum.


1
@babou Einverstanden, sehr nette Erklärung. Was wäre also eine bessere Metrik oder ein besserer Satz von Metriken , die verwendet werden können, um Sprachen mit ihren jeweiligen Compilern / Interpreten zu vergleichen? Auch ein kleiner Trottel: Sie sagen "Es gibt keine langsame oder schnelle Sprache" und dann "Python selbst ist schrecklich langsam", aber ich nehme an, Sie meinen den Python-Interpreter.
StrugglingProgrammer

2
@benalbrecht Mein Punkt ist, dass es keinen einzigen guten Satz solcher Metriken gibt. Es ist immer ein Kompromiss. Wenn Sie Gerätetreiber erstellen, möchten Sie vor allem korrekt sein. Wenn Sie das Rückgrat von Twitter aufbauen, möchten Sie vor allem effizient sein. In beiden Fällen verwenden Sie die Tools und stellen die Personen ein, die dies zulassen. Wenn Sie ein Android-App-Bastel-Startup sind, verwenden Sie, was Ihre Mitarbeiter wissen und / oder was Ihre Time-to-Market minimiert. Wenn Sie Algorithmen unterrichten, möchten Sie eine Sprache mit prägnanter, klarer Syntax und kleinem Boilerplate. Und so weiter. Die Prioritäten sind unterschiedlich und wir haben verschiedene Sprachen.
Raphael


23

Es gibt eine vergessene Sache bei der Optimierung.

Es gab eine lange Debatte darüber, ob Fortran C übertreffen sollte. Eine missgebildete Debatte wurde auseinander genommen: Der gleiche Code wurde in C geschrieben und Fortran (wie die Tester dachten) und die Leistung wurde basierend auf den gleichen Daten getestet. Das Problem ist, dass diese Sprachen unterschiedlich sind. C erlaubt das Aliasing von Zeigern, während fortran dies nicht tut.

Die Codes waren also nicht gleich, es gab keine Einschränkung in C-getesteten Dateien, was zu Unterschieden führte, nachdem Dateien neu geschrieben wurden, um dem Compiler mitzuteilen, dass Zeiger optimiert werden können, und die Laufzeiten sich ähnelten.

Der Punkt hier ist, dass einige Optimierungstechniken in neu erstellter Sprache einfacher (oder legaler) sind.

Auf lange Sicht ist es auch möglich, VMs mit JIT-Outperform C zu
erstellen . Es gibt zwei Möglichkeiten: JIT-Code kann den Vorteil der Maschine nutzen, auf der er ausgeführt wird (z. B. einige SSE oder andere, für einige CPU-vektorisierte Anweisungen exklusive Befehle), die nicht implementiert wurden Programm verglichen.X

Zweitens kann VM während der Ausführung einen Drucktest durchführen, um unter Druck stehenden Code zu erfassen und zu optimieren oder sogar während der Laufzeit vorzukalkulieren. Im Voraus kompiliertes C-Programm erwartet nicht, wo der Druck ist oder (meistens) es gibt generische Versionen von ausführbaren Dateien für die allgemeine Maschinenfamilie.

In diesem Test gibt es auch JS, also gibt es schnellere VMs als V8, und in einigen Tests ist es auch schneller als C.

Ich habe es überprüft und es gab einzigartige Optimierungstechniken, die in C-Compilern noch nicht verfügbar waren.

C-Compiler müsste sofort eine statische Analyse des gesamten Codes durchführen, auf einer bestimmten Plattform marschieren und Probleme mit der Speicherausrichtung umgehen.

VM hat nur einen Teil des Codes transliteriert, um die Assembly zu optimieren und auszuführen.

Über Julia - als ich überprüfte, dass es auf AST von Code funktioniert, hat GCC diesen Schritt übersprungen und gerade erst begonnen, einige Informationen von dort zu übernehmen. Dies und andere Einschränkungen und VM-Techniken könnten ein wenig erklären.

Beispiel: Nehmen wir eine einfache Schleife, die den Start- und Endpunkt von Variablen übernimmt und einen Teil der Variablen zur Laufzeit in die bekannten Berechnungen lädt.

Der C-Compiler generiert Ladevariablen aus Registern.
Aber zur Laufzeit sind diese Variablen bekannt und werden durch die Ausführung als Konstanten behandelt.
Anstatt also Variablen aus Registern zu laden (und keine Zwischenspeicherung durchzuführen, weil sie sich ändern können, und aus statischen Analysen nicht klar ist, werden sie vollständig wie Konstanten behandelt und gefaltet, weitergegeben.


12

Die vorangegangenen Antworten geben so ziemlich die Erklärung, wenn auch meistens aus einem pragmatischen Blickwinkel, denn so sehr die Frage Sinn macht , wie es Raphaels Antwort vortrefflich erklärt .

Ergänzend zu dieser Antwort sollten wir beachten, dass C-Compiler heutzutage in C geschrieben sind. Natürlich können, wie Raphael bemerkt, ihre Ausgabe und ihre Leistung unter anderem von der CPU abhängen, auf der sie ausgeführt wird. Es kommt aber auch auf den Optimierungsgrad des Compilers an. Wenn Sie in C einen besser optimierenden Compiler für C schreiben (den Sie dann mit dem alten kompilieren, um ihn ausführen zu können), erhalten Sie einen neuen Compiler, der C zu einer schnelleren Sprache macht als zuvor. Also, was ist die Geschwindigkeit von C? Beachten Sie, dass Sie den neuen Compiler sogar in einem zweiten Durchgang mit sich selbst kompilieren können, so dass er effizienter kompiliert wird, obwohl Sie immer noch denselben Objektcode angeben. Und der Vollbeschäftigungssatz zeigt, dass solche Verbesserungen kein Ende haben (danke an Raphael für den Hinweis).

Ich denke jedoch, dass es sich lohnen kann, das Problem zu formalisieren, da es einige grundlegende Konzepte und insbesondere die denotationale und die operationelle Sichtweise der Dinge sehr gut illustriert.

Was ist ein Compiler?

Ein Compiler , abgekürzt mit wenn keine Mehrdeutigkeit vorliegt, ist eine Realisierung einer berechenbaren Funktion , die einen Programmtext eine Funktion berechnet , in einer geschriebenen Quellsprache in den Programmtext in einer schriftlichen Zielsprache , wird das soll die gleiche Funktion berechnen .CSTCCSTP:SP SP:T TP

Aus semantischer Sicht, dh denotational , spielt es keine Rolle, wie diese Kompilierfunktion berechnet wird, dh welche Realisierung gewählt wird. Es könnte sogar durch ein magisches Orakel geschehen. Mathematisch ist die Funktion einfach eine Menge von Paaren .CSTCST{(P:S,P:T)PSSPTT}

Die semantische Kompilierungsfunktion ist korrekt, wenn sowohl als auch dieselbe Funktion berechnen . Diese Formalisierung gilt jedoch auch für einen inkorrekten Compiler. Der einzige Punkt ist, dass alles, was implementiert wird, unabhängig von den Implementierungsmitteln das gleiche Ergebnis erzielt. Was semantisch zählt, ist, was vom Compiler gemacht wird, nicht wie (und wie schnell) es gemacht wird.CSTPSPTP

Tatsächlich ist das Erhalten von von ein operationelles Problem, das gelöst werden muss. Aus diesem Grund muss die Kompilierungsfunktion eine berechenbare Funktion sein. Dann kann jede Sprache mit Turing-Power, egal wie langsam sie ist, Code so effizient produzieren wie jede andere Sprache, auch wenn dies möglicherweise weniger effizient ist.P:TP:SCST

Wenn wir das Argument verfeinern, möchten wir wahrscheinlich, dass der Compiler eine gute Effizienz aufweist, damit die Übersetzung in angemessener Zeit ausgeführt werden kann. Die Leistung des Compiler-Programms ist also für die Benutzer von Bedeutung, hat jedoch keinen Einfluss auf die Semantik. Ich sage Leistung, weil die theoretische Komplexität einiger Compiler viel höher sein kann, als man erwarten würde.

Über Bootstrapping

Dies wird die Unterscheidung veranschaulichen und eine praktische Anwendung zeigen.

Es ist nun üblich, zuerst eine Sprache mit einem Interpreter zu implementieren und dann einen Compiler in der Sprache selbst zu schreiben . Dieser Compiler kann mit dem Interpreter , um ein beliebiges Programm in ein Programm . Wir haben also einen laufenden Compiler von Sprache bis (Maschine?) Sprache , aber er ist sehr langsam, wenn auch nur, weil er auf einem Interpreter läuft.I S C S TSIS S C S TCST:SS I S P : S P : T STCST:SISP:SP:TST

Aber man kann diese Compilieren Anlage verwenden Sie den Compiler zu kompilieren , da es in der Sprache geschrieben ist , und somit erhalten Sie einen Compiler geschrieben in die Zielsprache . Wenn Sie, wie so oft, davon ausgehen, dass eine Sprache ist, die effizienter interpretiert wird (z. B. maschinenneutral), erhalten Sie eine schnellere Version Ihres Compilers, die direkt in der Sprache . Es erledigt genau den gleichen Job (dh es produziert die gleichen Zielprogramme), aber es erledigt es effizienter. S C S TCST:SS TTTCST:TTTT


"Was semantisch zählt, ist was gemacht wird, nicht wie (und wie schnell) es gemacht wird" - es ist zu erwähnen, dass in der Praxis nicht-funktionale Kriterien existieren. Es gibt viele funktional äquivalente Zielprogramme, aber aus irgendeinem Grund ziehen wir möglicherweise einige vor (Effizienz, Größe, bessere Speicherausrichtung, ...). Das heißt, die Ansicht eines Compilers als eine von Ihnen definierte Funktion ist eingeschränkter als von uns gewünscht (es werden auch häufig Nebenwirkungen übersprungen, z. B. E / A). Es dient jedoch dem erklärenden Zweck, den Sie möchten.
Raphael

@Raphael In Bezug auf den Satz über die Vollbeschäftigung habe ich das im Hinterkopf gehabt (in meinem Kommentar zu C), aber ich kannte den Namen nicht und habe die Suche nach einer Referenz verschoben. Danke dafür. --- Die Semantik, von der ich spreche, ist die des Compilers, nicht die des Zielprogramms. Das Zielprogramm bleibt syntaktisch und operativ erhalten, nicht nur semantisch. Oder habe ich deine Bemerkung falsch verstanden? Ich habe den Text bearbeitet, um ihn präziser zu gestalten.
Babou

@Raphael Da du deinen Kommentar nicht gelöscht hast, heißt das, dass ich ihn falsch verstanden oder nicht richtig beantwortet habe? Wie kommt es, dass die Sicht des Compilers (nicht des kompilierten Programms) als Funktion aus semantischer Sicht zu eingeschränkt ist? Natürlich kann es als Funktion auch andere Argumente als nur das kompilierte Programm (wie Optimierungsanweisungen) geben, aber dies ist ein Detail, das die Diskussion nicht ändern würde.
Babou

Ich denke, mein Kommentar ist ein Hinweis auf "Es gibt mehr als dieses Modell". Was Sie schreiben, ist nicht falsch, aber es ist nicht alles. Theoretisch scheint offensichtlich: „die“ Compiler - Funktion nicht per se ist gut definiert , da es gibt unendlich viele mögliche Zielprogramme, die alle semantisch äquivalent. Die Auswahl ist ein sehr wichtiger Bestandteil beim Entwerfen von Compilern.
Raphael

@Raphael Es gibt nur dieses Modell. Sie haben meinen Text falsch gelesen: Ich sage "eine berechenbare Funktion", weil andere möglich sind. Der Compiler implementiert eine spezielle Kompilierungsfunktion "curly ", die eine Funktion von S-Syntax bis T-Syntax ist und eine präzise Menge syntaktischer Programmpaare (kursive Buchstaben) definiert. Diese Paare sollen dieselbe Funktion "curly " berechnen , aber die zweite Komponente kann nicht durch eine andere mit derselben Eigenschaft ersetzt werden. Ich ignoriere absichtlich die Semantik von Programmen, um die Übersetzungsprozesse vergleichbar zu halten. PCP
Babou

6

Nach dem Speedup-Theorem von Blum gibt es Programme, die auf der schnellsten Computer / Compiler-Kombination geschrieben und ausgeführt werden, die auf Ihrem ersten PC, auf dem interpretiertes BASIC ausgeführt wird, langsamer als ein entsprechendes Programm ausgeführt wird. Es gibt einfach keine "schnellste Sprache". Alles, was Sie sagen können, ist, dass, wenn Sie denselben Algorithmus in mehreren Sprachen schreiben (Implementierungen; wie bereits erwähnt, gibt es viele verschiedene C-Compiler, und ich bin sogar auf einen ziemlich fähigen C-Interpreter gestoßen), dieser in jedem schneller oder langsamer läuft .

Es kann keine "immer langsamere" Hierarchie geben. Dies ist ein Phänomen, das allen bekannt ist, die mehrere Sprachen fließend beherrschen: Jede Programmiersprache wurde für einen bestimmten Anwendungstyp entwickelt, und die am häufigsten verwendeten Implementierungen wurden liebevoll für diesen Programmtyp optimiert. Ich bin mir ziemlich sicher, dass z. B. ein Programm zum Herumalbern mit in Perl geschriebenen Zeichenfolgen wahrscheinlich den gleichen Algorithmus wie C übertreffen wird, während ein Programm, das in großen Arrays von Ganzzahlen in C kaut, schneller ist als Perl.


1
"Jede Programmiersprache wurde für einen bestimmten Anwendungstyp entwickelt." Tatsächlich sind die meisten Programmiersprachen, die tatsächlich verwendet werden, Mehrzwecksprachen, im Gegensatz dazu, dass sie für bestimmte Anwendungen entwickelt wurden. Es ist nur so, dass bestimmte Sprachen hauptsächlich aufgrund sozialer Effekte in bestimmten Bereichen häufiger verwendet werden.
Cubic

Ich denke, es hängt davon ab, wie weit Sie den Begriff "bestimmte Art der Anwendung" interpretieren. Es stimmt zwar, dass die meisten Mainstream - Sprachen nicht DSLs sind, werden sie ganz sicher sind , mit bestimmten Anwendungen konzipiert. C wurde entwickelt, um Unix zu implementieren. Java wurde für die Skripterstellung von interaktiven Fernsehgeräten entwickelt. Smalltalk wurde zum Unterrichten von Kindern entwickelt. ECMAScript wurde für serverseitiges und clientseitiges Web-Scripting entwickelt. Perl wurde für die Textverarbeitung und Unix-Skripterstellung entwickelt. PHP wurde für serverseitiges Web-Scripting entwickelt. Erlang wurde für Zuverlässigkeit entwickelt. Das Schema war für die Erkundung des…
Jörg W Mittag

… Grundlagen von OO und dem Actor Model. APL wurde als Notation für den Mathematikunterricht entwickelt. Julia wurde für die wissenschaftliche Programmierung konzipiert. Alle diese Sprachen werden jetzt natürlich außerhalb ihrer ursprünglichen Problemdomäne verwendet, aber es gibt immer noch einige Eigenschaften in diesen Sprachen, die sie für bestimmte Arten von Anwendungen besser oder schlechter geeignet machen, obwohl sie alle zum Erstellen aller Arten von Anwendungen verwendet werden können Dinge.
Jörg W Mittag

4

Kehren wir zur ursprünglichen Zeile zurück: "Wie kann eine Sprache, deren Compiler in C geschrieben ist, jemals schneller sein als C?" Ich denke, das bedeutete wirklich zu sagen: Wie kann ein in Julia geschriebenes Programm, dessen Kern in C geschrieben ist, jemals schneller sein als ein in C geschriebenes Programm? Wie kann das in Julia geschriebene Programm "mandel" in 87% der Ausführungszeit des in C geschriebenen entsprechenden Programms "mandel" ausgeführt werden?

Babous Abhandlung ist die einzig richtige Antwort auf diese Frage. Alle anderen Antworten beantworten mehr oder weniger andere Fragen. Das Problem mit Babous Text ist, dass die viele Absätze lange theoretische Beschreibung von "Was ist ein Compiler?" So geschrieben ist, dass das Originalplakat wahrscheinlich Probleme mit dem Verständnis hat. Wer die Begriffe "semantisch", "denotational", "verwirklichend", "berechenbar" usw. versteht, kennt die Antwort auf die Frage bereits.

Die einfachere Antwort ist, dass weder C-Code noch Julia-Code direkt von der Maschine ausgeführt werden können. Beide müssen übersetzt werden, und dieser Übersetzungsprozess bietet viele Möglichkeiten, wie der ausführbare Maschinencode langsamer oder schneller sein kann, aber dennoch das gleiche Endergebnis liefert. Sowohl C als auch Julia kompilieren, was eine Reihe von Übersetzungen in eine andere Form bedeutet. Im Allgemeinen wird eine für Menschen lesbare Textdatei in eine interne Darstellung übersetzt und dann als Folge von Anweisungen ausgegeben, die der Computer direkt verstehen kann. Bei einigen Sprachen steckt mehr dahinter, und Julia ist eine davon - sie hat einen "JIT" -Compiler, was bedeutet, dass der gesamte Übersetzungsprozess nicht für das gesamte Programm auf einmal stattfinden muss. Aber das Endergebnis für jede Sprache ist Maschinencode, der keine weitere Übersetzung benötigt. Code, der direkt an die CPU gesendet werden kann, damit er etwas tut. Am Ende ist DIES die "Berechnung", und es gibt mehr als eine Möglichkeit, einer CPU mitzuteilen, wie sie die gewünschte Antwort erhält.

Man kann sich eine Programmiersprache vorstellen, die sowohl einen "Plus" - als auch einen "Multiplikations" -Operator und eine andere Sprache, die nur "Plus" hat. Wenn für Ihre Berechnung eine Multiplikation erforderlich ist, ist eine Sprache "langsamer", da die CPU natürlich beide Funktionen direkt ausführen kann. Wenn Sie jedoch keine Möglichkeit haben, die Notwendigkeit der Multiplikation von 5 * 5 auszudrücken, müssen Sie nur noch "5" schreiben + 5 + 5 + 5 + 5 ". Letzteres wird mehr Zeit in Anspruch nehmen, um die gleiche Antwort zu erhalten. Vermutlich ist etwas davon mit Julia los; Vielleicht erlaubt die Sprache dem Programmierer, das gewünschte Ziel der Berechnung eines Mandelbrot-Satzes auf eine Weise anzugeben, die nicht direkt in C ausgedrückt werden kann.

Der für den Benchmark verwendete Prozessor wurde als Xeon E7-8850 2.00GHz-CPU aufgeführt. Der C-Benchmark verwendete den gcc 4.8.2-Compiler, um Anweisungen für diese CPU zu erstellen, während Julia das LLVM-Compiler-Framework verwendet. Es ist möglich, dass das Backend von gcc (der Teil, der Maschinencode für eine bestimmte CPU-Architektur erzeugt) nicht so weit fortgeschritten ist wie das LLVM-Backend. Das könnte die Leistung verbessern. Es gibt noch viele andere Dinge: Der Compiler kann "optimieren", indem er Anweisungen in einer anderen als der vom Programmierer festgelegten Reihenfolge ausgibt oder einige Dinge überhaupt nicht ausführt, wenn er den Code analysieren und feststellen kann, dass dies nicht der Fall ist erforderlich, um die richtige Antwort zu erhalten. Und der Programmierer hat vielleicht einen Teil des C-Programms auf eine Weise geschrieben, die es langsam macht, aber er hat es nicht getan.

All dies sind Möglichkeiten zu sagen: Es gibt viele Möglichkeiten, Maschinencode für die Berechnung eines Mandelbrot-Satzes zu schreiben, und die von Ihnen verwendete Sprache hat einen großen Einfluss darauf, wie dieser Maschinencode geschrieben wird. Je besser Sie sich mit Kompilierung, Anweisungssätzen, Caches usw. auskennen, desto besser können Sie die gewünschten Ergebnisse erzielen. Die wichtigste Erkenntnis aus den für Julia angegebenen Benchmark-Ergebnissen ist, dass keine Sprache oder kein Tool in allem die besten Ergebnisse erzielt. Tatsächlich war der beste Geschwindigkeitsfaktor im gesamten Diagramm für Java!


2

Die Geschwindigkeit eines kompilierten Programms hängt von zwei Dingen ab:

  1. Die Leistungsmerkmale der Maschine, die sie ausführt
  2. Der Inhalt der ausführbaren Datei

Die Sprache, in der ein Compiler geschrieben ist, ist für (1) irrelevant. Zum Beispiel kann ein Java-Compiler in C oder Java oder Python geschrieben werden, aber in allen Fällen ist die "Maschine", die das Programm ausführt, die JVM.

Die Sprache, in der ein Compiler geschrieben ist, ist für (2) irrelevant. Es gibt beispielsweise keinen Grund, warum ein in Python geschriebener C-Compiler nicht genau dieselbe ausführbare Datei wie ein in C oder Java geschriebener C-Compiler ausgeben kann.


1

Ich werde versuchen, eine kürzere Antwort anzubieten.

Der Kern der Frage liegt in der Definition der "Geschwindigkeit" einer Sprache .

Die meisten, wenn nicht alle Geschwindigkeitsvergleichstests testen nicht die maximal mögliche Geschwindigkeit. Stattdessen schreiben sie ein kleines Programm in einer Sprache, die sie testen möchten, um ein Problem zu lösen. Beim Schreiben des Programms verwendet der Programmierer zum Zeitpunkt des Tests das, was er als Best Practice und Konventionen der Sprache ansieht. Dann messen sie die Geschwindigkeit, mit der das Programm ausgeführt wurde.

* Die Annahmen sind gelegentlich falsch.


0

Code, der in einer Sprache X geschrieben ist, deren Compiler in C geschrieben ist, kann einen in C geschriebenen Code übertreffen, vorausgesetzt, der C-Compiler führt im Vergleich zu dem der Sprache X eine schlechte Optimierung durch Objektcode als der vom C-Compiler generierte, dann kann auch in X geschriebener Code das Rennen gewinnen.

Wenn jedoch die Sprache X eine interpretierte Sprache ist und der Interpreter in C geschrieben ist und wir annehmen, dass der Interpreter der Sprache X und des in C geschriebenen Codes von demselben C-Compiler kompiliert wird, wird der in X geschriebene Code in keiner Weise den Code übertreffen geschrieben in C, vorausgesetzt beide Implementierungen folgen demselben Algorithmus und verwenden äquivalente Datenstrukturen.


2
Was bedeutet dies gegenüber früheren Antworten? Ich glaube auch nicht, dass Ihr zweiter Absatz aus anderen Gründen zutrifft.
Raphael
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.