Разработчик может использовать __builtin_expect
встроенный, чтобы помочь компилятору понять, в каком направлении находится ветка скорее всего, пойдет.
В будущем мы можем получить стандартный атрибут для этой цели, но на сегодняшний день по крайней мере все clang
, icc
и gcc
вместо этого поддерживайте нестандартный __builtin_expect
.
Однако icc
, похоже, создает странно ужасный код, когда вы используете его 1. То есть код, который использует встроенный, строго хуже кода без него, независимо от того, в каком направлении производится прогноз.
Возьмем, например, следующую игрушечную функцию:
int foo(int a, int b)
{
do {
a *= 77;
} while (b-- > 0);
return a * 77;
}
Из трех компиляторов icc
является единственным, который компилирует это в оптимальный скалярный цикл из 3 инструкций:
foo(int, int):
..B1.2: # Preds ..B1.2 ..B1.1
imul edi, edi, 77 #4.6
dec esi #5.12
jns ..B1.2 # Prob 82% #5.18
imul eax, edi, 77 #6.14
ret
Оба gcc и Clang управляют пропуском простое решение и используйте 5 инструкций.
С другой стороны, когда вы используете макросы likely
или unlikely
в условии цикла, icc
идет полностью braindead:
#define likely(x) __builtin_expect((x), 1)
#define unlikely(x) __builtin_expect((x), 0)
int foo(int a, int b)
{
do {
a *= 77;
} while (likely(b-- > 0));
return a * 77;
}
Этот цикл функционально эквивалентен предыдущему циклу (поскольку __builtin_expect
просто возвращает свой первый аргумент), но icc производит некоторый ужасный код
foo(int, int):
mov eax, 1 #9.12
..B1.2: # Preds ..B1.2 ..B1.1
xor edx, edx #9.12
test esi, esi #9.12
cmovg edx, eax #9.12
dec esi #9.12
imul edi, edi, 77 #8.6
test edx, edx #9.12
jne ..B1.2 # Prob 95% #9.12
imul eax, edi, 77 #11.15
ret #11.15
Функция удвоилась по размеру до 10 команд, и (что еще хуже!) критический цикл более чем удвоился до 7 команд с длинной цепью критической зависимости, включающей cmov
и другие странные вещи.
То же самое верно, если вы используете unlikely
подсказку, а также во всех версиях icc (13, 14, 17), что godbolt поддерживает. Таким образом, генерация кода строго хуже, независимо от подсказки, и независимо от фактического поведения во время выполнения.
Ни один gcc
и clang
не подвергается деградации при использовании советов.
Что с этим?
1 По крайней мере, в первом и последующих примерах, которые я пробовал.