Я бы сказал, что первое быстрее!
Даже если вы «оптимизируете» предсказание ветвления для второго, это просто означает, что return m_bVisible;
появится в коде ASM раньше, чем вашreturn false
, вам все равно придется выполнить до четырех сравнений в ваших операторах if.
Однако, поскольку, как вы говорите, они очень НЕВЕРНЫЕ, нет необходимости делать это, прежде чем взглянуть на m_bVisible
.В первом примере сначала проверяется m_bVisible
, который, похоже, не смещен ни в ЛОЖЬ, ни в ИСТИНА.
Я догадываюсь, что для всех случаев, когда m_bVisible == false
, первый будет быстрее.Во всех других случаях не будет большой разницы, за исключением минимальных издержек JMP во втором примере (которые требуют, чтобы вы перепрыгнули через return m_bVisible;
, чтобы перейти к фактическому return false
).
EDIT
Давайте более подробно рассмотрим сборки x86_64 (gcc 8) обоих вариантов:
Тестовый код:
#include <stdio.h>
bool a=true; int b=0; int c=0; bool d=false; bool e=true;
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
int main()
{
marker1:
return a && (b > 0 || c > 0) && (!d || e);
}
int alternative()
{
if (unlikely(b == 0 && c == 0))
{
return false;
}
if (unlikely(d && !e))
{
return false;
}
return a;
}
X86_86 Сборка (GCC 8.2) для обеих функций:
main ()
main:
push rbp
mov rbp, rsp
movzx eax, BYTE PTR a[rip]
test al, al
je .L3
mov eax, DWORD PTR b[rip]
test eax, eax
jg .L4
mov eax, DWORD PTR c[rip]
test eax, eax
jle .L3
.L4:
movzx eax, BYTE PTR d[rip]
xor eax, 1
test al, al
jne .L5
movzx eax, BYTE PTR e[rip]
test al, al
je .L3
.L5:
mov eax, 1
jmp .L6
.L3:
mov eax, 0
.L6:
movzx eax, al
pop rbp
ret
alternative ()
alternative():
push rbp
mov rbp, rsp
mov eax, DWORD PTR b[rip]
test eax, eax
sete al
movzx eax, al
test rax, rax
je .L9
mov eax, DWORD PTR c[rip]
test eax, eax
sete al
movzx eax, al
test rax, rax
je .L9
mov eax, 0
jmp .L10
.L9:
movzx eax, BYTE PTR d[rip]
movzx eax, al
test rax, rax
je .L11
movzx eax, BYTE PTR e[rip]
xor eax, 1
movzx eax, al
test rax, rax
je .L11
mov eax, 0
jmp .L10
.L11:
movzx eax, BYTE PTR a[rip]
movzx eax, al
.L10:
pop rbp
ret
В первом варианте, в main (), самый быстрый выход находится на пятой строке,je .L3
.Во втором варианте самый быстрый выход происходит намного позже ... в .L9 в je .L11
EDIT2
Просто, чтобы завершить, вот сборка для -O3
main:
xor eax, eax
cmp BYTE PTR a[rip], 0
je .L1
cmp DWORD PTR b[rip], 0
jle .L10
.L4:
cmp BYTE PTR d[rip], 0
mov eax, 1
je .L1
movzx eax, BYTE PTR e[rip]
.L1:
ret
.L10:
cmp DWORD PTR c[rip], 0
jg .L4
ret
И
alternative():
mov eax, DWORD PTR b[rip]
or eax, DWORD PTR c[rip]
je .L11
cmp BYTE PTR d[rip], 0
jne .L18
.L13:
movzx eax, BYTE PTR a[rip]
ret
.L18:
cmp BYTE PTR e[rip], 0
jne .L13
xor eax, eax
.L11:
ret
Даже при намотке первый, кажется, быстрее при O3!