Was regelt die "Geschwindigkeit" einer Programmiersprache?
Es gibt keine "Geschwindigkeit" einer Programmiersprache. Es gibt nur die Geschwindigkeit eines bestimmten Programms, das von einem bestimmten Programmierer geschrieben wurde und von einer bestimmten Version einer bestimmten Implementierung einer bestimmten Ausführungsmaschine ausgeführt wird, die in einer bestimmten Umgebung ausgeführt wird.
Es kann große Leistungsunterschiede geben, wenn derselbe Code in derselben Sprache auf demselben Computer mit verschiedenen Implementierungen ausgeführt wird. Oder Sie verwenden sogar verschiedene Versionen derselben Implementierung. Wenn Sie beispielsweise genau denselben ECMAScript-Benchmark auf genau demselben Computer mit einer SpiderMonkey-Version von vor 10 Jahren im Vergleich zu einer Version aus diesem Jahr ausführen, wird sich die Leistung wahrscheinlich zwischen 2 × –5 ×, möglicherweise sogar 10 ×, erhöhen. Bedeutet das dann, dass ECMAScript 2 × schneller ist als ECMAScript, weil das Ausführen desselben Programms auf demselben Computer mit der neueren Implementierung 2 × schneller ist? Das ergibt keinen Sinn.
Hat dies etwas mit Speicherverwaltung zu tun?
Nicht wirklich.
Warum passiert das?
Ressourcen. Geld. Microsoft beschäftigt wahrscheinlich mehr Leute, die Kaffee für ihre Compiler-Programmierer kochen, als die gesamte PHP-, Ruby- und Python-Community zusammen Leute an ihren VMs beschäftigt.
Für mehr oder weniger alle Funktionen einer Programmiersprache, die sich auf die Leistung auswirken, gibt es auch eine Lösung. Beispielsweise ist C (ich verwende C hier als Ersatz für eine Klasse ähnlicher Sprachen, von denen einige sogar vor C existierten) nicht speichersicher, sodass mehrere C-Programme, die gleichzeitig ausgeführt werden, mit Füßen treten können einander in Erinnerung. Wir erfinden also den virtuellen Speicher und lassen alle C-Programme eine Indirektionsebene durchlaufen, damit sie so tun können, als wären sie die einzigen, die auf der Maschine ausgeführt werden. Da dies jedoch langsam ist, erfinden wir die MMU und implementieren virtuellen Speicher in Hardware, um ihn zu beschleunigen.
Aber! Speichersichere Sprachen brauchen das alles nicht! Ein virtueller Speicher hilft ihnen kein bisschen. Schlimmer noch: Virtueller Speicher hilft nicht nur nicht bei speichersicheren Sprachen, sondern beeinträchtigt auch die Leistung, selbst wenn er in Hardware implementiert ist. Dies kann sich besonders nachteilig auf die Leistung von Garbage Collectors auswirken (was eine erhebliche Anzahl von Implementierungen von speichersicheren Sprachen verwendet).
Ein weiteres Beispiel: Moderne Mainstream-Allzweck-CPUs verwenden ausgeklügelte Tricks, um die Häufigkeit von Cache-Fehlern zu verringern. Viele dieser Tricks laufen darauf hinaus, vorherzusagen, welcher Code ausgeführt und welcher Speicher in Zukunft benötigt wird. Für Sprachen mit einem hohen Grad an Laufzeitpolymorphismus (z. B. OO-Sprachen) ist es jedoch sehr, sehr schwierig, diese Zugriffsmuster vorherzusagen.
Es gibt jedoch noch einen anderen Weg: Die Gesamtkosten für Cache-Fehler sind die Anzahl der Cache-Fehler multipliziert mit den Kosten für einen einzelnen Cache-Fehler. Mainstream-CPUs versuchen, die Anzahl der Ausfälle zu verringern. Was wäre, wenn Sie die Kosten eines einzelnen Ausfalls senken könnten?
Die Azul Vega-3-CPU wurde speziell für die Ausführung virtualisierter JVMs entwickelt und verfügte über eine sehr leistungsstarke MMU mit speziellen Anweisungen zur Unterstützung der Garbage Collection und Fluchterkennung (das dynamische Äquivalent zur statischen Fluchtanalyse) sowie über leistungsstarke Speichercontroller und das gesamte System könnte noch Fortschritte mit über 20000 ausstehenden Cache-Fehlern im Flug machen. Leider wurde sein Design, wie bei den meisten sprachspezifischen CPUs, von den "Giganten" Intel, AMD, IBM und anderen einfach überflüssig und brachial erzwungen.
Die CPU-Architektur ist nur ein Beispiel dafür, wie einfach oder schwierig es ist, eine Sprache mit hoher Leistung zu implementieren. Eine Sprache wie C, C ++, D, Rust, die gut zum modernen Mainstream-CPU-Programmiermodell passt, ist schneller zu erstellen als eine Sprache, die die CPU "bekämpfen" und umgehen muss, wie Java, ECMAScript, Python, Ruby PHP.
Wirklich, es ist alles eine Frage des Geldes. Wenn Sie gleich viel Geld für die Entwicklung eines Hochleistungsalgorithmus in ECMAScript ausgeben, einer Hochleistungsimplementierung von ECMAScript, einem Hochleistungsbetriebssystem für ECMAScript, einer Hochleistungs-CPU für ECMAScript, wie sie in den letzten Jahren verwendet wurde Jahrzehnte, um C-ähnliche Sprachen schnell zu machen, werden Sie wahrscheinlich die gleiche Leistung sehen. Es ist nur so, dass zu diesem Zeitpunkt viel mehr Geld für das schnelle Erstellen von C-ähnlichen Sprachen ausgegeben wurde als für das schnelle Erstellen von ECMAScript-ähnlichen Sprachen, und die Annahmen von C-ähnlichen Sprachen werden in den gesamten Stapel von MMUs und CPUs bis hin zu Betriebssystemen und integriert virtuelle Speichersysteme bis hin zu Bibliotheken und Frameworks.
Persönlich bin ich mit Ruby am besten vertraut (das allgemein als "langsame Sprache" angesehen wird), daher möchte ich zwei Beispiele nennen: die Hash
Klasse (eine der zentralen Datenstrukturen in Ruby, ein Schlüsselwert-Wörterbuch) im Rubinius Die Ruby-Implementierung ist zu 100% in Ruby geschrieben und hat ungefähr die gleiche Leistung wie dieHash
Klasse in YARV (die am häufigsten verwendete Implementierung), die in C geschrieben ist. Und es gibt eine Bildbearbeitungsbibliothek, die als C-Erweiterung für YARV geschrieben ist und auch eine (langsame) reine Ruby- "Fallback-Version" für Implementierungen enthält, die nicht funktionieren C, das eine Menge hochdynamischer und reflektierender Ruby-Tricks verwendet, wird nicht unterstützt. Ein experimenteller Zweig von JRuby, der das Truffle AST-Interpreter-Framework und das Graal JIT-Kompilierungs-Framework von Oracle Labs verwendet, kann diese reine Ruby- "Fallback-Version" ausführen, so schnell wie das YARV die ursprüngliche hochoptimierte C-Version ausführen kann. Dies wird einfach (naja, alles andere als) von einigen wirklich klugen Leuten erreicht, die wirklich kluge Sachen mit dynamischen Laufzeitoptimierungen, JIT-Kompilierung und teilweiser Auswertung machen.