502 Stimmen

Wann ist Assembler schneller als C?

Einer der angegebenen Gründe für Assembler-Kenntnisse ist, dass man damit gelegentlich Code schreiben kann, der leistungsfähiger ist als der Code in einer höheren Sprache, insbesondere C. Ich habe aber auch schon oft gehört, dass, obwohl das nicht ganz falsch ist, die Fälle, in denen Assembler helfen kann eigentlich zur Generierung von leistungsfähigerem Code verwendet werden können, sind extrem selten und erfordern Expertenwissen und Erfahrung mit Assembler.

Diese Frage geht noch nicht einmal auf die Tatsache ein, dass Assembler-Anweisungen maschinenspezifisch und nicht portierbar sind, oder auf andere Aspekte von Assembler. Es gibt viele gute Gründe, Assembler zu kennen, abgesehen von diesem, natürlich, aber dies soll eine spezifische Frage sein, die nach Beispielen und Daten fragt, nicht ein ausgedehnter Diskurs über Assembler gegenüber höheren Sprachen.

Kann jemand etwas über spezifische Beispiele in welchen Fällen Assembler schneller ist als gut geschriebener C-Code mit einem modernen Compiler, und können Sie diese Behauptung mit Profilergebnissen belegen? Ich bin mir ziemlich sicher, dass es diese Fälle gibt, aber ich möchte wirklich genau wissen, wie esoterisch diese Fälle sind, da dies ein Punkt zu sein scheint, über den man streiten kann.

0 Stimmen

Und nun wäre eine weitere Frage angebracht: Wann ist die Tatsache, dass Assembler schneller ist als C, tatsächlich von Bedeutung?

20 Stimmen

Eigentlich ist es recht trivial, kompilierten Code zu verbessern. Jeder, der über solide Kenntnisse in Assembler und C verfügt, kann dies erkennen, indem er den erzeugten Code untersucht. Eine einfache Möglichkeit ist die erste Leistungsklippe, von der man herunterfällt, wenn man in der kompilierten Version keine freien Register mehr hat. Im Durchschnitt wird der Compiler bei einem großen Projekt weitaus besser abschneiden als ein Mensch, aber es ist nicht schwer, bei einem Projekt von angemessener Größe Leistungsprobleme im kompilierten Code zu finden.

19 Stimmen

Die kurze Antwort lautet eigentlich: Assembler ist siempre Der Grund dafür ist, dass man Assembler ohne C haben kann, aber man kann C nicht ohne Assembler haben (in der binären Form, die wir früher "Maschinencode" nannten). Das heißt, die lange Antwort ist: C-Compiler sind ziemlich gut darin, zu optimieren und über Dinge "nachzudenken", an die man normalerweise nicht denkt, also hängt es wirklich von Ihren Fähigkeiten ab, aber normalerweise können Sie den C-Compiler immer schlagen; es ist immer noch nur eine Software, die nicht denken und Ideen bekommen kann. Sie können auch portablen Assembler schreiben, wenn Sie Makros verwenden und geduldig sind.

10voto

sharptooth Punkte 162790

Sie wissen nicht wirklich, ob Ihr gut geschriebener C-Code wirklich schnell ist, wenn Sie sich nicht die Disassemblierung des vom Compiler erzeugten C-Codes angesehen haben. Oft sieht man sich das an und stellt fest, dass "gut geschrieben" subjektiv ist.

Es ist also nicht notwendig, in Assembler zu schreiben, um den schnellsten Code überhaupt zu erhalten, aber es lohnt sich sicherlich, Assembler aus demselben Grund zu beherrschen.

3 Stimmen

"Es ist also nicht notwendig, in Assembler zu schreiben, um den schnellsten Code aller Zeiten zu erhalten" Nun, ich habe noch nie gesehen, dass ein Compiler in einem nicht trivialen Fall das Optimale tut. Ein erfahrener Mensch kann es in praktisch allen Fällen besser machen als der Compiler. Es ist also absolut notwendig, in Assembler zu schreiben, um "den schnellsten Code aller Zeiten" zu erhalten.

0 Stimmen

@cmaster Meiner Erfahrung nach ist die Compiler-Ausgabe zufällig. Manchmal ist es wirklich gut und optimal und manchmal ist es "wie konnte dieser Müll emittiert werden".

10voto

Dan Byström Punkte 8850

Enge Schleifen, wie beim Spielen mit Bildern, da ein Bild aus Millionen von Pixeln bestehen kann. Wenn man sich hinsetzt und herausfindet, wie man die begrenzte Anzahl von Prozessorregistern am besten nutzen kann, kann das einen Unterschied machen. Hier ist ein Beispiel aus der Praxis:

http://danbystrom.se/2008/12/22/optimizing-away-ii/

Dann haben Prozessoren oft einige esoterische Befehle, die für einen Compiler zu speziell sind, um sich damit zu beschäftigen, aber gelegentlich kann ein Assembler-Programmierer sie gut gebrauchen. Nehmen Sie zum Beispiel die XLAT-Anweisung. Wirklich großartig, wenn Sie Tabellen in einer Schleife nachschlagen müssen y die Tabelle ist auf 256 Bytes begrenzt!

Aktualisiert: Oh, da fällt mir gerade ein, was das Entscheidende ist, wenn wir von Schleifen im Allgemeinen sprechen: Der Compiler hat oft keine Ahnung, wie viele Iterationen der übliche Fall sein werden! Nur der Programmierer weiß, dass eine Schleife VIELE Male durchlaufen wird und dass es daher vorteilhaft ist, die Schleife mit etwas zusätzlicher Arbeit vorzubereiten, oder ob sie so wenige Male durchlaufen wird, dass der Aufbau tatsächlich länger dauert als die erwarteten Iterationen.

3 Stimmen

Die profilorientierte Optimierung gibt dem Compiler Informationen darüber, wie oft eine Schleife verwendet wird.

9voto

Maxim Masiutin Punkte 2895

Ich habe alle Antworten gelesen (mehr als 30) und habe keinen einfachen Grund gefunden: Assembler ist schneller als C, wenn man die Regeln gelesen und geübt hat. Handbuch zur Optimierung von Intel® 64- und IA-32-Architekturen , Der Grund, warum Assembler langsamer sein kann, ist also, dass die Leute, die solch langsames Assembler schreiben, das Optimierungshandbuch nicht gelesen haben. .

In den guten alten Zeiten des Intel 80286 wurde jeder Befehl mit einer festen Anzahl von CPU-Zyklen ausgeführt. Doch seit dem Pentium Pro, der 1995 auf den Markt kam, sind die Intel-Prozessoren superskalar und nutzen Complex Pipelining: Out-of-Order Execution & Register Renaming. Davor, beim Pentium, der 1993 hergestellt wurde, gab es U- und V-Pipelines. Mit dem Pentium wurden daher duale Pipelines eingeführt, die zwei einfache Befehle in einem Taktzyklus ausführen konnten, sofern sie nicht voneinander abhängig waren. Dies war jedoch nichts im Vergleich zur Out-of-Order Execution & Register Renaming, die mit dem Pentium Pro eingeführt wurde. Dieser mit dem Pentium Pro eingeführte Ansatz ist heute bei den meisten aktuellen Intel-Prozessoren praktisch identisch.

Lassen Sie mich die "Out-of-Order Execution" in wenigen Worten erklären. Der schnellste Code ist der, bei dem die Anweisungen nicht von vorherigen Ergebnissen abhängen, z.B. sollte man immer ganze Register löschen (durch movzx ), um die Abhängigkeit von früheren Werten der Register, mit denen Sie arbeiten, zu beseitigen, so dass sie intern von der CPU umbenannt werden können, um die parallele Ausführung von Befehlen oder eine andere Reihenfolge zu ermöglichen. Auf manchen Prozessoren können auch falsche Abhängigkeiten bestehen, die die Arbeit verlangsamen können, wie z.B. falsche Abhängigkeit von Pentium 4 für inc/dec Sie können also Folgendes verwenden add eax, 1 stattdessen oder inc eax um die Abhängigkeit vom vorherigen Zustand der Flaggen aufzuheben.

Wenn es Ihre Zeit erlaubt, können Sie mehr über Out-of-Order Execution & Register Renaming lesen. Im Internet sind zahlreiche Informationen verfügbar.

Es gibt noch viele andere wichtige Aspekte wie die Verzweigungsvorhersage, die Anzahl der Lade- und Speichereinheiten, die Anzahl der Gatter, die Mikro-OPs ausführen, Speicher-Cache-Kohärenzprotokolle usw., aber das Entscheidende ist die Out-of-Order-Ausführung. Die meisten Menschen sind sich der Out-of-Order Execution einfach nicht bewusst. Daher schreiben sie ihre Assembler-Programme wie für den 80286 und gehen davon aus, dass ihre Befehle unabhängig vom Kontext eine bestimmte Zeit für die Ausführung benötigen. Gleichzeitig sind sich C-Compiler der Out-of-Order Execution bewusst und generieren den Code korrekt. Deshalb ist der Code solcher uninformierten Leute langsamer, aber wenn Sie sich damit auskennen, wird Ihr Code schneller sein.

Neben der Out-of-Order Execution gibt es noch viele weitere Tipps und Tricks zur Optimierung. Lesen Sie einfach das oben erwähnte Optimierungshandbuch :-)

Allerdings hat die Assemblersprache ihre eigenen Nachteile, wenn es um die Optimierung geht. Laut Peter Cordes (siehe Kommentar unten) wären einige der Optimierungen, die Compiler vornehmen, für große Code-Basen in handgeschriebener Assembler-Sprache nicht mehr wartbar. Nehmen wir zum Beispiel an, Sie schreiben in Assembler. In diesem Fall müssen Sie eine Inline-Funktion (ein Assembler-Makro) beim Inlinen vollständig in eine Funktion ändern, die sie aufruft, wobei einige Argumente Konstanten sind. Gleichzeitig macht ein C-Compiler seine Arbeit sehr viel einfacher, indem er denselben Code auf unterschiedliche Weise in verschiedene Aufrufstellen einbindet. Die Möglichkeiten von Assembly-Makros sind begrenzt. Um also den gleichen Nutzen zu erzielen, müssten Sie dieselbe Logik an jeder Stelle manuell optimieren, um sie an die Konstanten und verfügbaren Register anzupassen, die Ihnen zur Verfügung stehen.

1 Stimmen

Es ist auch erwähnenswert, dass einige der Optimierungen, die die Compiler vornehmen, unwartbar für große Code-Basen in handgeschriebenem ASM. z.B. die vollständige Änderung einer Inline-Funktion (ASM-Makro) in eine Funktion, die sie mit einigen Args als Konstanten aufruft, was ihre Arbeit sehr viel einfacher macht. Und das Inlinen desselben Codes auf verschiedene Arten in verschiedene Aufrufseiten. Die Möglichkeiten von asm-Makros sind begrenzt. Um also den gleichen Nutzen zu erzielen, müssten Sie dieselbe Logik an jeder Stelle manuell optimieren, um die Konstanten und freien Register, die Sie haben, anzupassen.

8voto

Doug T. Punkte 61739

Ich denke, der allgemeine Fall, in dem Assembler schneller ist, ist, wenn ein kluger Assembler-Programmierer sich die Ausgabe des Compilers ansieht und sagt: "Das ist ein kritischer Pfad für die Leistung, und ich kann das so schreiben, dass es effizienter ist", und dann optimiert diese Person den Assembler oder schreibt ihn von Grund auf neu.

7voto

James Brooks Punkte 3823

Es könnte sich lohnen, einen Blick auf Optimierung von Unveränderlichkeit und Reinheit durch Walter Bright Es handelt sich nicht um einen profilierten Test, aber er zeigt ein gutes Beispiel für den Unterschied zwischen handgeschriebenem und vom Compiler generiertem ASM. Walter Bright schreibt optimierende Compiler, so dass es sich lohnen könnte, einen Blick auf seine anderen Blogbeiträge zu werfen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X