实际上我认为它可能会使代码更快。这是第一个函数的代码:
bool biconditional(bool a, bool b)
{
return (a && b) || (!a && !b);
}
bool biconditional_trick(bool a, bool b)
{
return a == b;
}
以及生成的程序集:
biconditional(bool, bool):
mov eax, esi
xor eax, 1
xor eax, edi
ret
biconditional_trick(bool, bool):
cmp dil, sil
sete al
ret
我使用来自 Compiler Explorer 的 gcc 5.3 和标志 -O3 -Wall -fno-verbose-asm -march=haswell。
显然,您可以减少 1 条指令。有趣的是 gcc 没有做这个优化。您可能想给他们发一封电子邮件并询问原因:https://gcc.gnu.org/lists.html
编辑:另一个答案说得很好:通过修剪不必要的部分可以更快地评估逻辑表达式。为了演示,我重写了代码以使用对返回 bool 而不是 bool 参数的函数的调用:
bool fa();
bool fb();
bool biconditional_with_function()
{
return (fa() && fb()) || (!fa() && !fb());
}
bool biconditional_with_function_trick()
{
return fa() == fb();
}
这是程序集:
biconditional_with_function():
sub rsp, 8
call fa()
test al, al
je .L7
call fb()
test al, al
jne .L10
.L7:
call fa()
mov edx, eax
xor eax, eax
test dl, dl
je .L14
.L10:
add rsp, 8
ret
.L14:
call fb()
add rsp, 8
xor eax, 1
ret
biconditional_with_function_trick():
push rbx
call fa()
mov ebx, eax
call fb()
cmp bl, al
pop rbx
sete al
ret
您可以看到,如果前半部分为真,则为biconditional_with_function 生成的代码使用跳转来跳过表达式的后半部分。有趣的是,在评估后半部分时,fa() 和 fb() 总共被调用了两次,因为编译器不知道它们是否总是返回相同的结果。如果是这种情况,则应通过将评估结果保存在自己的变量中来重写代码:
bool biconditional_with_function_rewritten()
{
bool a = fa();
bool b = fb();
return (a && b) || (!a && !b);
}
还有大会:
biconditional_with_function_rewritten():
push rbx
call fa()
mov ebx, eax
call fb()
xor eax, 1
xor eax, ebx
pop rbx
ret
我们可以看到它们几乎是相同的,只剩下 1 条指令的差异,让“trick”方法略有优势。
对于相反的非蕴涵,我们可以看到确实 GCC 在使用逻辑运算符时会避免评估第二个运算符,但在使用 < 运算符时不会:
bool fa();
bool fb();
bool converse_nonimplication()
{
return !fa() && fb();
}
bool converse_nonimplication_trick()
{
return fa() < fb();
}
组装:
converse_nonimplication():
sub rsp, 8
call fa()
test al, al
je .L5
xor eax, eax
add rsp, 8
ret
.L5:
add rsp, 8
jmp fb()
converse_nonimplication_trick():
push rbx
call fa()
mov ebx, eax
call fb()
cmp al, bl
pop rbx
seta al
ret