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())
理论上哪个是最有效的,为什么?
据我所知,如果条件变量不在缓存中,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.