布尔技巧上的比较算子 在C++中,逻辑运算符和< /代码>,代码> > ,!代码>存在,分别对应于连接、析取和否定
但是我注意到比较运算符布尔技巧上的比较算子 在C++中,逻辑运算符和< /代码>,代码> > ,!代码>存在,分别对应于连接、析取和否定,c++,boolean,comparison-operators,boolean-operations,C++,Boolean,Comparison Operators,Boolean Operations,但是我注意到比较运算符==,=,,=也可以用于布尔值!假设P和Q是布尔值: p==Q是双内容的 p!=Q是独占析取 pQ是非隐含的 p=Q是相反的含义 我的问题是: 通过这个技巧,程序的性能会更好吗 是否有使用此技巧的示例代码(任何语言) 这个技巧会提高性能吗 在任何有好处的处理器上,当p和Q是简单变量时,这将非常简单,您应该期望编译器已经使用它,而不需要任何源代码重写 但是请记住,p比较运算符超过逻辑运算符的缺点,我已经发布了一个优化建议,关于ISO C++提案。祝你们好运,但老实说,我不期望
==
,=
,
,=
也可以用于布尔值!假设P
和Q
是布尔值:
p==Q
是双内容的
p!=Q
是独占析取
p
是逆非蕴涵
p>Q
是非隐含的
p=Q
是相反的含义
我的问题是:
这个技巧会提高性能吗 在任何有好处的处理器上,当
p
和Q
是简单变量时,这将非常简单,您应该期望编译器已经使用它,而不需要任何源代码重写
但是请记住,p
通常比有明显的缺点!P&Q
:如果P
的计算结果为true
,则当结果已知时,需要对Q
进行计算。这同样适用于所有其他关系运算符
是否有使用此技巧的示例代码(任何语言)
这不是一个技巧,而是因为它可以带来更容易理解的代码(不是任何特定的语言):
它可以用
^
编写。哪个更容易阅读是一个意见问题。事实上,我认为这可能会使代码更快。以下是第一个函数的代码:
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
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_rewritten():
push rbx
call fa()
mov ebx, eax
call fb()
xor eax, 1
xor eax, ebx
pop rbx
ret
我使用了来自的gcc 5.3,带有标志-O3-Wall-fno verbose asm-march=haswell
很明显,你可以删掉一条指令。有趣的是,gcc没有进行这种优化。您可能想给他们发封电子邮件,问他们为什么:
编辑:另一个答案很好地说明了一点:通过修剪不必要的部分,可以更快地计算逻辑表达式。为了演示,我重写了代码以使用对返回bool
的函数的调用,而不是bool
参数:
bool fa();
bool fb();
bool biconditional_with_function()
{
return (fa() && fb()) || (!fa() && !fb());
}
bool biconditional_with_function_trick()
{
return fa() == fb();
}
以下是大会:
biconditional(bool, bool):
mov eax, esi
xor eax, 1
xor eax, edi
ret
biconditional_trick(bool, bool):
cmp dil, sil
sete al
ret
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_rewritten():
push rbx
call fa()
mov ebx, eax
call fb()
xor eax, 1
xor eax, ebx
pop rbx
ret
您可以看到,如果表达式的前半部分为true,则为biconditional_with_function
生成的代码使用跳转跳过表达式的后半部分。有趣的是,在计算后半部分时,fa()
和fb()
总共被调用两次,因为编译器不知道它们是否总是返回相同的结果。如果是这种情况,则应通过将评估结果保存在其自身变量中来重写代码:
bool biconditional_with_function_rewritten()
{
bool a = fa();
bool b = fb();
return (a && b) || (!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
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_rewritten():
push rbx
call fa()
mov ebx, eax
call fb()
xor eax, 1
xor eax, ebx
pop rbx
ret
我们可以看到它们几乎完全相同,只剩下1指令的差异,这给了“技巧”方法一点优势
对于相反的非蕴涵,我们可以看到,当使用逻辑运算符时,GCC确实会避免计算第二个运算符,但当
强制转换是什么时,则不会?为什么不P
,P>Q
,P=Q
?哦,我不知道,=也可以用来表示布尔值。谢谢。“这个技巧会提高性能吗?”比什么提高?速度。这不是很明显吗?我的意思是与只使用&&
,|
和的代码相比代码> >比较运算符超过逻辑运算符的缺点,我已经发布了一个优化建议,关于ISO C++提案。祝你们好运,但老实说,我不期望从中发生很多事情:您建议对语言规则添加一个特殊的例外,该规则可能改变现有代码的行为,不要给出任何令人信服的用例。你认为P
比有什么好处!P&Q
?嗯,真的。对于第一个示例,有一个旧的For-GCC,尽管现在生成的代码已经比以前好得多。这并不像“指令越少,速度越快”那么简单,因此可能值得检查两个版本的实际指令成本,但除非它们的速度完全相同,否则其中一个版本的优化可能会遗漏。