likely与unlikely
为什么使用likely与unlikely
在Linux kernel的源码中,存在这样的用法:
if (likely(cond)) { ...... }
if (unlikely(cond)) { ...... }
|
likely和unlikely是宏定义,若cond为真(非0),那么likely(cond)为真;反之,若cond为假(0),那么likely(cond)为假。unlikely的表现也是如此。因此,likely与unlikely并不会影响分支判断的结果。但它们有着一个额外的作用:向编译器提供分支预测的信息:likely告诉编译器这个分支条件更容易成立,即该分支更容易被执行,根据GCC的手册,分支成立具体的可能性默认是 90% ;unlikely正好相反,它告诉编译器相应分支不容易成立。
分支预测信息有什么用呢?
分支预测信息是给CPU看的,告诉CPU分支的可能性,帮助CPU做出正确的分支预测。曾经CPU是没有实现分支预测技术的,遇到分支指令的时候,因为没有办法判断下一条指令在哪里执行,所以会发生流水线等待,流水线越长,处理器等待的时间便越长,因为它必须等待分支指令处理完毕,才能确定下一条进入流水线的指令,这很影响效率。
分支预测技术便是为解决这一问题而出现的。CPU可以根据预测的结果来取指,流水线无需等待。但是一旦预测错误,流水线需要清空,所以提高分支预测的成功率能够提高CPU的执行效率。如果我们根据自身的经验可以判断出某个分支执行的可能性比较大或比较小,那么可将这个信息告诉编译器(这正是likely和unlikely所做的事情),从而提高CPU分支预测的成功率。
分支预测有哪些方式呢?
分支预测技术包含编译时进行的静态分支预测和硬件在执行时进行的动态分支预测。显然,likely和unlikely属于前者。
likely与unlikely的实现——__builtin_expect
likely与unlikely具体是借助GCC的一个内建函数__builtin_expect来实现的:
#define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) |
__builtin_expect
这个函数的原型是:
long __builtin_expect (long exp, long c) |
它的语义是期待(expect)这么一个事实:exp == c。它的返回值就是exp,因此不会影响分支判断。同时,这个"期待"成立(即exp == c为true)的可能性默认是90%。这个可能性可以由GCC的参数builtinexpect-probability控制。当然也可以由另一个类似的内建函数显式的给出这个可能性(0.0~1.0):
long __builtin_expect_with_probability(long exp, long c, double probability) |
由此,不难分析,if (likely(x))表明我们期待x被判断为逻辑真的可能性是90%,即告诉编译器,这条分支有90%的可能性要执行。同样的,if (unlikely(x))表明我们期待x被判断为逻辑假的可能性为90%,这当然也就表明分支不太容易被执行。这些信息最终会被CPU在分支预测时用到。
来自 https://blog.csdn.net/gzxb1995/article/details/102773437