c ++中的临时变量和性能

时间:2011-02-21 22:17:02

标签: c++

我们说我们有两个功能:

int f();
int g();

我想得到f()和g()的总和。

第一种方式:

int fRes = f();
int gRes = g();
int sum = fRes + gRes;

第二种方式:

int sum = f() + g();

在这两种情况下,性能会有什么不同吗?

复杂类型而不是整数的相同问题

修改

我是否理解正确在这种情况下我不应该担心性能(在每种情况下包括经常执行的任务)并使用临时变量来提高可读性并简化代码?

7 个答案:

答案 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)

如果您关闭了优化,可能会有。如果您打开它,它们可能会产生相同的代码。特别是您将fResgRes标记为const

因为它是legal for the compiler to elide the call to the copy constructor if fRes and gRes are complex types,所以复杂类型的性能也不会有所不同。

有人提到不止一次使用fResgRes。当然,这显然可能不那么理想,因为您不得不多次拨打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()中没有序列点,而当在不同的语句中进行调用时,在每个语句的末尾都有序列点。

缺少序列点意味着调用这两个函数的顺序是未指定的,可以按任何顺序调用它们,这可能有助于编译器优化它。