Hãy dịch ngược để xem GCC 4.8 làm gì với nó
Không có __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;
}
Biên dịch và dịch ngược với GCC 4.8.2 x86_64 Linux:
gcc -c -O3 -std=gnu11 main.c
objdump -dr main.o
Đầu ra:
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
Thứ tự lệnh trong bộ nhớ không thay đổi: đầu tiên printfvà sau đó putsvà retqtrả về.
Với __builtin_expect
Bây giờ thay thế if (i)bằng:
if (__builtin_expect(i, 0))
và chúng tôi nhận được:
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>
Các printf(biên soạn để __printf_chk) đã được chuyển đến tận cùng của hàm, sau putsvà sự trở lại để cải thiện dự đoán rẽ nhánh như đã đề cập bởi câu trả lời khác.
Vì vậy, về cơ bản nó giống như:
int main() {
    int i = !time(NULL);
    if (i)
        goto printf;
puts:
    puts("a");
    return 0;
printf:
    printf("%d\n", i);
    goto puts;
}
Tối ưu hóa này đã không được thực hiện với -O0.
Nhưng may mắn khi viết một ví dụ chạy nhanh __builtin_expecthơn không có, CPU thực sự thông minh ngày nay . Những nỗ lực ngây thơ của tôi đang ở đây .
C ++ 20 [[likely]]và[[unlikely]]
C ++ 20 đã tiêu chuẩn hóa các phần tử tích hợp C ++ đó: Cách sử dụng thuộc tính có khả năng / không có khả năng của C ++ 20 trong câu lệnh if-other Họ có thể (một cách chơi chữ!) Làm điều tương tự.