在我们系统的编程课程中,我们正在学习汇编语言。在大多数示例程序中,我们的教授。在课堂上表现出来;他正在使用:
XOR CX, CX
而不是
MOV CX, 0
或
OR AX, AX
JNE SOME_LABEL
而不是
CMP AX, 0
JNE SOME_LABEL
或
AND AL, 0FH ; To convert input ASCII value to numeral
; The value in AL has already been checked to lie b/w '0' and '9'
而不是
SUB AL, '0'
我的问题如下,使用AND
/ OR
或XOR
代替备用(易于理解/阅读)方法时,是否有某种更好的表现?< / p>
由于这些课程通常在理论讲座时间向我们展示,因此大多数课程无法用口头方式对其进行评估。为什么要花40分钟讲解这些琐碎的陈述?
答案 0 :(得分:6)
XOR CX, CX ;0x31 0xC9
仅使用两个字节:操作码0x31
和存储源和目标寄存器的ModR / M字节(在这种情况下,这两个相同)。
MOV CX, 0 ;0xB8 0x08 0x00 0x00
需要更多字节:操作码0xB8
,目的地的ModR / M(在本例中为CX)和立即用零填充的两个字节。
时钟视角没有区别(两者都只占用一个时钟),但mov
需要4个字节而xor
只使用两个字节。
OR AX, AX ;0x0A 0xC0
再次仅使用操作码字节和ModRM字节,而
CMP AX, 0 ;0x3D 0x00 0x00 <-- but usually 0x3B ModRM 0x00 0x00
使用三个或四个字节。在这种情况下,它使用三个字节(操作码0x3D
,字立即表示零)因为x86对于使用累加器寄存器的某些操作具有特殊操作码,但通常它将使用四个字节(操作码,ModR / M,字立即)。在谈论CPU时钟时,它也是一样的。
执行
时处理器没有区别AND AL, 0x0F ;0x24 0x0F <-- again special opcode for Accumulator
和
SUB AL, '0' ;0x2D 0x30 0x00 <-- again special opcode for Accumulator
(只有一个字节的差异),但是当您减去ASCII零时,您无法确定在累加器中不会保留大于9
的值。
同时将OF
和CF
设置为零,而sub
根据结果AND
设置它们可能更安全,但我个人认为此用法取决于上下文。
答案 1 :(得分:3)
除了其他答案中提到的代码大小节省外,我想我还会提到一些您可以在Intel's optimization manual和Agner Fog's x86 optimization guide中详细了解的内容:
XOR REG,REG
和SUB REG,REG
(REG
对于两个操作数都相同)被现代x86处理器识别为依赖断路器;意味着它们还有助于打破先前寄存器/标志值的错误依赖性。请注意,如果清除8位或16位寄存器,则不一定适用,但如果清除32位寄存器,则不一定适用。
OR AX, AX
JNE SOME_LABEL
我认为首选指令是TEST AX,AX
。在现代x86处理器上,TEST
可以与任何条件跳转(基本上与跳转指令组合成解码前的单个指令)进行宏融合。 CMP
只能与无符号条件跳转融合,至少在Nehalem架构之前。同样,我不确定16位操作数是否属于这种情况。
答案 2 :(得分:1)
一个重要的区别是它们是否会影响CPU操作标志。使用逻辑运算xor
,or
等时,操作标志会受到影响。所以:
XOR CX, CX
不仅将CX
清零,而且例如将设置CPU的零标志。 mov
指令不会影响标志。所以:
MOV CX, 0
例如,不会设置零标志。
答案 3 :(得分:1)
除了前面提到的指令调度,哪个指令更快还可能取决于正在执行的实际指令序列。
GMP成名的TorbjörnGranlund在this paper的第8页中看到了一个看似无辜的教导的例子。在页面右上角的示例三中,非常快速的分割循环以“nop”指令开始。根据同一页面上的脚注4,缺少nop指令会导致循环执行速度减慢1个时钟周期。 Granlund建议通过在循环内放置其他nops来进行实验,以实现进一步的加速。
我最初的,直觉反应是更多的指示=更多的时间。但是,与手册相比,指令调度和执行显然要多得多。
答案 4 :(得分:-1)
XOR操作比MOV工作得更快,因为它是按位操作,所有按位操作都由CPU执行得更快。