Для архитектур Intel есть ли способ инструктировать компилятор GCC генерировать код, который всегда вызывает предсказание ветвления определенным образом в моем коде? Поддерживает ли это оборудование Intel? А как насчет других компиляторов или оборудования?
Я бы использовал это в коде C ++, где я знаю случай, когда я хочу работать быстро, и не беспокоюсь о замедлении, когда нужно перейти в другую ветвь, даже если она недавно приняла эту ветку.
for (;;) {
if (normal) { // How to tell compiler to always branch predict true value?
doSomethingNormal();
} else {
exceptionalCase();
}
}
В качестве следующего вопроса для Евджана Мустафы, может ли подсказка просто указать подсказку, когда процессор впервые встречает инструкцию, все последующие предсказания ветвления работают нормально?
Ответы:
Начиная с C ++ 20 вероятные и маловероятные атрибуты должны быть стандартизированы и уже поддерживаются в g ++ 9 . Итак, как обсуждалось здесь , вы можете написать
например, в следующем коде блок else становится встроенным благодаря
[[unlikely]]
блоку ifссылка Godbolt для сравнения наличия / отсутствия атрибута
источник
[[unlikely]]
вif
vs[[likely]]
вelse
?GCC поддерживает функцию,
__builtin_expect(long exp, long c)
обеспечивающую такую возможность. Вы можете проверить документацию здесь .Где
exp
используется условие, а гдеc
- ожидаемое значение. Например, в вашем случае вы хотитеИз-за неудобного синтаксиса это обычно используется путем определения двух настраиваемых макросов, например
просто чтобы облегчить задачу.
Имейте в виду, что:
источник
constexpr
функцию?constexpr
функция может заменить этот макрос.if
Я считаю, что это должно быть прямо в заявлении. По той же причинеassert
никогда не могло бытьconstexpr
функцией.constexpr
говорит только о стоимости семантике, а не встраивание в конкретной реализации сборки); прямая интерпретация (без встроенного кода) бессмысленна. Нет никаких причин использовать для этого функцию.__builtin_expect
само по себе подсказка по оптимизации, поэтому утверждение, что метод, упрощающий его использование, зависит от оптимизации ... неубедительно. Кроме того, я добавилconstexpr
спецификатор не для того, чтобы он работал в первую очередь, а для того, чтобы он работал в постоянных выражениях. И да, есть причины использовать функцию. Например, я бы не хотел засорять все свое пространство имен милым маленьким именем, напримерlikely
. Я бы использовал, напримерLIKELY
, чтобы подчеркнуть, что это макрос, и избежать коллизий, но это просто уродливо.gcc имеет long __builtin_expect (long exp, long c) ( выделено мной ):
Как отмечается в документации, вы должны предпочесть использовать фактическую обратную связь с профилем, и в этой статье показан практический пример этого и то, как в их случае, по крайней мере, в конечном итоге становится лучше, чем использование
__builtin_expect
. Также см. Как использовать оптимизацию под руководством профиля в g ++? ,Мы также можем найти статью для новичков в ядре Linux о макросах ядра вероятно () и маловероятно (), которые используют эту функцию:
Обратите внимание на то, что
!!
используется в макросе, мы можем найти объяснение этого в разделе Почему использовать !! (условие) вместо (условие)? ,Тот факт, что этот метод используется в ядре Linux, не означает, что его всегда имеет смысл использовать. Из этого вопроса видно, что я недавно ответил на разницу между производительностью функции при передаче параметра как константы времени компиляции или переменной, что многие методы ручной оптимизации не работают в общем случае. Нам нужно тщательно профилировать код, чтобы понять, эффективен ли метод. Многие старые методы могут даже не иметь отношения к современной оптимизации компилятора.
Обратите внимание: хотя встроенные команды не переносятся, clang также поддерживает __builtin_expect .
Также на некоторых архитектурах это может не иметь значения .
источник
Нет, нет. (По крайней мере, на современных процессорах x86.)
__builtin_expect
упомянутые в других ответах влияют на то, как gcc упорядочивает код сборки. Он не влияет напрямую на предсказатель ветвления ЦП. Конечно, переупорядочение кода окажет косвенное влияние на предсказание ветвлений. Но на современных процессорах x86 нет инструкции, которая говорит ЦП «предположить, что эта ветвь занята / не занята».См. Этот вопрос для более подробной информации: Intel x86 0x2E / 0x3E Prefix Branch Prediction действительно используется?
Чтобы быть ясным,
__builtin_expect
и / или использование-fprofile-arcs
может улучшить производительность вашего кода, как путем предоставления подсказок для предсказателя ветвления через макет кода (см. Оптимизация производительности сборки x86-64 - Выравнивание и прогнозирование ветвления ), так и за счет улучшения поведения кеша за счет отделения «маловероятного» кода от «вероятного».источник
__builtin_expect
.__builtin_expect
. Так что это должен быть просто комментарий. Но это не ложь, поэтому я снял свой голос против.__builtin_expect
тривиально для создания тестового примера, который вы можете измерить,perf stat
который будет иметь очень высокую частоту ошибочного предсказания ветвления. Это просто влияет на расположение веток . И, кстати, Intel с Sandybridge или, по крайней мере, Haswell не использует статическое предсказание много / вообще; В BHT всегда есть какое-то предсказание, будь то устаревший псевдоним или нет. xania.org/201602/bpu-part-twoПравильный способ определения вероятных / маловероятных макросов в C ++ 11 следующий:
Этот метод, в отличие от него
[[likely]]
, совместим со всеми версиями C ++, но полагается на нестандартное расширение__builtin_expect
.Когда эти макросы определены таким образом:
Это может изменить смысл
if
операторов и нарушить код. Рассмотрим следующий код:И его вывод:
Как видите, определение LIKELY using
!!
в качестве приведенияbool
нарушает семантикуif
.Дело здесь не в этом
operator int()
иoperator bool()
должно быть связано. Это хорошая практика.Вместо этого использование
!!(x)
вместоstatic_cast<bool>(x)
теряет контекст для контекстных преобразований C ++ 11 .источник
switch
, спасибо. Используемое здесь контекстное преобразование частично связано с типомbool
и пятью конкретными контекстами, перечисленными там , которые не включаютswitch
контекст.(_Bool)(condition)
, потому что C не имеет перегрузки операторов.(condition)
, not!!(condition)
. Обаtrue
после изменения этого (протестировано с g ++ 7.1). Можете ли вы построить пример, который действительно демонстрирует проблему, о которой вы говорите, когда используете!!
для логического преобразования?Поскольку все другие ответы были адекватно предложены, вы можете использовать,
__builtin_expect
чтобы дать компилятору подсказку о том, как организовать код сборки. Как отмечают официальные документы , в большинстве случаев встроенный в ваш мозг ассемблер будет не так хорош, как ассемблер, созданный командой GCC. Для оптимизации кода всегда лучше использовать фактические данные профиля, а не гадать.Аналогичным образом, но еще не упомянутым, существует специфичный для GCC способ заставить компилятор генерировать код по «холодному» пути. Это предполагает использование
noinline
иcold
атрибутов, которые делают именно то , что они звучат , как они делают. Эти атрибуты могут применяться только к функциям, но в C ++ 11 вы можете объявлять встроенные лямбда-функции, и эти два атрибута также могут применяться к лямбда-функциям.Хотя это все еще попадает в общую категорию микрооптимизации, и поэтому применяется стандартный совет - тестируйте, а не угадайте - я считаю, что в целом он более полезен, чем
__builtin_expect
. Вряд ли какие-либо поколения процессоров x86 используют подсказки предсказания ветвления ( справочные ), поэтому единственное, на что вы все равно сможете повлиять, - это порядок кода сборки. Поскольку вы знаете, что такое код обработки ошибок или «пограничный» код, вы можете использовать эту аннотацию, чтобы гарантировать, что компилятор никогда не предскажет ответвление для него и свяжет его с «горячим» кодом при оптимизации по размеру.Пример использования:
Более того, GCC автоматически проигнорирует это в пользу обратной связи профиля, когда она доступна (например, при компиляции с
-fprofile-use
).См. Официальную документацию здесь: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes
источник
__builtin_expect
делает. Это совсем не бесполезно. Вы правы в том, чтоcold
атрибут тоже полезен, но__builtin_expect
я думаю , вы недооцениваете полезность .__builtin_expect можно использовать, чтобы сообщить компилятору, в каком направлении должна идти ветвь. Это может повлиять на то, как генерируется код. Типичные процессоры последовательно запускают код быстрее. Итак, если вы напишете
компилятор сгенерирует код вроде
Если ваша подсказка верна, код будет выполнен без каких-либо фактически выполненных ветвей. Он будет работать быстрее, чем обычная последовательность, где каждый оператор if будет ветвиться вокруг условного кода и выполнять три ветви.
В новых процессорах x86 есть инструкции для ветвей, которые, как ожидается, будут выполняться, или для веток, которые, как ожидается, не будут выполняться (есть префикс инструкции; не уверен в деталях). Не уверен, что процессор использует это. Это не очень полезно, потому что с этим отлично справится предсказание ветвления. Поэтому я не думаю, что вы действительно можете повлиять на предсказание ветвления .
источник
Что касается OP, нет, в GCC нет способа указать процессору всегда предполагать, что ветвь взята или не занята. У вас есть __builtin_expect, который делает то, что другие говорят. Кроме того, я думаю , вы не хотите сказать , процессор ли берется ветвь или не всегда . Современные процессоры, такие как архитектура Intel, могут распознавать довольно сложные шаблоны и эффективно адаптироваться.
Однако бывают случаи, когда вы хотите взять на себя управление тем, будет ли по умолчанию предсказано, что ветвление выполнено или нет: когда вы знаете, что код будет называться «холодным» в отношении статистики ветвления.
Один конкретный пример: код управления исключениями. По определению, код управления будет выполняться в исключительных случаях, но, возможно, когда это произойдет, желательна максимальная производительность (может возникнуть критическая ошибка, которую необходимо устранить как можно скорее), поэтому вы можете захотеть контролировать прогноз по умолчанию.
Другой пример: вы можете классифицировать свой ввод и перейти к коду, который обрабатывает результат вашей классификации. Если существует много классификаций, процессор может собирать статистику, но терять ее, потому что такая же классификация не происходит достаточно скоро, а ресурсы прогнозирования выделяются для недавно вызванного кода. Я бы хотел, чтобы был примитив, который говорил процессору «пожалуйста, не выделяйте ресурсы прогнозирования для этого кода», как иногда можно сказать «не кэшировать это».
источник