Ich habe festgestellt, dass einige Anwendungen oder Algorithmen, die auf einer Programmiersprache basieren, beispielsweise C ++ / Rust, schneller oder schneller ausgeführt werden als Java / Node.js, die auf demselben Computer ausgeführt werden. Ich habe ein paar Fragen dazu:
- Warum passiert das?
- Was regelt die "Geschwindigkeit" einer Programmiersprache?
- Hat dies etwas mit Speicherverwaltung zu tun?
Ich würde es wirklich begrüßen, wenn jemand dies für mich aufgeschlüsselt hätte.
programming-languages
compilers
evil_potato
quelle
quelle
Antworten:
Beim Design und der Implementierung von Programmiersprachen gibt es eine Vielzahl von Möglichkeiten, die sich auf die Leistung auswirken können. Ich werde nur einige erwähnen.
Jede Sprache muss letztendlich durch Ausführen von Maschinencode ausgeführt werden. Eine "kompilierte" Sprache wie C ++ wird zur Kompilierungszeit nur einmal analysiert, dekodiert und in Maschinencode übersetzt. Eine "interpretierte" Sprache wird, wenn sie direkt implementiert wird, zur Laufzeit bei jedem Schritt und zu jedem Zeitpunkt dekodiert. Das heißt, jedes Mal, wenn wir eine Anweisung ausführen, muss der Interpreter prüfen, ob es sich um ein Wenn-Dann-Anderen oder eine Anweisung usw. handelt, und entsprechend handeln. Dies bedeutet, dass wir, wenn wir 100-mal eine Schleife durchführen, den gleichen Code 100-mal dekodieren und Zeit verschwenden. Zum Glück optimieren Dolmetscher dies häufig, z. B. durch ein Just-in-Time-Kompilierungssystem. (Richtigerweise gibt es keine "kompilierte" oder "interpretierte" Sprache - sie ist eine Eigenschaft der Implementierung, nicht der Sprache. Dennoch
Verschiedene Compiler / Interpreter führen unterschiedliche Optimierungen durch.
Wenn die Sprache über eine automatische Speicherverwaltung verfügt, muss ihre Implementierung eine Speicherbereinigung durchführen. Dies hat Laufzeitkosten, entlastet den Programmierer jedoch von einer fehleranfälligen Aufgabe.
Eine Sprache könnte näher an der Maschine sein, so dass der erfahrene Programmierer alles mikrooptimieren und mehr Leistung aus der CPU herausholen kann. Es ist jedoch fraglich, ob dies in der Praxis tatsächlich von Vorteil ist, da die meisten Programmierer keine wirkliche Mikrooptimierung durchführen und der Compiler häufig eine gute höhere Programmiersprache besser optimieren kann, als dies ein durchschnittlicher Programmierer tun würde. (Manchmal kann es jedoch auch von Vorteil sein, weiter von der Maschine entfernt zu sein. Zum Beispiel ist Haskell extrem hochwertig, kann aber dank seiner Konstruktionsauswahl sehr leichte grüne Fäden aufweisen.)
Statische Typprüfung kann auch bei der Optimierung helfen. In einer dynamisch typisierten, interpretierten Sprache muss
x - y
der Interpreter bei jeder Berechnung häufig prüfen, ob beidex,y
Zahlen sind, und ansonsten (z. B.) eine Ausnahme auslösen. Diese Prüfung kann übersprungen werden, wenn die Typen zum Zeitpunkt der Kompilierung bereits geprüft wurden.Einige Sprachen melden Laufzeitfehler immer auf vernünftige Weise. Wenn Sie
a[100]
in Java schreiben, in dema
nur 20 Elemente vorhanden sind, erhalten Sie eine Laufzeitausnahme. In C würde dies undefiniertes Verhalten verursachen, was bedeutet, dass das Programm abstürzen, einige zufällige Daten im Speicher überschreiben oder sogar absolut alles andere ausführen könnte (der ISO C-Standard setzt keinerlei Grenzen). Dies erfordert eine Laufzeitprüfung, bietet dem Programmierer jedoch eine viel schönere Semantik.Beachten Sie jedoch, dass die Leistung bei der Bewertung einer Sprache nicht alles ist. Sei nicht besessen davon. Es ist eine häufige Falle, zu versuchen, alles zu optimieren und dennoch nicht zu erkennen, dass ein ineffizienter Algorithmus / eine ineffiziente Datenstruktur verwendet wird. Knuth hat einmal gesagt, "vorzeitige Optimierung ist die Wurzel allen Übels".
Unterschätzen Sie nicht, wie schwierig es ist, ein Programm richtig zu schreiben . Oft ist es besser, eine "langsamere" Sprache mit einer menschlicheren Semantik zu wählen. Darüber hinaus können bestimmte leistungskritische Teile immer in einer anderen Sprache implementiert werden. Nur als Referenz, beim ICFP-Programmierwettbewerb 2016 waren dies die Sprachen, die von den Gewinnern verwendet wurden:
Keiner von ihnen sprach eine einzige Sprache.
quelle
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.
Nicht wirklich.
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.quelle
int
aus Performance-Gründen feste Größen gewählt , obwohl unbegrenzte ganze Zahlen, wie sie von Python verwendet werden, mathematisch viel natürlicher sind. Unbegrenzte Ganzzahlen in Hardware zu implementieren, wäre nicht so schnell wie Ganzzahlen mit fester Größe. Sprachen, die versuchen, Implementierungsdetails zu verbergen, benötigen komplexe Optimierungen, um naiven C-Implementierungen nahe zu kommen.Theoretisch sollte die Leistung beider gleich sein, wenn Sie Code schreiben, der in zwei Sprachen genau dasselbe bewirkt, und beide mit einem "perfekten" Compiler kompilieren.
In der Praxis gibt es mehrere Gründe, warum die Leistung unterschiedlich sein wird:
Einige Sprachen sind schwerer zu optimieren.
Dies umfasst insbesondere Funktionen, die den Code dynamischer machen (dynamische Typisierung, virtuelle Methoden, Funktionszeiger), aber auch beispielsweise die Garbage Collection.
Es gibt verschiedene Möglichkeiten, Code mit solchen Features schnell zu erstellen, aber es endet normalerweise immer noch etwas langsamer als ohne sie.
Einige Sprachimplementierungen müssen zur Laufzeit kompiliert werden.
Dies gilt insbesondere für Sprachen mit virtuellen Maschinen (wie Java) und Sprachen, die den Quellcode ausführen, ohne Zwischenschritt für Binärdateien (wie JavaScript).
Solche Sprachimplementierungen müssen zur Laufzeit mehr Arbeit leisten, was sich auf die Leistung auswirkt, insbesondere auf die Startzeit / Leistung kurz nach dem Start.
Sprachimplementierungen verbringen absichtlich weniger Zeit mit Optimierungen als sie könnten.
Alle Compiler kümmern sich um die Leistung des Compilers selbst, nicht nur des generierten Codes. Dies ist besonders wichtig für Laufzeit-Compiler (JIT-Compiler), bei denen das Kompilieren zu lange dauert, um die Anwendungsausführung zu verlangsamen. Dies gilt jedoch auch für frühere Compiler, z. B. für C ++. Zum Beispiel ist die Registerzuweisung ein NP-vollständiges Problem, so dass es nicht realistisch ist, es perfekt zu lösen. Stattdessen werden Heuristiken verwendet, die in angemessener Zeit ausgeführt werden.
Unterschiede in Redewendungen in verschiedenen Sprachen.
Code, der in dem für eine bestimmte Sprache gemeinsamen Stil (idiomatischer Code) unter Verwendung gemeinsamer Bibliotheken geschrieben wurde, kann zu Leistungsunterschieden führen, da sich dieser idiomatische Code in jeder Sprache geringfügig anders verhält.
Betrachten Sie als Beispiel
vector[i]
in C ++,list[i]
in C # undlist.get(i)
in Java. Der C ++ - Code führt wahrscheinlich keine Bereichsprüfung durch und führt keine virtuellen Aufrufe durch, der C # -Code führt eine Bereichsprüfung durch, aber keine virtuellen Aufrufe, und der Java-Code führt eine Bereichsprüfung durch und es ist ein virtueller Aufruf. Alle drei Sprachen unterstützen virtuelle und nicht virtuelle Methoden. Sowohl C ++ als auch C # können die Bereichsprüfung einschließen oder beim Zugriff auf das zugrunde liegende Array vermeiden. Die Standardbibliotheken dieser Sprachen haben sich jedoch entschlossen, diese äquivalenten Funktionen unterschiedlich zu schreiben, was zu einer unterschiedlichen Leistung führt.Bei einigen Compilern fehlen möglicherweise einige Optimierungen.
Compiler-Writer verfügen über begrenzte Ressourcen, sodass sie nicht alle möglichen Optimierungen implementieren können, selbst wenn sie die anderen Probleme ignorieren. Und die Ressourcen, über die sie verfügen, konzentrieren sich möglicherweise mehr auf einen Optimierungsbereich als auf andere. Infolgedessen kann in verschiedenen Sprachen geschriebener Code aufgrund unterschiedlicher Compiler eine unterschiedliche Leistung aufweisen, auch wenn es keinen fundamentalen Grund gibt, warum eine Sprache schneller oder sogar einfacher zu optimieren sein sollte als die andere.
quelle
Dies ist eine sehr allgemeine Frage, aber in Ihrem Fall könnte die Antwort einfach sein. C ++ kompiliert zu Maschinencode, wobei Java zu Java-Bytecode kompiliert wird, der dann von einer virtuellen Java-Maschine ausgeführt wird (obwohl es auch eine Just-in-Time-Kompilierung gibt, die den Java-Bytecode weiter zu systemeigenem Maschinencode kompiliert). Ein weiterer Unterschied könnte die Speicherbereinigung sein, die nur von Java bereitgestellt wird.
quelle
Ihre Frage ist zu allgemein, daher kann ich leider keine genaue Antwort geben, die Sie benötigen.
Schauen wir uns für meine beste Erklärung die Plattformen C ++ und .Net an.
C ++, sehr nahe am Maschinencode und aufgrund der Leistung eine der bevorzugten Programmierungen in der älteren Zeit (wie vor mehr als 10 Jahren?). Selbst mit der IDE sind nicht viele Ressourcen erforderlich, um C ++ - Programme zu entwickeln und auszuführen. Sie sind sehr leicht und sehr schnell. Sie können auch C ++ - Code in die Konsole schreiben und von dort aus ein Spiel entwickeln. In Bezug auf Speicher und Ressourcen habe ich ungefähr vergessen, wie viel Kapazität ein Computer benötigt, aber die Kapazität ist mit der aktuellen Generation von Programmiersprachen nicht zu vergleichen.
Nun schauen wir uns .Net an. Voraussetzung für die .NET-Entwicklung ist eine riesige IDE, die nicht nur aus einer Art von Programmiersprachen besteht. Auch wenn Sie nur C # als Entwickler verwenden möchten, enthält die IDE standardmäßig viele Programmierplattformen, wie z. B. J #, VB, Mobile usw. Sofern Sie keine benutzerdefinierte Installation durchführen und nur genau das installieren, was Sie möchten, vorausgesetzt, Sie verfügen über ausreichende Erfahrung, um mit der IDE-Installation zu spielen.
Abgesehen von der Installation der IDE-Software verfügt .Net auch über eine große Kapazität an Bibliotheken und Frameworks, um den Zugriff auf alle Arten von Plattformen zu vereinfachen, die Entwickler benötigen UND nicht benötigen.
Die Entwicklung in .Net kann eine unterhaltsame Erfahrung sein, da standardmäßig viele gängige Funktionen und Komponenten verfügbar sind. Sie können viele Validierungsmethoden, das Lesen von Dateien, den Datenbankzugriff und vieles mehr per Drag & Drop in der IDE verwenden.
Infolgedessen besteht ein Kompromiss zwischen Computerressourcen und Entwicklungsgeschwindigkeit. Diese Bibliotheken und Frameworks belegen Speicher und Ressourcen. Wenn Sie ein Programm in .Net IDE ausführen, kann der Versuch, die Bibliotheken, Komponenten und alle Ihre Dateien zu kompilieren, eine Menge Zeit in Anspruch nehmen. Und wenn Sie debuggen, benötigt Ihr Computer viele Ressourcen, um den Debugging-Prozess in Ihrer IDE zu verwalten.
Normalerweise benötigen Sie für die .Net-Entwicklung einen guten Computer mit RAM und Prozessor. Andernfalls können Sie auch gar nicht programmieren. In dieser Hinsicht ist die C ++ - Plattform viel besser als .Net. Sie brauchen zwar immer noch einen guten Computer, aber die Kapazität wird im Vergleich zu .Net nicht allzu sehr ins Gewicht fallen.
Hoffe, meine Antwort könnte bei Ihrer Frage helfen. Lassen Sie es mich wissen, falls Sie mehr wissen möchten.
BEARBEITEN:
Nur eine Klarstellung zu meinem Hauptpunkt, ich beantworte hauptsächlich die Frage "Was regelt die Geschwindigkeit der Programmierung".
Unter IDE-Gesichtspunkten wirkt sich die Verwendung von C ++ oder .Net in der relativen IDE nicht auf die Geschwindigkeit des Codeschreibens aus, wirkt sich jedoch auf die Entwicklungsgeschwindigkeit aus, da der Visual Studio-Compiler längere Zeit für die Ausführung des Programms benötigt, C ++ IDE jedoch wesentlich leichter ist und verbrauchen weniger Computerressourcen. Auf lange Sicht können Sie mit C ++ schneller programmieren als mit .Net IDE, was stark von den Bibliotheken und dem Framework abhängt. Wenn Sie etwas warten, bis die IDE gestartet, kompiliert, das Programm ausgeführt usw. ist, wirkt sich dies auf die Programmiergeschwindigkeit aus. Es sei denn, "die Programmiergeschwindigkeit" ist eigentlich nur auf "Geschwindigkeit beim Schreiben von Code" ausgerichtet.
Die Anzahl der Bibliotheken und des Frameworks in Visual Studio beansprucht auch die Computerkapazität. Ich bin nicht sicher, ob dies die Frage der "Speicherverwaltung" beantwortet, aber ich möchte nur darauf hinweisen, dass Visual Studio IDE beim Ausführen viele Ressourcen in Anspruch nehmen und somit die allgemeine "Programmiergeschwindigkeit" verlangsamen kann. Dies hat natürlich nichts mit der "Geschwindigkeit beim Schreiben von Code" zu tun.
Wie Sie vielleicht erraten haben, kenne ich mich mit C ++ nicht besonders gut aus, da ich es nur als Beispiel verwende. Mein Hauptthema ist die schwere IDE des Typs Visual Studio, die Computerressourcen verbraucht.
Wenn ich auf die Idee gekommen bin und die Frage zum Threadstart überhaupt nicht beantwortet habe, entschuldige mich für den langen Beitrag. Aber ich würde dem Threadstarter raten, die Frage klar zu stellen und genau zu fragen, was er / sie über "schneller" und "langsamer" wissen muss.
quelle