如果不使用结果值,i++和++i之间是否有性能差异?
当前回答
如果你担心微观优化,这里有一个额外的观察。递减循环“可能”比递增循环更有效(取决于指令集架构,例如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系统开发人员指南。
其他回答
执行摘要:没有。
i++可能比++ I慢,因为I的旧值 可能需要保存以备以后使用,但实际上都是现代的 编译器将对其进行优化。
我们可以通过查看这个函数的代码来证明这一点, 都是++i和i++。
$ cat i++.c
extern void g(int i);
void f()
{
int i;
for (i = 0; i < 100; i++)
g(i);
}
文件是一样的,除了++i和i++:
$ diff i++.c ++i.c
6c6
< for (i = 0; i < 100; i++)
---
> for (i = 0; i < 100; ++i)
我们将编译它们,并获得生成的汇编器:
$ gcc -c i++.c ++i.c
$ gcc -S i++.c ++i.c
我们可以看到生成的对象和汇编程序文件都是相同的。
$ md5 i++.s ++i.s
MD5 (i++.s) = 90f620dda862cd0205cd5db1f2c8c06e
MD5 (++i.s) = 90f620dda862cd0205cd5db1f2c8c06e
$ md5 *.o
MD5 (++i.o) = dd3ef1408d3a9e4287facccec53f7d22
MD5 (i++.o) = dd3ef1408d3a9e4287facccec53f7d22
我的C有点生疏了,所以我提前道歉。就速度而言,我可以理解结果。但是,我对这两个文件是如何得到相同的MD5哈希感到困惑。也许for循环也可以运行,但是下面两行代码不会生成不同的程序集吗?
myArray[i++] = "hello";
vs
myArray[++i] = "hello";
第一个函数将值写入数组,然后将i加1,第二个函数将i加1,然后将值写入数组。我不是汇编专家,但我只是不明白这两行不同的代码如何生成相同的可执行文件。
这只是我的个人意见。
我可以想到一种情况,后缀比前缀增量慢:
想象一个寄存器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;).
如果你担心微观优化,这里有一个额外的观察。递减循环“可能”比递增循环更有效(取决于指令集架构,例如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系统开发人员指南。
我总是喜欢预增量,然而……
我想指出的是,即使在调用运算符++函数的情况下,如果函数得到内联,编译器将能够优化掉临时函数。由于操作符++通常很短,并且经常在头文件中实现,因此它很可能被内联。
因此,出于实际目的,这两种形式的性能之间可能没有太大差异。然而,我总是喜欢预增量,因为它似乎更好地直接表达我想说的,而不是依赖于优化器来解决它。
此外,给优化器更少的任务可能意味着编译器运行得更快。