哪种汇编语言语句执行得更快?

Which assembly language statement executes faster?

我正在学习 x86 上的汇编语言并遇到一个问题,哪个更快,为什么?

ADD AX, 100 

ADD AX, BX  

书上的答案是第二种,但我觉得第二种需要先读一个寄存器,第一种可以直接加。那么谁能告诉我答案吗?

这取决于上下文(程序的其余部分)。

第二条指令引入了数据依赖,如果你只是从主存中加载BX,你可能需要停顿很长时间。另一方面,第一条指令增加了数据占用空间,因此在指令缓存中需要更多 space 来编码立即值,如果它足以在某些性能上造成一些额外的缺失,这可能是至关重要的——关键循环。

最重要的是,现在有一些 CPU 可以在不执行任何操作的情况下执行寄存器复制(仅使用寄存器重命名),因此它还取决于您使用的确切微体系结构。

我的建议是 - 找另一本书,一本不会告诉你总会发生什么的书。此外,使用 AX 和 BX 意味着它相当旧......

在较旧的 80X86 CPU 中,操作数的立即值需要从内存中读取,而寄存器操作数是在指令本身中编码的,这已经是 'read'。所以

add ax, bx

是一条指令;看完后,一应俱全"inside" CPU 马上就可以处理了

说明

add ax, 100

被解析为 add ax, ?,因此 CPU 需要从内存中读取下一个单词才能继续。

新的 CPU 不再如此,但 OP 所指的书(未提及书名和出版日期)可能已经足够旧了。

答案将取决于 CPU 的实际实现,这取决于它的设计时间。较旧的 CPU 的时间与较新的不同。

对于现代 CPUs,一般来说它们的速度是一样的,因为 CPU 设计者已经投入了大量资源来使基本指令在常见情况下变得更快。

即便如此,也可以构建 ADD AX,BX 速度更快的环境(最后一条指令完全在缓存行内,下一条缓存行尚未从内存到达,即使预取也是如此)和某些情况下ADD AX,100 会更快(BX 由一些较早的指令提供,需要很长时间才能完成)。

对于这对特殊指令,我不会花太多时间担心它。最好使用您认为合理的选择来编写代码(浮点加法几乎总是比整数加法慢,因为它要复杂得多)。 [一旦你写了相当多的汇编代码,这就很容易了]。拥有 运行 代码后,测量性能并在必要时进行优化。通常需要优化的地方是惊喜

在现代处理器中,性能没有差异。如果将立即数从 100 更改为 128(或更大),则可能会有显着差异。我知道这听起来很奇怪。

有多家 x86 处理器制造商(Intel、AMD、Via),每家都有多代处理器设计(微架构)。您的问题无法笼统回答,因为答案取决于微架构。对于英特尔,解决这类问题的一个很好的资源是

Intel® 64 and IA-32 Architectures Optimization Reference Manual

现代高性能 CPU 是复杂的机器。对于大多数代码,您不必担心这种级别的细节,您可以使用高级语言编写,使用优化编译器,并且会很开心。当代码的性能至关重要时,您可能不得不关注这些细节。如果是这种情况,那么您需要了解您所针对的特定微体系结构、处理器所处的模式,以及可能的立即数的实际值(惊喜!)。与您的问题相关的是处理器是否在

您问题中的指令 ADD AX,100 是将 16 位立即数(可以编码为带符号的 8 位立即数)添加到 16 位寄存器。与使用不适合 8 位的带符号立即数相比,这可以使用不同的操作码来完成。我使用以下网站 assemble 这些说明:

https://defuse.ca/online-x86-assembler.htm#disassembly

请注意,将 8 位有符号立即数的 ADD 编码为 AX 可以使用与使用 16 位有符号立即数编码和 ADD 不同的操作码来完成。

16 位(实模式,虚拟 8086 模式)

0:  83 c0 64             add    ax,100
3:  05 80 00             add    ax,128

您可能想知道,那又怎样?它是相同的字节数......但还有更多。在 32 位模式下,一些在实模式下被解释为 16 位 ADD 的指令编码现在被解释为 32 位 ADD。为了在 32 位模式下对 16 位添加进行编码,x86 需要 operand size override prefix byte, 0x66。 8位ADD的编码保持不变:

32 位或 x86-64(长模式)

0:  66 83 c0 64             add    ax,100
4:  66 05 80 00             add    ax,128
8:     83 c0 64             add    eax,100
b:     05 80 00 00 00       add    eax,128

重要的是,请注意 0x05 操作码后跟两个字节(当存在 0x66 前缀时)或四个字节(默认情况下,当不存在 0x66 时)。这对试图一次解码许多指令的指令预解码器造成严重破坏,并且由于 x86 指令可以是 1 到 15 字节的任何地方,它根据操作码假设默认大小。具有 16 位立即数的指令的 0x66 前缀会改变指令的总长度......这被称为 length changing prefixes (LCP) 并且可以引入 三到六个周期停顿 在解码器中,取决于微架构,这可能很重要。

在英特尔的优化手册中搜索以下规则以获取更多信息

Assembly/Compiler Coding Rule 21. (MH impact, MH generality) Favor generating code using imm8 or imm32 values instead of imm16 values.

Assembly/Compiler Coding Rule 27. (M impact, MH generality) Avoid using prefixes to change the size of immediate and displacement.

回溯到 8086/8088,lea ax,100[ax] 比 add ax,100 快。我不确定 80286。