В чем разница между вероятными и маловероятными вызовами в ядре?

Что между вероятными и маловероятными вызовами в ядре. При поиске по источнику ядра я нашел эти утверждения.

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

Может ли кто-нибудь пролить свет на него?

2 Solutions collect form web for “В чем разница между вероятными и маловероятными вызовами в ядре?”

Это компиляторы для GCC. Они используются в условных выражениях, чтобы сообщить компилятору, может ли ветвь быть принята или нет. Это может помочь компилятору, устанавливая код таким образом, который оптимален для наиболее частого результата.

Они используются следующим образом:

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

Его следует использовать с большой осторожностью (т.е. на основе фактических результатов профилирования ветвей). Неправильный намек может ухудшить производительность (очевидно).

Некоторые примеры оптимизации кода можно легко найти, GCC __builtin_expect поиск GCC __builtin_expect . Это сообщение в блоге gcc: __builtin_expect, например, детализирует разборку с ним.

Вид оптимизации, который может быть сделан, очень специфичен для процессора. Основная идея заключается в том, что часто процессоры будут быстрее запускать код, если он не будет разветвляться / прыгать повсюду. Чем более линейным оно является, и чем более предсказуемы ветви, тем быстрее он будет работать. (Это особенно верно для процессоров с глубокими конвейерами, например).

Таким образом, компилятор будет генерировать код таким образом, чтобы наиболее вероятная ветвь не включала скачок, если это то, что предпочитает целевой процессор.

Давайте декомпилируем, чтобы узнать, что с ним делает 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 а затем puts и возвращает retq .

С ожиданием

Теперь замените 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_chk (скомпилированный в __printf_chk ) был перенесен в самый конец функции, после того, как puts и return улучшили предсказание ветви, как упоминалось в других ответах.

Таким образом, это в основном то же самое, что:

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

Эта оптимизация не была выполнена с -O0 .

Но удача в написании примера, который работает быстрее с __builtin_expect чем без, в те дни процессоры действительно умны . Мои наивные попытки здесь .

  • Установка определенной версии ядра на debian linux
  • Что нового в Kernel 3.0?
  • Ошибка при создании ядра Linux для универсальной платы
  • Как обрабатывать обновления ядра Linux при использовании настраиваемого ядра?
  • Как хранить данные на диске непосредственно в модуле ядра?
  • Ядро Linux 3.10.0-123.4.2 процессов нет
  • Не удалось установить root fs после компиляции нового ядра в VMware
  • Не удалось загрузить виртуальную машину: Ошибка 13: Неверный или неподдерживаемый исполняемый формат
  • configure allowed_tcp_congestion_control через конфигурационные файлы в ядре linux
  • Нет / proc / sys / fs / binfmt_misc / CLR в моей системе?
  • Как использовать собственный алгоритм шифрования в ipsec?
  • Linux и Unix - лучшая ОС в мире.