我通常认为preincrement比C++中的postincrement更有效.但是,当我最近阅读" 游戏引擎架构"(第二版)这本书时,有一节说,后增量优于for循环中的preincrement.因为,正如我引用的那样,"preincrement会在代码中引入数据依赖关系 - CPU必须等待增量操作完成才能在表达式中使用它的值." 这是真的?(这真的颠覆了我对这个问题的看法.)
以下是您感兴趣的部分的引用:
5.3.2.1增量前与增量后增量
请注意,在上面的示例中,我们使用的是C++的postincrement运算符
p++,而不是preincrement运算符++p.这是一个微妙但有时重要的优化.preincrement运算符在表达式中使用其(现在已修改的)值之前递增变量的内容.postincrement运算符在使用后递增变量的内容.这意味着写入会在代码中++p引入数据依赖关系 - CPU必须等待增量操作完成才能在表达式中使用其值.在深度流水线的CPU上,这引入了一个停顿.另一方面,p++没有数据依赖性.变量的值可以立即使用,增量操作可以在以后或与其使用并行进行.无论哪种方式,管道中都没有失速.当然,在
forloop(for(init_expr; test_expr; update_expr) { ... })的"update"表达式中,前后增量之间应该没有区别.这是因为任何好的编译器都会认识到没有使用变量的值update_expr.但是在使用该值的情况下,后增量是优越的,因为它不会在CPU的管道中引入停顿.因此,养成一直使用后增量的习惯是好的,除非你绝对需要preincrement的语义.
编辑:添加"上面的例子".
void processArray(int container[], int numElements)
{
int* pBegin = &container[0];
int* pEnd = &container[numElements];
for (int* p = pBegin; p != pEnd; p++)
{
int element = *p;
// process element...
}
}
void processList(std::list<int>& container)
{
std::list<int>::iterator pBegin = container.begin();
std::list<int>::iterator pEnd = container.end();
std::list<inf>::iterator p;
for (p = pBegin; p != pEnd; p++)
{
int element = *p;
// process element...
}
}
Run Code Online (Sandbox Code Playgroud)
eer*_*ika 20
preincrement在您的代码中引入了数据依赖关系 - CPU必须等待增量操作完成才能在表达式中使用它的值."
这是真的?
这大多是正确的 - 尽管可能过于严格.预增量不一定会引入数据依赖 - 但它可以.
博览会的一个简单例子:
a = b++ * 2;
Run Code Online (Sandbox Code Playgroud)
这里,增量可以与乘法并行执行.增量和乘法的操作数立即可用,并且不依赖于任一操作的结果.
另一个例子:
a = ++b * 2;
Run Code Online (Sandbox Code Playgroud)
这里,乘法必须在增量之后执行,因为乘法的一个操作数取决于增量的结果.
当然,这些语句稍有不同,因此编译器可能无法始终将程序从一种形式转换为另一种形式,同时保持语义相同 - 这就是为什么使用post增量可能会使性能略有不同.
一个实际的例子,使用循环:
for(int i= 0; arr[i++];)
count++;
for(int i=-1; arr[++i];) // more typically: (int i=0; arr[i]; ++i;)
count++;
Run Code Online (Sandbox Code Playgroud)
人们可能会认为后者必然更快,如果他们推断"后增量复制" - 在非基本类型的情况下这是非常正确的.但是,由于数据依赖性(并且因为int是增量运算符没有过载函数的基本类型),前者在理论上可以更有效.是否取决于cpu架构和优化器的能力.
对于它的价值 - 在一个简单的程序中,在x86 arch上,使用启用了优化的g ++编译器,上面的循环具有相同的汇编输出,因此在这种情况下它们完全相同.
经验法则:
如果计数器是基本类型并且未使用增量的结果,那么使用post/pre增量也没有区别.
如果计数器不是基本类型并且未使用增量的结果并且禁用了优化,则预增量可能更有效.启用优化后,通常没有区别.
如果计数器是基本类型并且使用增量的结果,那么在某些上下文中使用某些编译器,后增量在理论上可以稍微更有效 - 在某些cpu体系结构中.
如果计数器不是基本类型并且使用增量的结果,则预增量通常比后增量快.另见R Sahu关于此案的答案.
从我的经验中得到的一点数据.
将后增量更改为std::map::iteratorin for循环的预增量会导致我工作中的核心算法显着节省.
通常,当对作为类的迭代器进行icrement,即它不是指针时,在使用预增量运算符时应注意节省.原因是预增量运算符函数将对象更改到位,而后增量运算符函数通常涉及创建临时对象.
预增量运算符通常实现为:
typename& typename::operator++()
{
// Change state
...
// Return the object
return *this;
}
Run Code Online (Sandbox Code Playgroud)
而后增量运算符通常实现为:
typename typename::operator++(int)
{
// Create a temporary object that is a copy of the current object.
typename temp(*this):
// Change state of the current object
...
// Return the temporary object.
return temp;
}
Run Code Online (Sandbox Code Playgroud)