我一直在挖掘Linux内核的某些部分,发现这样的调用:
if (unlikely(fd < 0))
{
/* Do something */
}
Run Code Online (Sandbox Code Playgroud)
要么
if (likely(!err))
{
/* Do something */
}
Run Code Online (Sandbox Code Playgroud)
我找到了它们的定义:
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
Run Code Online (Sandbox Code Playgroud)
我知道它们是为了优化,但它们是如何工作的?使用它们可以预期性能/尺寸减少多少?至少在瓶颈代码中(当然在用户空间中)是否值得麻烦(并且可能失去可移植性).
我遇到了#define他们使用的一个__builtin_expect.
文件说:
内置功能:
long __builtin_expect (long exp, long c)您可以使用
__builtin_expect为编译器提供分支预测信息.一般来说,你应该更喜欢使用实际的配置文件反馈(-fprofile-arcs),因为程序员在预测程序实际执行情况方面是非常糟糕的.但是,有些应用程序难以收集此数据.返回值是值
exp,它应该是一个整数表达式.内置的语义是预期的exp == c.例如:Run Code Online (Sandbox Code Playgroud)if (__builtin_expect (x, 0)) foo ();表示我们不打算打电话
foo,因为我们预计x会为零.
那么为什么不直接使用:
if (x)
foo ();
Run Code Online (Sandbox Code Playgroud)
而不是复杂的语法__builtin_expect?
我刚刚进入一个拥有相当庞大代码库的项目.
我主要处理C++,他们编写的很多代码都使用布尔逻辑的双重否定.
if (!!variable && (!!api.lookup("some-string"))) {
do_some_stuff();
}
Run Code Online (Sandbox Code Playgroud)
我知道这些人都是聪明的程序员,很明显他们不会偶然这样做.
我不是经验丰富的C++专家,我唯一猜测他们这样做的原因是他们想要绝对肯定被评估的值是实际的布尔表示.所以他们否定它,然后再次否定它以使其恢复到它的实际布尔值.
这是正确的,还是我错过了什么?