Переносные подсказки для прогнозирования отрасли
Есть ли какой-либо переносной способ подсказки предсказания ветвления? Рассмотрим следующий пример:
if (unlikely_condition) {
/* ..A.. */
} else {
/* ..B.. */
}
Разве это не так:
if (!unlikely_condition) {
/* ..B.. */
} else {
/* ..A.. */
}
Или это единственный способ использовать конкретные подсказки компилятора? (например, __builtin_expect на GCC)
Будут ли компиляторы рассматривать условия if
по-разному на основе упорядочения условий?
Ответы
Ответ 1
Канонический способ предсказать статическую ветвь заключается в том, что if
предсказан не-разветвленным (т.е. выполняется каждое предложение if
, а не else
), а петли и обратное - goto
. Поэтому не ставьте общий случай в else
, если вы ожидаете, что статическое предсказание будет значительным. Обход незанятой петли не так прост; Я никогда не пробовал, но я полагаю, что предложение else
должно работать довольно портативно.
Многие компиляторы поддерживают некоторую форму #pragma unroll
, но по-прежнему необходимо охранять ее каким-то #if
для защиты других компиляторов.
Рекомендации по прогнозированию отрасли могут теоретически выразить полное описание того, как преобразовать граф управления потоком программ и упорядочить основные блоки в исполняемой памяти... поэтому есть множество вещей, которые можно выразить, и большинство из них не будут очень переносимыми.
Как рекомендует GNU в документации для __builtin_expect
, оптимизация на основе профиля превосходит намеки и с меньшими усилиями.
Ответ 2
В большинстве случаев следующий код
if (a)
{
...
}
else
{
...
}
на самом деле
evaluate(A)
if (!A)
{
jmp p1
}
... code A
jmp p2
p1:
... code !A
p2:
Обратите внимание, что если A истинно, "код A" уже находится в стадии разработки. Процессор увидит команду "jmp p2" вперед и загрузит код p2 в конвейер.
Если A является ложным, "код! A" может не находиться в pipleline, поэтому он может быть медленнее.
Выводы:
- do Если (X), если X более вероятно, чем! X
- попытайтесь оценить A как можно раньше, чтобы CPU мог динамически оптимизировать конвейер.
:
evaluate(A)
do more stuff
if (A)
...
Ответ 3
Оптимизация по сути является компилятором, поэтому вам нужно использовать функциональность компилятора, чтобы помочь ей. Сам язык не заботится о (или мандате) оптимизации.
Итак, лучшее, что вы можете сделать без расширений, связанных с компилятором, - это организовать ваш код таким образом, чтобы ваши компиляторы "поступили правильно" без помощи. Но если вы хотите быть уверенным, подключитесь к расширениям компилятора. (Вы можете попытаться абстрагировать их за препроцессором, поэтому ваш код останется портативным.)
Ответ 4
Просто соглашайтесь с тем, что вы делаете. Мне нравится использовать
if (!(someExpression))
Но компилятор должен относиться к этому в равной степени.
Ответ 5
Что не так с проверкой конкретного компилятора с помощью #ifdef
и спрятать эти вещи за пользовательским макросом? Вы можете #define
его развернуть до простого выражения в случаях, когда у вас нет компилятора, который поддерживает эти подсказки оптимизации. Недавно я сделал что-то подобное с явным кэшем, который GCC поддерживает через встроенную функцию.