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

时间:2012-09-18 14:50:15

标签: c++ performance operators

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

x = x + a;

x += a;

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

10 个答案:

答案 0 :(得分:210)

任何有价值的编译器都会为任何内置类型(intfloat等)的构造生成完全相同的机器语言序列,只要该语句真的很简单作为x = x + a; 并启用优化。 (值得注意的是,GCC的-O0是默认模式,执行反优化,例如将完全不必要的存储插入内存,以确保调试器始终可以找到变量值。)

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

*f() += a;

只调用f一次,而

*f() = *f() + a;

调用两次。如果f有副作用,那么其中一个就会出错(可能是后者)。即使f没有副作用,编译器也可能无法消除第二个调用,因此后者可能确实更慢。

由于我们在这里讨论的是C ++,因此重载operator+operator+=的类类型的情况完全不同。如果x是这种类型,那么 - 在优化之前 - x += a会转换为

x.operator+=(a);

x = x + a转换为

auto TEMP(x.operator+(a));
x.operator=(TEMP);

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

答案 1 :(得分: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  

对于用户定义的类型,您可以重载operator +operator +=,这取决于各自的实现。

答案 2 :(得分:11)

是的!在x可能有副作用的情况下,后者编写速度更快,阅读速度更快,更容易弄明白。所以它对人类来说总体上更快。人的时间一般比计算机时间花费的多,所以这一定是你所要求的。正确?

答案 3 :(得分:7)

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

   T x, a;
   ....
   x = x + a;

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

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

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

答案 4 :(得分:6)

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

答案 5 :(得分:6)

你问的是错误的问题。

这不太可能推动应用或功能的性能。即便如此,找出答案的方法是配置文件代码并知道它对您的影响。在清晰度,正确性和可读性方面,思考更重要,而不是担心这个级别哪个更快。

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

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

答案 6 :(得分:6)

如果你说+=,你的编译器会让生活变得更轻松。为了让编译器能够识别x = x+ax += a相同,编译器必须

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

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

如果您的意思是将a添加到x,编译器编写者会在您说出您的意思时表示赞赏。 那样的话,你没有运用编译器希望他/她得到所有错误的编译器的一部分,并且实际没有任何生命对你来说更容易,除非你老老实实无法摆脱Fortran模式。

答案 7 :(得分: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 */
}

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

答案 8 :(得分:4)

我认为这应该取决于机器及其架构。如果它的体系结构允许间接内存寻址,编译器编写者MIGHT只需使用此代码(用于优化):

mov $[y],$ACC

iadd $ACC, $[i] ; i += y. WHICH MIGHT ALSO STORE IT INTO "i"

然而,i = i + y可能会被转换为(没有优化):

mov $[i],$ACC

mov $[y],$B 

iadd $ACC,$B

mov $B,[i]

<小时/> 也就是说,还应该考虑其他并发症,例如if i是一个返回指针等的函数。大多数生产级编译器(包括GCC)为两个语句生成相同的代码(如果它们是整数)。

答案 9 :(得分:2)

不,两种方式都得到了相同的处理。