Вероятный / маловероятный эквивалент для MSVC
Компилятор GCC поддерживает оператор __builtin_expect, который используется для определения вероятных и маловероятных макросов.
например.
#define likely(expr) (__builtin_expect(!!(expr), 1))
#define unlikely(expr) (__builtin_expect(!!(expr), 0))
Есть ли эквивалентное утверждение для компилятора Microsoft Visual C или что-то подобное?
8 ответов
Я говорю просто пунт
Там нет ничего подобного. Есть __assume (), но не используйте его, это директива оптимизатора другого рода.
Действительно, причина, по которой встроенная в GNU оболочка заключена в макрос, заключается в том, что вы можете просто избавиться от нее автоматически, если __GNUC__
не определено. В этих макросах нет ничего необходимого, и я уверен, что вы не заметите разницу во времени выполнения.
Резюме
Просто избавиться от (ноль) *likely
на не-GNU. Вы не пропустите это.
Стандарт C++20 будет включать [[likely]]
а также [[unlikely]]
атрибуты прогнозирования ветвления.
Последнюю версию предложения об атрибутах можно найти по http://wg21.link/p0479.
Оригинальное предложение по атрибутам можно найти по http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0479r0.html
Программисты должны предпочесть PGO. Атрибуты могут легко снизить производительность, если применяются неправильно или позже становятся неправильными при изменении программы.
Согласно http://www.akkadia.org/drepper/cpumemory.pdf (стр. 57), все еще имеет смысл использовать статическое предсказание ветвления, даже если процессор предсказывает правильно динамически. Это связано с тем, что кэш L1i будет использоваться еще эффективнее, если статическое предсказание выполнено правильно.
Я знаю, что этот вопрос касается Visual Studio, но я постараюсь ответить для как можно большего числа компиляторов (включая Visual Studio)...
Спустя десятилетие прогресс есть! Начиная с Visual Studio 2019 MSVC по-прежнему не поддерживает ничего подобного (хотя это самый популярный встроенный / встроенный), но, как упоминал выше Паули Ниеминен, C++20 имеет likely
/ unlikely
атрибуты, которые можно использовать для создания вероятных / маловероятных макросов, и MSVC обычно довольно быстро добавляет поддержку новых стандартов C++ (в отличие от C), поэтому я ожидаю, что Visual Studio 2021 будет их поддерживать.
В настоящее время (2019-10-14) только GCC поддерживает эти атрибуты, и даже тогда применяется только к меткам, но этого достаточно, чтобы хотя бы провести базовое тестирование. Вот быстрая реализация, которую вы можете протестировать в Compiler Explorer:
#define LIKELY(expr) \
( \
([](bool value){ \
switch (value) { \
[[likely]] case true: \
return true; \
[[unlikely]] case false: \
return false; \
} \
}) \
(expr))
#define UNLIKELY(expr) \
( \
([](bool value){ \
switch (value) { \
[[unlikely]] case true: \
return true; \
[[likely]] case false: \
return false; \
} \
}) \
(expr))
Вы, вероятно, захотите использовать #ifdef вокруг него для поддержки компиляторов, которые не могут с этим справиться, но, к счастью, большинство компиляторов поддерживают __builtin_expect
:
- GCC 3.0
- лязгать
- ICC как минимум с 13, наверное, намного дольше.
- Oracle Development Studio 12.6+, но только в режиме C++.
- ARM 4.1
- IBM XL C/C++ по крайней мере с 10.1, возможно, дольше.
- TI с версии 6.1
- TinyCC с версии 0.9.27
GCC 9+ также поддерживает __builtin_expect_with_probability
. Он больше нигде не доступен, но, надеюсь, однажды… Требуется много догадок, чтобы понять, использовать ли вероятность / маловероятно или нет - вы просто устанавливаете вероятность, и компилятор (теоретически) поступает правильно.
Кроме того, clang поддерживает __builtin_unpredictable
(начиная с 3.8, но проверьте это с помощью __has_builtin(__builtin_unpredictable)
). Поскольку в наши дни многие компиляторы основаны на clang, он, вероятно, и в них работает.
Если вы хотите, чтобы все это было готово к работе, возможно, вас заинтересует один из моих проектов, Hedley. Это единый общедоступный заголовок C/C++, который работает практически со всеми компиляторами и содержит множество полезных макросов, включая HEDLEY_LIKELY
, HEDLEY_UNLIKELY
, HEDLEY_UNPREDICTABLE
, HEDLEY_PREDICT
, HEDLEY_PREDICT_TRUE
, а также HEDLEY_PREDICT_FALSE
. У него еще нет версии C++20, но она скоро должна быть там...
Даже если вы не хотите использовать Hedley в своем проекте, вы можете проверить его реализации вместо того, чтобы полагаться на приведенные выше списки; Я, вероятно, забуду обновить этот ответ новой информацией, но Хедли всегда должен быть в курсе.
В соответствии с документом Intel по реорганизации филиалов и циклов для предотвращения ошибочных прогнозов:
Чтобы эффективно написать свой код, чтобы воспользоваться этими правилами, при написании операторов if-else или switch сначала проверьте наиболее распространенные случаи и постепенно переходите к наименьшим.
К сожалению, вы не можете написать что-то вроде
#define if_unlikely(cond) if (!(cond)); else
потому что оптимизатор MSVC от VS10 игнорирует такую "подсказку".
Поскольку я предпочитаю сначала иметь дело с ошибками в своем коде, я пишу менее эффективный код. К счастью, во второй раз, когда ЦП встречает ветку, он использует свою статистику вместо статической подсказки.
__assume должен быть похожим.
Однако, если вы хотите сделать это действительно хорошо, вы должны использовать Оптимизацию профиля, а не статические подсказки.
Теперь MS сказал, что они реализовали вероятные / маловероятные атрибуты
Но на самом деле нет никакой разницы между использованием «вероятно» или «неиспользованием».
Я скомпилировал эти коды и получил тот же результат .
int main()
{
int i = rand() % 2;
if (i) [[likely]]
{
printf("Hello World!\n");
}
else
{
printf("Hello World2%d!\n",i);
}
}
int main()
{
int i = rand() % 2;
if (i)
{
printf("Hello World!\n");
}
else [[likely]]
{
printf("Hello World2%d!\n",i);
}
}
int pdb._main (int argc, char **argv, char **envp);
0x00401040 push ebp
0x00401041 mov ebp, esp
0x00401043 push ecx
0x00401044 call dword [rand] ; pdb.__imp__rand
; 0x4020c4
0x0040104a and eax, 0x80000001
0x0040104f jns 0x401058
0x00401051 dec eax
0x00401052 or eax, 0xfffffffe ; 4294967294
0x00401055 add eax, 1
0x00401058 je 0x40106d
0x0040105a push str.Hello_World ; pdb.___C__0O_NFOCKKMG_Hello_5World__CB_6
; 0x402108 ; const char *format
0x0040105f call pdb._printf ; int printf(const char *format)
0x00401064 add esp, 4
0x00401067 xor eax, eax
0x00401069 mov esp, ebp
0x0040106b pop ebp
0x0040106c ret
0x0040106d push 0
0x0040106f push str.Hello_World2_d ; pdb.___C__0BB_DODJFBPJ_Hello_5World2__CFd__CB_6
; 0x402118 ; const char *format
0x00401074 call pdb._printf ; int printf(const char *format)
0x00401079 add esp, 8
0x0040107c xor eax, eax
0x0040107e mov esp, ebp
0x00401080 pop ebp
0x00401081 ret
Поскольку вопрос старый, ответы о том, что в MSVC нет / или что нет никакого воздействия, устарели.
Последняя версия MSVC поддерживает
[[likely]]
/
[[unlikely]]
в
/std:c++20
а также
/std:c++latest
режимы.
См. Демонстрацию в обозревателе компилятора Godbolt, которая показывает разницу.
Как видно из приведенной выше ссылки, один видимый эффект на x86 / x64 для
if-else
Утверждение состоит в том, что условный переход вперед будет для маловероятной ветви. До C++20 и поддерживающей версии VS этого же можно было добиться, поместив вероятную ветвь в
if
часть, и маловероятное разветвление в
else
часть, отрицая условие по мере необходимости.
Учтите, что эффект от такой оптимизации минимален. Для часто вызываемого кода в замкнутом цикле динамическое предсказание ветвления в любом случае будет правильным.