我一直在深入研究Linux内核的一些部分,发现了这样的调用:

if (unlikely(fd < 0))
{
    /* Do something */
}

or

if (likely(!err))
{
    /* Do something */
}

我找到了它们的定义:

#define likely(x)       __builtin_expect((x),1)
#define unlikely(x)     __builtin_expect((x),0)

我知道它们是为了优化,但它们是如何工作的呢?使用它们可以减少多少性能/大小?至少在瓶颈代码中(当然是在用户空间中),这样做是否值得(可能还会失去可移植性)。


当前回答

根据Cody的评论,这与Linux无关,但这是对编译器的一个提示。会发生什么取决于体系结构和编译器版本。

Linux中的这个特殊特性在驱动程序中被误用了。正如osgx在hot属性语义中指出的那样,在一个块中调用的任何热函数或冷函数都可以自动提示该条件是否可能。例如,dump_stack()被标记为冷,所以这是多余的,

 if(unlikely(err)) {
     printk("Driver error found. %d\n", err);
     dump_stack();
 }

gcc的未来版本可能会根据这些提示选择性地内联一个函数。也有建议说,它不是布尔值,而是一个分数,就像在最有可能,等等。一般来说,应该优先使用一些替代机制,如冷。没有理由在炎热的道路以外的任何地方使用它。编译器在一种架构上的功能在另一种架构上可能完全不同。

其他回答

在许多linux版本中,你可以在/usr/linux/中找到compiler.h,你可以简单地把它包含进来。另一种观点是,unlikely()比likely()更有用,因为

if ( likely( ... ) ) {
     doSomething();
}

它也可以在许多编译器中进行优化。

顺便说一下,如果你想观察代码的详细行为,你可以简单地做如下所示:

测试 Objdump -d测试。O > obj.s

然后,打开obj。S,你可以找到答案。

这些宏向编译器提供了关于分支可能走向的提示。如果这些宏可用,它们将扩展为特定于GCC的扩展。

GCC使用这些来优化分支预测。例如,如果您有以下内容

if (unlikely(x)) {
  dosomething();
}

return x;

然后它可以重新构造这段代码,使其更像:

if (!x) {
  return x;
}

dosomething();
return x;

这样做的好处是,当处理器第一次使用分支时,会有很大的开销,因为它可能已经预先加载和执行了代码。当它决定使用分支时,它必须使其无效,并从分支目标开始。

大多数现代处理器现在都有某种分支预测,但这只在您之前已经通过了分支,并且分支仍然在分支预测缓存中时才有帮助。

在这些场景中,编译器和处理器还可以使用许多其他策略。你可以在Wikipedia上找到关于分支预测器如何工作的更多细节:http://en.wikipedia.org/wiki/Branch_predictor

这些是GCC函数,供程序员向编译器提示给定表达式中最有可能出现的分支条件。这允许编译器构建分支指令,以便在最常见的情况下执行最少的指令。

如何构建分支指令取决于处理器架构。

long __builtin_expect(long EXP, long C);

这个构造告诉编译器表达式EXP . xml 最有可能的值是c。返回值是EXP。 __builtin_expect用于条件句 表达式。在几乎所有的情况下,它将被用于 上下文的布尔表达式在这种情况下,它是很多 定义两个helper宏更方便:

#define unlikely(expr) __builtin_expect(!!(expr), 0)
#define likely(expr) __builtin_expect(!!(expr), 1)

这些宏可以用于

if (likely(a > 1))

参考:https://www.akkadia.org/drepper/cpumemory.pdf

根据Cody的评论,这与Linux无关,但这是对编译器的一个提示。会发生什么取决于体系结构和编译器版本。

Linux中的这个特殊特性在驱动程序中被误用了。正如osgx在hot属性语义中指出的那样,在一个块中调用的任何热函数或冷函数都可以自动提示该条件是否可能。例如,dump_stack()被标记为冷,所以这是多余的,

 if(unlikely(err)) {
     printk("Driver error found. %d\n", err);
     dump_stack();
 }

gcc的未来版本可能会根据这些提示选择性地内联一个函数。也有建议说,它不是布尔值,而是一个分数,就像在最有可能,等等。一般来说,应该优先使用一些替代机制,如冷。没有理由在炎热的道路以外的任何地方使用它。编译器在一种架构上的功能在另一种架构上可能完全不同。