Longpoke, es gibt nur eine Einschränkung: Zeit. Wenn Sie nicht die Ressourcen haben, um jede einzelne Änderung am Code zu optimieren und Ihre Zeit mit der Zuweisung von Registern, der Optimierung von wenigen Überläufen und so weiter zu verbringen, wird der Compiler jedes Mal gewinnen. Man nimmt seine Änderungen am Code vor, kompiliert neu und misst. Wenn nötig, wiederholen Sie das.
Außerdem kann man auf der höheren Ebene viel tun. Außerdem kann die Inspektion der resultierenden Baugruppe den Eindruck erwecken, dass der Code Mist ist, aber in der Praxis wird er schneller laufen, als Sie denken, dass er schneller ist. Beispiel:
int y = data[i]; // hier einige Dinge tun.. call_function(y, ...);
Der Compiler liest die Daten, schiebt sie auf den Stack (spill) und liest später vom Stack und gibt sie als Argument weiter. Klingt beschissen? Es könnte tatsächlich ein sehr effektiver Latenzausgleich sein und zu einer schnelleren Laufzeit führen.
// optimierte Version call_function(data[i], ...); // doch nicht so optimiert..
Die Idee bei der optimierten Version war, dass wir den Registerdruck reduziert haben und ein Überlaufen vermeiden. Aber in Wahrheit war die "beschissene" Version schneller!
Wenn man sich den Assemblercode ansieht und nur die Anweisungen betrachtet und daraus schließt: mehr Anweisungen, langsamer, wäre das eine Fehleinschätzung.
Dabei gilt es zu beachten: Viele Montageexperten piense en sie wissen viel, aber sehr wenig. Die Regeln ändern sich auch von einer Architektur zur nächsten. So gibt es zum Beispiel keinen x86-Code, der immer der schnellste ist. Heutzutage ist es besser, sich an Faustregeln zu halten:
- der Speicher ist langsam
- Der Cache ist schnell
- versuchen, den Cache besser zu nutzen
- Wie oft werden Sie fehlschlagen? Haben Sie eine Strategie zum Ausgleich von Latenzzeiten?
- Sie können 10-100 ALU/FPU/SSE-Befehle für einen einzigen Cache-Miss ausführen
- Anwendungsarchitektur ist wichtig..
- aber es hilft nicht, wenn das Problem nicht in der Architektur liegt
Außerdem ist es reines Wunschdenken, wenn man sich zu sehr darauf verlässt, dass ein Compiler schlecht durchdachten C/C++-Code auf magische Weise in "theoretisch optimalen" Code verwandelt. Sie müssen den Compiler und die Toolkette, die Sie verwenden, kennen, wenn Sie sich für "Leistung" auf dieser niedrigen Ebene interessieren.
Compiler in C/C++ sind im Allgemeinen nicht sehr gut darin, Unterausdrücke neu zu ordnen, weil die Funktionen zum Beispiel Seiteneffekte haben. Funktionale Sprachen haben diesen Nachteil nicht, aber sie passen nicht so gut in das aktuelle Ökosystem. Es gibt Compiler-Optionen, die eine Lockerung der Präzisionsregeln ermöglichen, so dass die Reihenfolge der Operationen durch den Compiler/Linker/Codegenerator geändert werden kann.
Dieses Thema ist eine kleine Sackgasse; für die meisten ist es nicht relevant, und die anderen wissen ohnehin schon, was sie tun.
Es läuft alles auf Folgendes hinaus: "Zu verstehen, was man tut", ist etwas anderes als zu wissen, was man tut.
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.
13 Stimmen
Ich bin ganz und gar nicht der Meinung, dass Antworten auf diese Frage "meinungsbasiert" sein müssen - sie können durchaus objektiv sein - es ist nicht so, als würde man versuchen, die Leistung der Lieblingssprachen zu vergleichen, für die jede von ihnen Stärken und Schwächen hat. Hier geht es darum, zu verstehen, wie weit uns Compiler bringen können und ab welchem Punkt es besser ist, sie zu übernehmen.
0 Stimmen
Es ist nicht einmal immer der Fall, dass Sie etwas in Assembler umschreiben müssen, um die Vorteile der Assembler-Kenntnisse zu nutzen. Wenn Sie Ihren C-Algorithmus einfach in verschiedenen Formen neu kompilieren und die vom Compiler erzeugte Assemblerdatei beobachten, können Sie effizienteren Code in C schreiben.
0 Stimmen
Ein esoterisches Beispiel: Suchen Sie im Internet nach
pclmulqdq crc
. pclmulqdq ist eine spezielle Assembler-Anweisung. Die optimierten Beispiele benötigen etwa 500 Zeilen Assemblercode. Einige X86 haben auch einencrc32c
Anweisung für einen bestimmten Fall von crc32. Benchmark-Ergebnisse für die Erzeugung von crc32 über ein 256MB (256*1024*1024) Byte-Array: c-Code mit Tabelle => 0,516749 sec, Assembler mit pcmuldq => 0,0783919 sec, c-Code mit crc32 intrinsic => 0,0541801 sec.30 Stimmen
Zu Beginn meiner beruflichen Laufbahn habe ich in einer Softwarefirma viel in C und Mainframe-Assembler geschrieben. Einer meiner Kollegen war, wie ich es nennen würde, ein "Assembler-Purist" (alles musste in Assembler sein), also wettete ich mit ihm, dass ich eine bestimmte Routine schreiben könnte, die in C schneller lief als das, was er in Assembler schreiben konnte. Ich habe gewonnen. Nachdem ich gewonnen hatte, sagte ich ihm, ich wolle eine zweite Wette abschließen - dass ich etwas in Assembler schneller schreiben könne als das C-Programm, das ihn bei der ersten Wette geschlagen hatte. Auch diese Wette habe ich gewonnen, was beweist, dass es mehr auf die Fähigkeiten des Programmierers ankommt als auf alles andere.
0 Stimmen
@ValerieR Nun, Sie haben auch bewiesen, dass Ihr Assemblerprogramm schneller war als Ihr C-Programm :-) Vielleicht könnte man sagen, dass Sie unabhängig von Ihrem Kenntnisstand in der C-Programmierung wahrscheinlich ein Assembler-Programm schreiben können, das schneller ist?
1 Stimmen
@RobertF: Wir lassen bei diesen Fragen oft den Teil "zu welchem Preis" weg. Ich kann schnelles C oder Assembler schreiben - manchmal ist das C billiger zu schreiben, und manchmal ist der Assembler billiger zu schreiben. Geschwindigkeit kommt oft von zwei Seiten: bessere Algorithmen oder Ausnutzung der Low-Level-Infrastruktur - Quicksort in C wird typischerweise schneller sein als Bubble Sort in Assembler. Aber wenn Sie identische Logik in beiden implementieren, bietet Ihnen der Assembler normalerweise Möglichkeiten, die Maschinenarchitektur besser auszunutzen als der Compiler - der Compiler ist universell einsetzbar, und Sie erstellen eine spezifische Anpassung für einen einzigen Anwendungsfall.
0 Stimmen
Ich habe mehrere M68000-Ersetzungen für C-Funktionen (memset, snprintf usw.) geschrieben, die erheblich schneller (und immer noch sicherer) als ihre C-Pendants sind. Ich habe auch Testcode geschrieben, um zu überprüfen, dass es a) funktioniert und b) tatsächlich schneller ist.