Linux内核中最常用的优化技术之一是“ __builtin_expect”。当使用条件代码(if-else语句)时,我们通常知道哪个分支是正确的,哪个分支不是。如果编译器事先知道此信息,则它可以生成最优化的代码。
让我们从Linux内核代码“ http://lxr.linux.no/linux+v3.6.5/include/linux/compiler.h”中看到“ likely()”和“ unlikely()”宏的宏定义[行号146和147]。
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
在以下示例中,我们将branch标记为可能为true:
const char *home_dir ;
home_dir = getenv("HOME");
if (likely(home_dir))
printf("home directory: %s\n", home_dir);
else
perror("getenv");
对于上面的示例,我们将“ if”条件标记为“ likely()”为true,因此编译器将在分支后立即放置true代码,并在branch指令内放置false代码。通过这种方式,编译器可以实现优化。但是不要盲目使用“ likely()”和“ unlikely()”宏。如果预测正确,则意味着跳转指令的周期为零,但是如果预测错误,则将花费几个周期,因为处理器需要刷新其流水线,这比没有预测要糟糕。
与其他CPU操作相比,访问内存是最慢的CPU操作。为了避免这种限制,CPU使用“ CPU缓存”,例如L1缓存,L2缓存等。缓存的想法是将一部分内存复制到CPU本身。我们可以比任何其他内存更快地访问缓存。但是问题是,“缓存内存”的大小有限,我们无法将整个内存复制到缓存中。因此,CPU必须猜测在不久的将来将使用哪个内存,然后将该内存加载到CPU缓存中,而上面的宏提示将内存加载到CPU缓存中。