English version: https://telegra.ph/likelyunlikely-11-27
Многие задают вопрос про последнюю статью: что это за атрибут
[[unlikely]]? Этот атрибут взят из черновика стандарта C++20: https://en.cppreference.com/w/cpp/language/attributes/likelyОн подсказывает компилятору, какая ветка условия или case в switch выражении выполняется чаще.
На x86/x64 компилятор поместит likely-ветку в ассемблере сразу же после проверки условия, максимально утилизируя кэш инструкций процессора. Никакого статического предсказания перехода тут нет. Однако не удивлюсь, что в какой-нибудь архитектуре PowerPC найдется отдельная инструкция для подсказки предсказателю переходов.
В компиляторах пока еще нет поддержки likely/unlikely атрибутов, но зато есть расширения:
#define likely(x) __builtin_expect(!!(x), 1)Пример (только Clang и GCC), где ожидаем, что IsValid() чаще будет возвращать true:
#define unlikely(x) __builtin_expect(!!(x), 0)
bool IsValid();Ассемблер:
int Bar();
int Baz();
int Foo() {
if (likely(IsValid()))
return Bar();
else
return Baz();
}
Foo(): # @Foo()Пример, где ожидаем, что IsValid() чаще будет возвращать false:
push rax
call IsValid()
test al, al
je .LBB0_2
pop rax
jmp Bar() # TAILCALL
.LBB0_2:
pop rax
jmp Baz() # TAILCALL
bool IsValid();Ассемблер:
int Bar();
int Baz();
int Foo() {
if (unlikely(IsValid()))
return Bar();
else
return Baz();
}
Foo(): # @Foo()Можно заметить, что компилятор ставит сразу после проверки (команда test) соответствующую нашей подсказке ветку кода. Это потенциально может дать неплохое ускорение, если все правильно разметили (а ведь можно дать и неверную подсказку).
push rax
call IsValid()
test al, al
jne .LBB0_1
pop rax
jmp Baz() # TAILCALL
.LBB0_1:
pop rax
jmp Bar() # TAILCALL
Нужно еще иметь ввиду, что сейчас процессоры очень хорошо предсказывают ветвления, так что разницу можно особо и не заметить.