2010-01-11 45 views
27

考虑以下几点:C++编译器是否将通过const引用POD参数传递给pass by copy?

struct Point {double x; double y;}; 

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

做的编译器优化传递通过引用到通通过复制,防止频繁的反引用?换句话说转换complexComputation这个:

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 
} 

由于点是一个POD,也可以通过使呼叫者的背后,请右键没有副作用?

如果是这样的话,那么我可以永远只是const引用传递POD对象,不管多么小,而不必担心最佳传递机制。对?

编辑: 我很感兴趣,特别是GCC编译器。我想我可能不得不编写一些测试代码并查看ASM。

+0

我试过寻找这个问题,但我不断地想出关于传递值,传递引用等的abc的点击。如果已经讨论过,我很抱歉。 – 2010-01-11 18:34:50

+3

通常,相反可能是更好的方法(http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/)。按价值传递,并让编译器将其转为通过引用时,它想要 – jalf 2010-01-11 18:43:51

+0

+1好问题 – 2010-01-11 18:44:30

回答

4

我不能代表所有的编译器说话,但一般的答案是否定的。它不会做这种优化。

请参阅GOTW#81了解如何在C++中转换为const不会影响某些人可能认为的优化。如果需要,可以

7

你的编译器绝对提升点成员变量到寄存器。然而,这与编译器将函数调用本身转换为按值传递不同。

您应检查生成的汇编看到正在做什么优化。

FWIW,我使用的一般规则是通过值引用所有的主要类型和所有的类/ UDT(POD或不),我可以通过const引用,并让编译器找出最好的办法。我们不应该担心编译器正在做什么的细节,它比我们更聪明。

+2

我同意不担心,除非基准测试/分析告诉我们。但我只是好奇编译器是否确实可以做这种类型的优化。 – 2010-01-11 19:01:11

5

有2个问题。

首先,编译器将不转换通过逐REF通过按值,特别是如果complexComputation不是static(即可以通过外部对象使用)。

原因是API的相容性。对于CPU来说,不存在“参考”这样的东西。编译器会将引用转换为指针。参数被传递堆叠或通过寄存器,因此调用complexComputation一个代码可能会被称为(假定double是长度为4的片刻):

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 

只有8个字节被压​​入堆栈。

路过的副本,而另一方面,将推动整个结构压入堆栈,所以汇编代码看起来像

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 

注意,这一次16个字节被压​​入堆栈,内容是数字,而不是指针。如果complexComputation更改其参数传递语义,输入将​​变成垃圾并且您的程序可能会崩溃。


在另一方面,优化

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 
} 

可以轻松完成,因为编译器可以识别被经常使用哪些变量和 它们存储到保留的寄存器(例如R4〜R13中ARM体系结构和许多sXX/dXX寄存器),以便更快地访问。毕竟,如果你想知道一个编译器是否做了某事,你总是可以反汇编生成的对象并进行比较。

相关问题