我一直在深入研究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)
我知道它们是为了优化,但它们是如何工作的呢?使用它们可以减少多少性能/大小?至少在瓶颈代码中(当然是在用户空间中),这样做是否值得(可能还会失去可移植性)。
这些宏向编译器提供了关于分支可能走向的提示。如果这些宏可用,它们将扩展为特定于GCC的扩展。
GCC使用这些来优化分支预测。例如,如果您有以下内容
if (unlikely(x)) {
dosomething();
}
return x;
然后它可以重新构造这段代码,使其更像:
if (!x) {
return x;
}
dosomething();
return x;
这样做的好处是,当处理器第一次使用分支时,会有很大的开销,因为它可能已经预先加载和执行了代码。当它决定使用分支时,它必须使其无效,并从分支目标开始。
大多数现代处理器现在都有某种分支预测,但这只在您之前已经通过了分支,并且分支仍然在分支预测缓存中时才有帮助。
在这些场景中,编译器和处理器还可以使用许多其他策略。你可以在Wikipedia上找到关于分支预测器如何工作的更多细节:http://en.wikipedia.org/wiki/Branch_predictor
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