逆コンパイルして、GCC 4.8がGCC 4.8で何をするかを見てみましょう
なし __builtin_expect
#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、次にputs、そしてretq戻り。
と __builtin_expect
次に置き換えます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 main() {
    int i = !time(NULL);
    if (i)
        goto printf;
puts:
    puts("a");
    return 0;
printf:
    printf("%d\n", i);
    goto puts;
}
この最適化は、では行われていません-O0。
しかし__builtin_expect、CPUは、使用しない場合よりも速く実行される例を書くのに幸運です。最近のCPUは本当に賢いです。私の素朴な試みはここにあります。
C ++ 20 [[likely]]および[[unlikely]]
C ++ 20はこれらのC ++ビルトインを標準化しました:if-elseステートメントでC ++ 20の見込み/非見込み属性を使用する方法彼らはおそらく(しゃれ!)同じことをします。