请考虑以下事项:
struct Point {double x; double y;};
double complexComputation(const& Point p1, const Point& p2)
{
// p1 and p2 used frequently in computations
}
编译器是否会将pass-by-reference优化为pass-by-copy以防止频繁解除引用?换句话说,将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
}
由于Point是一个POD,通过在调用者后面复制一个副本没有副作用,对吗?
如果是这种情况,那么我总是可以通过const引用传递POD对象,无论多小,都不必担心最佳传递语义。正确?
编辑: 我特别感兴趣的是GCC编译器。我想我可能要编写一些测试代码并查看ASM。
答案 0 :(得分:7)
如果需要,您的编译器可以绝对将Point成员变量提升到寄存器中。但是,这与编译器将函数调用本身转换为按值传递不同。
您应该检查生成的程序集以查看正在进行的优化。
而FWIW,我使用的一般规则是在可能的情况下通过const引用按值和所有类/ UDT(POD或不是POD)传递所有原始类型,并让编译器整理出最好的事情。我们不应该担心编译器正在做什么的细节,它比我们聪明得多。
答案 1 :(得分:5)
有两个问题。
首先,编译器将不将pass-by-ref转换为pass-by-value,尤其是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
}
可以轻松完成,因为编译器可以识别经常使用的变量 将它们存储到保留寄存器(例如ARM体系结构中的r4~r13,以及许多sXX / dXX寄存器)以便更快地访问。
毕竟,如果你想知道编译器是否做了什么,你总是可以反汇编结果对象并进行比较。
答案 2 :(得分:4)
我不能代表每个编译器,但一般答案是否。它不会进行优化。
请参阅GOTW#81,了解C ++中对const
的转换如何不会像某些人想的那样影响优化。