Какой код лучше подходит для оптимизации прогнозирования ветвлений?

10

Учитывая прогноз ветвления, а также эффект оптимизации компилятора, какой код имеет тенденцию предлагать превосходную производительность?

Обратите внимание, что bRareExceptionPresent представляет собой необычное условие. Это не нормальный путь логики.

/* MOST COMMON path must branch around IF clause */

bool SomeFunction(bool bRareExceptionPresent)
{
  // abort before function
  if(bRareExceptionPresent)
  {
     return false;
  }    
  .. function primary body ..    
  return true;
}

/* MOST COMMON path does NOT branch */

bool SomeFunction(bool bRareExceptionPresent)
{
  if(!bRareExceptionPresent)
  {
    .. function primary body ..
  }
  else
  {
    return false;
  }
  return true;
}
dyasta
источник
9
Я собираюсь выйти на конечности здесь и сказать, что нет никакой разницы вообще.
Роберт Харви
7
Вероятно, это зависит от конкретного процессора, для которого вы компилируете, так как они имеют разные архитектуры конвейерной обработки (временные интервалы и временные интервалы отсутствуют). Время, которое вы потратили на размышления об этом, вероятно, намного больше, чем время, сэкономленное при запуске - сначала профиль, а затем оптимизация.
2
Это почти наверняка преждевременная микрооптимизация.
Роберт Харви
2
@MichaelT Да, профилирование - действительно единственный надежный способ узнать, что на самом деле происходит с производительностью кода на целевой платформе в ее контексте. Однако мне было любопытно, был ли один вообще предпочтен.
Дьяста
1
@RobertHarvey: Это преждевременная микрооптимизация, за исключением случаев, когда выполняются оба условия: (1) цикл называется миллиардами (не миллионами) раз; и (2) по иронии судьбы, когда тело цикла является крошечным с точки зрения машинного кода. Условие № 2 означает, что доля времени, затрачиваемого на накладные расходы, не является незначительной по сравнению со временем, потраченным на полезную работу. Хорошей новостью является то, что обычно в таких ситуациях, когда выполняются оба условия, SIMD (векторизация), которая по своей природе не имеет ответвлений, решает все проблемы производительности.
Rwong

Ответы:

10

В современном мире это не имеет большого значения, если это вообще так.

Динамическое прогнозирование ветвлений (о чем думали десятилетия (см. «Анализ рабочих нагрузок системы динамического прогнозирования ветвлений», опубликованный в 1996 г.)) - довольно распространенное место.

Пример этого можно найти в процессоре ARM. Из информационного центра Arm по прогнозированию отраслей

Для повышения точности прогнозирования ветвлений используется комбинация статических и динамических методов.

Тогда возникает вопрос: что такое динамическое предсказание ветвления в процессоре ARM? Последовательное чтение динамического прогнозирования ветвлений показывает, что в нем используется 2-битная схема прогнозирования (описанная в статье), которая позволяет получить информацию о том, является ли ветвь сильной или слабой или нет.

Со временем (и под временем я имею в виду несколько проходов через этот блок) это накапливает информацию о том, каким образом пойдет код.

Для статического предсказания он смотрит на то, как выглядит сам код и как выполняется ветвление в тесте - на предыдущую инструкцию или на следующую в коде:

Схема, используемая в процессоре ARM1136JF-S, предсказывает, что все прямые условные ветви не взяты, а все обратные ветви приняты. Приблизительно 65% всех ветвей предшествуют достаточному количеству циклов, не связанных с ветвями, чтобы быть полностью предсказанными.

Как упоминал Спарки, это основано на понимании того, что петля чаще всего петля. Цикл ветвится в обратном направлении (у него есть ветвь в конце цикла, чтобы перезапустить его вверху) - он обычно делает это.

Опасность попытки угадать компилятор заключается в том, что вы не знаете, как этот код будет скомпилирован (и оптимизирован). И по большей части это не имеет значения. С динамическим прогнозированием, дважды через функцию он будет предсказывать пропуск через оператор защиты для преждевременного возврата. Если производительность двух очищенных конвейеров имеет критическое значение, есть и другие проблемы, о которых следует беспокоиться.

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


источник
7
Знаменитый вопрос о стекопереработке показал, что предсказание ветвления имеет значение даже сегодня
Флориан Маргэйн
3
@FlorianMargaine, хотя и имеет значение, но оказывается, что он действительно имеет значение, требует понимания того, что вы компилируете и как это работает (рука против x86 против mips ...). Написание кода, пытающегося выполнить эту микрооптимизацию в начале, вероятно, работает из ошибочных предположений и не дает желаемого эффекта.
Ну, конечно, давайте не будем цитировать ДК. Но я думаю, что этот вопрос был явно в смысле оптимизации, когда вы уже прошли этап профилирования. :-)
Флориан Маргэйн
2
@MichaelT Хороший ответ, и я очень согласен с вашим выводом. Этот вид предварительной профилирования / абстрактной оптимизации, безусловно, может быть контрпродуктивным. В конце концов, это игра в догадки, заставляющая принимать дизайнерские решения по иррациональным причинам. Тем не менее, я обнаружил, что мне любопытно; o
dyasta
5
@ 90h stackoverflow.com/questions/11227809/…
Флориан Маргейн
9

Насколько я понимаю, в первый раз, когда ЦП встречает ветвь, он будет предсказывать (если поддерживается), что прямые ветки не берутся, а обратные ветви. Основанием для этого является то, что циклы (которые обычно разветвляются в обратном направлении) предполагаются взятыми.

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

Кроме того, некоторые компиляторы C также поддерживают статическое предсказание ветвления, так что вы можете указать компилятору, какая ветвь более вероятна. В свою очередь, он может реорганизовать сгенерированный код или использовать измененные инструкции, чтобы воспользоваться этой информацией (или даже просто игнорировать ее).

__builtin_expect((long)!!(x), 1L)  /* GNU C to indicate that <x> will likely be TRUE */
__builtin_expect((long)!!(x), 0L)  /* GNU C to indicate that <x> will likely be FALSE */

Надеюсь это поможет.

Спарки
источник
3
«Насколько я понимаю, в первый раз, когда ЦП встречает ветвь, он будет предсказывать (если поддерживается), что прямые ветвления не принимаются, а обратные ветвятся». Это очень интересная мысль. Есть ли у вас доказательства того, что это действительно реализовано в общих архитектурах?
blubb
5
Прямо изо рта лошади: передняя ветвь по умолчанию не принята. Обратная ветвь по умолчанию принята . И с той же страницы: «Префикс 0x3E - статически предсказывать ветвь, как принято».
MSalters
Есть ли прагма, независимая от платформы __builtin_expect?
MarcusJ