我们说我们有两个功能:
int f();
int g();
我想得到f()和g()的总和。
第一种方式:
int fRes = f();
int gRes = g();
int sum = fRes + gRes;
第二种方式:
int sum = f() + g();
在这两种情况下,性能会有什么不同吗?
复杂类型而不是整数的相同问题
修改
我是否理解正确在这种情况下我不应该担心性能(在每种情况下包括经常执行的任务)并使用临时变量来提高可读性并简化代码?
答案 0 :(得分:9)
您可以通过编译汇编语言(当然还有优化)并检查输出来自己回答这样的问题。如果我把你的榜样变成一个完整的,可编辑的程序......
extern int f();
extern int g();
int direct()
{
return f() + g();
}
int indirect()
{
int F = f();
int G = g();
return F + G;
}
并编译它(g++ -S -O2 -fomit-frame-pointer -fno-exceptions test.cc
;最后两个开关消除了输出中的一堆干扰),我得到了这个(进一步分散了注意力分散):
__Z8indirectv:
pushq %rbx
call __Z1fv
movl %eax, %ebx
call __Z1gv
addl %ebx, %eax
popq %rbx
ret
__Z6directv:
pushq %rbx
call __Z1fv
movl %eax, %ebx
call __Z1gv
addl %ebx, %eax
popq %rbx
ret
正如您所看到的,为这两个函数生成的代码是相同的,因此您的问题的答案是否定的,没有性能差异。现在让我们看一下复杂的数字 - 相同的代码,但始终是s/int/std::complex<double>/g
,顶部是#include <complex>
;相同的编译开关 -
__Z8indirectv:
subq $72, %rsp
call __Z1fv
movsd %xmm0, (%rsp)
movsd %xmm1, 8(%rsp)
movq (%rsp), %rax
movq %rax, 48(%rsp)
movq 8(%rsp), %rax
movq %rax, 56(%rsp)
call __Z1gv
movsd %xmm0, (%rsp)
movsd %xmm1, 8(%rsp)
movq (%rsp), %rax
movq %rax, 32(%rsp)
movq 8(%rsp), %rax
movq %rax, 40(%rsp)
movsd 48(%rsp), %xmm0
addsd 32(%rsp), %xmm0
movsd 56(%rsp), %xmm1
addsd 40(%rsp), %xmm1
addq $72, %rsp
ret
__Z6directv:
subq $72, %rsp
call __Z1gv
movsd %xmm0, (%rsp)
movsd %xmm1, 8(%rsp)
movq (%rsp), %rax
movq %rax, 32(%rsp)
movq 8(%rsp), %rax
movq %rax, 40(%rsp)
call __Z1fv
movsd %xmm0, (%rsp)
movsd %xmm1, 8(%rsp)
movq (%rsp), %rax
movq %rax, 48(%rsp)
movq 8(%rsp), %rax
movq %rax, 56(%rsp)
movsd 48(%rsp), %xmm0
addsd 32(%rsp), %xmm0
movsd 56(%rsp), %xmm1
addsd 40(%rsp), %xmm1
addq $72, %rsp
ret
这是更多的指令,并且编译器没有做一个完美的优化工作,看起来像,但尽管如此,为这两个函数生成的代码是相同的。
答案 1 :(得分:4)
我认为在第二种方式中,当函数返回一个值时,它被分配给一个临时变量。但是,当您需要多次使用f()
和g()
中的值时,将它们存储到变量而不是每次重新计算时都会有所帮助。
答案 2 :(得分:2)
如果您关闭了优化,可能会有。如果您打开它,它们可能会产生相同的代码。特别是您将fRes
和gRes
标记为const
。
因为它是legal for the compiler to elide the call to the copy constructor if fRes
and gRes
are complex types,所以复杂类型的性能也不会有所不同。
有人提到不止一次使用fRes
和gRes
。当然,这显然可能不那么理想,因为您不得不多次拨打f()
或g()
。
答案 3 :(得分:2)
正如你所写的那样,只有一个微妙的区别(另一个答案解决了,一个与另一个有一个序列点)。
如果您这样做了将不同:
int fRes;
int gRes;
fRes = f();
fRes = g();
int sum = fRes + gRes;
(想象int
实际上是其他类型的非平凡构造函数。)
在这种情况下,您可以调用默认构造函数,然后调用赋值运算符,这可能会更有效。
答案 4 :(得分:1)
完全取决于编译器执行的优化。两者可以编译为略有不同或完全相同的字节码。即使略有不同,也无法衡量这些特定样品在时间和空间成本方面的统计显着差异。
答案 5 :(得分:1)
在我的平台上启用了完全优化,一个函数将sum
从两个不同的案例中返回到完全相同的机器代码。
两个示例之间唯一的微小差别是第一个保证调用f()
和g()
的顺序,因此理论上第二个允许编译器稍微灵活一些。这是否会产生影响取决于f()
和g()
实际上做了什么,也许还有可能是内联的。
答案 6 :(得分:0)
两个例子之间略有不同。在表达式f() + g()
中没有序列点,而当在不同的语句中进行调用时,在每个语句的末尾都有序列点。
缺少序列点意味着调用这两个函数的顺序是未指定的,可以按任何顺序调用它们,这可能有助于编译器优化它。