Подсказки прогнозирования переносимых переходов

Есть ли какой-нибудь портативный способ делать подсказки предсказания ветвления? Рассмотрим следующий пример:

  if (unlikely_condition) {
    /* ..A.. */
  } else {
    /* ..B.. */
  }

Отличается ли это от выполнения:

  if (!unlikely_condition) {
    /* ..B.. */
  } else {
    /* ..A.. */
  }

Или это единственный способ использовать конкретные подсказки компилятора? (например, __builtin_expect в GCC)

Будут ли компиляторы обрабатывать условия if по-разному в зависимости от порядка условий?


person Oskar N.    schedule 13.09.2010    source источник
comment
Интересно, может ли это быть атрибутом C++0x, чтобы придерживаться условий для if? Как 2_? В настоящее время синтаксис не позволяет этого. Однако он разрешает if([[unlikely]] bool b = ...) { }. Может быть, этим можно злоупотреблять :)   -  person Johannes Schaub - litb    schedule 13.09.2010
comment
Код GNU содержит смехотворное количество if(likely(...)) мусора в коде, совершенно не критичном для производительности, и, по моему мнению, это действительно плохо. Во-первых, это неестественно читается по-английски — это звучит так, будто это условие может быть истинным, а не если это условие истинно, что, скорее всего, так и есть. А для другого это просто беспорядок. Если у вас нет большого количества критичных к производительности условных выражений, которые уже не будут компилироваться в cmov или подобное, просто игнорируйте подсказки предсказания ветвления.   -  person R.. GitHub STOP HELPING ICE    schedule 13.09.2010
comment
@R.. Кажется, я понимаю, почему ядро ​​Linux завалено if(unlikely(...)). Они предпочитают ранние выходы, которые упрощают отслеживание потока кода. Если бы они этого не сделали, то предсказание статического перехода всегда терпело бы неудачу.   -  person Oskar N.    schedule 14.09.2010
comment
И это сделало бы Linux на 0,00001% медленнее. Не измеримо. Если это так, просто поместите эту хрень в несколько условных выражений, где она измерима, а не везде.   -  person R.. GitHub STOP HELPING ICE    schedule 14.09.2010
comment
Это тоже своего рода документальная подсказка. Я часто использую его, чтобы различать активный рабочий код и исключительный код обработки ошибок. Это говорит об архитектуре, над которой я работаю, это довольно полезный механизм, поскольку ISA имеет биты подсказок в инструкции ветвления (SPARC).   -  person Patrick Schlüter    schedule 14.09.2010
comment
Оскар: Я думаю, что @R.. имел в виду glibc, который, э-э... скажем так, не так приятно читать, как Linux Kernel. Но, может быть, это только у меня, знаете ли, вкусы у всех разные.   -  person ninjalj    schedule 01.10.2011
comment
@tristopia: MIPS также имеет нечто подобное: вероятные инструкции ветвления.   -  person ninjalj    schedule 01.10.2011


Ответы (6)


Канонический способ предсказания статического перехода заключается в том, что if предсказывается без ветвления (т. е. выполняется каждое предложение if, а не else), а также выполняются циклы и goto назад. Итак, не помещайте общий случай в else, если вы ожидаете, что статический прогноз будет значительным. Обойти незанятый цикл не так просто; Я никогда не пробовал, но я полагаю, что предложение else должно работать довольно переносимо.

Многие компиляторы поддерживают ту или иную форму #pragma unroll, но для защиты других компиляторов по-прежнему необходимо защищать ее каким-либо видом #if.

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

Как рекомендует GNU в документации для __builtin_expect, оптимизация по профилю лучше, чем подсказки, и с меньшими усилиями.

person Potatoswatter    schedule 13.09.2010
comment
И у VS тоже есть PGO, так что это беспроигрышный вариант. :) - person GManNickG; 13.09.2010

В большинстве случаев следующий код

if (a)
{
   ...
}
else
{
    ...
}

на самом деле

evaluate(A)

if (!A)
{
   jmp p1
}

... code A

   jmp p2

p1:

... code !A

p2:

Обратите внимание, что если A истинно, «код A» уже находится в конвейере. Процессор увидит впереди команду «jmp p2» и загрузит код p2 в конвейер.

Если A имеет значение false, «код !A» может отсутствовать в конвейере, поэтому он может работать медленнее.

Выводы:

  1. do If(X), если X более вероятен, чем !X
  2. постарайтесь оценить A как можно раньше, чтобы ЦП мог динамически оптимизировать конвейер.

:

evaluate(A)

do more stuff

if (A)
   ...
person Lior Kogan    schedule 13.09.2010
comment
понятно! Пожалуйста, держите код «если» в фигурных скобках, даже если это одна строка. В противном случае это немного сбивает с толку! - person Ayyappa; 01.10.2011
comment
постарайтесь оценить A как можно раньше, чтобы ЦП мог динамически оптимизировать конвейер. - Не могли бы вы дать несколько указателей на это утверждение? Я хочу знать, как он это сделает. - person Ayyappa; 01.10.2011
comment
Много информации здесь: download.intel.com/design/pentiumii/manuals/ 24281603.pdf - person Lior Kogan; 01.10.2011
comment
Ссылка @LiorKogan больше не работает. Есть ли способ найти его снова? - person Carlos; 15.03.2021
comment
@Карлос: да. Гугл Интел 24281603. - person Lior Kogan; 15.03.2021

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

Таким образом, лучшее, что вы можете сделать без расширений, специфичных для компилятора, — это организовать свой код таким образом, чтобы ваши компиляторы «делали правильные вещи» без посторонней помощи. Но если вы хотите быть уверенным, подключитесь к расширениям компилятора. (Вы можете попробовать абстрагировать их за препроцессором, чтобы ваш код оставался переносимым.)

person GManNickG    schedule 13.09.2010
comment
Однако существует множество прецедентов языка, предоставляющего подсказки по оптимизации (inline, restrict, register). Некоторые из них более важны для современных компиляторов, чем другие. Не имеет значения, действительно ли конкретная реализация что-то делает с ними: если есть разумный шанс, что кто-то сделает что-то полезное, то это будет хорошая функция. Я считаю, что статическое предсказание ветвлений в основном не соответствует этому критерию, поэтому я не думаю, что было бы плохим решением отказаться от него. Впрочем, это суждение по существу дела, не совсем нам плевать на оптимизацию, никогда. - person Steve Jessop; 13.09.2010
comment
@Steve: Да, наверное, я автоматически игнорирую их, поэтому забываю о них. Ты прав. - person GManNickG; 13.09.2010
comment
Я думаю, что restrict, вероятно, стоит преждевременной оптимизации. Это не принесет никакого вреда, но может принести значительную пользу, предотвратив ужасные зависимости сохранения/загрузки, а там, где это применимо, это предположительно задокументированное требование (как в memcpy), независимо от того, отражено оно в исходном коде или нет. Остальные (и в С++ 03) я согласен с вашим громким голоском :-) - person Steve Jessop; 14.09.2010
comment
@Steve: я согласен, я игнорировал restrict, поскольку он не является частью текущего стандарта. :) - person GManNickG; 14.09.2010
comment
Ах, но это один из тех вопросов с тегами C-и-C++, где даже самый простой ответ может исчезнуть в вихре предостережений. - person Steve Jessop; 14.09.2010
comment
@Steve: О, я даже этого не видел. :Икс - person GManNickG; 14.09.2010

C++20 предлагает вероятные и маловероятные атрибуты

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

person Tony Tannous    schedule 30.01.2020
comment
К сожалению, эта функция еще недоступна в Clang. - person Franklin Yu; 15.04.2020
comment
@FranklinYu кажется, Clang 12 поддерживает это в соответствии с en.cppreference.com/w/cpp/compiler_support< /а>. - person Tony Tannous; 04.12.2020
comment
Это хорошая новость! Спасибо за обновления. - person Franklin Yu; 04.12.2020

Просто будьте последовательны в том, что вы делаете. мне нравится использовать

if (!(someExpression))

Но компилятор должен относиться к этому одинаково.

person JonH    schedule 13.09.2010

Что плохого в проверке конкретного компилятора через #ifdef и сокрытии этих вещей за пользовательским макросом? Вы можете #define расширить его до простого выражения, если у вас нет компилятора, поддерживающего эти подсказки по оптимизации. Недавно я сделал что-то подобное с явной предварительной выборкой кеша, которую GCC поддерживает с помощью встроенной функции.

person sellibitze    schedule 13.09.2010
comment
Я сделал это. С более чем двумя или тремя компиляторами может возникнуть настоящая путаница, если разные компиляторы используют разный синтаксис. - person David Thornley; 13.09.2010