Предположим, у меня есть какая-то функция IsTrue()
который возвращает bool
, Если программист решит, что IsTrue()
будет обычно возвращаться true
она может использовать GCC __builtin_expect
или аналогичные для ускорения инструкций в этих типичных случаях.
Взяв следующие 2 примера:
// example 1
const bool result = IsTrue();
if (__builtin_expect(result, true))
// do something
;
// example 2
if (__builtin_expect(IsTrue(), true))
// do something
;
При условии, что IsTrue
нетривиально, есть ли разница между ними? Делает размещение IsTrue()
внутри __builtin_expect
список параметров вызывает оценку кэша команд до результат IsTrue
известен или применяется только ветвление после результат IsTrue
рассчитывается?
Все современные процессоры используют суперскалярный конвейер, который предварительно вычисляет инструкции, следующие за тем, который фактически выполняется. Используя __builtin_expect
компилятор переупорядочивает инструкции так, чтобы ожидаемый (вероятный) путь не использовал переходы, которые приводят к пропуску суперскалярного конвейера и, следовательно, делают все предварительно вычисленные результаты бесполезными.
РЕДАКТИРОВАТЬ: это, конечно, упрощено. Современные процессоры также имеют предсказание ветвления, поэтому они пытаются предсказать, по какому пути будет следовать код, но путь без переходов все еще предпочтителен (это также лучше для кэша, потому что путь к исполняемому коду компактен). Также обратите внимание, что эти изменения в скорости, как правило, довольно незначительны, и вам не нужно беспокоиться, если ваш код выполняется не слишком часто или действительно требует как можно большей скорости.
Как говорится в документации GCC, предпочитайте строить с оптимизацией по профилю (PGO, -fprofile-generate
с последующим запуском тестовых сценариев и восстановлением с -fprofile-use
) чтобы писать с __builtin_expect
,
Встроенный в CPU прогноз ветвлений будет кэшировать большинство веток в «типичных» путях выполнения программы. Статическое предсказание ветвлений имеет значение, главным образом, когда оно предоставляет надежные данные о все ветви, поэтому, когда у компилятора есть выбор между выполнением одного из нескольких тестов в первую очередь, он может организовать наиболее эффективную последовательность. При этом учитывается не только то, к чему относятся значения, но какие else if
заявление выбирается чаще всего.
__builtin_expect
редко будет иметь значение. Для ветвящихся программ, -fprofile-use
обычно обеспечивает мгновенный прирост на 10-30% без каких-либо усилий при программировании.
Делает размещение
IsTrue()
внутри__builtin_expect
список параметров приводит к тому, что кэш инструкций должен оцениваться до результатаIsTrue
известен, или предсказание ветвления применяется только после результатаIsTrue
рассчитывается?
__builtin_expect
происходит во время компиляции. Кеш инструкций еще даже не существует. Компилятор догадается, что IsTrue
является true
и затем во время выполнения процессор сделает еще одно, более обоснованное предположение.