x + =比x = x + a快吗?

Chi*_*ffa 84 c++ performance operators

我正在阅读Stroustrup的"The C++ Programming Language",他说有两种方法可以向变量添加内容

x = x + a;
Run Code Online (Sandbox Code Playgroud)

x += a;
Run Code Online (Sandbox Code Playgroud)

他更喜欢,+=因为它最有可能更好地实施.我认为他的意思是它的工作速度也更快.
但它真的吗?如果它取决于编译器和其他东西,我该如何检查?

zwo*_*wol 210

任何编译器称职将产生正是为了既构建了相同的机器语言序列中的任何内置类型(int,float,等),只要该语句确实是非常简单x = x + a; 和优化启用.(值得注意的是,GCC -O0是默认模式,执行反优化,例如将完全不必要的存储插入内存,以确保调试器始终可以找到变量值.)

但是,如果声明更复杂,它们可能会有所不同.假设f是一个返回指针的函数

*f() += a;
Run Code Online (Sandbox Code Playgroud)

f只召唤一次,而

*f() = *f() + a;
Run Code Online (Sandbox Code Playgroud)

叫它两次.如果f有副作用,两者之一将是错误的(可能是后者).即使f没有副作用,编译器也可能无法消除第二次调用,因此后者可能确实更慢.

既然我们在这里讨论的是C++,那么重载operator+和类的类类型的情况就完全不同了operator+=.如果x是这样的类型,那么 - 在优化之前 - x += a转换为

x.operator+=(a);
Run Code Online (Sandbox Code Playgroud)

x = x + a翻译成

auto TEMP(x.operator+(a));
x.operator=(TEMP);
Run Code Online (Sandbox Code Playgroud)

现在,如果正确编写了类并且编译器的优化器足够好,那么它们最终都会生成相同的机器语言,但它不像内置类型那样可靠.这可能是Stroustrup鼓励使用时的想法+=.

  • 如果你发现自己写的是`*f()=*f()+ a;`你可能想要仔细看看你真正想要实现的目标...... (21认同)
  • 还有另一个方面--_readability_.将`expr`添加到`var`的C++习语是`var + = expr`,用另一种方式编写它会让读者感到困惑. (14认同)
  • @PiotrDobrogost:回答问题有什么问题?在任何情况下,应该是提问者检查重复. (7认同)
  • 在我看来@PiotrDobrogost就像你有点......嫉妒......如果你想四处寻找重复,那就去吧.举个例子,我更喜欢回答问题,而不是寻找愚蠢(除非这是我特别记得曾经见过的问题).它有时可能更快,而且ergo帮助那个更快地提出问题的人.另外,请注意,这甚至不是循环.`1`是常量,`a`可以是volatile,用户定义的类型,或者其他什么.完全不同.事实上,我不明白这是如何关闭的. (4认同)
  • 如果var = var + expr让你感到困惑,但var + = expr却没有,那么你就是我见过的最奇怪的软件工程师.要么是可读的; 只是确保你是一致的(我们都使用op =,所以它无论如何都是有道理的= P) (3认同)
  • @Gorpik关闭作为重复也给出了答案加上你没有多个问题要求相同,没有办法在它们之间轻松导航.至于谁应该检查重复; 你真的认为用287代表的用户应该比用24k代表的用户更了解网站的工作方式吗? (2认同)

Luc*_*ore 56

您可以通过查看dissasembly进行检查,这将是相同的.

对于基本类型,两者都同样快.

这是由调试版本生成的输出(即没有优化):

    a += x;
010813BC  mov         eax,dword ptr [a]  
010813BF  add         eax,dword ptr [x]  
010813C2  mov         dword ptr [a],eax  
    a = a + x;
010813C5  mov         eax,dword ptr [a]  
010813C8  add         eax,dword ptr [x]  
010813CB  mov         dword ptr [a],eax  
Run Code Online (Sandbox Code Playgroud)

对于用户定义的类型,在这里你可以过载operator +operator +=,这取决于它们各自的实现.

  • @James:如果你的程序对`++ x`和`temp = x + 1之间的性能差异敏感; x = temp;`,那么很可能它应该用汇编而不是c ++来编写...... (3认同)

Mar*_*ler 11

是! 在x可能有副作用的情况下,对于后者来说,写入更快,阅读更快,更快更好.所以它对人类来说总体上更快.人的时间一般比计算机时间花费的多,所以这一定是你所要求的.对?

  • 一个聪明的答案) (3认同)

Tom*_*ner 8

它实际上取决于x和a的类型以及+的实现.对于

   T x, a;
   ....
   x = x + a;
Run Code Online (Sandbox Code Playgroud)

编译器必须创建一个临时T来包含x + a的值,同时它对它进行求值,然后它可以分配给x.(在此操作期间,不能使用x或a作为工作空间).

对于x + = a,它不需要临时的.

对于琐碎的类型,没有区别.


Sag*_*ire 8

x = x + a和之间的差异x += a是机器必须经历的工作量 - 一些编译器可能(通常会)优化它,但通常,如果我们忽略优化一段时间,会发生的事情是在前一段代码片段中,机器必须查找x两次的值,而在后一种情况下,此查找只需要发生一次.

但是,正如我所提到的,今天大多数编译器都足够聪明,可以分析指令并减少所需的机器指令.

PS:Stack Overflow的第一个答案!


Ski*_*izz 6

正如你已经标注了这个C++,没有办法从你发布的两个语句中知道.你需要知道'x'是什么(它有点像答案'42').如果x是POD,那么它实际上并没有太大的区别.但是,如果x是一个类,则可能存在导致执行时间差异的不同行为的方法operator +operator +=方法的重载.


Joe*_*orn 6

你问的是错误的问题.

这不太可能推动应用程序或功能的性能.即使它是,但找出的方法是分析代码并知道它对你的影响.在清晰度,正确性和可读性方面考虑更重要的是,不要担心这个级别哪个更快.

当您考虑到这一点时尤其如此,即使这是一个重要的性能因素,编译器也会在一段时间内发展.有人可能会想出一个新的优化,今天的正确答案可能会在明天变得错误.这是过早优化的经典案例.

这并不是说性能根本不重要......只是这是实现你的性能目标的错误方法.正确的方法是使用分析工具来了解代码实际花费时间的位置,从而集中精力.

  • 正如其他人的答案(和赞成票)所示,OP的问题是完全合法的。虽然我们明白你的观点(首先介绍一下等等),但了解这类东西绝对是有趣的 - 你真的要介绍你写的每一个琐碎的陈述,介绍你做出的每个决定的结果吗?即使有人已经研究、剖析、拆解了这个案例并提供了一个一般性的答案? (2认同)

Mik*_*vey 6

如果你说+=你让编译器的生活变得更容易.为了使编译器能够识别出与编译器x = x+a相同的内容x += a,编译器必须这样做

  • 分析左侧(x)以确保它没有副作用并始终引用相同的l值.例如,它可能是z[i],它必须确保两个zi不改变.

  • 分析右手边(x+a)并确保它是一个求和,并且左手边在右边出现一次,即使它可以被转换,如同z[i] = a + *(z+2*0+i).

如果你的意思是添加ax,编译器作者赞赏它,当你刚刚说你的意思.那样的话,你没有运用编译器希望他/她得到所有错误的编译器的一部分,这实际上并没有让你的生活变得更容易,除非你真的不能理解你的想法Fortran模式.


Ran*_*832 5

举一个具体的例子,想象一个简单的复数类型:

struct complex {
    double x, y;
    complex(double _x, double _y) : x(_x), y(_y) { }
    complex& operator +=(const complex& b) {
        x += b.x;
        y += b.y;
        return *this;
    }
    complex operator +(const complex& b) {
        complex result(x+b.x, y+b.y);
        return result;
    }
    /* trivial assignment operator */
}
Run Code Online (Sandbox Code Playgroud)

对于a = a + b的情况,它必须创建一个额外的临时变量然后复制它.