哪种汇编语言语句执行得更快?
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 是复杂的机器。对于大多数代码,您不必担心这种级别的细节,您可以使用高级语言编写,使用优化编译器,并且会很开心。当代码的性能至关重要时,您可能不得不关注这些细节。如果是这种情况,那么您需要了解您所针对的特定微体系结构、处理器所处的模式,以及可能的立即数的实际值(惊喜!)。与您的问题相关的是处理器是否在
- 实模式(16 位)
- 32 位模式,或 x86-64 long mode
您问题中的指令 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。
我正在学习 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 是复杂的机器。对于大多数代码,您不必担心这种级别的细节,您可以使用高级语言编写,使用优化编译器,并且会很开心。当代码的性能至关重要时,您可能不得不关注这些细节。如果是这种情况,那么您需要了解您所针对的特定微体系结构、处理器所处的模式,以及可能的立即数的实际值(惊喜!)。与您的问题相关的是处理器是否在
- 实模式(16 位)
- 32 位模式,或 x86-64 long mode
您问题中的指令 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。