【问题标题】:temporary variables and performance in c++ [duplicate]C ++中的临时变量和性能[重复]
【发布时间】:2011-02-21 22:17:02
【问题描述】:

假设我们有两个函数:

int f();
int g();

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

第一种方式:

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

第二种方式:

int sum = f() + g();

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

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

编辑

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

【问题讨论】:

  • 错误的问题。问题应该是“我应该担心这里的性能吗”,答案是 NO WAI。

标签: c++


【解决方案1】:

您可以通过编译为汇编语言(当然要进行优化)并检查输出来自己回答此类问题。如果我将您的示例充实为一个完整的可编译程序...

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 获取实际证据。请务必注意,编译器通常会了解您正在做的事情的要点,并会产生相同的结果,从而使您的代码更具可读性。
  • 虽然不能证明生成的代码在两种情况下必须相同。
  • 嗯,不,没有保证生成的代码将是相同的。如果您使用 gcc 的 -O0 而不是 -O2 我希望看到差异。但是为这些生成等效代码是本书中最基本的优化之一(通常称为“前向传播”),而现代编译器不这样做,当优化开启时,不适合目的。
【解决方案2】:

我认为第二种方式是在函数返回值时将其分配给临时变量。但是,当您需要多次使用来自 f()g() 的值时,将它们存储到一个变量而不是每次都重新计算它们会有所帮助。

【讨论】:

    【解决方案3】:

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

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

    有人提到不止一次使用fResgRes。当然,这显然不是最优的,因为您必须多次调用 f()g()

    【讨论】:

      【解决方案4】:

      正如您所写,只有细微的差别(另一个答案解决了这个问题,一个与另一个之间有一个序列点)。

      如果您改为这样做,它们有所不同:

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

      (将int 想象成实际上是其他具有非平凡默认构造函数的类型。)

      在这种情况下,您调用默认构造函数,然后调用赋值运算符,这可能需要更多工作。

      【讨论】:

        【解决方案5】:

        这完全取决于编译器执行的优化。两者可以编译为略有不同或完全相同的字节码。即使略有不同,您也无法衡量这些特定样本在时间和空间成本方面的统计显着差异。

        【讨论】:

          【解决方案6】:

          在我的平台上开启了全面优化,一个从两种不同情况返回 sum 的函数编译为完全相同的机器代码。

          两个示例之间唯一的细微差别是第一个保证了调用f()g() 的顺序,因此理论上第二个允许编译器稍微灵活一些。这是否会产生影响将取决于 f()g() 实际做了什么,也许还取决于它们是否可以内联。

          【讨论】:

            【解决方案7】:

            这两个示例之间存在细微差别。在表达式f() + g() 中没有序列点,而在不同语句中进行调用时,每个语句的末尾都有序列点。

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2013-01-12
              • 2015-08-12
              • 2015-06-04
              • 1970-01-01
              • 1970-01-01
              • 2016-02-27
              • 2011-08-07
              • 1970-01-01
              相关资源
              最近更新 更多