内核中的可能调用和不太可能调用之间有什么区别?


11

内核中可能的调用与不太可能的调用之间是什么?在搜索内核源代码时,我发现了这些语句。

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

有人可以给它一些启示吗?


这确实是一个编程问题,更适合Stack OVerflow
吉尔斯(Gilles)“所以

Answers:


14

它们是GCC的编译器提示。它们在条件语句中用于告知编译器是否可能采用分支。它可以帮助编译器以最适合最频繁结果的方式放置代码。

它们的用法如下:

if (likely(some_condition)) {
  // the compiler will try and make the code layout optimal for the case
  // where some_condition is true, i.e. where this block is run
  most_likely_action();
} else {
  // this block is less frequently used
  corner_case();
}

应该非常小心地使用它(即基于实际的分支概要分析结果)。错误的提示可能会降低性能(显然)。

通过搜索可以轻松找到一些有关如何优化代码的示例GCC __builtin_expect。此博客文章gcc优化:例如__builtin_expect详细介绍了反汇编。

可以进行的优化类型非常取决于处理器。通常的想法是,如果处理器未在各处进行分支/跳转,则处理器通常会更快地运行代码。它越线性,分支越可预测,它将运行得越快。(例如,对于具有深管线的处理器尤其如此。)

因此,编译器将发出代码,例如,如果目标CPU希望这样做,则最有可能的分支将不涉及跳转。


独角兽是什么意思?是技术术语还是仅仅是填充词?

我删除了独角兽以避免混乱。

您能否详细说明一下编译器将尝试使代码布局适合这种情况?我想知道它是如何做到的。

在此添加了一些信息。没有通用的优化代码的方法,这完全取决于处理器。

2

让我们反编译以查看GCC 4.8的功能

没想到

#include "stdio.h"
#include "time.h"

int main() {
    /* Use time to prevent it from being optimized away. */
    int i = !time(NULL);
    if (i)
        printf("%d\n", i);
    puts("a");
    return 0;
}

使用GCC 4.8.2 x86_64 Linux进行编译和反编译:

gcc -c -O3 -std=gnu11 main.c
objdump -dr main.o

输出:

0000000000000000 <main>:
   0:       48 83 ec 08             sub    $0x8,%rsp
   4:       31 ff                   xor    %edi,%edi
   6:       e8 00 00 00 00          callq  b <main+0xb>
                    7: R_X86_64_PC32        time-0x4
   b:       48 85 c0                test   %rax,%rax
   e:       75 14                   jne    24 <main+0x24>
  10:       ba 01 00 00 00          mov    $0x1,%edx
  15:       be 00 00 00 00          mov    $0x0,%esi
                    16: R_X86_64_32 .rodata.str1.1
  1a:       bf 01 00 00 00          mov    $0x1,%edi
  1f:       e8 00 00 00 00          callq  24 <main+0x24>
                    20: R_X86_64_PC32       __printf_chk-0x4
  24:       bf 00 00 00 00          mov    $0x0,%edi
                    25: R_X86_64_32 .rodata.str1.1+0x4
  29:       e8 00 00 00 00          callq  2e <main+0x2e>
                    2a: R_X86_64_PC32       puts-0x4
  2e:       31 c0                   xor    %eax,%eax
  30:       48 83 c4 08             add    $0x8,%rsp
  34:       c3                      retq

内存中的指令顺序保持不变:先是printf,然后putsretq返回。

有期待

现在替换if (i)为:

if (__builtin_expect(i, 0))

我们得到:

0000000000000000 <main>:
   0:       48 83 ec 08             sub    $0x8,%rsp
   4:       31 ff                   xor    %edi,%edi
   6:       e8 00 00 00 00          callq  b <main+0xb>
                    7: R_X86_64_PC32        time-0x4
   b:       48 85 c0                test   %rax,%rax
   e:       74 11                   je     21 <main+0x21>
  10:       bf 00 00 00 00          mov    $0x0,%edi
                    11: R_X86_64_32 .rodata.str1.1+0x4
  15:       e8 00 00 00 00          callq  1a <main+0x1a>
                    16: R_X86_64_PC32       puts-0x4
  1a:       31 c0                   xor    %eax,%eax
  1c:       48 83 c4 08             add    $0x8,%rsp
  20:       c3                      retq
  21:       ba 01 00 00 00          mov    $0x1,%edx
  26:       be 00 00 00 00          mov    $0x0,%esi
                    27: R_X86_64_32 .rodata.str1.1
  2b:       bf 01 00 00 00          mov    $0x1,%edi
  30:       e8 00 00 00 00          callq  35 <main+0x35>
                    31: R_X86_64_PC32       __printf_chk-0x4
  35:       eb d9                   jmp    10 <main+0x10>

printf(编译__printf_chk)被转移到功能的尽头,后puts由其他的答案中提到,并返回提高分支预测。

因此,它基本上与以下内容相同:

int i = !time(NULL);
if (i)
    goto printf;
puts:
puts("a");
return 0;
printf:
printf("%d\n", i);
goto puts;

未使用进行优化-O0

但是,如果能编写一个__builtin_expect比有没有更快的运行速度的例子,那时候的CPU确实很聪明。我的天真尝试在这里

C ++ 20 [[likely]][[unlikely]]

C ++ 20已标准化了这些C ++内置代码:https : //stackoverflow.com/questions/51797959/how-to-use-c20s-likely-unlikely-attribute-in-if-else-statement它们可能会(a双关!)做同样的事情。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.