Pentium 4 (он же микроархитектура Netburst) имел подсказки-предикторы ветвей в качестве префиксов к инструкциям jcc, но только P4 когда-либо с ними что-то делал.
См http://ref.x86asm.net/geek32.html. А
Раздел 3.5 превосходного руководства Агнера Фога по ассемблеру , из http://www.agner.org/optimize/. У него также есть руководство по оптимизации в C ++.
Более ранние и более поздние процессоры x86 молча игнорируют эти байты префикса. Есть ли какие-либо результаты теста производительности для использования вероятных / маловероятных подсказок? упоминает, что PowerPC имеет некоторые инструкции перехода, которые содержат подсказку предсказания ветвления как часть кодирования. Это довольно редкая архитектурная особенность. Статическое прогнозирование ветвей во время компиляции очень трудно сделать точно, поэтому обычно лучше оставить это аппаратному обеспечению, чтобы понять это.
Официально опубликовано немного о том, как именно работают предсказатели ветвления и целевые буферы ветвления в самых последних процессорах Intel и AMD. Руководства по оптимизации (их легко найти на сайтах AMD и Intel) дают некоторые советы, но не документируют конкретное поведение. Некоторые люди запускают тесты, чтобы попытаться угадать реализацию, например, сколько записей BTB есть у Core2 ... В любом случае, идея явного намека на предиктор была заброшена (пока).
Что задокументировано, например, то, что Core2 имеет буфер истории ветвлений, который может избежать неправильного прогнозирования выхода из цикла, если цикл всегда выполняет постоянное короткое число итераций, <8 или 16 IIRC. Но не спешите развертывать, потому что цикл, который умещается в 64 байта (или 19 мегапикселей на Penryn), не будет иметь узких мест при извлечении инструкций, потому что он воспроизводит из буфера ... читайте pdf-файлы Agner Fog, они <em>отлично .
См. Также Почему Intel изменила механизм статического прогнозирования ветвлений за эти годы? : Intel, поскольку Sandybridge вообще не использует статическое прогнозирование, насколько мы можем судить по экспериментам с производительностью, которые пытаются обратить вспять -инженер, что делают процессоры. (Многие старые процессоры имеют статическое предсказание как запасной вариант, когда динамическое предсказание отсутствует. Нормальное статическое предсказание состоит в том, что прямые ветви не берутся, а обратные ветви берутся (потому что обратные ветви часто являются ветвями цикла).)
Эффект макросов likely()
/ unlikely()
с использованием GNU C __builtin_expect
(как упоминается в ответе Дракоши) действительно не напрямую вставляет подсказки BP в asm . (Это может возможно сделать с gcc -march=pentium4
, но не при компиляции для чего-либо еще).
Фактический эффект состоит в том, чтобы выложить код так, чтобы на быстром пути было меньше принятых ветвей и, возможно, меньше команд. Это поможет предсказанию ветвлений в случаях, когда статическое предсказание вступает в игру (например, динамические предикторы холодны, на процессорах, которые используют статическое предсказание вместо того, чтобы просто давать ветвям псевдонимы друг другу в кэшах предикторов.)
См. В чем преимущество __builtin_expect GCC в операторах if else? для конкретного примера code-gen.
Взятые ветки стоят немного дороже, чем не взятые, даже если прогноз точно. Когда процессор выбирает код в виде 16-байтовых блоков для параллельного декодирования, взятая ветвь означает, что более поздние инструкции в этом блоке выборки не являются частью потока инструкций, которые должны быть выполнены. Он создает пузыри во внешнем интерфейсе, которые могут стать узким местом в коде с высокой пропускной способностью (который не останавливается во внутреннем интерфейсе при промахах кэша и имеет высокий параллелизм на уровне команд).
Перемещение между различными блоками также потенциально затрагивает больше строк кэша кода , увеличивая объем кэша L1i и, возможно, вызывая больше промахов кэша инструкций, если он был холодным. (И, возможно, след Uop-кэша). Так что это еще одно преимущество в том, чтобы быстрый путь был коротким и линейным.
Оптимизация в профиле GCC обычно делает ненужные / маловероятные макросы ненужными. Компилятор собирает данные времени выполнения о том, каким образом каждая ветвь пошла для принятия решений по компоновке кода, а также для определения горячих и холодных блоков / функций. (например, он будет развертывать циклы в горячих, но не холодных функциях.) См. -fprofile-generate
и -fprofile-use
в руководстве по GCC . Как использовать профильные оптимизации в g ++?
В противном случае GCC должен угадать, используя различные эвристики, если вы не использовали вероятные / маловероятные макросы и не использовали PGO. -fguess-branch-probability
включено по умолчанию при -O1
и выше.
https://www.phoronix.com/scan.php?page=article&item=gcc-82-pgo&num=1 имеет результаты тестов для PGO по сравнению с обычным с gcc8.2 на ЦП масштабируемого сервера Xeon. (Skylake-AVX512). Каждый тест получил по крайней мере небольшое ускорение, а некоторые выиграли примерно на 10%. (Большая часть этого, вероятно, происходит из-за развертывания цикла в горячих циклах, но, вероятно, из-за лучшего расположения ветвей и других эффектов.)