我正在研究有关汇编的一些事情,以及我正在阅读的材料,作者说在16位编译的程序在x86操作系统上旋转得更慢,x64也是如此,32位编译程序运行速度较慢64 ...
为什么会这样? 在计算机内存和处理器中会发生什么,以便程序16位或32位机器分别以32位和64位旋转得更慢?
答案 0 :(得分:2)
大约16位程序在32位系统中运行速度较慢,我可以告诉你。 当英特尔从16位变为32位时,他们必须扩展指令集以应对新的32位寄存器,但保持与16位程序的二进制兼容性。
为了实现这一点,他们添加了一个前缀,如果我记得很清楚,则为66h,当应用于使用16位寄存器的任何指令时,使该指令使用32位寄存器。
例如,一个16位指令,如MOV AX,BX,前缀为66h,变为MOV EAX,EBX
但是这会对新的32位指令施加惩罚,因为它们至少需要额外的内存提取周期来执行。然后英特尔创建了所谓的32位段和16位段。
基本上,任何一段代码都必须驻留在代码段中。在80386之前,所有段都使用16位指令,并假设所有指令都使用16位寄存器。
Intel的32段也包含代码,但这次假设每条指令都使用32位寄存器,因此在32位段中,MOV EAX的操作码,EBX与MOV AX的操作码相同, BX在16位段中。
这允许程序不必为每个32位指令使用66h前缀。没有惩罚了。
但是......如果我必须在一个32位段的程序中使用16位寄存器怎么办?那些使用16位寄存器的指令必须使用前缀66h。
因此:使用16位寄存器的指令在16位段中没有前缀,并且以32位格式作为前缀。使用32位寄存器的指令在32位段中没有前缀,并且以16位段为前缀。
此外:从奔腾处理器开始,我们有两个并行执行指令的管道。对于要使用的这些管道,输入它们的指令必须属于英特尔命名的“RISC核心”:指令的子集,不再作为CPU内部的微程序执行,而是使用有线逻辑。你猜怎么着?使用16位寄存器的16位段中的前缀指令和代码不属于该组,因此不能与另一个并行执行。当前缀指令设法进入其中一个管道时,另一个被停止,从而影响CPU的性能。
答案 1 :(得分:1)
关于“程序旋转得更慢”......嗯......程序不会“旋转”,而是“执行”。如果你在谈论位旋转指令......好吧。碰巧8086有两个版本的位旋转指令:一个使用指定要旋转的位数的中间参数,另一个使用寄存器(通常是CX / ECX)来指定它。
问题是8086处理器不允许任何其他值超过1作为inmediate参数(但CX / ECX中的值可能大于1)。 80386及更高版本的处理器允许使用任何其他值作为中间操作数。此外,32位处理器仅使用操作数的低5位来指定旋转量,因此操作不超过31(将32位重新驱动旋转超过31次是没有意义的)。 8086处理器不会施加此限制,因此在操作中花费更多时间。
我真的不知道这是你的书的意思是“旋转得更慢”。我记得旋转操作只能在其中一个管道中执行,而不能同时在两个管道中执行,因此两个连续的旋转指令无法配对。
答案 2 :(得分:0)
我不确定你的意思是旋转(装配操作?),但一般来说这里可能有几个因素 -
CPU公司并没有真正去支持旧的传统模式和ISA子集。 x87就是一个很好的例子,任何不需要这种精确度的东西最好不要使用SSE / AVX来完成性能关键任务,而不仅仅是因为矢量化。
每次x86 CPU公司增加其寄存器大小时,它们都会保留旧的寄存器集,并为较长的版本添加逻辑名称。对兼容性的需求要求旧操作仍然可以在相同的寄存器上工作,因此您现在可以在同一程序中写入ah / al,ax,eax和rax。在某些情况下(即 - 8位/ 16位部分),这种兼容性要求CPU在仅写入下部时保持寄存器的上部完整,这样做会隐式引入合并操作,这可能会导致减速。更糟糕的是,您可能会引入错误的依赖关系,因为每次写入16位寄存器都需要您合并到早期操作中保留的上部。
另见 - Why do most x64 instructions zero the upper part of a 32 bit register