如果不使用结果值, i++++i之间会有性能差异吗?

===============>>#1 票数:375 已采纳

内容提要:否。

i++可能比++i慢,因为可能需要保存i的旧值以供以后使用,但实际上所有现代编译器都会对其进行优化。

我们可以通过使用++ii++查看此函数的代码来证明这一点。

$ cat i++.c
extern void g(int i);
void f()
{
    int i;

    for (i = 0; i < 100; i++)
        g(i);

}

这些文件是相同的,除了++ii++

$ 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

===============>>#2 票数:106

效率与意图 (作者Andrew Koenig):

首先,至少在涉及整数变量的情况下, ++ii++效率更高还很明显。

和:

因此,一个人应该问的问题不是这两个操作中的哪个更快,而是这两个操作中的哪个更准确地表达了您要完成的任务。 我认为,如果您不使用表达式的值,则永远没有理由使用i++而不是++i ,因为永远没有理由复制变量的值,增加变量然后抛出复制。

因此,如果不使用结果值,我将使用++i 但这不是因为它更有效:因为它正确地表达了我的意图。

===============>>#3 票数:45

更好的答案是++i有时会更快,但永远不会慢。

每个人似乎都假设i是一个常规的内置类型,例如int 在这种情况下,没有可测量的差异。

但是,如果i是复杂类型,那么您可能会发现可测量的差异。 对于i++您必须在增加类之前对其进行复制。 取决于副本所涉及的内容,它的确确实会变慢,因为使用++it可以只返回最终值。

Foo Foo::operator++()
{
  Foo oldFoo = *this; // copy existing value - could be slow
  // yadda yadda, do increment
  return oldFoo;
}

另一个区别是,使用++i您可以选择返回引用而不是值。 同样,根据制作对象副本所涉及的内容,这可能会更慢。

迭代器的使用是一个现实的例子。 复制迭代器在您的应用程序中不太可能成为瓶颈,但是在不影响结果的情况下,养成使用++i而不是i++的习惯仍然是一种好习惯。

===============>>#4 票数:17

摘自Scott Meyers的《 更有效的c ++ 项目6》:区分增量和减量运算的前缀和后缀形式

对于对象,尤其是对于迭代器,始终优先使用前缀版本而不是后缀。

如果查看运营商的呼叫方式,则其原因是。

// Prefix
Integer& Integer::operator++()
{
    *this += 1;
    return *this;
}

// Postfix
const Integer Integer::operator++(int)
{
    Integer oldValue = *this;
    ++(*this);
    return oldValue;
}

看这个例子,很容易看出前缀运算符将总是比后缀更有效率。 由于在使用postfix时需要一个临时对象。

这就是为什么当您看到使用迭代器的示例时,它们始终使用前缀版本的原因。

但是正如您为int指出的那样,由于可以进行编译器优化,因此实际上没有区别。

===============>>#5 票数:16

简短答案:

在速度方面, i++++i从来没有任何区别。 好的编译器在两种情况下不应生成不同的代码。

长答案:

其他所有答案都没有提到, ++ii++之间的区别仅在找到的表达式中才有意义。

for(i=0; i<n; i++)i++本身就是它自己的表达式: i++之前有一个序列点,而i++之后有一个点。 因此,生成的唯一机器代码是“将i递增1 ”,并且很好地定义了如何相对于程序的其余部分对其进行排序。 因此,如果将其更改为前缀++ ,则丝毫没有关系,您仍将获得机器代码“将i递增1 ”。

++ii++之间的区别仅在诸如array[i++] = x;表达式中array[i++] = x; vs array[++i] = x; 有人可能会说,后缀在这种操作中会变慢,因为i居住的寄存器必须稍后重新加载。 但是请注意,只要编译器不按C标准所称的那样“破坏抽象机的行为”,它就可以按照自己喜欢的任何方式自由地对其指令进行排序。

因此,尽管您可以假设array[i++] = x; 转换为机器代码为:

  • i值存储在寄存器A中。
  • 将数组的地址存储在寄存器B中。
  • 添加A和B,将结果存储在A中。
  • 在以A表示的这个新地址处,存储x的值。
  • i值存储在寄存器A //中效率低下,因为这里有额外的指令,我们已经执行了一次。
  • 增量寄存器A。
  • 将寄存器A存储在i

编译器也可能更有效地产生代码,例如:

  • i值存储在寄存器A中。
  • 将数组的地址存储在寄存器B中。
  • 添加A和B,将结果存储在B中。
  • 增量寄存器A。
  • 将寄存器A存储在i
  • ... //其余代码。

仅仅因为您作为C程序员已被训练为认为后缀++会在末尾出现,所以不必以这种方式对机器代码进行排序。

因此,在C ++中,前缀和后缀++之间没有区别。作为C程序员,您现在应该有所不同的是,在某些情况下不一致使用前缀的人和在其他情况下不一致使用后缀的人,没有任何理由。 这表明他们不确定C的工作方式或对语言的了解不正确。 这总是一个不好的信号,它确实表明他们正在基于迷信或“宗教教条”在他们的程序中做出其他可疑的决定。

实际上,“ Prefix ++总是更快”是一种可能在C语言程序员中普遍存在的错误教条。

===============>>#6 票数:16

如果您担心微优化,这是另外一个观察结果。 给定以下条件,减少循环可能比增加循环更有效率(取决于指令集架构,例如ARM):

for (i = 0; i < 100; i++)

在每个循环中,您将分别具有一条指令:

  1. 1加到i
  2. 比较i是否小于100
  3. 如果i小于100则为条件分支。

而递减循环:

for (i = 100; i != 0; i--)

该循环将为每个指令提供一条指令:

  1. i ,设置CPU寄存器状态标志。
  2. 根据CPU寄存器状态( Z==0 )的条件分支。

当然,这仅在减为零时有效!

从《 ARM系统开发人员指南》中记住。

===============>>#7 票数:11

请不要让“哪个更快”的问题成为使用哪个的决定因素。 您永远都不会在乎那么多,此外,程序员的阅读时间比机器学习的时间要昂贵得多。

使用对人类阅读代码最有意义的方法。

===============>>#8 票数:10

首先:在C中, i++++i之间的区别可以忽略不计。


到细节。

1.众所周知的C ++问题: ++i更快

在C ++中,如果i是某种带有重载增量运算符的对象,则++i效率更高。

为什么?
++i ,对象首先增加,然后可以作为const引用传递给任何其他函数。 如果表达式是foo(i++)这是不可能的,因为现在需要在调用foo()之前完成增量,但是需要将旧值传递给foo() 因此,在对原始文件执行增量运算符之前,编译器被迫复制i 额外的构造函数/析构函数调用是最糟糕的部分。

如上所述,这不适用于基本类型。

2.鲜为人知的事实: i++ 可能会更快

如果不需要调用构造函数/析构函数,这在C ++i总是如此, ++ii++应该一样快,对吗? 不。它们的速度几乎一样快,但可能会有一些细微的差异,大多数其他应答者都采用了错误的方法。

如何能i++更快?
关键是数据依赖性。 如果需要从内存中加载该值,则需要对其进行两个后续操作,使其递增并使用它。 对于++i ,需要先进行递增, 然后才能使用该值。 使用i++ ,使用不取决于增量,CPU可以与增量操作并行执行使用操作。 区别最多是一个CPU周期,因此它确实可以忽略不计,但是确实存在。 这是许多人期望的另一种方式。

===============>>#9 票数:7

@Mark尽管允许编译器优化变量的(基于堆栈的)临时副本,而gcc(在最新版本中)正在这样做,但这并不意味着所有编译器都会一直这样做。

我只是用我们当前项目中使用的编译器对其进行了测试,但四分之三并未对其进行优化。

永远不要假设编译器正确无误,尤其是在可能更快但永远不会慢的代码易于阅读的情况下。

如果您的代码中没有真正愚蠢的操作符实现:

比起i ++,Alwas更喜欢++ i。

===============>>#10 票数:5

在C语言中,如果未使用结果,则编译器通常可以将它们优化为相同。

但是,在C ++中,如果使用其他提供自己的++运算符的类型,则前缀版本可能比后缀版本快。 因此,如果不需要后缀语义,最好使用前缀运算符。

===============>>#11 票数:4

我可以想到后缀比前缀增量慢的情况:

想象一下,将具有寄存器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++; 语句,无论后缀或前缀使用情况如何,编译器都可以(并且确实)简单地生成增量指令。


附带说明一下,我想提到一个存在b++的表达式不能简单地用++b转换为一个表达式而无需任何额外的努力(例如,通过添加- 1 )。 因此,如果将两者作为某个表达式的一部分,则将它们进行比较是不正确的。 通常,在表达式中使用b++情况下,不能使用++b ,因此即使++b可能更有效,也完全是错误的。 如果表达式请求它,当然是例外(例如a = b++ + 1;可以更改为a = ++b; )。

===============>>#12 票数:4

我已经通过大部分的答案在这里读了许多的意见,我没有看到,我能想到的,其中的一个实例的任何引用i++的效率比++i (也许令人惊讶--i i--更有效率。 那是针对DEC PDP-11的C编译器!

PDP-11的组装说明用于寄存器的递减和递增,但并非相反。 该指令允许将任何“通用”寄存器用作堆栈指针。 因此,如果使用*(i++)之类的东西,则可以将其编译为单个汇编指令,而*(++i)不能。

这显然是一个非常深奥的例子,但是它确实提供了例外,即后增量效率更高(或者我应该说 ,因为这几天对PDP-11 C代码的需求不大)。

===============>>#13 票数:2

我总是喜欢预增量,但是...

我想指出的是,即使在调用operator ++函数的情况下,如果函数被内联,编译器也将能够优化掉临时函数。 由于operator ++通常很短,并且经常在标头中实现,因此很可能会内联。

因此,出于实际目的,这两种形式的性能可能没有太大区别。 但是,我总是更喜欢预增量,因为直接表达我想说的似乎更好,而不是依靠优化器来解决。

同样,减少优化器的工作量可能意味着编译器运行得更快。

===============>>#14 票数:0

我的C有点生锈,所以我提前道歉。 从速度上看,我可以理解结果。 但是,我对这两个文件如何出现在相同的MD5哈希中感到困惑。 也许for循环运行相同,但是以下两行代码不会生成不同的汇编吗?

myArray[i++] = "hello";

myArray[++i] = "hello";

第一个将值写入数组,然后递增i。 然后,第二个增量i写入数组。 我不是汇编专家,但我只是看不到这两行不同的代码将如何生成相同的可执行文件。

只要我两美分。

  ask by Mark Harrison translate from so

未解决问题?本站智能推荐:

2回复

JavaScript中的i ++和++ i之间是否存在性能差异? [关闭]

我读过C中的i ++和++ i之间有性能差异吗? : 如果不使用结果值,i ++和++ i之间是否存在性能差异? JavaScript的答案是什么? 例如,以下哪项更好? 1) 2)
17回复

C ++中的i ++和++ i在性能上有区别吗?

我们有一个问题, 在C中 i++和++i之间是否存在性能差异? C ++的答案是什么?
5回复

为什么i = i + 1比i ++快?

在Flash中测试此代码: 为什么在ActionScript 3中i=i+1更快,而在其他情况下它更慢? 对不起,我犯了一些错误。上面的代码使用的时间相同。 但如果把它投入使用,结果会有所不同。
20回复

什么更有效,i ++或++ i? [重复]

确切重复 : 在C ++中i ++和++ i之间是否存在性能差异? 确切重复 : 循环中i ++和++ i之间的区别? 什么更有效,i ++或++ i? 我只在Java和C / C ++中使用过这个,但我真的要求实现它的所有语言。 在大学里我有一位教授告诉我们++我更有效
1回复

在什么情况下,C中的前缀和后缀增/减运算符之间的性能是否有所不同? [重复]

这个问题已经在这里有了答案: 在C中,i ++和++ i之间是否存在性能差异? 14个答案 我知道C ++中的这个问题可能是复制对象和/或重载的问题 。 我也了解到优化起了作用,并且在大多数情况下(即使不是所有情况)也可以使优化成为一个问题。 我的问题是,当前
3回复

s [++ i]之间的区别; 和s [i]; ++ i;

我正在使用“ C编程语言”进行一些练习,无法弄清楚是什么给了我一定的输出。 并不是真正的障碍,因为我得到了想要的输出,但是我不明白为什么更改我的代码的某些部分实际上就给了我想要的输出。 只是在寻找一个解释。 这是按我想要的方式工作的代码。 我不理解的部分是s[++i] = ' ';
3回复

C和JAVA中的增减运算符的差异

这个问题已经在这里有了答案: “ x = x ++”之后的x是什么? 17个答案 为什么这些构造使用前后递增的未定义行为? 14个答案 请考虑以下声明: 根据我的逻辑,n应该是10。但是我在c中得到的输出是不同的(输出是7)但是在java中,我
21回复

C:++ i和i ++有什么区别?

在C语言中,使用++i和i++什么区别,应该在for循环的增量块中使用哪个?
7回复

为什么在for循环中“ ++ i”和“ i ++”之间没有区别?

在我看来,这 和 不应该做同样的事情。 对于第二个示例,对我来说,自循环开始以来,我应等于1更具逻辑性。