【问题标题】:Most Efficient way to set Register to 1 or (-1) on original 8086将寄存器设置为 1 或 (-1) 的最有效方法
【发布时间】:2011-02-19 02:10:55
【问题描述】:

我现在正在学习装配课程,检查我们家庭作业的人是一个非常迂腐的老派优化怪胎。比如他看到就扣10%:

mov ax, 0

代替:

xor ax,ax

即使只用过一次。

我不是汇编编程的初学者,但我不是优化专家,所以我需要你的帮助(可能是一个非常愚蠢的问题,但我还是会问): 如果我需要将寄存器值设置为 1 或 (-1) 最好使用:

mov ax, 1

或做类似的事情:

xor ax,ax
inc ax

我真的需要一个好成绩,所以我试图让它尽可能优化。 (我需要优化时间和代码大小)

【问题讨论】:

  • 每个上下文中使用相同的指令序列不会为您提供最佳速度大小。对于(一个糟糕的)示例,如果在您需要将ax 设置为1 时,cx 被保证为1 怎么办?你可以mov ax, cx

标签: assembly performance xor cpu-registers x86-16


【解决方案1】:

8086 instructions timings size 的快速 google 出现了 http://8086.tk/,它似乎包含 8086(以及更多)指令集的所有时序和大小。

毫无疑问,您可以在网上找到具有类似信息的英特尔官方文档。

针对您的具体问题:

xor ax,ax
inc ax

需要 3+3=6 个时钟周期和 2+1=3 个字节,而

mov ax,1

需要 4 个时钟周期和 3 个字节。

所以在这种情况下后者更好。


但是你需要和你的教育机构谈谈这个人。 10% 的钱用于这种乞丐信仰的简单事情。

你应该问在你有两种可能性的情况下应该怎么做,一种更快,一种更短。

然后,一旦他们承认根据您要实现的目标有不同的编码方式,请告诉他们您要实现的目标是可读性和可维护性,并且真的无法实现飞跃关于这里或那里浪费的周期或字节*a.

当您遇到性能问题时,您通常会进行优化,在一段代码处于接近完成状态之后 - 当代码仍然受到不小的更改可能性影响时,几乎总是浪费精力.

就其价值而言,sub ax,ax 在时钟周期和字节方面似乎与 xor ax,ax 相当,所以也许你下次可以把它混在一起,让他做更多的工作。

*a) 不,不是真的,但偶尔发泄一下很有趣:-)

【讨论】:

  • @Bob,对不起,伙计,我在inc ax 上遗漏了成本是一个错误——事实证明mov ax,1 实际上更短、更快(并且更具可读性)。
  • 我们的教授说过这样的话:“我知道在大多数情况下,这些优化是无关紧要且微不足道的,但你们应该了解它们,因为有一天你可能需要做一个。”还有类似“在我的时代,你真的可以看到性能上的差异”
  • @Bob:如果您开发了自己的编译器,那将是有意义的,我相信您不会想到它可以解决其他任务。编译器经常进行自动优化。
  • sub ax,axxor ax,ax 可能看起来相似,但现代处理器知道 xor 并没有真正依赖于 ax 值; sub 不太确定。
  • @lion,那是专门为 8086 设计的,我不知道它是否有你漂亮的东西。但在我看来,xor ax,ax 和 sub ax,ax 的依赖关系和效果完全相同,xor ax,N 和 sub ax,N 也是如此,其中 N 是任何类型的对象。
【解决方案2】:

根据你的情况,你也许可以逃脱...

 sbb ax, ax

如果未设置进位标志,则结果将为 0,如果设置了进位标志,则结果将为 -1。

但是,如果上面的例子不适用于你的情况,我会推荐

xor  ax, ax
inc  ax

方法。它应该满足你的教授的大小。但是,如果您的处理器使用任何流水线,我希望两条指令之间会有一些类似耦合的延迟(我很可能是错的)。如果存在这样的耦合,可以通过稍微重新排序指令以在它们之间添加另一条指令(不使用 ax 的指令)来稍微提高速度。

希望这会有所帮助。

【讨论】:

    【解决方案3】:

    在任何情况下我都会使用mov [e]ax, 1。它的编码不比黑客的xor 序列长,而且我很确定它几乎在任何地方都更快。 8086 只是奇怪到足以成为例外,而且由于它是如此缓慢,因此像这样的微优化会产生最大的不同。但在其他任何地方:执行 2 条“简单”指令总是比执行 1 条慢,尤其是在考虑数据危险和长管道的情况下。您正在尝试在修改后的下一条指令中读取寄存器,因此除非您的 CPU 可以将结果从管道的第 N 阶段(xor 正在执行)绕过到第 N-1 阶段(其中inc 正在尝试加载寄存器,更不用说在其值上加 1),你会遇到停顿。

    其他需要考虑的事情:指令获取带宽(对于 16 位代码没有实际意义,都是 3 字节); mov 避免更改标志(比将它们全部归零更有用);根据其他寄存器可能保存的值,您也许可以执行 lea ax,[bx+1] (也是 3 个字节,即使在 32 位代码中,对标志也没有影响);正如其他人所说,sbb ax,ax 在某些情况下也可以工作 - 它也更短,只有 2 个字节。

    当面对这些类型的微优化时,您真的应该衡量替代方案,而不是盲目地依赖处理器手册。

    附:新作业:xor bx,bx 是否比xor bx,cx 快(在任何处理器上)?

    【讨论】:

    • 对您的 PS 问题:是的。在现代处理器上,两个相同寄存器上的异或指令得到 CPU 的特殊处理,导致它不会对寄存器的先前值产生错误的依赖,这更快并减少了 CPU 需要使用的内部寄存器的数量。有些处理器没有对子指令的这种检查,所以这里最好使用 xor。
    • @Berd:xor bx,bx 是 16 位操作数大小,EBX 的高字节保持不变。不过,它打破了 Intel P6 系列的依赖关系,它积极地重命名了部分寄存器。但在 Sandybridge 系列中,16 位 xor-zeroing 并不特别。但是xor ebx,ebxhas many advantages,例如甚至不需要 Sandybridge 上的执行单元,因此它的吞吐量(每个时钟 4 个)比 xor ebx,ecx(每个时钟 3 个,不包括通过 EBX 的依赖链)更高。
    【解决方案4】:

    你会更好

    mov AX,1

    在 8086 上。如果您正在跟踪寄存器内容,如果您知道,例如 BX 中已经有一个 1,您可能会做得更好:

    mov AX,BX

    或者如果你知道 AH 是 0:

    mov AL,1

    等等

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-19
      • 1970-01-01
      • 1970-01-01
      • 2021-11-14
      • 2020-09-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多