Kann eine dynamische Sprache wie Ruby / Python eine C / C ++ - ähnliche Leistung erreichen?

64

Ich frage mich, ob es möglich ist, Compiler für dynamische Sprachen wie Ruby zu erstellen, die eine ähnliche und vergleichbare Leistung wie C / C ++ aufweisen. Nach meinem Verständnis von Compilern, zum Beispiel Ruby, kann das Kompilieren von Ruby-Code niemals effizient sein, da die Art und Weise, wie Ruby mit Reflektionen umgeht, Features wie die automatische Typkonvertierung von Ganzzahlen in große Ganzzahlen und das Fehlen statischer Typisierung das Erstellen eines effizienten Compilers erschweren für Ruby extrem schwer.

Ist es möglich, einen Compiler zu erstellen, der Ruby oder andere dynamische Sprachen zu einer Binärdatei kompilieren kann, die C / C ++ sehr nahe kommt? Gibt es einen fundamentalen Grund, warum JIT-Compiler wie PyPy / Rubinius irgendwann oder nie mit C / C ++ in der Leistung mithalten können?

Hinweis: Ich verstehe, dass „Leistung“ vage sein kann. Um das zu klären, habe ich gemeint, wenn Sie X in C / C ++ mit Leistung Y ausführen können, können Sie X in Ruby / Python mit Leistung nahe Y ausführen? Wobei X von Gerätetreibern und Betriebssystemcode bis hin zu Webanwendungen alles ist.

Ichiro
quelle
1
Können Sie die Frage so umformulieren, dass sie zu Antworten ermutigt, die auf richtigen Beweisen gegenüber anderen beruhen?
Raphael
@Raphael Ich habe weiter gemacht und bearbeitet. Ich denke, meine Bearbeitung ändert die Bedeutung der Frage nicht grundlegend, macht sie aber weniger einladend für Meinungsbeiträge.
Gilles 'SO- hör auf böse zu sein'
1
Insbesondere denke ich, dass Sie eine (oder einige) konkrete Leistungskennzahlen festlegen müssen . Laufzeit? Raumnutzung? Energieverbrauch? Entwicklerzeit? Kapitalrendite? Beachten Sie auch diese Frage in unserem Meta , die diese Frage (bzw. deren Antworten) betrifft.
Raphael
Diese Frage ist ein typischer Auslöser religiöser Kriege. Und wie wir den Antworten entnehmen können, haben wir eine, wenn auch sehr zivilisierte.
Andrej Bauer
Es gibt dynamische Sprachen, die optionale Typanmerkungen ermöglichen (z. B. Clojure). Soweit ich weiß, entspricht die mit den typenbeschrifteten Funktionen verbundene Leistung dem Zeitpunkt, zu dem eine Sprache statisch typisiert würde.
Pedro Morte Rolo

Antworten:

68

Allen, die mit „Ja“ geantwortet haben, biete ich einen Kontrapunkt an, bei dem die Antwort „Nein“ lautet . Diese Sprachen werden niemals mit der Leistung statisch kompilierter Sprachen mithalten können.

Kos bot den (sehr gültigen) Punkt an, dass dynamische Sprachen zur Laufzeit mehr Informationen über das System haben, die zur Optimierung des Codes verwendet werden können.

Es gibt jedoch eine andere Seite der Medaille: Diese zusätzlichen Informationen müssen nachverfolgt werden. Auf modernen Architekturen ist dies ein Leistungskiller.

William Edwards bietet einen schönen Überblick über das Argument .

Insbesondere die von Kos erwähnten Optimierungen können nur in sehr begrenztem Umfang angewendet werden, wenn Sie die Ausdruckskraft Ihrer Sprachen, wie von Devin erwähnt, drastisch einschränken. Dies ist natürlich ein tragfähiger Kompromiss, aber im Interesse der Diskussion haben Sie am Ende eine statische Sprache, keine dynamische. Diese Sprachen unterscheiden sich grundlegend von Python oder Ruby, da die meisten Leute sie verstehen würden.

William zitiert einige interessante IBM-Folien :

  • Jede Variable kann dynamisch typisiert werden: Typprüfungen erforderlich
  • Jede Anweisung kann aufgrund von Typenkonflikten möglicherweise Ausnahmen auslösen
  • Jedes Feld und Symbol kann zur Laufzeit hinzugefügt, gelöscht und geändert werden: Zugriffsprüfungen erforderlich
  • Der Typ jedes Objekts und seine Klassenhierarchie können zur Laufzeit geändert werden: Klassenhierarchieprüfungen erforderlich

Einige dieser Überprüfungen können nach der Analyse beseitigt werden (Hinweis: Diese Analyse benötigt auch Zeit - zur Laufzeit).

Darüber hinaus argumentiert Kos, dass dynamische Sprachen sogar die Leistung von C ++ übertreffen könnten. Die GEG kann in der Tat das Verhalten des Programms analysieren und geeignete Optimierungen vornehmen.

Aber C ++ - Compiler können das Gleiche tun! Moderne Compiler bieten eine sogenannte profilgesteuerte Optimierung an, die bei entsprechender Eingabe das Programmlaufzeitverhalten modellieren und dieselben Optimierungen anwenden kann, die für eine JIT gelten würden.

Dies hängt natürlich vom Vorhandensein realistischer Trainingsdaten ab, und außerdem kann das Programm seine Laufzeitmerkmale nicht anpassen, wenn sich das Verwendungsmuster während der Ausführung ändert. JITs können theoretisch damit umgehen. Es würde mich interessieren, wie sich dies in der Praxis auswirkt, da die JIT zur Umstellung von Optimierungen fortlaufend Nutzungsdaten sammeln müsste, was die Ausführung erneut verlangsamt.

Zusammenfassend bin ich nicht davon überzeugt, dass Laufzeit-Hotspot-Optimierungen den Aufwand für die Verfolgung von Laufzeitinformationen auf lange Sicht im Vergleich zur statischen Analyse und Optimierung überwiegen .

Konrad Rudolph
quelle
2
@Raphael Das ist dann ein "Manko" des Compilers. Wurde javacjemals eine profilgesteuerte Optimierung durchgeführt? Nicht so weit ich weiß. Im Allgemeinen ist es nicht sinnvoll, den Compiler einer JITted-Sprache für die Optimierung gut zu machen, da die JIT damit umgehen kann (und zumindest auf diese Weise profitieren mehr Sprachen von dem Aufwand). javacSoweit ich weiß, wurde (verständlicherweise) nie viel Aufwand in den Optimierer gesteckt (für die .NET-Sprachen ist dies definitiv der Fall).
Konrad Rudolph
1
@Raphael Das Schlüsselwort ist: vielleicht. Es zeigt es nicht so oder so. Das ist alles was ich sagen wollte. Ich habe Gründe (aber keinen Beweis) für meine Annahme in den vorhergehenden Absätzen angegeben.
Konrad Rudolph
1
@Ben Ich bestreite nicht, dass es kompliziert ist. Dies ist nur eine Intuition. Das Verfolgen all dieser Informationen zur Laufzeit ist schließlich mit Kosten verbunden. Ihr Standpunkt zu IO überzeugt mich nicht. Wenn dies vorhersehbar ist (= typischer Anwendungsfall), kann PGO dies vorhersagen. Wenn es falsch ist, bin ich nicht davon überzeugt, dass das JIT es auch optimieren könnte. Vielleicht ab und zu aus purem Glück. Aber zuverlässig? …
Konrad Rudolph
2
@Konrad: Deine Intuition ist falsch. Es geht nicht darum, zur Laufzeit zu variieren, sondern um Unvorhersehbarkeit bei der Kompilierung . Der Sweet Spot für JITs im Vergleich zur statischen Optimierung liegt nicht darin, dass sich das Verhalten des Programms zur Laufzeit "zu schnell" für die Profilerstellung ändert, sondern darin, dass das Verhalten des Programms in jedem einzelnen Programmlauf einfach zu optimieren ist, jedoch stark variiert läuft. Ein statischer Optimierer muss im Allgemeinen nur für einen Satz von Bedingungen optimieren, während eine JIT jeden Lauf separat für die Bedingungen optimiert, die in diesem Lauf auftreten.
Ben
3
Hinweis: Dieser Kommentarthread entwickelt sich zu einem Mini-Chatraum. Wenn Sie diese Diskussion fortsetzen möchten, bringen Sie sie zum Chat mit. Kommentare sollten verwendet werden, um den ursprünglichen Beitrag zu verbessern. Bitte unterbrechen Sie dieses Gespräch hier. Vielen Dank.
Robert Cartaino
20

Wenn Sie X in C / C ++ mit der Leistung Y ausführen können, können Sie X in Ruby / Python mit einer Leistung nahe Y ausführen?

Ja. Nehmen Sie als Beispiel PyPy. Es ist eine Sammlung von Python-Code, der bei der Interpretation in der Nähe von C ausgeführt wird (nicht ganz so nah, aber auch nicht ganz so weit entfernt). Dazu führt es eine vollständige Programmanalyse des Quellcodes durch, um jeder Variablen einen statischen Typ zuzuweisen ( Details finden Sie in den Dokumenten Annotator und Rtyper ). Sobald Sie mit denselben Typinformationen wie in C ausgestattet sind, kann es dieselben Aktionen ausführen Optimierungen. Zumindest theoretisch.

Der Nachteil ist natürlich, dass nur eine Teilmenge des Python-Codes von RPython akzeptiert wird. Selbst wenn diese Einschränkung aufgehoben wird, kann im Allgemeinen nur eine Teilmenge des Python-Codes eine gute Leistung erbringen: die Teilmenge, die analysiert und mit statischen Typen versehen werden kann.

Wenn Sie Python ausreichend einschränken, können Optimierer erstellt werden, die die eingeschränkte Teilmenge nutzen und zu effizientem Code kompilieren. Dies ist kein wirklich interessanter Vorteil, in der Tat ist es bekannt. Der springende Punkt bei der Verwendung von Python (oder Ruby) war jedoch, dass wir interessante Funktionen verwenden wollten, die möglicherweise nicht gut analysiert werden und zu einer guten Leistung führen! Die interessante Frage ist also eigentlich ...

Werden JIT-Compiler wie PyPy / Rubinius jemals in der Leistung mit C / C ++ mithalten können?

Nein.

Womit ich meine: Sicher, wenn sich Code-Läufe ansammeln, können Sie genügend Tippinformationen und Hotspots abrufen, um den gesamten Code bis hin zum Maschinencode zu kompilieren. Und vielleicht können wir erreichen, dass dies für einige Codes eine bessere Leistung als C erbringt. Ich finde das nicht sehr kontrovers. Es muss sich jedoch noch "aufwärmen" und die Leistung ist immer noch etwas weniger vorhersehbar. Für bestimmte Aufgaben, die eine konsistent und vorhersehbar hohe Leistung erfordern, ist es nicht so gut wie C oder C ++.

Die vorhandenen Leistungsdaten für Java, die sowohl über mehr Typinformationen als Python oder Ruby als auch über einen besser entwickelten JIT-Compiler als Python oder Ruby verfügen, stimmen immer noch nicht mit C / C ++ überein. Es befindet sich jedoch im selben Stadion.

Devin Jeanpierre
quelle
1
"Der Nachteil ist natürlich, dass nur eine Teilmenge des Python-Codes akzeptiert wird oder vielmehr nur eine Teilmenge des Python-Codes: die Teilmenge, die analysiert und mit statischen Typen versehen werden kann." Das ist nicht ganz richtig. Nur die Teilmenge kann vom RPython-Compiler überhaupt akzeptiert werden. Das Kompilieren in RPython mit einigermaßen effizientem C funktioniert nur genau, weil die schwer zu kompilierenden Teile von Python in RPython-Programmen garantiert nie vorkommen. Es ist nicht nur so, dass sie nicht optimiert werden, wenn sie auftreten. Es ist der kompilierte Interpreter, der Python vollständig verarbeitet.
Ben
1
Über JIT: Ich habe mehr als einen Benchmark gesehen, bei dem Java mehrere Varianten von C (++) übertroffen hat. Nur C ++ mit Boost scheint zuverlässig vorne zu bleiben. In diesem Fall frage ich mich nach der Leistung pro Entwicklerzeit, aber das ist ein anderes Thema.
Raphael
@ Ben: Sobald Sie RPython haben, ist es trivial, einen Compiler / Interpreter zu erstellen, der auf die Verwendung des CPython-Interpreters zurückgreift, wenn der RPython-Compiler ausfällt.
Lie Ryan
9
@Raphael Es wurde oft gezeigt, dass gut geschriebener C ++ - Code Java übertrifft. Es ist der "gut geschriebene" Teil, der in C ++ etwas schwieriger zu bekommen ist, so dass man auf vielen Bänken die Ergebnisse sieht, die Java gegenüber C ++ übertrifft. C ++ ist daher teurer, aber wenn die strenge Mem-Kontrolle und der Metallstaub benötigt werden, wenden Sie sich an C / C ++. Insbesondere C ist nur ein AC / P-Assembler.
TC1
7
Der Vergleich der maximalen Leistung anderer Sprachen mit Sprachen wie C / C ++ ist eine sinnlose Übung, da Sie die Inline-Assemblierung direkt als Teil der Sprachspezifikation durchführen können. Alles, was die Maschine tun kann, um ein Programm auszuführen, das in einer beliebigen Sprache geschrieben ist, können Sie im schlimmsten Fall duplizieren, indem Sie eine Assembly aus einer Ablaufverfolgung der ausgeführten Anweisungen schreiben. Eine viel interessantere Metrik wäre, wie @Raphael in einem früheren Kommentar vorschlägt, die Leistung pro Entwicklungsaufwand (Arbeitsstunden, Codezeilen usw.).
Patrick87
18

Die kurze Antwort lautet: Wir wissen nicht , fragen Sie in 100 Jahren erneut. (Dann wissen wir es vielleicht immer noch nicht. Vielleicht werden wir es nie erfahren.)

Theoretisch ist das möglich. Nehmen Sie alle jemals geschriebenen Programme, übersetzen Sie sie manuell in den effizientesten Maschinencode und schreiben Sie einen Interpreter, der Quellcodes auf Maschinencodes abbildet. Dies ist möglich, da bisher nur eine begrenzte Anzahl von Programmen geschrieben wurde (und wenn mehr Programme geschrieben werden, behalten Sie die manuellen Übersetzungen bei). Dies ist natürlich auch praktisch völlig idiotisch.

Theoretisch sind Hochsprachen zwar in der Lage, die Leistung von Maschinencode zu erreichen, werden diese jedoch nicht übertreffen. Dies ist immer noch sehr theoretisch, da wir praktisch sehr selten auf das Schreiben von Maschinencode zurückgreifen. Dieses Argument gilt nicht für den Vergleich übergeordneter Sprachen: Es bedeutet nicht, dass C effizienter als Python sein muss, nur, dass Maschinencode nicht schlechter als Python sein kann.

Wenn wir von der anderen Seite kommen, sehen wir rein experimentell, dass interpretierte Hochsprachen die meiste Zeit schlechter abschneiden als kompilierte Niedrigsprachen. Wir neigen dazu, nicht zeitkritischen Code in sehr höheren Sprachen und zeitkritischen inneren Schleifen in Assembler zu schreiben, wobei Sprachen wie C und Python dazwischen liegen. Ich habe zwar keine Statistiken, um dies zu sichern, aber in den meisten Fällen ist dies in der Tat die beste Entscheidung.

Es gibt jedoch unbestrittene Fälle, in denen Hochsprachen den Code übertreffen, den man realistisch schreiben würde: spezielle Programmierumgebungen. Programme wie Matlab und Mathematica sind oft weitaus besser in der Lage, bestimmte Arten von mathematischen Problemen zu lösen, als das, was nur Sterbliche schreiben können. Die Bibliotheksfunktionen wurden möglicherweise in C oder C ++ geschrieben (was für die "Sprachen auf niedriger Ebene sind effizienter"), aber das geht mich nichts an, wenn ich Mathematica-Code schreibe, ist die Bibliothek eine Black Box.

Ist es theoretisch möglich, dass Python der optimalen Leistung so nahe oder sogar noch näher kommt als C? Wie oben gesehen, ja, aber davon sind wir heute sehr weit entfernt. Andererseits haben die Compiler in den letzten Jahrzehnten große Fortschritte gemacht, und diese Fortschritte werden nicht langsamer.

Hochsprachen machen in der Regel mehr Dinge automatisch, so dass sie mehr Arbeit haben und daher weniger effizient sind. Auf der anderen Seite enthalten sie tendenziell mehr semantische Informationen, sodass es einfacher ist, Optimierungen zu erkennen (wenn Sie einen Haskell-Compiler schreiben, müssen Sie sich keine Sorgen machen, dass ein anderer Thread eine Variable unter Ihrer Nase ändert). Eine von mehreren Versuchen, Äpfel und Orangen in verschiedenen Programmiersprachen zu vergleichen, ist das Computersprachen-Benchmark-Spiel (früher als Shootout bekannt). Fortran neigt dazu, bei numerischen Aufgaben zu glänzen; aber wenn es darum geht, strukturierte Daten oder hochratige Thread-Kommutierungen zu manipulieren, F # und Scala machen es gut. Nehmen Sie diese Ergebnisse nicht als Evangelium: Vieles, was sie messen, ist, wie gut der Autor des Testprogramms in jeder Sprache war.

Ein Argument für Hochsprachen ist, dass die Leistung auf modernen Systemen nicht so stark mit der Anzahl der ausgeführten Befehle korreliert, und dies im Laufe der Zeit weniger. Low-Level-Sprachen eignen sich gut für einfache sequentielle Maschinen. Wenn eine Hochsprache doppelt so viele Anweisungen ausführt, den Cache jedoch intelligenter nutzt, so dass nur halb so viele Cache-Fehler auftreten, kann dies zum Sieger werden.

Auf Server- und Desktop-Plattformen haben CPUs fast ein Plateau erreicht, auf dem sie nicht schneller werden (auch mobile Plattformen sind auf dem Weg dorthin). Dies begünstigt Sprachen, in denen Parallelität leicht auszunutzen ist. Viele Prozessoren warten die meiste Zeit auf eine E / A-Antwort. Die für die Berechnung aufgewendete Zeit ist im Vergleich zur Anzahl der E / A-Vorgänge von geringer Bedeutung, und eine Sprache, mit der der Programmierer die Kommunikation minimieren kann, ist von Vorteil.

Alles in allem haben Hochsprachen, die mit einer Strafe beginnen, mehr Raum für Verbesserungen. Wie nah können sie kommen? Fragen Sie noch einmal in 100 Jahren.

Schlussbemerkung: Häufig erfolgt der Vergleich nicht zwischen dem effizientesten Programm, das in Sprache A geschrieben werden kann, und demselben in Sprache B, noch zwischen dem effizientesten Programm, das jemals in jeder Sprache geschrieben wurde, sondern zwischen dem effizientesten Programm, das geschrieben werden kann von einem Menschen in einer bestimmten Zeit in jeder Sprache. Dies führt ein Element ein, das selbst im Prinzip nicht mathematisch analysiert werden kann. In der Praxis bedeutet dies häufig, dass die beste Leistung ein Kompromiss zwischen der Menge an Code auf niedriger Ebene, die Sie zum Erreichen der Leistungsziele schreiben müssen, und der Menge an Code auf niedriger Ebene ist, für die Sie Zeit haben, um die Veröffentlichungstermine einzuhalten.

Gilles 'SO - hör auf böse zu sein'
quelle
Ich denke, die Unterscheidung zwischen High-Level und Low-Level ist die falsche. C ++ kann (sehr) hoch sein. Modernes C ++ auf hoher Ebene bietet jedoch (nicht unbedingt) eine schlechtere Leistung als ein Äquivalent auf niedriger Ebene - im Gegenteil. C ++ und seine Bibliotheken wurden sorgfältig entwickelt, um Abstraktionen auf hoher Ebene ohne Leistungseinbußen anzubieten . Gleiches gilt für Ihr Haskell-Beispiel: Die Abstraktionen auf hoher Ebene ermöglichen häufig Optimierungen, anstatt sie zu verhindern. Die ursprüngliche Unterscheidung zwischen dynamischen und statischen Sprachen ist in dieser Hinsicht sinnvoller.
Konrad Rudolph
@KonradRudolph Du hast recht, bei Low-Level / High-Level handelt es sich um eine etwas willkürliche Unterscheidung. Aber dynamische und statische Sprachen erfassen auch nicht alles. Ein JIT kann einen Großteil des Unterschieds beseitigen. Im Wesentlichen sind die bekannten theoretischen Antworten auf diese Frage trivial und nutzlos, und die praktische Antwort lautet "es kommt darauf an".
Gilles 'SO- hör auf böse zu sein'
Nun, ich denke, die Frage wird einfach: "Wie gut können JITs werden, und wenn sie die statische Kompilierung überholen, können auch statisch kompilierte Sprachen davon profitieren?" Zumindest verstehe ich die Frage so, wenn wir JITs berücksichtigen. Und ja, ich bin mit Ihrer Einschätzung einverstanden, aber sicherlich können wir einige fundierte Vermutungen anstellen, die über "es kommt darauf an" hinausgehen. ;-)
Konrad Rudolph
@KonradRudolph Wenn ich raten wollte, würde ich nach Software Engineering fragen .
Gilles 'SO - hör auf böse zu sein'
1
Das Language Shootout ist leider eine fragwürdige Quelle für quantitative Benchmarks: Sie akzeptieren nicht alle Programme, sondern nur die für die Sprache typischen. Dies ist eine knifflige und sehr subjektive Anforderung. Dies bedeutet, dass Sie nicht davon ausgehen können, dass eine Shootout-Implementierung tatsächlich von Nutzen ist (und in der Praxis haben einige Implementierungen offensichtlich überlegene Alternativen abgelehnt). Auf der Kehrseite; Dies sind Mikrobenchmarks, und einige Implementierungen verwenden ungewöhnliche Techniken, die Sie in einem normaleren Szenario niemals in Betracht ziehen würden, um die Leistung zu verbessern. Also: Es ist ein Spiel, keine sehr zuverlässige Datenquelle.
Eamon Nerbonne
10

Der grundlegende Unterschied zwischen der C ++ Anweisung x = a + bund der Python - Anweisung x = a + bist , dass ein C / C ++ Compiler von dieser Aussage sagen können (und eine wenig mehr Informationen , die es über die Arten von leicht verfügbar ist x, aund b) genau das, was Code - Maschine muss ausgeführt werden . Um zu bestimmen, welche Operationen die Python-Anweisung ausführen soll, müssen Sie das Halteproblem lösen.

In C wird diese Anweisung im Grunde genommen zu einer von wenigen Arten von Maschinenadditionen kompiliert (und der C-Compiler weiß, welche). In C ++ kann es auf diese Weise kompiliert werden, oder es kann kompiliert werden, um eine statisch bekannte Funktion aufzurufen, oder (im schlimmsten Fall) es muss kompiliert werden, um eine virtuelle Methode zu suchen und aufzurufen, aber selbst dies hat einen relativ geringen Maschinencode-Aufwand. Noch wichtiger ist jedoch, dass der C ++ - Compiler anhand der statisch bekannten Typen erkennen kann, ob er eine einzelne schnelle Additionsoperation ausführen kann oder ob er eine der langsameren Optionen verwenden muss.

In Python könnte ein Compiler tun theoretisch fast so gut , wenn sie wusste , dass aund bwaren beide ints. Es gibt einen zusätzlichen Boxing-Aufwand, aber wenn Typen statisch bekannt wären, könnten Sie diesen wahrscheinlich auch beseitigen (während Sie weiterhin die Schnittstelle präsentieren, dass Ganzzahlen Objekte mit Methoden, Hierarchien von Superklassen usw. sind). Das Problem ist, dass ein Compiler für Python nicht kannDies ist bekannt, da Klassen zur Laufzeit definiert werden, zur Laufzeit geändert werden können und sogar die Module, die das Definieren und Importieren ausführen, zur Laufzeit aufgelöst werden (und selbst welche Importanweisungen ausgeführt werden, hängt von Dingen ab, die nur zur Laufzeit bekannt sind). Der Python-Compiler müsste also wissen, welcher Code ausgeführt wurde (dh, das Halting-Problem lösen), um zu wissen, was die Anweisung, die er kompiliert, bewirkt.

Selbst mit den ausgefeiltesten Analysen, die theoretisch möglich sind , können Sie einfach nicht viel darüber sagen, was eine bestimmte Python-Anweisung im Voraus tun wird. Dies bedeutet, dass selbst wenn ein ausgefeilter Python-Compiler implementiert wäre, in fast allen Fällen noch Maschinencode ausgegeben werden müsste, der dem Python-Dictionary-Lookup-Protokoll folgt, um die Klasse eines Objekts zu bestimmen und Methoden zu finden (MRO der Klassenhierarchie durchqueren, Dies kann sich auch dynamisch zur Laufzeit ändern und ist daher schwierig in eine einfache virtuelle Methodentabelle zu kompilieren. Im Grunde tun sie das, was die (langsamen) Interpreter tun. Aus diesem Grund gibt es keine ausgeklügelten Optimierungscompiler für dynamische Sprachen. Es ist nicht nur schwer, eine zu erstellen, die maximal mögliche Auszahlung ist nicht

Beachten Sie, dass dies nicht auf basiert , was der Code ist zu tun, es ist auf das, was der Code könnte tun. Sogar Python-Code, der eine einfache Folge von Ganzzahl-Arithmetikoperationen ist, muss so kompiliert werden, als würde er beliebige Klassenoperationen aufrufen. Statische Sprachen haben größere Einschränkungen in Bezug auf die Möglichkeiten, die der Code bieten kann, und folglich können ihre Compiler mehr Annahmen treffen.

JIT-Compiler profitieren davon, indem sie auf die Laufzeit warten, um zu kompilieren / optimieren. Auf diese Weise können sie Code emittieren , die für funktioniert , was der Code ist zu tun , anstatt was es tun könnte. Und aus diesem Grund haben JIT-Compiler für dynamische Sprachen eine viel größere potenzielle Auszahlung als für statische Sprachen. Für statischere Sprachen kann vieles, was ein Optimierer wissen möchte, im Voraus bekannt sein. Sie können es also genauso gut optimieren, sodass ein JIT-Compiler weniger Zeit hat.

Es gibt verschiedene JIT-Compiler für dynamische Sprachen, die behaupten, Ausführungsgeschwindigkeiten zu erreichen, die mit denen von kompiliertem und optimiertem C / C ++ vergleichbar sind. Es gibt sogar Optimierungen, die von einem JIT-Compiler vorgenommen werden können, die von einem vorzeitigen Compiler für keine Sprache durchgeführt werden können, sodass theoretisch die JIT-Kompilierung (für einige Programme) eines Tages den bestmöglichen statischen Compiler übertreffen könnte. Wie Devin jedoch zutreffend hervorhob, sind die Eigenschaften der JIT-Kompilierung (nur die "Hotspots" sind schnell und erst nach einer Aufwärmphase) so, dass JIT-kompilierte dynamische Sprachen wahrscheinlich nicht für alle möglichen Anwendungen geeignet sind, selbst wenn sie es werden so schnell oder schneller als statisch kompilierte Sprachen im Allgemeinen.

Ben
quelle
1
Das sind jetzt zwei Gegenstimmen ohne Kommentare. Ich würde mich über Verbesserungsvorschläge für diese Antwort freuen!
Ben
Ich habe nicht abgelehnt, aber Sie sind über "Notwendigkeit, das Anhalten-Problem zu lösen" falsch. Unter vielen Umständen wurde gezeigt, dass Code in dynamischen Sprachen zu einem optimalen Zielcode kompiliert werden kann, während meines Wissens keine dieser Demonstrationen eine Lösung für das Problem des Anhaltens beinhaltete :-)
mikera
@mikera Es tut mir leid, aber nein, du bist falsch. Niemand hat jemals einen Compiler (in dem Sinne, dass wir verstehen, dass GCC ein Compiler ist) für ganz allgemeines Python oder andere dynamische Sprachen implementiert. Jedes derartige System funktioniert nur für eine Teilmenge der Sprache oder nur für bestimmte Programme oder gibt manchmal Code aus, bei dem es sich im Grunde um einen Interpreter handelt, der ein fest codiertes Programm enthält. Wenn Sie möchten, schreibe ich Ihnen ein Python-Programm, das die Zeile enthält, in der foo = x + ydas Verhalten des Additionsoperators zur Kompilierungszeit von der Lösung des Halteproblems abhängt.
Ben
Ich habe recht und ich denke, Sie verpassen den Punkt. Ich sagte "unter vielen Umständen". Ich habe nicht "unter allen Umständen" gesagt. Ob Sie ein konstruiertes Beispiel im Zusammenhang mit dem Stopp-Problem konstruieren können oder nicht, ist in der realen Welt weitgehend irrelevant. FWIW, Sie könnten auch ein ähnliches Beispiel für C ++ konstruieren, sodass Sie sowieso nichts beweisen würden. Wie auch immer, ich bin nicht hierher gekommen, um mich auf einen Streit einzulassen, nur um Verbesserungen für Ihre Antwort vorzuschlagen. Nimm es oder lass es.
Mikera
@mikea Ich denke, Sie könnten den Punkt verpassen. Um zu x + yeffizienten Maschinenhinzufügungsoperationen zu kompilieren , müssen Sie beim Kompilieren wissen, ob dies der Fall ist oder nicht. Die ganze Zeit , nicht nur ein Teil der Zeit. Für dynamische Sprachen ist dies mit realistischen Programmen so gut wie nie möglich, obwohl vernünftige Heuristiken die meiste Zeit richtig raten würden. Compilation erfordert Compile-Zeit garantiert . Wenn Sie also von "unter vielen Umständen" sprechen, sprechen Sie meine Antwort überhaupt nicht an.
Ben
9

Nur ein kurzer Hinweis, der das Worst-Case-Szenario für dynamische Sprachen umreißt:

Perl-Parsing ist nicht berechenbar

Infolgedessen kann (volles) Perl niemals statisch kompiliert werden.


Generell kommt es wie immer darauf an. Ich bin zuversichtlich, dass wenn Sie versuchen, dynamische Features in einer statisch kompilierten Sprache zu emulieren, durchdachte Interpreten oder (teilweise) kompilierte Varianten die Leistung statisch kompilierter Sprachen annähern oder unterschreiten können.

Ein weiterer zu beachtender Punkt ist, dass dynamische Sprachen ein anderes Problem als C lösen . C ist für Assembler kaum mehr als eine nette Syntax, während dynamische Sprachen reichhaltige Abstraktionen bieten. Die Laufzeitleistung ist oft nicht das Hauptanliegen: Die Zeit bis zur Markteinführung hängt beispielsweise davon ab, dass Ihre Entwickler in der Lage sind, komplexe, qualitativ hochwertige Systeme in kurzer Zeit zu schreiben. Erweiterbarkeit ohne Neukompilierung, zum Beispiel mit Plugins, ist ein weiteres beliebtes Feature. Welche Sprache bevorzugen Sie in diesen Fällen?

Raphael
quelle
5

In dem Versuch, eine objektivere wissenschaftliche Antwort auf diese Frage zu geben, argumentiere ich wie folgt. Für eine dynamische Sprache ist ein Interpreter oder eine Laufzeit erforderlich, um zur Laufzeit Entscheidungen treffen zu können. Dieser Interpreter oder diese Laufzeit ist ein Computerprogramm und wurde als solches in einer Programmiersprache geschrieben, entweder statisch oder dynamisch.

Wenn der Interpreter / die Laufzeit in einer statischen Sprache geschrieben wurde, könnte man ein Programm in dieser statischen Sprache schreiben, das (a) dieselbe Funktion wie das dynamische Programm ausführt, das es interpretiert, und (b) mindestens ebenso ausführt. Dies ist hoffentlich selbstverständlich, da ein strenger Nachweis dieser Behauptungen zusätzlichen (möglicherweise erheblichen) Aufwand erfordern würde.

Unter der Annahme, dass diese Behauptungen wahr sind, besteht der einzige Ausweg darin, dass der Interpreter / die Laufzeit auch in einer dynamischen Sprache geschrieben sein muss. Wir haben jedoch das gleiche Problem wie zuvor: Wenn der Interpreter dynamisch ist, ist ein Interpreter / eine Laufzeit erforderlich, der / die ebenfalls in einer Programmiersprache (dynamisch oder statisch) geschrieben sein muss.

Wenn Sie nicht davon ausgehen, dass eine Instanz eines Interpreters zur Laufzeit in der Lage ist, sich selbst zu interpretieren (ich hoffe, dies ist offensichtlich absurd), besteht die einzige Möglichkeit, statische Sprachen zu schlagen, darin, dass jede Interpreterinstanz von einer separaten Interpreterinstanz interpretiert wird. Dies führt entweder zu einem unendlichen Rückschritt (ich hoffe, dass dies selbstverständlich absurd ist) oder zu einem geschlossenen Kreis von Dolmetschern (ich hoffe, dass dies ebenfalls selbstverständlich absurd ist).

Es scheint also, dass dynamische Sprachen auch theoretisch keine bessere Leistung erbringen können als statische Sprachen im Allgemeinen. Bei der Verwendung von Modellen realistischer Computer erscheint dies noch plausibler. Schließlich kann eine Maschine nur Sequenzen von Maschinenbefehlen ausführen, und alle Sequenzen von Maschinenbefehlen können statisch kompiliert werden.

In der Praxis kann es erforderlich sein, den Interpreter / die Laufzeit in einer statischen Sprache erneut zu implementieren, um die Leistung einer dynamischen Sprache mit einer statischen Sprache abzugleichen. Es ist jedoch der springende Punkt dieses Arguments, dass Sie dies überhaupt tun können. Es ist eine Henne-Ei-Frage, und vorausgesetzt, Sie stimmen den oben gemachten unbewiesenen (meiner Meinung nach meist selbstverständlichen) Annahmen zu, können wir sie tatsächlich beantworten. Wir müssen den statischen, nicht den dynamischen Sprachen das Nicken geben.

Ein anderer Weg, die Frage im Lichte dieser Diskussion zu beantworten, ist der folgende: In dem gespeicherten Programm control = data model of computing, das dem modernen Computing zugrunde liegt, ist die Unterscheidung zwischen statischer und dynamischer Kompilierung eine falsche Zweiteilung; Statisch kompilierte Sprachen müssen die Möglichkeit haben, zur Laufzeit beliebigen Code zu generieren und auszuführen. Es hängt im Wesentlichen mit der universellen Berechnung zusammen.

Patrick87
quelle
Ich glaube nicht, dass es wahr ist, wenn ich das noch einmal lese. Die JIT-Kompilierung unterbricht Ihre Argumentation. Sogar der einfachste Code main(args) { for ( i=0; i<1000000; i++ ) { if ( args[0] == "1" ) {...} else {...} }kann erheblich beschleunigt werden, sobald der Wert von argsbekannt ist (vorausgesetzt, er ändert sich nie, was wir möglicherweise behaupten können). Ein statischer Compiler kann keinen Code erstellen, der den Vergleich jemals verwirft. (Natürlich ziehen Sie in diesem Beispiel nur den Ring ifaus der Schleife. Aber das Ding ist möglicherweise komplizierter.)
Raphael
@Raphael Ich denke, JIT macht wahrscheinlich meine Argumentation. Programme, die eine JIT-Kompilierung durchführen (z. B. JVM), sind in der Regel statisch kompilierte Programme. Wenn ein statisch kompiliertes JIT-Programm ein Skript schneller ausführen kann als ein anderes statisches Programm, müssen Sie das Skript nur mit dem JIT-Compiler "bündeln" und das Bundle als statisch kompiliertes Programm bezeichnen. Dies muss mindestens genauso gut funktionieren wie die JIT, die ein separates dynamisches Programm ausführt, was jedem Argument widerspricht, dass JIT besser sein muss.
Patrick87
Hm, das ist, als würde man sagen, wenn man ein Ruby-Skript mit seinem Interpreter bündelt, erhält man ein statisch kompiliertes Programm. Ich stimme nicht zu (da diesbezüglich alle sprachlichen Unterschiede beseitigt werden), aber das ist eine Frage der Semantik, nicht der Konzepte. Konzeptionell kann die Anpassung des Programms zur Laufzeit (JIT) Optimierungen bewirken, die zur Kompilierungszeit (statischer Compiler) nicht möglich sind, und das ist mein Punkt.
Raphael
@Raphael Dass es keine aussagekräftige Unterscheidung gibt, ist der springende Punkt der Antwort: Jeder Versuch, einige Sprachen starr als statisch zu klassifizieren und damit unter Leistungseinschränkungen zu leiden, schlägt aus genau diesem Grund fehl: Es gibt keinen zwingenden Unterschied zwischen einem vorgefertigten (Ruby , script) und ein C-Programm. Wenn (Ruby, Skript) die Maschine veranlassen kann, die richtige Folge von Anweisungen auszuführen, um ein bestimmtes Problem effizient zu lösen, könnte dies auch ein geschickt erstelltes C-Programm sein.
Patrick87
Aber Sie können den Unterschied definieren. Eine Variante sendet den vorliegenden Code unverändert (C) an den Prozessor, die andere kompiliert zur Laufzeit (Ruby, Java, ...). Das erste ist das, was wir unter "statischer Kompilierung" verstehen, während das letztere "Just-in-Time-Kompilierung" ist (was datenabhängige Optimierungen ermöglicht).
Raphael
4

Können Sie Compiler für dynamische Sprachen wie Ruby erstellen, die eine ähnliche und vergleichbare Leistung wie C / C ++ aufweisen?

Ich denke, dass die Antwort "Ja" ist . Ich glaube auch, dass sie die aktuelle C / C ++ - Architektur in Bezug auf Effizienz sogar übertreffen können (wenn auch geringfügig).

Der Grund ist einfach: Es gibt mehr Informationen zur Laufzeit als zur Kompilierungszeit.

Dynamische Typen sind nur ein kleines Hindernis: Wenn eine Funktion immer oder fast immer mit denselben Argumenttypen ausgeführt wird, kann ein JIT-Optimierer einen Verzweigungs- und Maschinencode für diesen speziellen Fall generieren. Und es gibt noch so viel mehr zu tun.

Siehe Dynamic Languages ​​Strike Back , eine Rede von Steve Yegge von Google (irgendwo, glaube ich, gibt es auch eine Videoversion). Er erwähnt einige konkrete JIT-Optimierungstechniken aus V8. Inspirierend!

Ich freue mich auf das, was wir in den nächsten 5 Jahren haben werden!

Kos
quelle
2
Ich mag den Optimismus.
Dave Clarke
Ich glaube, es gab einige sehr spezifische Kritikpunkte an Ungenauigkeiten in Steves Vortrag. Ich werde sie posten, wenn ich sie finde.
Konrad Rudolph
1
@ DaveClarke das ist, was mich am Laufen hält :)
Kos
2

Leute, die anscheinend denken, dass dies theoretisch möglich ist oder in ferner Zukunft, sind meiner Meinung nach völlig falsch. Der Punkt liegt in der Tatsache, dass dynamische Sprachen einen völlig anderen Programmierstil bieten und auferlegen. Tatsächlich gibt es zwei Unterschiede, auch wenn beide Aspekte zusammenhängen:

  • Symbole (vars oder vielmehr id <-> datum Bindings aller Art) sind untypisiert.
  • Strukturen (die Daten, alles, was zur Laufzeit lebt) werden ebenfalls durch die Typen ihrer Elemente untypisiert.

Der zweite Punkt ist die kostenlose Generizität. Beachten Sie, dass Strukturen hier zusammengesetzte Elemente, Auflistungen, aber auch Typen selbst und sogar (!) Routinen aller Art (Funktionen, Aktionen, Operationen) sind. Wir könnten Strukturen anhand ihrer Elementtypen eingeben, aber aufgrund des ersten Punktes des check würde sowieso zur Laufzeit passieren. Wir könnten Symbole getippt haben und noch strukturierte Symbole haben, die entsprechend ihren Elementtypen untypisiert sind (ein Array awürde nur als Array und nicht als Array von Ints getippt werden), aber selbst diese wenigen sind in einer dynamischen Sprache nicht wahr ( akönnten auch enthalten) ein Faden).

L

  • ElementLL
  • ElementL
  • Alle Strukturen (auch hier Modellroutinen eingeschlossen) erhalten nur Elemente

Es ist für mich klar, dass dies nur eine enorme Leistungsstrafe ist; und ich berühre nicht einmal alle Konsequenzen (die unzähligen Laufzeitprüfungen aller Art, die notwendig sind, um die Sensibilität des Programms sicherzustellen), die in anderen Posts gut beschrieben wurden.

spir
quelle
+1 Sehr interessant. Hast du meine Antwort gelesen? Ihr und mein Denken scheinen ähnlich zu sein, obwohl Ihre Antwort mehr Details und eine interessante Perspektive bietet.
Patrick87
Dynamische Sprachen müssen nicht untypisiert sein. Die Implementierung eines Modells einer dynamischen Sprache in C schränkt die Optimierungsmöglichkeiten erheblich ein. Dies erschwert dem Compiler das Erkennen von Optimierungen auf hoher Ebene (z. B. unveränderliche Daten) und zwingt einige grundlegende Operationen (z. B. Funktionsaufrufe) dazu, C zu durchlaufen vorbewertet; native Compiler tendieren dazu, eine deutlich bessere Leistung zu erbringen. Ich bezweifle, dass die Bytecode-Dekodierung den Unterschied rechtfertigen kann.
Gilles 'SO- hör auf böse zu sein'
@ Patrick87: du hast recht, unsere gedankenlinien scheinen sehr ähnlich zu sein (hatte noch nie gelesen, sorry, meine reflexion kommt von der momentanen umsetzung einer dyn lang in c).
Spir
@Gilles: Ich stimme eher mit "... muss nicht untypisiert sein" überein, wenn du nicht statisch typisiert meinst . Aber das ist es nicht, was die Leute von Dyn langs im Allgemeinen halten, denke ich. Ich persönlich betrachte die Generizität (im allgemeinen Sinne der obigen Antwort) als ein Merkmal, das viel mächtiger ist und ohne das man viel schwerer leben kann. Wir können leicht Wege finden, mit statischen Typen umzugehen, indem wir überlegen, wie ein gegebener (scheinbar polymorpher) Typ definiert werden kann, oder indem wir Instanzen direkt mehr Flexibilität verleihen.
Spir
1

Ich hatte nicht die Zeit, alle Antworten im Detail zu lesen ... aber ich war amüsiert.

In den sechziger und frühen siebziger Jahren gab es eine ähnliche Kontroverse (die Geschichte der Informatik wiederholt sich häufig): Können Hochsprachen kompiliert werden, um Code zu erzeugen, der so effizient ist wie der von einem Programmierer manuell erzeugte Maschinencode, also Assembler-Code. Jeder weiß, dass ein Programmierer viel schlauer ist als jedes andere Programm und sich eine sehr clevere Optimierung einfallen lassen kann. Das ist natürlich Ironie von meiner Seite.

Es gab sogar ein Konzept der Codeerweiterung: das Verhältnis der Größe des von einem Compiler erstellten Codes zur Größe des Codes für dasselbe Programm, das von einem guten Programmierer erstellt wurde (als ob es zu viele davon gegeben hätte :-). Die Idee war natürlich, dass dieses Verhältnis immer größer als 1 war. Die damaligen Sprachen waren Cobol und Fortran 4 oder Algol 60 für die Intellektuellen. Ich glaube, Lisp wurde nicht berücksichtigt.

Nun, es gab einige Gerüchte, dass jemand einen Compiler entwickelt hat, der manchmal eine Expansionsrate von 1 hat ... bis es einfach zur Regel wurde, dass kompilierter Code viel besser ist als handgeschriebener Code (und auch zuverlässiger). Die Leute machten sich damals Sorgen um die Codegröße (kleine Speicher), aber das Gleiche gilt für die Geschwindigkeit oder den Energieverbrauch. Ich werde nicht auf die Gründe eingehen.

Merkwürdige Merkmale, dynamische Merkmale einer Sprache spielen keine Rolle. Entscheidend ist, wie sie verwendet werden, ob sie verwendet werden. Die Leistung, in welcher Einheit auch immer (Codegröße, Geschwindigkeit, Energie, ...), hängt häufig von sehr kleinen Programmteilen ab. Daher besteht eine gute Chance, dass Einrichtungen, die Ausdruckskraft verleihen, nicht wirklich im Weg stehen. Mit guter Programmierpraxis werden fortschrittliche Einrichtungen nur auf disziplinierte Weise verwendet, um sich neue Strukturen vorzustellen (das war die Lektion für Lispes).

Die Tatsache, dass eine Sprache keine statische Typisierung aufweist, hat niemals dazu geführt, dass in dieser Sprache geschriebene Programme nicht statisch typisiert sind. Andererseits kann es sein, dass das von einem Programm verwendete Typsystem noch nicht ausreichend formalisiert ist, um einen Typprüfer zu erstellen.

In der Diskussion gab es mehrere Hinweise auf die Worst-Case-Analyse ("Stop-Problem", PERL-Analyse). Die Worst-Case-Analyse spielt jedoch meist keine Rolle. Was zählt, ist, was in den meisten Fällen oder in nützlichen Fällen passiert ... wie auch immer definiert oder verstanden oder erfahren. Hier kommt eine andere Geschichte, die direkt mit der Programmoptimierung zusammenhängt. Es fand vor langer Zeit an einer großen Universität in Texas statt, zwischen einem Doktoranden und seinem Berater (der später an einer der nationalen Akademien gewählt wurde). Soweit ich mich erinnere, bestand der Student darauf, ein Analyse- / Optimierungsproblem zu untersuchen, das der Berater als nicht nachvollziehbar erwiesen hatte. Bald sprachen sie nicht mehr miteinander. Aber der Student hatte Recht: Das Problem war in den meisten praktischen Fällen so leicht zu lösen, dass die von ihm erstellte Dissertation zum Nachschlagewerk wurde.

Und um die Aussage weiter zu kommentieren, dass Perl parsing is not computable, was auch immer mit diesem Satz gemeint ist, es ein ähnliches Problem mit ML gibt, das eine bemerkenswert gut formalisierte Sprache ist. Type checking complexity in ML is a double exponential in the lenght of the program.Das ist ein sehr präzises und formales Ergebnis in der Worst-Case-Komplexität ... das spielt überhaupt keine Rolle. Afaik, ML-Benutzer warten noch auf ein praktisches Programm, das die Typprüfung explodieren lässt.

In vielen Fällen ist die menschliche Zeit und Kompetenz nach wie vor knapper als die Rechenleistung.

Das eigentliche Problem der Zukunft wird darin bestehen, unsere Sprachen so weiterzuentwickeln, dass neues Wissen und neue Programmierformen integriert werden, ohne dass die gesamte noch verwendete Legacy-Software neu geschrieben werden muss.

Wenn man sich die Mathematik ansieht, ist es eine sehr große Wissensbasis. Die verwendeten Sprachen, Notationen und Konzepte haben sich im Laufe der Jahrhunderte weiterentwickelt. Es ist einfach, alte Sätze mit den neuen Konzepten zu schreiben. Wir passen die Hauptbeweise an, kümmern uns aber nicht um viele Ergebnisse.

Bei der Programmierung müssen wir jedoch möglicherweise alle Beweise von Grund auf neu schreiben (Programme sind Beweise). Es kann sein, dass wir wirklich sehr gute und entwicklungsfähige Programmiersprachen brauchen. Optimierer-Designer werden gerne folgen.

babou
quelle
0

Ein paar Notizen:

  • Nicht alle Hochsprachen sind dynamisch. Haskell hat ein sehr hohes Niveau, ist jedoch vollständig statisch typisiert. Selbst Systemprogrammiersprachen wie Rust, Nim und D können Abstraktionen auf hoher Ebene kurz und effizient ausdrücken. Tatsächlich können sie so prägnant sein wie dynamische Sprachen.

  • Es gibt hochoptimierte Frühkompilierer für dynamische Sprachen. Gute Lisp-Implementierungen erreichen die Hälfte der Geschwindigkeit von Äquivalent C.

  • Die JIT-Kompilierung kann hier ein großer Gewinn sein. Die Web Application Firewall von CloudFlare generiert Lua-Code, der von LuaJIT ausgeführt wird. LuaJIT optimiert die tatsächlich verwendeten Ausführungspfade (normalerweise die Nichtangriffspfade) erheblich, so dass der Code viel schneller ausgeführt wird als der Code, der von einem statischen Compiler auf der tatsächlichen Arbeitslast erstellt wird. Im Gegensatz zu einem statischen Compiler mit profilgesteuerter Optimierung passt sich LuaJIT zur Laufzeit an veränderte Ausführungspfade an.

  • Die Deoptimierung ist ebenfalls von entscheidender Bedeutung. Anstatt dass JIT-kompilierter Code nach einer Klasse suchen muss, die monkeypatched ist, löst der Vorgang des monkeypatching einen Hook im Laufzeitsystem aus, der den von der alten Definition abhängigen Maschinencode verwirft.

Demi
quelle
Wie ist das eine Antwort? Nun, vielleicht Punkt drei, wenn Sie Referenzen hinzugefügt haben.
Raphael
Ich bin sehr skeptisch gegenüber der Behauptung, dass PGO die Leistung von LuaJIT für Webanwendungscode unter typischen Arbeitsbedingungen nicht erreichen kann.
Konrad Rudolph
@KonradRudolph Der Hauptvorteil einer JIT besteht darin, dass die JIT den Code anpasst, wenn verschiedene Pfade heiß werden.
Demi
@ Demetri Ich weiß das. Aber es ist sehr schwer zu quantifizieren, ob dies ein Vorteil ist - siehe meine Antwort und die Kommentardiskussion dort. Kurz gesagt: Während sich das JIT an Nutzungsänderungen anpassen kann, muss es auch zur Laufzeit Dinge verfolgen, die mit einem Mehraufwand verbunden sind. Der Break-Even dafür ist intuitiv nur dort möglich, wo häufige Verhaltensänderungen auftreten. Für Web-Apps gibt es wahrscheinlich nur ein einziges (oder nur sehr wenige) Verwendungsmuster, für das sich die Optimierung auszahlt, sodass die minimale Leistungssteigerung aufgrund der Anpassungsfähigkeit den Aufwand für kontinuierliches Profiling nicht ausgleichen kann.
Konrad Rudolph