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.

4voto

MSN Punkte 51308

Einer der berühmtesten Schnipsel der Montage stammt aus Michael Abrashs Texturabbildungsschleife ( hier im Detail erläutert ):

add edx,[DeltaVFrac] ; add in dVFrac
sbb ebp,ebp ; store carry
mov [edi],al ; write pixel n
mov al,[esi] ; fetch pixel n+1
add ecx,ebx ; add in dUFrac
adc esi,[4*ebp + UVStepVCarry]; add in steps

Heutzutage drücken die meisten Compiler fortgeschrittene CPU-spezifische Befehle als Intrinsics aus, d.h. als Funktionen, die bis zum eigentlichen Befehl herunterkompiliert werden. MS Visual C++ unterstützt Intrinsics für MMX, SSE, SSE2, SSE3 und SSE4, so dass Sie sich weniger Gedanken darüber machen müssen, ob Sie zu Assembler wechseln müssen, um die Vorteile plattformspezifischer Anweisungen zu nutzen. Visual C++ kann mit der entsprechenden /ARCH-Einstellung auch die Vorteile der aktuellen Architektur nutzen, auf die Sie abzielen.

0 Stimmen

Noch besser ist, dass diese SSE-Intrinsics von Intel spezifiziert sind, so dass sie tatsächlich ziemlich portabel sind.

3voto

Wie wäre es mit Maschinencode zur Laufzeit erzeugen?

Mein Bruder hat einmal (um das Jahr 2000) einen extrem schnellen Echtzeit-Raytracer durch Generierung von Code zur Laufzeit realisiert. Ich kann mich nicht mehr an die Details erinnern, aber es gab eine Art Hauptmodul, das Objekte in einer Schleife durchlief und dann einen Maschinencode vorbereitete und ausführte, der für jedes Objekt spezifisch war.

Mit der Zeit wurde diese Methode jedoch durch neue Grafikhardware überholt und unbrauchbar.

Heute denke ich, dass möglicherweise einige Operationen auf Big Data (Millionen von Datensätzen) wie Pivot-Tabellen, Drilling, Berechnungen on-the-fly usw. mit dieser Methode optimiert werden könnten. Die Frage ist: Lohnt sich der Aufwand?

0 Stimmen

"Erstellen von Maschinencode zur Laufzeit": Das ist nur eine Methode der Auswertung. Sie ist auch bekannt als eine thunk . Die umfassende Verwendung von Thunks kann einige Methoden zur Verbesserung der Leistung erleichtern, aber das ist in der Regel nicht ihr Hauptziel, so wie man auch die objektorientierte Programmierung nicht speziell zur Lösung von Leistungsproblemen einsetzt.

1voto

PhiLho Punkte 39496

In Zeiten, in denen die Prozessorgeschwindigkeit in MHz gemessen wurde und die Bildschirmgröße unter 1 Megapixel lag, war ein bekannter Trick, um eine schnellere Anzeige zu erreichen, das Abrollen von Schleifen: Schreiboperationen für jede Abtastzeile des Bildschirms. Dadurch wurde der Overhead vermieden, der durch die Pflege eines Schleifenindex entsteht! In Verbindung mit der Erkennung der Bildschirmaktualisierung war dies sehr effektiv.
Das ist etwas, was ein C-Compiler nicht tun würde... (obwohl man oft zwischen der Optimierung für Geschwindigkeit oder für Größe wählen kann, ich nehme an, dass ersterer einige ähnliche Tricks verwendet).

Ich weiß, dass einige Leute gerne Windows-Anwendungen in Assembler schreiben. Sie behaupten, sie seien schneller (schwer zu beweisen) und kleiner (tatsächlich!).
Es macht zwar Spaß, aber es ist wahrscheinlich verschwendete Zeit (außer zum Lernen natürlich!), insbesondere für GUI-Operationen... Nun, vielleicht können einige Operationen, wie das Durchsuchen einer Zeichenkette in einer Datei, durch sorgfältig geschriebenen Assemblercode optimiert werden.

7 Stimmen

Das Abrollen von Schleifen ist bei modernen Compilern Standard.

1 Stimmen

Der aktuelle gcc rollt standardmäßig auf x86 nicht aus, außer bei profilgesteuerter Optimierung. Seine Schleifen führen oft zu Engpässen im Frontend, aber die meisten Schleifen laufen nicht oft genug, um die Kosten für die Codegröße des Abrollens wert zu sein. Und ohne PGO weiß gcc nicht, welche kleinen Schleifen heiß sind.

1 Stimmen

@PeterCordes - FWIW scheint es weder das eine noch das andere zu sein. clang noch gcc hier wirklich richtig: die nunca Unroll-Verhalten ist ein bisschen extrem: eine Menge einfacher Schleifen mit 1 oder 2 Anweisungen "Nutzlast" würde wirklich durch eine 2x oder 4x unroll geholfen werden. clang auf der anderen Seite ist in Ordnung diese Schleife vollständig abwickeln mit 2175 Iterationen in ~200 expliziten Anweisungen, was die Funktion wahrscheinlich auf fast 2K Bytes aufbläht. Im Allgemeinen scheinen die Compiler recht einfache Heuristiken für das Abrollen zu verwenden, die oft zu suboptimalen Ergebnissen führen.

1voto

Michael Borgwardt Punkte 334642

Dies ist sehr schwer zu beantworten, da die Frage sehr unspezifisch ist: Was genau ist ein "moderner Compiler"?

So ziemlich jede manuelle Assembler-Optimierung könnte theoretisch auch von einem Compiler durchgeführt werden - ob sie tatsächlich es kann nicht allgemein gesagt werden, sondern nur über eine bestimmte Version eines bestimmten Compilers. Viele erfordern wahrscheinlich so viel Aufwand, um festzustellen, ob sie in einem bestimmten Kontext ohne Nebenwirkungen angewandt werden können, dass sich die Compiler-Autoren nicht mit ihnen befassen.

0 Stimmen

Für die Zwecke dieser Frage ist ein "moderner" Compiler einfach "der beste verfügbare Compiler für diese Aufgabe". Fälle, in denen Assembler besser ist, nur weil ein schlechter Compiler gewählt wurde, sollten nicht zählen. Daher frage ich nach konkreten Beispielen: Sie benutzen den besten verfügbaren Compiler, aber asm ist immer noch besser.

0 Stimmen

Natürlich ist "am besten" subjektiv, aber es sollte zumindest keine so schlechte Wahl sein, dass jemand sagen kann: "Ja, aber wenn du einen solchen Compiler verwendest, würde er diese Optimierung selbst vornehmen."

0 Stimmen

Mit anderen Worten, geben Sie einfach ein konkretes Beispiel, bei dem Ihr Compiler nicht die Rosinen herauspickt, um sicherzustellen, dass Assembler gewinnt.

1voto

Mark Diaz Punkte 193

Ein historischer Rückblick.

Als ich noch viel jünger war (1970er Jahre), war Assembler meiner Erfahrung nach eher für die Größe des Codes wichtig als für die Geschwindigkeit des Codes.

Wenn ein Modul in einer höheren Sprache, sagen wir, 1300 Bytes Code umfasste, aber eine Assembler-Version des Moduls 300 Bytes, waren diese 1K Bytes sehr wichtig, wenn man versuchte, die Anwendung in 16K oder 32K Speicher unterzubringen.

Die Compiler waren zu dieser Zeit nicht besonders gut.

In altmodischem Fortran

X = (Y - Z)
IF (X .LT. 0) THEN
 ... do something
ENDIF

Der damalige Compiler hat einen SUBTRACT-Befehl und dann einen TEST-Befehl auf X angewendet. In Assembler würde man einfach den Bedingungscode (LT zero, zero, GT zero) nach der Subtraktion prüfen.

Bei modernen Systemen und Compilern ist das alles kein Problem.

Ich denke, dass es immer noch wichtig ist zu verstehen, was der Compiler tut. Wenn Sie in einer höheren Sprache programmieren, sollten Sie verstehen, was den Compiler daran hindert, eine Schleifenabwicklung durchzuführen.

Und mit Pipe-Lining und Vorausberechnung unter Einbeziehung von Konditionalen, wenn der Compiler einen "branch-likley" macht

Assembler wird immer noch benötigt, um Dinge zu tun, die eine höhere Sprache nicht erlaubt, wie das Lesen oder Schreiben in prozessorspezifische Register.

Aber im Großen und Ganzen wird es für den allgemeinen Programmierer nicht mehr benötigt, außer um ein grundlegendes Verständnis dafür zu haben, wie der Code kompiliert und ausgeführt werden könnte.

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