Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/278.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么可以';t如果(!A&;!B)优化为单个测试指令?_C#_C++_Assembly_X86_Compiler Optimization - Fatal编程技术网

C# 为什么可以';t如果(!A&;!B)优化为单个测试指令?

C# 为什么可以';t如果(!A&;!B)优化为单个测试指令?,c#,c++,assembly,x86,compiler-optimization,C#,C++,Assembly,X86,Compiler Optimization,如果(!A&&!B)看起来应该编译为 mov eax, dword ptr[esp + A_offset] test eax, dword ptr[esp + B_offset] jne ~~~~~~~~~~ 编译器实际生成 mov eax, dword ptr[esp + A_offset] test eax, eax jne ~~~~~~~~~~ mov eax, dword ptr[esp + B_offset] test eax, eax j

如果(!A&&!B)
看起来应该编译为

mov    eax, dword ptr[esp + A_offset]
test   eax, dword ptr[esp + B_offset]
jne    ~~~~~~~~~~

编译器实际生成

mov    eax, dword ptr[esp + A_offset]
test   eax, eax
jne    ~~~~~~~~~~
mov    eax, dword ptr[esp + B_offset]
test   eax, eax
jne    ~~~~~~~~~~
看到这里了吗

8B 45 F8             mov         eax,dword ptr [b]  
83 7D FC 00          cmp         dword ptr [a],0  
75 04                jne         main+32h (0A71072h)  
85 C0                test        eax,eax  
75 00                jne         main+32h (0A71072h)   

为什么不使用单个测试指令来保存分支和指令?

否。
TEST
指令执行操作数的按位and运算,并根据结果设置标志,请参阅

因此,编译器生成的代码是正确的。

因为

让我们注意上面的代码

如果A是真实的(不是0),
!A&&!B
变为
0(假)
。 对,您不必检查B的值。 它应该跳过(跳转)if语句的代码块

mov eax, dword ptr[esp + A_offset]
test eax, eax   ; If `A & A`
jne ~~~~~~~~~~  ; is not 0(If A is not 0), skip this if-codeblock.
mov eax, dword ptr[esp + B_offset] ; Otherwise,
test eax, eax   ; If `B & B`
jne ~~~~~~~~~~  ; is not 0(If B is not 0), skip this if-codeblock.
......          ; Both A and B are 0, and `!A && !B` is `1(TRUE)`! Run the if-codeblock.
加上:

看来你的代码错了

mov eax, dword ptr[esp + A_offset]
mov ebx, dword ptr[esp + B_offset]
test eax, ebx  ; `A & B`
jne ~~~~~~~~~~ ; If `A & B != 0`, skip this code-block for the if statement.
...... ; In other words, this code-block will be run when `A & B == 0`,
       ; which will be `TRUE` when A is 1(0b00000001) and B is 2(0b00000010).

如果A=1,B=2怎么办?然后测试会给出错误的答案。@Yuval这不是重复的问题(至少不是那个问题)。这里的问题是关于
(a!=0)和(b!=0
(a&b)!=0
,而不是懒惰的评估。
如果(!a&&!b)
只有当
a
b
都为零(
如果(!(a&b))
)时才是真的。所以如果你是“代码高尔夫”,这可以通过
moveax、
来完成
jnz not_true
。编译器使用了更多的指令,但分离了A和B之间的直接依赖关系,特定的非零测试可以按任何顺序进行,无论哪个值首先从内存中到达(如果“B”分支作为预测推测性地无序运行-我不知道CPU在重新排序方面是否先进)。如果A和B都是同一范围内的局部变量,我打赌0.01美元我的版本是最优的。;@PeterCordes例如i8051和克隆具有位数据类型和面向位的指令。然而,位变量的数量被硬件限制为256。根据我的经验,他们最好实施一些有用的数学。可能是因为缺乏实用性,在更大的CPU中丢弃面向位的指令GCC已经变成了
!a&&!b进入
!(a | b)
当它知道b可以安全阅读时。Peter,我能想到的最接近的操作是1)对simd向量的缩减操作和2)谓词操作(设置它,使操作b!=0仅在a!=0返回true时执行)。但我不知道有多少指令集…谢谢。我只是感到困惑,如果有一种有效的方法可以同时测试两者,编译器就会这样做。请注意,OP的代码在测试
a
之前已经加载到寄存器中,因此编译器的决策与避免工作或短路评估无关,而与
bool(a&b)
!=<代码>A&&B
当A和B为非零且具有非交叉位模式时。还要注意gcc开发者Marc Glisse的评论,gcc变成了
!a&&!b进入
!(a | b)
当它知道
b
可以安全阅读时。@PeterCordes好吧,公平地说,GCC在
b
不安全阅读时不进行优化的原因是因为短路评估意味着如果
a
是真的,它永远不会被访问。@RossRidge:对,显然,这是维护C源代码语义的先决条件。但我的观点是,问题中真正的编译器输出(最后一个代码块)在第一次测试之前进行了两次加载,证明了它们都可以安全访问,并且它没有试图避免,所以这个答案的第一个假设是错误的。
mov eax, dword ptr[esp + A_offset]
mov ebx, dword ptr[esp + B_offset]
test eax, ebx  ; `A & B`
jne ~~~~~~~~~~ ; If `A & B != 0`, skip this code-block for the if statement.
...... ; In other words, this code-block will be run when `A & B == 0`,
       ; which will be `TRUE` when A is 1(0b00000001) and B is 2(0b00000010).