【问题标题】:Is there some benefit in the following assembly commands?以下汇编命令有什么好处吗?
【发布时间】:2013-08-12 17:09:06
【问题描述】:

在我们系统的编程课程中,我们学习的是汇编语言。在大多数示例程序中,我们的教授。已在课堂上展示;他正在使用:

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/ORXOR 而不是替代(易于理解/阅读)方法时,是否有某种更好的性能?

由于这些程序通常在理论课时向我们展示,因此大多数班级无法实际口头评估它们。为什么要花 40 分钟的时间来解释这些琐碎的陈述?

【问题讨论】:

  • 指令可能更短,并且它们不会产生空字节。
  • ...还有一些特殊的优化,比如识别xor eax,eax的寄存器重命名

标签: assembly x86 masm x86-16


【解决方案1】:
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 的值。 同样anding将OFCF设置为零,而sub根据结果设置它们ANDing可以更安全,但我个人认为这种用法取决于上下文。

【讨论】:

    【解决方案2】:

    除了其他答案中提到的代码大小节省之外,我想我还要提一些您可以在Intel's optimization manualAgner Fog's x86 optimization guide 中了解更多信息的内容:

    XOR REG,REGSUB REG,REGREG 对两个操作数相同)被现代 x86 处理器识别为依赖性破坏者;这意味着它们还有助于打破对先前寄存器/标志值的错误依赖。请注意,如果您清除 8 位或 16 位寄存器,这不一定适用,但如果您清除 32 位寄存器,则适用。


    OR AX, AX
    JNE SOME_LABEL
    

    我相信首选指令是TEST AX,AXTEST 可以与现代 x86 处理器上的任何条件跳转(基本上在解码之前与跳转指令组合成一条指令)进行宏融合。 CMP 只能与无符号条件跳转融合,至少在 Nehalem 架构之前是这样。同样,我不确定 16 位操作数是否属于这种情况。

    【讨论】:

    • mov 也打破了对寄存器先前值的依赖关系。它只在xor 等情况下被提及,因为在一般情况下,输出确实 取决于先前的值,因此需要特殊支持才能识别这种情况。 movzxmovd 等等 dest reg 的其余部分全部为零,从而中断 dep 链。 (相对于pinsrwmovlhps。)
    【解决方案3】:

    一个重要的区别是它们是否会影响 CPU 操作标志。当您使用xoror 等逻辑操作时,操作标志会受到影响。所以:

    XOR  CX, CX
    

    不仅将CX 归零,而且,例如,CPU 的归零标志将被设置。 mov 指令不影响标志。所以:

    MOV  CX, 0
    

    例如,不会设置零标志。

    【讨论】:

    • xor 使用后何时需要 ZF?
    • @user35443,如果您在代码中的多个位置检查标志,则可能需要它。所以检查发生的地方可能不知道之前影响标志的指令是xor
    【解决方案4】:

    除了前面提到的指令调度,哪个指令更快也可能取决于实际执行的指令序列。

    看似无辜的指令却产生了巨大的影响,请参见 this paper 第 8 页,作者是 GMP 的 Torbjörn Granlund。在页面右上角的示例三中,一个非常快速的除法循环以“nop”指令开始。根据同一页上的脚注 4,没有 nop 指令会导致循环执行慢 1 个时钟周期。 Granlund 建议通过在循环中放置其他 nop 来进行试验,以实现进一步的加速。

    我最初的直觉反应是更多的指示 = 更多的时间。然而,指令调度和执行显然比从手册中收集到的要多得多。

    【讨论】:

    • 这可能更适合复杂/简单解码器的后续指令。 Core2 早于循环缓存 (Nehalem) 和 uop 缓存 (Sandybridge),因此即使是短循环,解码器吞吐量也是一个因素。
    【解决方案5】:

    XOR 运算比 MOV 更快,因为它是按位运算,所有按位运算都由 CPU 执行。

    【讨论】:

    • 嗯?为什么要使用移位器来实现异或?
    • 我是按位写的,不好意思
    • 这不是真的。 mov reg, imm 和 xor reg, reg 都只占用一个时钟。
    猜你喜欢
    • 2013-01-18
    • 2020-03-21
    • 1970-01-01
    • 2012-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-13
    • 1970-01-01
    相关资源
    最近更新 更多