通过寄存器重命名器对寄存器进行微体系结构归零:性能与mov?

时间:2013-07-31 21:37:47

标签: assembly x86 x86-64 cpu-architecture

我读到on a blog post最近的X86微体系结构也能够在寄存器重命名器中处理公共寄存器归零习语(例如将寄存器与自身进行xor-ing);用作者的话说:

  

“寄存器重命名器也知道如何执行这些指令 - 它可以将寄存器本身归零。”

有人知道这在实践中是如何运作的吗?我知道有些ISA,如MIPS,包含一个在硬件中始终设置为零的架构寄存器;这是否意味着在内部,X86微体系结构在内部具有类似的“零”寄存器,以便在方便时映射到寄存器?或者我的心理模型对于这些东西如何在微体系结构上运作不太正确?

我之所以要问的原因是(从某些观察中)似乎从一个包含零的寄存器到一个循环中的目的地的mov仍然比通过xor将寄存器归零更快。循环。

基本上它发生的是我想根据条件将循环中的寄存器归零;这可以通过提前分配架构寄存器来存储零(在这种情况下为%xmm3)来完成,在整个循环期间不会对其进行修改,并在其中执行以下内容:

movapd  %xmm3, %xmm0

或者用xor技巧代替:

xorpd   %xmm0, %xmm0

(AT& T语法)。

换句话说,选择是在循环外提升常数零或在每次迭代中将其重新物化。后者将实时架构寄存器的数量减少一个,并且通过处理器假设的特殊情况感知和xor习语的处理,似乎它应该像前者一样快(特别是因为这些机器具有比架构寄存器更多的物理寄存器,因此它应该能够在内部完成与我在装配中所做的相同的工作,通过在内部提升常数零甚至更好,并充分意识和控制它自己的资源)。但它似乎不是,所以我很好奇是否有任何具有CPU架构知识的人可以解释是否有一个很好的理论原因。

这种情况下的寄存器由SSE寄存器发生,机器恰好是Ivy Bridge;我不确定这些因素有多重要。

3 个答案:

答案 0 :(得分:13)

执行摘要:与较慢的xor ax, ax说明相比,每个周期最多可以运行四条mov immediate, reg条指示。

详细信息和参考资料

Wikipedia对寄存器重命名有一个很好的概述:http://en.wikipedia.org/wiki/Register_renaming

Torbj¨ornGranlund的时间表 指令延迟和吞吐量 AMD和Intel x86处理器位于:http://gmplib.org/~tege/x86-timing.pdf

Agner Fog很好地涵盖了他Micro-architecture study中的细节:

  

8.8注册分配和重命名

     

寄存器重命名由寄存器别名表(RAT)和   重排序缓冲区(ROB)...来自解码器和堆栈的μops   引擎通过队列进入RAT然后到ROB-read和   预订站。 RAT每个时钟周期可以处理4μs。该   RAT可以在每个时钟周期重命名四个寄存器,甚至可以重命名   在一个时钟周期内相同的寄存器四次。

     

独立的特殊情况

     

将寄存器设置为零的常用方法是将其与自身进行异或   或从中减去它,例如   XOR EAX,EAX。 Sandy Bridge处理器认识到这一点   如果,指令独立于寄存器的先前值   两个操作数寄存器是相同的。该寄存器设置为零   在重命名阶段,不使用任何执行单元。这适用于   以下所有说明:XOR,SUB,PXOR,XORPS,XORPD,   VXORPS,VXORPD以及PSUBxxx和PCMPGTxx的所有变体,但不是   PANDN等。

     

不需要执行单元的说明

     

上述特殊情况,其中寄存器通过XOR等指令设置为零   EAX,EAX在寄存器重命名/分配阶段处理而没有   使用任何执行单位。这使得使用这些归零   指令极其高效,吞吐量为四   每个时钟周期的指导。

答案 1 :(得分:6)

归零中最大的性能成本隐藏在这句话中:

  

基本上它发生的是我想要归零   根据条件在循环中注册

那句话意味着一个分支。即使分支被正确预测,它仍然可能花费多于归零寄存器。

至于寄存器重命名......

在OutOfOrder(OOO)CPU中,每次写入寄存器时,CPU都会为您提供一个新的寄存器。如果你执行了这三条指令:

xor eax,eax
add eax,eax
add eax,1

然后对于第一条指令CPU(如果它最近是Intel CPU)只更新其映射,说eax现在指的是内部零寄存器。在第一次添加时,它从eax读取(两次,因为它被用作输入两次),然后更新其映射以指向新寄存器并将结果写入该寄存器。第二次添加也会发生同样的事情。因此,在这三条指令的过程中,eax寄存器被更改为指向三个不同的物理寄存器。

为什么呢?因此:

mov eax,[esi]    ; Load from esi
add eax, 1
mov [esi], eax   ; Store to esi
mov eax,[esi+4]  ; Load from esi+4
add eax, 1
mov [esi+4], eax ; Store to esi+4

在OOO处理器上,性能的主要限制之一是依赖性。必须按顺序执行一到三个指令。必须按顺序执行指令4到6。但是这两个块之间没有依赖关系。因此,一对三和四对六可以并行执行。但是,他们都提到了eax。

没问题。注册重命名解决了这个问题。第一和第四指令同时执行。 CPU为指令流中的每个点创建一个单独的eax映射,后续指令对这些重命名的寄存器进行操作。这允许两个指令块完全并行执行。

由于各种原因,这实际上非常复杂,但它确实有效,而且它是允许现代CPU运行如此之快的主要因素之一。

无论如何,长话短说,“xor eax,eax”从未执行过,这很酷。这种优化可以应用于任何总是产生零或总是产生零的指令,或者其他什么,但英特尔只会在重要时花费晶体管来做这件事。我猜xorpd还没有成功。

我在博客上发表了这篇文章(http://randomascii.wordpress.com/2012/12/29/the-surprising-subtleties-of-zeroing-a-register/),因为我觉得这很酷。我还喜欢这样的想法,即'add'和'sub',它们大多数是相同的指令,由于这种行为,可能会有轻微或极大的不同性能,尽管只是在从自身中减去寄存器的情况下。

答案 2 :(得分:1)

Ice Lake Client Microarchitecture

除了没有延迟外,零成语的另一个好处是,在现代英特尔微体系结构上,重命名,移动消除和零成语阶段甚至发生在调度微指令之前。因此,虽然零举成语作为标准存在,但它不竞争允许更多ILP的执行端口。

由于重命名程序检测到并删除了零个习惯用语,因此它们具有 没有执行延迟。