Я просто хочу добавить, что существуют диспетчеры пакетов, которые компилируют пакеты из источника и обрабатывают все зависимости от пакета, флаги, и т.д.
В системах BSD это ports
: Использование набора портов
В Debian, apt-get
диспетчер пакетов может установить из источника также: ПРАКТИЧЕСКОЕ РУКОВОДСТВО APT: Работа с исходными пакетами (То же идет для Ubuntu, Linux Mint и всего остального на основе Debian),
Хинду распределение использует portage
диспетчер пакетов, который компилирует целую систему из источника только: Введение Перевозки.
Slackware может скомпилировать пакеты, но я не знаю, существует ли какой-либо диспетчер пакетов для этого там..=)
Так или иначе можно всегда компилировать пакеты вручную как Sandy, упомянул выше =) Также должно быть возможно использовать apt-get
или portage
диспетчеры пакетов в любом другом дистрибутиве...
Они - подсказки компилятора для 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, например, детализирует дизассемблирование с ним.
Вид оптимизации, которая может быть сделана, является очень определенным для процессора. Общее представление состоит в том, что часто, процессоры выполнят код быстрее, если он не будет переходить/нещадно критиковать место. Чем более линейно это, и более предсказуемое, которое ответвления, тем быстрее это будет работать. (Это особенно верно для процессоров с глубокими конвейерами, например.)
Таким образом, компилятор испустит код, таким образом, что наиболее вероятное ответвление не включит переход, если это будет тем, что целевой ЦП предпочитает, например.
Давайте декомпилируем, чтобы посмотреть, что с этим делает GCC 4.8
Без 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
return.
С ожиданием
Теперь замените 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
, чем без него, процессоры действительно умны в наши дни. Мои наивные попытки здесь.
C++20 [[вероятно]]
и [[маловероятно]]
C++20 стандартизировал эти встроенные модули C++: https://stackoverflow.com/questions/51797959/how-to-use-c20s-likely-unlikely-attribute-in-if-else-statement Они, вероятно (каламбур!), будут делать то же самое.