x86-64是否支持16-32或64位中的一个比其他更好? “本土”或“扩展”这个词有什么意义?

时间:2014-02-20 20:15:17

标签: performance assembly x86-64 cpu-architecture

根据wiki x86-64支持16位,32位和64位程序。在运行代码的意义上,它是否比其他更好地支持一个?有人(可能是我的屁股上冒烟)告诉我Operteron CPU是第一个运行64位但仍然是32位原生的。

在这种背景下成为“本土”意味着什么?我在维基百科上注意到它说64位是x86的扩展,那么它有什么影响呢?

2 个答案:

答案 0 :(得分:5)

x86-64能够在基本相同的单位时间内执行多种操作数宽度的指令,因此具有64位操作数的指令通常不会比其他宽度更快或更慢。 (某些较小宽度的指令实际上可能因部分寄存器写入停顿而减慢)。

但是64位指令更高效 ,因为每单位时间处理更多数据位。 (对于整数操作数,我想知道这在实践中是多么有效:大多数只是普通整数都非常小并且处理全零或全1的前56位并不能真正增加价值,而只是增加了热量)。

x86-64还为汇编编码器和编译器提供了额外的8个整数寄存器,这有助于复杂的循环避免溢出到内存,从而使某些程序实际上更快。

X86-64运行带有64位操作数的指令,x86-32根本无法做到,因此存在真正的质量差异。这允许处理更大的单个值,并且可能是64位系统的主要优点,更大的数据集而没有复杂的地址空间管理。从技术上讲,X86-64可以运行更大的程序,但实际上没有人编写足够大的单个程序来解决这个问题。

关于“native”:我怀疑你的“某人”说“以32位为原生”只意味着它将运行32位指令而没有任何有效的惩罚,除了操作数的大小。

我怀疑皓龙是第一个这样做的。 (几乎没有人声称自己是第一位的;首先考虑克里斯托弗·哥伦布)。大型机人员(例如,IBM)在很长一段时间内从32位转换为64位,允许32位和64位指令形式在同一CPU上“本地”运行,只需更改PSW中的一位。 Opterons可能是第一个在英特尔指令集空间中执行此操作的人。

答案 1 :(得分:3)

哎呀,我误解了这个问题。我正在回答"在64位模式下,哪种操作大小最有效"。请参阅下面的答案。 >。<

如果CPU支持它,那么没有任何CPU不值得使用64位模式。 Atom / Silvermont可能处于边缘,因为当指令需要太多的前缀字节时它们可以减速,并且REX计数。 (所以必要的前缀字节实际上是SSE指令操作码的一部分。)据我所知,64位仍然是他们的净胜利,但可能没有那么大的胜利。

低内存系统有时可以在32位操作系统上比64位操作系统更好。其中一些是64位操作系统仍然需要32位库的副本,因此它们可以运行32位或64位程序。 Windows特别倾向于同时运行32位和64位进程,因此许多库的两个版本实际上将同时存在于内存中,而不仅仅是在磁盘上。我还没有看到Linux或Windows在32到64位的情况下在裸机桌面上使用更多内存的情况更糟糕,但至少Linux桌面没有任何32位进程可以使用#32; t共享其他所有使用的32位库。对于SO来说,这段话很偏僻,对不起。

在实践中,32位模式背负着更糟糕的ABI,并且不能假设SSE2为基线,因此这些因素与32位代码相反。

即使理想的AVX2支持的x86-32代码中的理想ABI也会受到寄存器稀缺性的限制(7个通用寄存器不包括堆栈指针,只有8个向量寄存器)。 64位模式具有15GP和16个向量寄存器,新的RIP相对寻址模式主要消除了制作位置无关(库)代码的开销。额外的注册和更好的ABI通常被引用为约15%的性能。 这些因素特别适用于x86-32与x86-64,而不是32对64位(如PowerPC或SPARC:在那些系统上它对于简单程序(如ls)来说很常见)是32位)。只有那些可能需要超过4GiB地址空间的程序才能从64位受益,并且需要使用两倍大的指针。 64位ARM比32位ARM有一些设计改进,但AFAIK几乎没有x86从AMD64获得的飞跃。

换句话说:x86-64的好处主要不在于将每个寄存器扩展到64b,它是其他架构改进以及有多年向后兼容性的部分突破的机会(特别是在软件标准方面.Insn集的改进可能会更好,但AMD概率想要尽可能地使解码共享晶体管。他们可能已经弃用了更多无用的指令并增加了新指令。{{1}会非常好,并且本来可以使用两个被删除的BCD操作码。setcc r/m32也很整齐。每个操作码都有两个操作,并且在mod / rm字节中加上一个3比特字段,给出编码所有16 cc条件所需的4位。重新定义移位指令以始终写入标志,而不是根据移位计数有条件地不改变标志,这将使它们更便宜,但是再次需要更多的晶体管,因为32位模式仍然需要要快,所以它没有一个干净的地方打破了x86 ISA的难题,但这并不是现代芯片高性能的主要障碍。)

Linux x32 ABI试图提供现代ABI和64位模式的加速,而不需要64位指针的负担。在具有指针重的数据结构的代码中,这是一个巨大的胜利。 (请注意,即使RAM很便宜,缓存也不是,因此较小的数据结构很重要。)

64位模式(包括x32)允许使用64位整数进行更高效的复制和计算。 任何适用于文件大小的东西都需要64位数学。很多东西现在都使用64位数字,因为他们已经足够大了,并且每个人都有效地支持他们#34;尺寸。甚至在32位模式真的过时之前,文件大小必须是64位,但现在64位时间值正在取代自纪元以来的32位秒,以及类似的东西。 (我们必须在2038年之前完成这项工作,以避免32位环绕)。

16位模式对实际中的任何事情都没有用,但据我所知,现代CPU在16位模式下仍然全速运行。您更有可能在16位代码中遇到部分寄存器停顿,因为它经常使用字节寄存器。 386的16位代码有时也使用32位寄存器,产生更多的停顿(对于大于8b的immediates,可能会有长度变化的前缀)。

在CPU上本机运行的16位实模式不能使用分页,因此您永远不会有TLB未命中。 (在正常的32位操作系统下,在虚拟8086模式或16位保护模式下运行16位代码将启用分页。甚至在虚拟机内部以实模式运行。)

您也可以在32位保护模式下禁用分页,因此这不是真正 16位代码的优势。但64位长模式要求启用分页。你可以使用一些1GB大页面映射所有内存,这样你就可以获得很少的TLB失误。

虚拟内存/内存保护并不是大多数人,特别是开发人员想要做的事情!同样,这对于16位代码来说并不是一个实际的优势。

上一个答案:哪个操作数大小效率最高

32位操作数大小是64位代码中最快的。使用32位变量具有代码大小优势(除非需要额外的insn来将数组索引符号扩展到64位,因此它们可以用于指针寻址模式)。 64位也便宜,但16b和8b。可能会变得丑陋,而且比代码大小差异更糟糕。

相同的操作码用于16,32和64位操作数大小,操作数大小为cmovcc r, imm32前缀,无前缀或0x66前缀及其W字段集(aka {{ 1}})。 8位insn具有单独的操作码,因此它们具有相同的代码大小优势。

除此之外,通常所有操作数大小的选择都会解码为相同数量的uop(大多数insn为1),具有相同的延迟和吞吐量。分部是主要的例外。 64位整数除法(128b / 64b - > 64b)即使在当前的CPU(尤其是Intel' s)上也较慢。乘法也因不同的操作数大小而异,尤其是。单操作数N * N-> 2N位形式。例如Skylake

  • REX:1 uop,3c延迟(只有一个输出寄存器:AX = AL * src)
  • REX.W:4次uops,4c延迟。
  • mul r8:3次uops,4c延迟。
  • mul r16:2次uops,3c延迟。

1操作数mul r32的结果进入[E / R] DX:[E / R] AX,所以乘法器的输出可能需要额外的uop来分割将64位输出的两半分为两个寄存器。当16位时,即使mul r64的2和3操作数形式也是额外的uop。

如果你在Agner Fog的指令表中搜索(搜索" r32"或" r64"),你会发现其他事情的例子。一个操作数大小更快。例如在Silvermont:mul是1uop,2c延迟。在16和64位操作数大小时,它是10uops,延迟为10c。这是一个非常极端的情况,并表明他们只是在32b的顶部进行了接线。 (或者其他什么,我不是硬件设计师!)

一些早期支持64位的CPU在64位模式下有一些限制。例如Core2(英特尔的64位P6系列设计)只能在32位模式下进行宏熔丝比较和分支。无论操作数大小如何,这都适用,并且取决于模式。

64位模式真的"用螺栓固定在"在p4中,where shl r32, imm is 1c latency, but shl r64, imm is 7c latency:甚至一些简单的执行单元也不是32b。 IIRC,这对K8 Opteron来说不​​是问题。 64位CPU本身也运行32位代码,即使操作系统是64b(不像IA-64,它有慢速ia32硬件或纯仿真)。可能你听到的是那个乱码的三手版本。虽然正如Paul Clayton所指出的那样,早期Itanics上的慢速x86硬件类似于" native"。

8位和16位操作数大小往往会在Intel CPU(pre IvB)上产生部分寄存器停顿。写入8b或16b寄存器并不清除高位,因此依赖于完整寄存器的先前内容。有些CPU只是让这些insn等待完整的reg准备就绪。当16位代码仍然相关时(PPro was released in Nov 1995)设计了英特尔P6,所以在此之前设计明显开始。我认为即使Win95仍然有大量的16位代码。)这可能是英特尔P6(以及后来的SnB-)的原因。 family)在8和16b部分寄存器上注册重命名。在写入部分reg之后读取更宽的reg会导致停顿(或者只是插入合并的uop:SnB族)。或者在Haswell以及之后,根本没有任何惩罚:没有错误依赖的所有好处,但即使编写像imul r16, r/m16, imm8这样的注册然后阅读shld r32, r32, imm也没有任何惩罚。 (除了high8寄存器外,IvB没有处罚)。

这不是混合32位和64位的问题,因为对32b寄存器的任何写入都会将完整64b的高32归零。这很好地避免了错误依赖问题。 When you do need to merge 64b regs, you can just AND/OR, or use shld

具有16位立即操作数(如ah)的16位指令也会导致解码停顿。操作数大小前缀会改变指令其余部分的长度(从eaxadd ax, 1024),英特尔解码器也不会这样。