为了获得更好的性能,可能/不可能的语句应该放在哪里?

NK-*_*ell 6 c linux-kernel compiler-optimization dpdk likely-unlikely

一些软件(通常是面向性能的,例如Linux内核、DPDK)具有用于影响分支预测的C帮助程序。

我有一个绝对简单的代码片段(假设我知道 a > b 的百分比)来表示嵌套某些逻辑时条件嵌套和应用likely/的问题:unlikely

bool foo()
{
    foo1(1);
    foo2(2);

    /* if (unlikely(a > b)) */
    /* if (a > b)*/
    {
        puts("Ohhh!!! Rare case");
        return true;
    }
    return false;
}

int main(void)
{
    /* if (unlikely(foo())) */
    /* if (foo()) */
    {
        puts("Azaza");
    }
}
Run Code Online (Sandbox Code Playgroud)

那么从理论角度来看,为了提高性能,哪两行应该取消注释呢?

显然有3种方法可以帮助编译器进行分支预测:

1. if (unlikely(a > b)) ... if (unlikely(foo()))

2. if (a > b) ... if (unlikely(foo()))

3. if (unlikely(a > b)) ... if (foo())

理论上哪个是最有效的,为什么?

Ben*_*ter 0

据我所知,如果条件变量不在缓存中,likely/unlikely则语句显示出最佳效果。让我更详细地解释一下。

在您的代码中,处理器foo无论如何都必须执行。因此,likely这里不会产生任何强烈的影响,因为在推测执行期间不能跳过任何代码路径。该函数必须被执行。假设您将foobefore 的结果保存在变量中,代码如下所示:

int x = foo();
if (likely(x))
{
    puts("Azaza");
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,它likely(x)可能只会影响预取器/解码器,因为处理器刚刚计算了 x 并且该值很可能缓存在 L1 中(除非它在此时被中断)。然而,为了得到准确的答案,我们必须非常了解微架构,因为当前的 CPU 可能非常先进,以至于它们可以同时获取和解码两个分支。

现在假设您有一个全局变量volatile int c = 15,我们更改您的代码:

if (likely(b == 15))
{
    puts("Azaza");
} else {
    puts("Bzaza");
}
Run Code Online (Sandbox Code Playgroud)

当我们执行代码并b第一次被访问时,它不会在缓存中,处理器必须从内存中获取它。这会花费数百个 CPU 周期,并且 CPU 不会停止运行,而是在不知道 的值的情况下开始推测性地执行代码b。在这种情况下,可能的关键字指示我们应该执行第一个分支。请注意,当时执行的指令对外界是不可见的。现代 x86 处理器可以推测性地执行多达 400 个微操作,并且只有在预测成立时才提交结果。

因此,为了回答您的问题,我会将likely/unlikely关键字放在a > b.