Можно ли сказать предиктору ветки, насколько вероятно следовать за веткой? - PullRequest
72 голосов
/ 05 декабря 2009

Просто чтобы прояснить, я не собираюсь здесь переносить что-либо, поэтому любые решения, которые привязывают меня к определенной коробке, подойдут.

По сути, у меня есть оператор if, который в 99% случаев оценивается как true, и я пытаюсь использовать все последние часы производительности, могу ли я выполнить какую-то команду компилятора (используя GCC 4.1.2 и x86 ISA, если это имеет значение) сообщить предиктору ветки, что он должен кешировать эту ветку?

Ответы [ 7 ]

69 голосов
/ 05 декабря 2009

Да, но эффект будет нет . Исключения составляют старые (устаревшие) архитектуры до Netburst, и даже тогда они не делают ничего измеримого.

В некоторых старых архитектурах Intel вводила код операции "подсказка ветвления", представленный в архитектуре Netburst, и статическое предсказание ветвления по умолчанию для холодных переходов (прогнозирование назад выполнено, прогнозирование вперед не принято). GCC реализует это с помощью __builtin_expect (x, prediction), где прогноз обычно равен 0 или 1. Код операции, передаваемый компилятором, игнорируется на всех более новых архитектурах процессора (> = Core 2). Небольшой угловой случай, когда это действительно что-то делает, - это случай холодного прыжка на старой архитектуре Netburst. Корпорация Intel рекомендует теперь не использовать статические подсказки ветвления, возможно потому, что они считают увеличение размера кода более вредным, чем возможное предельное ускорение.

Помимо бесполезной подсказки ветвления для предиктора, __builtin_expect имеет свое применение, компилятор может изменить порядок кода, чтобы улучшить использование кэша или сохранить память.

Есть несколько причин, по которым он не работает должным образом.

  • Процессор может отлично предсказывать небольшие циклы (n <64). </li>
  • Процессор может отлично предсказать небольшие повторяющиеся последовательности (n ~ 7).
  • Сам процессор может оценить вероятность перехода во время выполнения лучше, чем компилятор / программист во время компиляции.
  • Предсказуемость (= вероятность того, что ветвь будет предсказана правильно) ветви намного важнее, чем вероятность того, что ветвь взята. К сожалению, это сильно зависит от архитектуры, и предсказать предсказуемость ветвления очень сложно.

Подробнее о внутренних работах предсказания ветвлений читайте в Agner Fogs manual . См. Также список рассылки gcc .

58 голосов
/ 05 декабря 2009

Да. http://kerneltrap.org/node/4705

__builtin_expect - это метод, который GCC (версии> = 2,96) предложение для программисты указать ветку прогнозная информация к компилятор. Возвращаемое значение __builtin_expect является первым аргументом (который может быть только целым числом) перешел к нему.

if (__builtin_expect (x, 0))
                foo ();

     [This] would indicate that we do not expect to call `foo', since we
     expect `x' to be zero. 
30 голосов
/ 05 декабря 2009

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%. (Большая часть этого, вероятно, происходит из-за развертывания цикла в горячих циклах, но, вероятно, из-за лучшего расположения ветвей и других эффектов.)

6 голосов
/ 05 декабря 2009

Я предлагаю вместо того, чтобы беспокоиться о прогнозировании ветвей, профилируйте код и оптимизируйте код, чтобы уменьшить количество ветвей. Один пример - развертывание цикла, а другой - использование методов логического программирования вместо использования операторов if.

Большинство процессоров любят предварительно выбирать операторы. Как правило, оператор ветвления генерирует ошибку внутри процессора, заставляя его очищать очередь предварительной выборки. Это где самый большой штраф. Чтобы уменьшить это время штрафа, перепишите (и спроектируйте) код, чтобы было доступно меньше ветвей. Кроме того, некоторые процессоры могут условно выполнять инструкции без необходимости ветвления.

Я оптимизировал программу с 1 часа времени выполнения до 2 минут, используя развертывание цикла и большие буферы ввода / вывода. В этом случае предсказание ветвления не принесло бы большой экономии времени.

1 голос
/ 05 декабря 2009

SUN C Studio имеет несколько прагм, определенных для этого случая.

# прагма редко * вызывается ()

Это работает, если одна часть условного выражения является вызовом функции или начинается с вызова функции.

Но нет способа пометить универсальный оператор if / while

0 голосов
/ 05 декабря 2009

Это звучит для меня как излишнее - этот тип оптимизации сэкономит крошечное количество времени. Например, использование более современной версии gcc окажет гораздо большее влияние на оптимизацию. Кроме того, попробуйте включить и отключить все различные флаги оптимизации; не все они улучшают производительность.

По сути, кажется невероятным, что это будет иметь какое-то существенное значение по сравнению со многими другими плодотворными путями.

РЕДАКТИРОВАТЬ: спасибо за комментарии. Я сделал это сообщество вики, но оставил его, чтобы другие могли видеть комментарии.

0 голосов
/ 05 декабря 2009

Нет, потому что нет команды ассемблера, чтобы сообщить предиктору ветвления. Не беспокойтесь об этом, предсказатель ветвления довольно умен.

Также обязателен комментарий о преждевременной оптимизации и о том, как это плохо.

РЕДАКТИРОВАТЬ: Drakosha упомянул некоторые макросы для GCC. Однако я считаю, что это оптимизация кода и на самом деле не имеет ничего общего с предсказанием ветвлений.

...