如果不使用结果值,i++和++i之间是否有性能差异?
当前回答
我可以想到一种情况,后缀比前缀增量慢:
想象一个寄存器a的处理器被用作累加器,它是许多指令中使用的唯一寄存器(一些小型微控制器实际上是这样的)。
现在想象一下下面的程序和它们转换成一个假设的程序集:
前缀增量:
a = ++b + c;
; increment b
LD A, [&b]
INC A
ST A, [&b]
; add with c
ADD A, [&c]
; store in a
ST A, [&a]
后缀增加:
a = b++ + c;
; load b
LD A, [&b]
; add with c
ADD A, [&c]
; store in a
ST A, [&a]
; increment b
LD A, [&b]
INC A
ST A, [&b]
注意b的值是如何被强制重新加载的。使用前缀增量,编译器可以只增加值并继续使用它,可能避免重新加载它,因为所需的值在增量之后已经在寄存器中。然而,使用后缀增量,编译器必须处理两个值,一个是旧值,一个是增加的值,正如我上面所示,这会导致更多的内存访问。
当然,如果增量的值没有被使用,比如单个i++;语句,编译器可以(并且确实)简单地生成一个增量指令,而不管后缀或前缀的使用。
As a side note, I'd like to mention that an expression in which there is a b++ cannot simply be converted to one with ++b without any additional effort (for example by adding a - 1). So comparing the two if they are part of some expression is not really valid. Often, where you use b++ inside an expression you cannot use ++b, so even if ++b were potentially more efficient, it would simply be wrong. Exception is of course if the expression is begging for it (for example a = b++ + 1; which can be changed to a = ++b;).
其他回答
请不要让“哪个更快”的问题成为使用哪个的决定因素。你可能永远不会关心那么多,此外,程序员的阅读时间比机器的时间要昂贵得多。
使用任何对阅读代码的人最有意义的方法。
首先:在C中i++和++i之间的差异是可以忽略的。
到细节。
1. 众所周知的c++问题:++i更快
在c++中,如果i是具有重载自增操作符的某种对象,则++i的效率更高。
为什么? 在++i中,对象首先递增,然后可以作为const引用传递给任何其他函数。如果表达式是foo(i++),这是不可能的,因为现在增量需要在foo()调用之前完成,但旧的值需要传递给foo()。因此,编译器被迫在对原始i执行自增操作符之前复制i。额外的构造函数/析构函数调用是不好的部分。
如上所述,这不适用于基本类型。
2. 鲜为人知的事实是:i++可能更快
如果不需要调用构造函数/析构函数,这在C中总是如此,那么++i和i++应该同样快,对吗?不。他们的速度几乎一样快,但可能有一些小的差异,这是大多数其他答案的错误理解。
How can i++ be faster? The point is data dependencies. If the value needs to be loaded from memory, two subsequent operations need to be done with it, incrementing it, and using it. With ++i, the incrementation needs to be done before the value can be used. With i++, the use does not depend on the increment, and the CPU may perform the use operation in parallel to the increment operation. The difference is at most one CPU cycle, so it is really neglegible, but it is there. And it is the other way round then many would expect.
参考Scott Meyers的《更有效的c++第六条:区分增减操作的前缀和后缀形式》。
对于对象,尤其是对于迭代器,前缀版本总是优于后缀版本。
原因是,如果你看一下操作符的调用模式。
// Prefix
Integer& Integer::operator++()
{
*this += 1;
return *this;
}
// Postfix
const Integer Integer::operator++(int)
{
Integer oldValue = *this;
++(*this);
return oldValue;
}
看看这个例子,很容易看出前缀操作符总是比后缀操作符更有效率。因为需要在临时对象中使用后缀。
这就是为什么当你看到使用迭代器的例子时,他们总是使用前缀版本。
但正如你所指出的,对于int型,实际上没有什么区别,因为编译器优化可以发生。
在C语言中,如果结果未被使用,编译器通常可以将它们优化为相同。
然而,在c++中,如果使用提供自己的++操作符的其他类型,前缀版本可能比后缀版本更快。因此,如果不需要后缀语义,最好使用前缀操作符。
如果你担心微观优化,这里有一个额外的观察。递减循环“可能”比递增循环更有效(取决于指令集架构,例如ARM),给定:
for (i = 0; i < 100; i++)
在每个循环中,你将有一个指令:
i加1。 比较i是否小于100。 如果i小于100,则为条件分支。
而递减循环:
for (i = 100; i != 0; i--)
循环将有一个指令用于以下每一个:
递减i,设置CPU寄存器状态标志。 一个依赖于CPU寄存器状态(Z==0)的条件分支。
当然,这只适用于递减到零!
记得ARM系统开发人员指南。
推荐文章
- C99 'restrict'关键字的实际用法?
- 检查子字符串存在于C中的字符串中
- 从标准输入中捕获字符,而不需要等待按enter键
- 一个字符串的字符串列表
- DDD -实体不能直接访问存储库的规则
- 为什么STL如此严重地基于模板而不是继承?
- 查找当前可执行文件的路径,不包含/proc/self/exe
- 如何在Objective-C中声明类级属性?
- 面向方面编程与面向对象编程
- .NET反射的成本有多高?
- 在c#中检查字符串是否只包含数字的最快方法
- JavaScript变量声明在循环外还是循环内?
- 如何创建可变参数宏(参数数量可变)
- c++中类似于java的instanceof
- 为什么C语言这么快,为什么其他语言没有这么快或更快?