我们的问题是,在C语言中i++和++i的性能有区别吗?

c++的答案是什么?


当前回答

[执行摘要:如果没有特定的理由使用i++,请使用++i。]

对于c++来说,答案有点复杂。

如果i是一个简单类型(不是c++类的实例),那么C给出的答案(“不,没有性能差异”)成立,因为编译器正在生成代码。

但是,如果i是c++类的实例,则i++和++i将调用其中一个操作符++函数。下面是这些函数的标准组合:

Foo& Foo::operator++()   // called for ++i
{
    this->data += 1;
    return *this;
}

Foo Foo::operator++(int ignored_dummy_value)   // called for i++
{
    Foo tmp(*this);   // variable "tmp" cannot be optimized away by the compiler
    ++(*this);
    return tmp;
}

由于编译器不生成代码,而只是调用运算符++函数,因此没有办法优化掉tmp变量及其相关的复制构造函数。如果复制构造函数的开销很大,则会对性能产生重大影响。

其他回答

当您将操作符视为值返回函数以及它们的实现方式时,++i和i++之间的性能差异将更加明显。为了更容易理解发生了什么,下面的代码示例将使用int,就像它是一个结构体一样。

++i对变量加1,然后返回结果。这可以就地完成,并且只需要最少的CPU时间,在许多情况下只需要一行代码:

int& int::operator++() { 
     return *this += 1;
}

但是i++就不一样了。

后递增(i++)通常被视为在递增之前返回原始值。但是,函数只能在完成时返回结果。因此,有必要创建一个包含原始值的变量的副本,增加变量,然后返回包含原始值的副本:

int int::operator++(int& _Val) {
    int _Original = _Val;
    _Val += 1;
    return _Original;
}

当增量前和增量后之间没有功能差异时,编译器可以执行优化,使两者之间没有性能差异。但是,如果涉及到结构或类等复合数据类型,则在增量后调用复制构造函数,如果需要深度复制,则不可能执行此优化。因此,前增量通常比后增量更快,需要的内存更少。

既然你也要求c++,下面是java的基准测试(用jmh制作):

private static final int LIMIT = 100000;

@Benchmark
public void postIncrement() {
    long a = 0;
    long b = 0;
    for (int i = 0; i < LIMIT; i++) {
        b = 3;
        a += i * (b++);
    }
    doNothing(a, b);
}

@Benchmark
public void preIncrement() {
    long a = 0;
    long b = 0;
    for (int i = 0; i < LIMIT; i++) {
        b = 3;
        a += i * (++b);
    }
    doNothing(a, b);
}  

结果表明,即使在某些计算中实际使用了增量变量(b)的值,迫使需要存储额外的值以防止后增量,每个操作的时间也完全相同:

Benchmark                         Mode  Cnt  Score   Error  Units
IncrementBenchmark.postIncrement  avgt   10  0,039   0,001  ms/op
IncrementBenchmark.preIncrement   avgt   10  0,039   0,001  ms/op

@Ketan

...加薪忽略了关于意图和性能的细节。有时我们想用iter++而不是++iter。

Obviously post and pre-increment have different semantics and I'm sure everyone agrees that when the result is used you should use the appropriate operator. I think the question is what should one do when the result is discarded (as in for loops). The answer to this question (IMHO) is that, since the performance considerations are negligible at best, you should do what is more natural. For myself ++i is more natural but my experience tells me that I'm in a minority and using i++ will cause less metal overhead for most people reading your code.

毕竟,这就是语言不叫“c++ C”的原因。[*]

[*]插入关于c++ C是一个更合乎逻辑的名称的必要讨论。

++i比i++快,因为它不返回值的旧副本。

它也更直观:

x = i++;  // x contains the old value of i
y = ++i;  // y contains the new value of i 

这个C语言的例子输出的是“02”而不是你所期望的“12”:

#include <stdio.h>

int main(){
    int a = 0;
    printf("%d", a++);
    printf("%d", ++a);
    return 0;
}

c++也是一样:

#include <iostream>
using namespace std;

int main(){
    int a = 0;
    cout << a++;
    cout << ++a;
    return 0;
}

我想指出Andrew Koenig最近在Code Talk上发表的一篇出色的文章。

http://dobbscodetalk.com/index.php?option=com_myblog&show=Efficiency-versus-intent.html&Itemid=29

在我们公司,我们也在适用的情况下使用++iter的一致性和性能。但Andrew提出了关于意图与性能的忽略细节。有时我们想用iter++而不是++iter。

所以,首先决定你的意图,如果pre或post不重要,那么使用pre,因为它将有一些性能优势,避免创建额外的对象并抛出它。