x + =比x = x + a快吗?
我正在阅读Stroustrup的“C ++编程语言”,他说在两种方式中将某些东西添加到variables
x = x + a;
和
x += a;
他更喜欢+=
因为它最有可能更好的实施。 我认为他的意思是说它的工作也更快。
但是真的吗? 如果依赖于编译器和其他东西,我该如何检查?
只要语句真的像x = x + a;
一样简单,任何编译器都会为其任何内置types( int
, float
等)生成完全相同的机器语言序列x = x + a;
并启用优化 。 (值得注意的是,GCC的-O0
(默认模式)执行反优化 ,例如将完全不必要的存储插入到内存中,以确保debugging器始终能够findvariables值。
如果陈述更为复杂,可能会有所不同。 假设f
是一个返回指针的函数
*f() += a;
只调用一次,而
*f() = *f() + a;
称它两次。 如果f
有副作用,两者中的一个将是错误的(可能是后者)。 即使f
没有副作用,编译器可能无法消除第二个调用,所以后者可能确实比较慢。
由于我们在这里讨论的是C ++,对于重载operator+
和operator+=
类types,情况完全不同。 如果x
是这样一个types,那么 – 优化前 – x += a
转换为
x.operator+=(a);
而x = x + a
转化为
auto TEMP(x.operator+(a)); x.operator=(TEMP);
现在,如果类是正确编写的,而且编译器的优化器足够好的话,那么两者都将生成相同的机器语言,但是对于内置types来说这不是一个确定的事情。 这可能是Stroustrup在鼓励使用+=
的时候所想的。
你可以通过查看disassembly来检查,这将是相同的。
对于基本types ,两者同样快速。
这是由debugging版本(即不进行优化)生成的输出:
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
对于用户定义的types ,您可以重载operator +
和operator +=
,这取决于它们各自的实现。
是! 写的速度更快,阅读更快,而且更快捷,对于后者在x
可能有副作用的情况下。 所以对人类来说总体来说更快 一般人的时间花费比电脑时间要多得多,所以一定是你所问的。 对?
x = x + a
和x += a
之间的差别是机器必须经过的工作量 – 有些编译器可能(通常是)优化它,但通常,如果我们忽略优化一段时间,会发生什么在前面的代码片段中,机器必须两次查找x
的值,而在后者中,这个查找只需要发生一次。
但是,正如我所提到的那样,今天大多数编译器都足够智能地分析指令并减less所需的机器指令。
PS:堆栈溢出的第一个答案!
这实际上取决于x和a的types以及+的实现。 对于
T x, a; .... x = x + a;
编译器必须创build一个临时T来包含x + a的值,同时对它进行求值,然后将其分配给x。 (在这个操作中不能使用x或者a作为工作区)。
对于x + = a,它不需要临时的。
对于琐碎的types,没有区别。
正如你已经标记了这个C ++,你无法从你发布的两条语句中知道。 你需要知道'x'是什么(有点像'42'的答案)。 如果x
是一个POD,那么它不会有太大的区别。 然而,如果x
是一个类,那么operator +
和operator +=
方法可能会有重载,这些方法可能会导致不同的行为,从而导致执行时间差异很大。
如果你说+=
你让编译器的生活变得更容易。 为了使编译器认识到x = x+a
与x += a
相同,编译器必须这样做
-
分析左手边(
x
)以确保它没有副作用并始终指相同的l值。 例如,它可能是z[i]
,它必须确保z
和i
都不会改变。 -
分析右边(
x+a
)并确保它是一个求和,而左边只出现一次,即使它可以被转换,就像在z[i] = a + *(z+2*0+i)
。
如果你的意思是要添加a
x
,编译器的作者会在你说出你的意思时欣赏它。 这样一来,你就不会执行编译器的一部分,编者希望他/她能够得到所有的错误,而这实际上并不会让你的生活变得更容易,除非你真的不能把你的脑袋拿出来Fortran模式。
你问的是错误的问题。
这不太可能推动应用程序或function的性能。 即使是这样,find的方法是分析代码,并知道它如何影响你一定。 不要担心在这个层面上哪个更快,从清晰性,正确性和可读性的angular度来考虑更为重要 。
当你考虑到这一点时尤其如此,即使这是一个重要的性能因素,编译器也会随着时间的推移而发展。 有人可能会找出一个新的优化,今天正确的答案可能会成为错误的明天。 这是一个过早优化的经典案例。
这并不是说性能根本不重要,只是这是实现性能目标的错误方法。 正确的方法是使用分析工具来了解您的代码实际在哪里花费时间,从而在哪里集中精力。
举一个具体的例子,设想一个简单的复杂数字types:
struct complex { double x, y; complex(double _x, double _y) : x(_x), y(_y) { } complex& operator +=(const complex& b) { x += bx; y += by; return *this; } complex operator +(const complex& b) { complex result(x+bx, y+by); return result; } /* trivial assignment operator */ }
对于a = a + b的情况,它必须创build一个额外的临时variables,然后复制它。
我认为这应该取决于机器和它的架构。 如果其架构允许间接寻址,那么编译器作者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]
也就是说,如果i
是一个函数返回指针等其他的复杂性也应该考虑。 大多数生产级编译器(包括GCC)为这两个语句(如果它们是整数)生成相同的代码。
不,两种方式都是一样的。