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

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

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

  • Перезагрузка без отключения?
  • Куда отправился Trampoline.S?
  • что такое глобальная политика планирования Linux?
  • Обновление ядра linux с 3 по 4 в Debian 8
  • Какое «семейство процессоров» выбрать в разделе «Тип и функции процессора»?
  • график производительности TCP-соединения на основе `ss -i`
  • Как использовать собственный алгоритм шифрования в ipsec?
  • Нужно ли перестраивать модуль nvida после перекомпиляции ядра?
  • В: Как сообщить ядру Linux игнорировать устройства MULTIPLE ata?
  • «Make» останавливается при установке tty0tty (эмулятор нуль-модема)
  • Безопасно ли отключать убийцу OOM в веб-сервере / обратном прокси?
  • Interesting Posts

    компиляция с gcc для пользователей, но отлично подходит для root

    Почему некоторые мои журналы используют Localhost и другие Hostname – разные серверы

    insserv: предупреждение: скрипт «XXX.sh» отсутствует теги LSB и переопределения

    Как заменить символ в sed, только если это происходит до буквы?

    Храните драйвер печати Windows на Samba для сервера печати CUPS

    не удалось смонтировать сгоревшую файловую систему ISO9660 до извлечения и повторной установки носителя

    Что произойдет, когда мы подключим устройство к компьютеру

    Лучший способ сжать папку с небольшими предварительно сжатыми файлами (изображениями)?

    Значок прекращает загрузку после перезапуска приложения

    Может ли SSH и SFTP * off * сервер, но не может SFTP * на сервер?

    Заменить строку, содержащую новую строку в огромном файле

    Восстановление файлов с помощью testdisk перестает показывать рост данных и теперь просто отображает количество неудачных попыток ввода

    Установка старого кириллического раздела HFS в современном Linux

    Передайте 150 ГБ файлов со старого MacBook Pro с Ubuntu 13.04 Live DVD

    7za – Извлечь файл с паролем

    Linux и Unix - лучшая ОС в мире.