GCC-Optimierungstrick, funktioniert das wirklich?

8

Bei der Betrachtung einiger Optimierungsfragen sollten Sie akzeptierte Antwort für die Frage nach Programmierpraktiken für den effektivsten Einsatz des Optimierers piked meine Neugier. Die Behauptung ist, dass lokale Variablen für Berechnungen in einer Funktion verwendet werden sollten, nicht für die Ausgabe von Argumenten. Es wurde vorgeschlagen, dass dies dem Compiler ermöglichen würde, zusätzliche Optimierungen zu machen, die sonst nicht möglich wären.

Also, ein einfaches Codebeispiel für die Beispiel-Foo-Klasse zu schreiben und die Codefragmente mit g ++ v4.4 und -O2 zu kompilieren gab einige Assembler-Ausgaben (verwenden Sie -S). Die Teile des Assemblers werden nur mit dem unten gezeigten Schleifenabschnitt aufgelistet. Bei Betrachtung der Ausgabe scheint die Schleife für beide nahezu identisch zu sein, mit nur einem Unterschied in einer Adresse. Diese Adresse ist ein Zeiger auf das Ausgabeargument für das erste Beispiel oder die lokale Variable für das zweite Beispiel.

Es scheint keine Änderung in der tatsächlichen Wirkung zu geben, ob die lokale Variable verwendet wird oder nicht. Also die Frage gliedert sich in 3 Teile:

a) ist GCC nicht führt zusätzliche Optimierung durch, selbst wenn der Hinweis vorgeschlagen wird;

b) ist GCC erfolgreich Optimierung in beiden Fällen, sollte aber nicht sein;

c) ist GCC erfolgreich in beiden Fällen zu optimieren, und produziert konforme Ausgabe wie durch den C ++ - Standard definiert?

Hier ist die nicht optimierte Funktion:

%Vor%

Und entsprechende Montage:

%Vor%

Hier ist die neu geschriebene Funktion:

%Vor%

Und hier ist die Compiler-Ausgabe für die Funktion mit einer lokalen Variablen:

%Vor%     
casualcoder 14.10.2011, 00:03
quelle

2 Antworten

26

Das in dieser Antwort angegebene Beispiel war nicht sehr gut, weil der Aufruf einer unbekannten Funktion, über die der Compiler nicht viel Rechenschaft ablegen kann. Hier ist ein besseres Beispiel:

%Vor%

Die erste Version wird schlecht optimiert, da sie vor der Möglichkeit schützen muss, dass jemand sie als

bezeichnet %Vor%

ergibt {1, 1, 0, 1, 1, 1, 0, 0, 0, 0 } , da die Iteration mit i=1 den Parameter startIndex ändert.

Der zweite muss sich keine Sorgen machen über die Möglichkeit, dass array[localIndex + i] = 1 localIndex ändert, weil localIndex eine lokale Variable ist, deren Adresse noch nie vergeben wurde.

In Assembly (Intel Notation, weil ich das verwende):

%Vor%

HINZUGEFÜGT: Hier ist ein Beispiel, wo die Einsicht des Compilers in Bar ist und nicht in munge:

%Vor%

Der resultierende Code ist

%Vor%

Beachten Sie, dass die innere Schleife in Adjust10A den Wert neu berechnen muss, da sie vor der Möglichkeit schützen muss, dass foo.munge bar geändert hat.

Das heißt, diese Art der Optimierung ist kein Slam Dunk. (Zum Beispiel hätten wir den gleichen Effekt erzielen können, indem wir bar.getValue() in localValue manuell zwischenspeichern.) Dies ist für vektorisierte Operationen am hilfreichsten, da diese parallelisiert werden können.

    
Raymond Chen 14.10.2011, 01:02
quelle
2

Zuerst gehe ich davon aus, dass munge() nicht inline sein kann - das heißt, seine Definition ist nicht in der gleichen Übersetzungseinheit; Sie haben keine vollständige Quelle angegeben, daher kann ich mir nicht ganz sicher sein, aber es würde diese Ergebnisse erklären.

Da foo1 als Referenz an munge übergeben wird, übergibt der Compiler auf der Implementierungsebene nur einen Zeiger. Wenn wir nur uns darauf, unser Argument, das ist schön und schnell - alle Aliasing Probleme sind munge() 's Problem - und sein müssen, da munge() kann nichts über ihre Argumente übernehmen, und wir können nichts davon ausgehen, über Was munge() könnte mit ihnen tun (da die Definition von munge() nicht verfügbar ist).

Wenn wir jedoch in eine lokale Variable kopieren, müssen wir in eine lokale Variable kopieren und einen Zeiger auf die lokale Variable übergeben. Das liegt daran, dass munge() einen Unterschied im Verhalten beobachten kann - wenn es den Zeiger auf sein erstes Argument nimmt, kann es sehen, dass es nicht gleich &foo1 ist. Da die Implementierung von munge() nicht im Bereich ist, kann der Compiler nicht davon ausgehen, dass dies nicht der Fall ist.

Diese lokale Variablen-Kopie hier Trick so pessimizing endet, anstatt zu optimieren - die Optimierungen versucht es möglich zu helfen, nicht, weil munge() nicht inlined werden kann; Aus dem gleichen Grund verletzt die lokale Variable aktiv die Leistung.

Es wäre lehrreich, dies noch einmal zu versuchen, indem Sie sicherstellen, dass munge() nicht virtuell und als inlinable-Funktion verfügbar ist.

    
bdonlan 14.10.2011 00:55
quelle

Tags und Links