2010-01-11 4 views
27

Betrachten Sie die folgenden:Do C++ - Compiler optimieren Pass by Const Referenz POD-Parameter in Kopie weitergeben?

struct Point {double x; double y;}; 

double complexComputation(const& Point p1, const Point& p2) 
{ 
    // p1 and p2 used frequently in computations 
} 

Sie Compiler den Pass-by-reference in Pass-by-Kopie optimieren häufige dereferenzierenden zu verhindern? Mit anderen Worten complexComputation in diesen umwandeln:

double complexComputation(const& Point p1, const Point& p2) 
{ 
    double x1 = p1.x; double x2 = p2.x; 
    double y1 = p1.y; double y2 = p2.y; 
    // x1, x2, y1, y2 stored in registers and used frequently in computations 
} 

Seit Point ist ein POD kann es, indem Sie eine Kopie hinter dem Anrufer zurück, rechts keine Nebenwirkung sein?

Wenn das der Fall ist, dann kann ich POD-Objekte immer durch const-Verweis übergeben, egal wie klein, und muss nicht um die optimale Weitergabe Semantik kümmern. Recht?

EDIT: Ich interessiere mich speziell für den GCC-Compiler. Ich denke, ich muss vielleicht einen Testcode schreiben und mir den ASM ansehen.

+0

Ich habe versucht, nach dieser Frage zu suchen, aber ich kam immer wieder mit Hits über die Abc's von Vorbeifahr-Wert, Pass-by-Reference, etc. Es tut mir leid, wenn es bereits diskutiert wurde. –

+3

Normalerweise ist das Gegenteil ein besserer Ansatz (http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/). Übergeben Sie nach Wert, und lassen Sie den Compiler es in Referenz umwandeln, wenn es will – jalf

+0

+1 Gute Frage –

Antwort

4

Ich kann nicht für jeden Compiler sprechen, aber die allgemeine Antwort ist keine. Es wird diese Optimierung nicht machen.

Siehe GOTW#81, um zu lesen, wie Casting zu const in C++ die Optimierung nicht beeinflusst, wie einige vielleicht denken.

7

Ihr Compiler kann absolut Lift Punkt Mitglied Variablen in Registern, wenn nötig. Dies ist jedoch nicht dasselbe wie der Compiler, der den Funktionsaufruf selbst in Wert übergeben umwandelt.

Sie sollten die generierte Assembly überprüfen, um zu sehen, welche Optimierungen vorgenommen werden. Die allgemeine Regel, die ich benutze, besteht darin, alle primativen Typen nach Wert und allen Klassen/UDTs (PODs oder nicht) durch const-Referenz zu übergeben, wenn ich kann, und den Compiler die beste Sache erledigen zu lassen. Wir sollten uns nicht mit den Details dessen beschäftigen, was der Compiler macht, es ist viel schlauer als wir.

+2

Ich stimme nicht besorgniserregend, es sei denn, Benchmarking/Profiling sagt uns zu. Aber ich war nur neugierig, ob ein Compiler tatsächlich diese Art von Optimierung durchführen kann. –

5

Es gibt 2 Probleme.

Erstens wird der Compiler nicht convert Pass-by-ref passieren-by-Wert, insbesondere wenn nicht complexComputationstatic ist (d kann durch externe Objekte verwendet werden).

Der Grund ist API-Kompatibilität. Für die CPU gibt es keine "Referenz". Der Compiler konvertiert Verweise auf Zeiger. Die Parameter werden auf dem Stack oder über das Register übergeben, so dass ein Code complexComputation Aufruf wird wahrscheinlich wie genannt wird (double nehmen ist die Länge 4 für einen Moment):

str x1, [r7, #0x20] 
str y1, [r7, #0x24] 
str x2, [r7, #0x50] 
str y2, [r7, #0x54] 
push r7, #0x20  ; push address of p1 onto the stack 
push r7, #0x50  ; push address of p2 onto the stack 
call complexComputation 

Nur 8 Bytes auf den Stapel geschoben werden.

Pass von Kopie, auf der anderen Seite, wird die ganze Struktur auf den Stapel schieben, so wird der Assembler-Code aussieht

push x1 ; push a copy of p1.x onto the stack 
push y1 ; push a copy of p1.y onto the stack 
push x2 ; push a copy of p2.x onto the stack 
push y2 ; push a copy of p2.y onto the stack 
call complexComputation 

Beachten Sie, dass diese Zeit 16 Bytes auf den Stapel geschoben werden, und der Inhalt sind die Zahlen, keine Zeiger. Wenn die ihre Parameterübergabe Semantik ändert, wird die Eingabe unbrauchbar und Ihr Programm kann abstürzen.


Auf der anderen Seite, die Optimierung

double complexComputation(const Point& p1, const Point& p2) { 
    double x1 = p1.x; double x2 = p2.x; 
    double y1 = p1.y; double y2 = p2.y; 
    // x1, x2, y1, y2 stored in registers and used frequently in computations 
} 

kann leicht gemacht werden, da der Compiler erkennen können, welche Variablen verwendet werden, sehr oft und speichern sie in reservierte Register (zB r4 ~ r13 in die ARM-Architektur und viele der sXX/dXX-Register) für einen schnelleren Zugriff.


Nach allem, wenn Sie wissen wollen, ob ein Compiler etwas getan hat, kann man immer die resultierenden Objekte zerlegen und vergleichen.