我读到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;我不确定这些因素有多重要。
答案 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)