循环和分支效率

时间:2015-01-16 20:59:09

标签: c performance

哪个更有效 -

foo->bar |= x;
for(i=0; i<n; i++)
{  
    if (foo->bar & SOMETHING)
        doOnething(i);
    else if (foo->bar & SOMETHINGELSE)
        doSecondthing(i);
    else
        doAnotherThing(i);
}

OR

foo->bar |= x;
if (foo->bar & SOMETHING) {
    for(i=0; i<n; i++) {
        doOneThing(i);
    }
}
else if (foo->bar & SOMETHINGELSE) {
    for(i=0; i<n; i++) {
        doSecondThing(i);
    }
}
else {
    for(i=0; i<n; i++) {
        doAnotherThing(i);
    }
}

我知道声明循环会产生开销成本,但实际上在两种情况下都只执行一次。尽管在运行期间工作是相同的,但在编译期间还有更多工作要做?我们是否还考虑了指针被取消引用的位置?

感谢。

4 个答案:

答案 0 :(得分:3)

我会选择第二个(对于更少的if-else命中和未命中)并将循环放在函数内部以减少函数开销。

foo->bar |= x;
if (foo->bar & SOMETHING) {
        doOneThing(n); // loops inside so no excessive function overhead
}
else if (foo->bar & SOMETHINGELSE) {
        doSecondThing(n);
}
else {
        doAnotherThing(n);
}

答案 1 :(得分:2)

假设编译器优化,有两种可能的结果:

  • 编译器可以证明没有任何东西可以触及foo->bar。那么你的代码样本实际上是一样的。
  • 无法证明,第一个示例在运行时可能有点慢。由于分支预测if-else分支可能(或可能不是,取决于)几乎是免费的。所以它是在每次迭代时从foo->bar字段读取 - 我们是否幸运地仍然可以在缓存中使用它。

至于编译时性能,它完全取决于具体做什么*事情。在内联之后,第一个例子可以在循环迭代中引入一些棘手的数据依赖,或者增加额外的寄存器压力,并强制编译器比第二个做更多的工作。

答案 2 :(得分:1)

什么是SOMETHING,什么是SOMETHINGELSE?是否受到&#34; i&#34;不知何故?

如果是,第一个代码可以为每次迭代运行不同的函数(doOneThing,doSecondThing或doAnotherThing),而第二个代码将对每次迭代执行相同的函数。所以,两个代码都不相等。

假设SOMETHING和SOMETHINGELSE不受&#34; i&#34;的值的影响,在第一个代码中,if-else在每次迭代中被检查,而第二个代码只被检查一次,这会使你的性能在第一种情况下运行时间更糟糕。如果n具有高值,它可以产生真正的差异。

答案 3 :(得分:0)

这两种情况之间存在一个潜在的差异。如果编译器无法确定foofoo->barx在被调用函数内部是不可见的(通过指针),则可能必须每次重新加载并检查值。在第一种情况下循环。

第二种情况的一个缺点是,当您对代码进行更改时,您可能会意外地得到稍微不同的循环控件。

有时,使用函数指针组合这两种方法是有益的:

void (*fp)(int);

foo->bar |= x;

if (foo->bar & SOMETHING)
    fp = doOnething;
else (foo->bar & SOMETHINGELSE)
    fp = doSecondthing;
else
    fp = doAnotherThing;

for(i=0; i<n; i++) {
    fp(i);
}