在C ++中,逻辑运算符&&
,||
,!
在那里,对应于连接,析取,否定{{0分别。
但我注意到比较运算符==
,!=
,<
,>
,<=
,>=
可以用作布尔值好!鉴于P
和Q
是布尔值:
P == Q
是bicontitional ,
P != Q
是独家分离,
P < Q
是相反的非复制,
P > Q
是非复制,
P <= Q
含义为,
而P >= Q
是相反的含义。
我的问题是:
这个技巧会让程序表现得更好吗?
是否有使用此技巧的示例代码(使用任何语言)?
答案 0 :(得分:4)
这个技巧会增加性能吗?
在任何有益处的处理器上,当P
和Q
是简单变量时,这很简单,你应该期望编译器在不需要任何源代码的情况下使用它重写。
但请注意,P < Q
通常与!P && Q
有明显的缺点:它需要评估Q
,当结果已知时P
评估为true
。这同样适用于所有其他关系运算符。
是否有使用此技巧的示例代码(使用任何语言)?
不是一个技巧,但因为它可以说导致代码更容易理解(不是任何特定的语言):
if ((a == null) != (b == null))
throw "a and b must either both be null, or both be non-null";
可以用^
编写。哪个更容易阅读是一个意见问题。
答案 1 :(得分:2)
实际上我认为它可能会使代码更快。这是第一个函数的代码:
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个指令差异,使“技巧”方法略有优势。
对于逆向非重复,我们可以看到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