如果结果值未使用,i++
和++i
之间是否存在性能差异?
如果结果值未使用,i++
和++i
之间是否存在性能差异?
执行摘要:不。
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
++i
而不是 i++
仍然是一个好的实践。毫无理由不这么做,如果你的软件通过了一个不能将其优化的工具链,那么你的软件将更加高效。考虑到输入 ++i
和 i++
同样容易,没有理由一开始就不使用 ++i
。 - monokrome根据Andrew Koenig的文章 《效率与意图》:
首先,至少在涉及整数变量时,
++i
比i++
更有效并不明显。
而且:
因此,人们应该问的问题不是这两个操作中哪个更快,而是这两个操作中哪个更准确地表达了您要完成的任务。我认为,如果您不使用表达式的值,则永远没有理由使用
i++
而不是++i
,因为永远没有必要复制变量的值、增加变量的值,然后丢弃复制品。
因此,如果结果值不被使用,我会使用++i
,但不是因为它更有效,而是因为它正确表达了我的意图。
i++
与i += n
或i = i + n
编码方式一样,即按照目标 动词 对象 的形式编码,其中目标操作数位于动词运算符的左侧。在i++
的情况下,没有右边的对象,但规则仍然适用,将目标保持在动词运算符的左侧。 - David R Tribble++i
有时会更快,但永远不会更慢。每个人似乎都在假设i
是普通的内置类型,比如int
。在这种情况下,没有可测量的差异。然而,如果i
是复杂类型,则可能会发现可测量的差异。对于i++
,您必须在递增之前复制类。根据复制涉及的内容,它可能确实较慢,因为对于++i
,您只需返回最终值。Foo Foo::operator++()
{
Foo oldFoo = *this; // copy existing value - could be slow
// yadda yadda, do increment
return oldFoo;
}
另一个区别是,使用++i
时,您可以选择返回引用而不是值。同样,这取决于复制对象所涉及的内容,这可能会更慢。
一个实际的例子是迭代器的使用。复制迭代器不太可能成为应用程序的瓶颈,但养成在结果不受影响时使用++i
而不是i++
的习惯仍然是个好习惯。
简短回答:
在速度方面,i++
和 ++i
没有任何区别。一个好的编译器不应该在这两种情况下生成不同的代码。
详细回答:
其他回答都没有提到的是,在表达式中找到++i
与i++
之间的区别只有在表达式内才有意义。
在for(i=0; i<n; i++)
的情况下,i++
独立于其自己的表达式中: 在i++
之前和之后都有序列点。因此,唯一生成的机器码是“将 i 增加1”,并且定义了如何在程序的其余部分中对其进行排序。因此,如果您将其更改为前缀++
,那么毫无影响,您仍然只会得到机器码“增加 i 1”。
仅在表达式array[i++] = x;
与array[++i] = x;
等表达式中,++i
和i++
之间的区别才有意义。有人可能会争论说,后缀在这些操作中会更慢,因为必须稍后重新加载 i 所在的寄存器。但是请注意,编译器可以随意按任何顺序排列您的指令,只要它不会“破坏抽象机器的行为”,正如C标准所称。
因此,尽管您可能认为array[i++] = x;
将被转换为机器码:
i
的值存储在寄存器A中。i
的值存储在寄存器A中 // 这是低效的,因为这里有额外的指令,我们已经执行过一次了。i
中。编译器可能会更有效地生成代码,例如:
i
的值存储在寄存器A中。i
中。只是因为你作为C程序员习惯于认为后缀++
发生在末尾,机器码不必按照那种方式排序。
所以在C语言中,前缀和后缀++
没有区别。现在,作为C程序员,你应该注意的是,有些人在某些情况下不一致地使用前缀,在其他情况下使用后缀,而没有任何合理的解释。这表明他们对C语言的工作原理不确定,或者他们对该语言的知识不正确。这总是一个坏迹象,它反过来表明他们在程序中做出了其他可疑的决策,基于迷信或“宗教教条”。
“前缀++
总是更快”的说法确实是一种错误的教条,在许多几乎相同的情况下,前缀和后缀都具有相同的速度。
借鉴Scott Meyers的More Effective c++第6条:区分递增和递减操作的前缀和后缀形式。
对于对象,特别是迭代器,始终优先使用前缀版本而非后缀版本。
原因在于运算符的调用模式。
// Prefix
Integer& Integer::operator++()
{
*this += 1;
return *this;
}
// Postfix
const Integer Integer::operator++(int)
{
Integer oldValue = *this;
++(*this);
return oldValue;
}
如果您担心微观优化,以下是一个额外的观察。在一些指令集结构(例如ARM)中,递减循环可能比递增循环更有效率:
这取决于以下因素:
for (i = 0; i < 100; i++)
在每次循环中,您将为以下各项中的每一项提供一条指令:
i
增加 1
。i
是否小于 100
。i
小于 100
,则进行条件分支。而对于一个递减循环:
for (i = 100; i != 0; i--)
循环将有一个指令来执行以下操作:
Z==0
)进行条件分支。当递减到零时才能正常工作!
来自ARM系统开发人员指南的回忆。
a[N+i]
而失去,而不是a[i]
。 - Lawrence Doli++
和++i
之间的区别可以忽略不计。
++i
更快在C++中,当i
是某种具有重载递增运算符的对象时,++i
更高效。
为什么?
在++i
中,对象首先进行递增,然后可以作为const引用传递给任何其他函数。如果表达式为foo(i++)
,则不可能这样做,因为现在需要在调用foo()
之前执行递增,但旧值需要传递给foo()
。因此,在执行原始值上的递增运算符之前,编译器被迫对i
进行复制。附加的构造函数/析构函数调用是糟糕的部分。
如上所述,这不适用于基本类型。
i++
可能更快如果不需要调用构造函数/析构函数(这在C中始终如一),++i
和i++
应该同样快,对吗?不。它们在实质上几乎一样快,但可能存在轻微的差异,大多数其他答案都弄反了。
i++
如何更快?
关键在于数据依赖性。如果需要从内存中加载值,则需要对其进行两个后续操作,即递增和使用。对于++i
,必须在使用值之前进行递增。对于i++
,使用并不依赖于递增,CPU可以将使用操作与递增操作“并行”执行。差异最多为一个CPU周期,因此它真的可以忽略不计,但是确实存在。而且这与许多人的想法相反。
++i
或i++
在另一个表达式中使用,那么在它们之间进行更改会改变表达式的语义,因此任何可能的性能增益/损失都不成立。如果它们是独立的,即操作的结果不会立即使用,则任何合理的编译器都会将其编译为相同的内容,例如一个INC
汇编指令。 - Shahbazi++
和++i
,因此它们在程序员所做的事情上是相近等价的。2)尽管两者都编译为相同的指令,但它们对CPU的执行方式有所不同。在i++
的情况下,CPU可以并行计算增量和使用相同值的其他指令(CPU确实会这样做!),而在++i
的情况下,CPU必须在增量之后安排其他指令的执行。 - cmaster - reinstate monicaif(++foo == 7) bar();
和 if(foo++ == 6) bar();
在功能上是等效的。然而,第二个可能会更快一周期,因为CPU可以并行计算比较和增量。虽然单个周期并不重要,但差异确实存在。 - cmaster - reinstate monica<
和 <=
),而 ++
通常被使用,因此它们之间的转换通常很容易实现。 - Shahbaz++i
或 i++
的值没有被使用,那么它们是完全等价的,并且应该编译成完全相同的代码。(除非 i
是一个具有非标准、不同的前/后增量运算符的类对象)。 - cmaster - reinstate monica--i
和i ++
的程序员之一。 - daShier@Mark 虽然编译器允许优化掉(基于堆栈的)临时变量的副本,而且gcc(在最近版本中)也确实这样做了, 但这并不意味着所有编译器都会始终这样做。
我刚刚测试了我们当前项目中使用的编译器,其中有3个没有进行优化。
永远不要假设编译器是正确的,特别是当可能更快但从未更慢的代码同样易读时。
如果你的代码中没有一个运算符的实现真的很愚蠢:
始终首选++i而不是i++。