无法解释C ++性能

时间:2012-04-17 20:53:19

标签: c++ performance

如果你有一个永不改变并且总是等于零的变量A,函数F,函数G和函数H,并在现代英特尔台式机处理器上使用-O3优化现代调用以下代码海湾合作委员会的版本:

for(i = 0; i < a_big_number; i++)
{
if(A != 0) F();
else G();
}

执行需要2秒。请注意,永远不会调用F,因为A始终为0.或者,

for(i = 0; i < a_big_number; i++)
{
if(A != 0) H();
else G();
}

执行只需1秒钟。同样,A总是0而H从不被调用。最后,

for(i = 0; i < a_big_number; i++)
{
G();
}

只需0.5秒即可执行。

鉴于前两个例子中的条件陈述,为什么F和H的内容是什么重要?既然它们从未被调用过,为什么它会对它们的作用产生影响呢?鉴于英特尔处理器具有复杂的分支预测,处理器难道不应该总是调用G(),甚至不会在条件语句上浪费时间吗?我知道条件指令应该浪费一些时间,但我不明白为什么浪费这么多时间。

2 个答案:

答案 0 :(得分:1)

假设编译器理解A是一个常量,它应该转为这个代码:

for(i = 0; i < a_big_number; i++)
{
    if(A != 0) F();
    else G();
}

进入这个:

if(A != 0)
    for(i = 0; i < a_big_number; ++i)
        F();
else
    for(i = 0; i < a_big_number; ++i)
        G();

如果常量看起来是编译时常量,则完全优化F()函数调用。

如果没有发生这种情况(即可能存在副作用或其他原因 - 编译器无法保证您如何以及优化它),循环将遇到来自分支错误预测的性能损失。如果A没有改变并且被调用的函数足够小,CPU应该锁定循环并记住分支,因此它不会一遍又一遍地重复相同的错误。另一方面,循环可能会展开,这可能会造成很大的损害,因为它无法并行化,只会破坏代码大小和CPU必须跟踪的一些事情。

你如何衡量执行时间虽然对我来说是一个谜,以及你在循环中调用的函数正在做什么。例如,您可能正在测量流程执行交换。因此,除非您提供完整的工作示例并详细说明您的测量方法,否则无法告诉您发生了什么。

无论如何,我打赌您的时间测量不正确,或者您在未显示的代码或上述所有内容中做了一些不好的事情。

答案 1 :(得分:0)

据我所知,编译器无法确定分支是否不会执行。编译器可以做的最好的事情是预测哪个分支更有可能。