在'for循环'内使用'break'的性能影响

3
我已经尽力阅读了大量SO.SE上的Q&A,但是我没有找到与我的特定问题相关的答案。大多数与for-loopbreak有关的问题涉及嵌套循环,而我关注的是性能。 我想知道在C++中,在for-loop中使用break是否会影响性能(假设几乎从不调用break)。如果有影响,我也想暂时知道处罚的大小。 我非常怀疑它确实会影响性能(尽管我不知道有多大)。所以我想问你。我的推理如下:

无论条件语句的额外代码为何触发break(如if),它必然向我的循环添加附加指令。

此外,它可能还会在编译器尝试展开for-loop时出现问题,因为它不再知道将在编译时运行的迭代次数,从而有效地将其转换为while-loop

因此,我怀疑它确实对性能产生影响,这对于非常快速和紧密的循环来说可能是相当大的。


所以,这就带我提出了一个后续问题。在性能方面,for-loopbreak是否与while-loop相等?就像在以下代码片段中一样,我们假设checkCondition()的评估99.9%是true。我会失去for-loop的性能优势吗?
// USING WHILE
int i = 100;
while( i-- && checkCondition())
{
    // do stuff
}


// USING FOR
for(int i=100; i; --i)
{
    if(checkCondition()) {
        // do stuff
    } else {
        break;
    }
}

我已经在我的电脑上尝试过了,但是执行时间相同。并且我担心编译器及其优化功能的黑魔法,所以我想知道概念上的答案。
编辑: 请注意,在我的完整代码中,我已经测量了两个版本的执行时间,没有任何实质性的差异。此外,我不信任使用-s进行编译(我通常这样做),因为我对我的编译器的特定结果不感兴趣。我更感兴趣的是概念本身(从学术角度来看),因为我不确定我是否理解得完全正确 :)

9
不,它们之间的差异为零,尤其是使用优化编译器时。不要试图微调你的代码,让编译器为你做这件事。 - Hatted Rooster
循环实际上并不等价。 - Barry
1
@andresgongora 是的,它们仍然不等价。 - Barry
3
关于循环展开:我不认为编译器不能展开带有break的循环,它只需多次包含测试即可。关于执行break本身的惩罚:几乎为零。关于带有break的循环性能:你也可以从其他角度来看待。如果没有break,你将处理整个循环,而有了break,你有时会早早退出,所以在这种情况下,break实际上极大地提高了性能。如果你能以优雅的方式编写无分支版本的算法,那当然要这样做,但在许多情况下,具有分支的简洁算法仍然是最好的。 - Ped7g
3
关于优化,已经有研究表明“直觉”通常是错误的(当你在该领域没有很多实践经验时),最好的方法始终是测量。您还可以检查生成的汇编代码以了解发生了什么。但不要相信自己的直觉。有时您可能会使情况变得更糟。 - Hayt
显示剩余9条评论
2个回答

7
主要答案是,在验证该条件评估是瓶颈之前,避免花费时间进行类似的微调优化。
真正的答案是,CPU拥有强大的分支预测电路,这些电路在实践中运行得非常好。
将发生的情况是,您的CPU将选择是否采用分支,并执行代码,就好像if条件甚至不存在一样。当然,这依赖于多个假设,例如对条件计算没有副作用(因此循环体的某个部分依赖于它),并且这个条件将永远不会评估为false,直到某个点为止,该条件将变为true并停止循环。
一些编译器还允许您指定评估的可能性作为分支预测器的提示。
如果您想看到两个代码版本之间的语义差异,请使用“-S”编译它们并检查生成的asm代码,没有其他神奇的方法。

那么,在循环内部使用break确实会对性能产生影响吗? - andresgongora
1
你可以阅读一下这个非常受欢迎的答案:https://dev59.com/iWgu5IYBdhLWcg3wkH6P#11227902 通过阅读这篇文章,你将更好地理解@Jack的回答。 - mtb
@mtb 谢谢。也许我没有理解杰克的回答。 - andresgongora
不,我是认真的。这是SO上得票最高的问题的最受欢迎的答案。它涉及分支预测能力。 - mtb

3
"什么是...的性能影响?" 的唯一合理回答是“测量它”。很少有通用的答案。
在你展示的特定情况下,如果一个优化编译器为这两个示例生成了显著不同的代码,那将是相当令人惊讶的。另一方面,我可以相信像以下这样的循环:"
unsigned sum = 0;
unsigned stop = -1;
for (int i = 0; i<32; i++)
{
    stop &= checkcondition();  // returns 0 or all-bits-set;
    sum += (stop & x[i]);
}

可能比以下更快:

unsigned sum = 0;
for (int i = 0; i<32; i++)
{
    if (!checkcondition())
        break;
    sum += x[i];
}

对于特定编译器、特定平台、正确的优化级别设置以及特定的“checkcondition”结果模式,可以进行优化。

...但唯一的方法是进行测量。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接