今天我开始玩分支检查两个布尔值。我非常确定在某些优化级别它们只会被添加然后检查,但gcc和clang的情况并非如此。为什么不通过添加和检查来替换它们来优化两个bool检查?让我告诉你一个例子:
void test(bool a, bool b)
{
// Branch 1
if (a && b)
{
std::cout << "Branch 1";
}
// Branch 2
if (static_cast<int>(a) + static_cast<int>(b))
{
std::cout << "Branch 2";
}
}
gcc(即使具有最大优化级别)也会为分支1生成以下代码:
test dil,dil
je 400794 <test(bool, bool)+0x14>
test sil,sil
jne 4007b0 <test(bool, bool)+0x30>
虽然它为分支2生成以下代码:
movzx ebx,bl
movzx ebp,bpl
add ebx,ebp
jne 4007cf <test(bool, bool)+0x4f>
两个分支(test + je)不应该比加法和分支(加+ jne)慢吗?
编辑:我真正的意思是乘法,因为在真和假(1 + 0)的情况下,加法给出真(1),但乘法给出正确的结果(0)。
答案 0 :(得分:6)
在抽象机器的级别上,如果第一个表达式为false,则&&
强制不评估第二个表达式。通过as-if规则,编译器可以选择评估第二个表达式 - 如果它可以证明它已经定义了行为(或者未定义的行为不重要)并且没有副作用;然而编译器编写者已明确认定不值得。
如果您 希望短片,&
可以提供帮助(带评论)。
答案 1 :(得分:2)
为什么不通过替换它们来优化两个bool检查 另外还有一张支票?
建议的优化不正确。 添加不是&&
运算符的正确替代品,因为当至少(不是两者)时,前者将评估为true
条件是true
。
问题仍然存在,如何进行优化?
C ++标准保证bool
值转换为具有定义值的int
:false
转换为0
,true
转换为1
}。因此,以下构造是完全合法的(假设a
和b
只是bool
个变量):
if (a & b) // OK, integral promotions take place (bool ---> int)
假设编译器始终存储具有bool
(例如true
)和0x1
(false
)的相同内部表示的0x0
值,则条件可能为直接翻译成x86 test
指令:
test sil, dil
这是棘手的部分。显然,GCC编译器改变了主线4.6和4.7之间的行为。即使对int
进行显式转换,它也会保持两次单独的跳转。条件:
if (static_cast<int>(a) & static_cast<int>(b))
生成代码(GCC 6.2.0,-O3
优化级别):
test dil, dil
je .L1
test sil, sil
je .L1
另一方面,ICC和MSVC 2015编译器都执行&#34; true&#34;按位和:
movzx eax, BYTE PTR b$[rsp]
test al, BYTE PTR a$[rsp]
je SHORT $LN6@main
版本4.7之前的GCC也是如此(带-O3
的GCC 4.6.3):
movzx edi, dil
test esi, edi
答案 2 :(得分:-2)
您忘记了逻辑运算符short-circuit evaluation。
对于逻辑,如果左侧为假,则不评估右侧。