7. Pentium M流水线
7.1. PM中的流水线
本章适用于Intel Pentium M,Core Solo及Core Duo,但不适用于Core 2。在本手册中缩写PM包括Pentium M,Core Solo与Core Duo。
PM构建在与PPro,P2及P3相同的基本微架构上,而P4/NetBurst设计已被中断。在流水线中的主要阶段有:分支预测,指令获取,指令解码,寄存器重命名,重排缓冲读。保留站。执行端口。重排缓冲回写,以及回收。
虽然进行了几个小的改动,但整体功能与PPro流水线几乎相同,如第62页图6.1所示。Intel没有透露PM流水线的实际结构。他们唯一透露的是流水线更长了,因此以下讨论主要是基于我自己测量的猜测。
从分支误预测代价可以估计流水线的总长度(第3页)。代价比P2及P3多了3 - 4个时钟周期。这表示流水线可能多了3或4个阶段。我们可以尝试猜一下这些阶段用来做什么。
在PM里,分支预测机制远比之前的处理器复杂(第18页),因此很可能这个机制要求3个流水线阶段,而不是2个。
指令获取也被改进了,因此16字节边界或缓存行边界不会在跳转中造成时延(下面第73页)。这可能要求将指令获取单元从3个阶段扩展到4个阶段。
新栈引擎(第82页)靠近指令解码实现,根据下面提到的Intel公开信息。几乎可以确定,栈引擎及栈同步μop插入需要至少一个额外的流水线阶段(第82页)。这个声明基于我的观察,一条由D1或D2解码的指令,在不增加解码时间的前提下,可以产生一个同步μop,即使这些解码器仅能产生一个μop。因此这个额外的同步μop必须在包含解码器D0-2阶段后的一个流水线阶段生成。
有人可能怀疑μop融合机制(下面第80页解释)是否要求额外的流水线阶段。从ROB-读到ROB-回写阶段的数目可以通过测量寄存器读暂停(第66页)来估计。我的测量表示这个距离仍然仅是3个时钟周期。因此我们可以断定,在执行单元前,没有额外的流水线阶段用于分解融合μop。一个融合μop的两个部分共享相同的ROB项,它们被提交到不同的端口,因此在回收站前,同样没有额外的流水线阶段用于合并分解的μop。
RAT,ROB-读及RS阶段都被修改,以处理带有3个输入依赖的融合μop。很可能一个额外流水线阶段被加到RAT,因为这个阶段中额外的工作负荷,但我没有支持这个假想的实验证据。根据我测量的上面提及的ROB-读到ROB-回写的距离,RS仍然只需一个时钟周期。猜测RS与ROB相比之前的处理器可能更小了,不过没有得到我测量的证实。RS与ROB可能分别保存20与40融合μop。
结论是PM流水线可能比PPro流水线多3到4个阶段,包括分支预测的一个额外阶段,指令获取的一个额外阶段,及栈引擎的一个额外阶段。
PM有许多节能特性,能在不使用时,关闭部分的内部总线,执行单元等。这些新特性是否需要额外的流水线阶段是未知的。这些节能特性,在不使芯片过热的前提下,对提升最大时钟频率有积极影响。
μop融合,栈引擎及复杂的分支预测都是不仅降低功耗,而且加速执行的改进。
(文献:S. Gochman, et al.: The Intel Pentium M Processor: Microarchitecture and Performance. Intel Technology Journal, vol. 7, no. 2, 2003)
7.2. Core Solo与Duo的流水线
我还没有机会进行Core Solo以及Core Duo的吞吐率测试,但一个初级的测试表明其内核非常类似于Pentium M。Core Solo/Duo比Pentium M有更多先进的节能特性,包括在低工作负荷时能够降低CPU电压与时钟频率的“SpeedStep”技术。Core Duo有两个带有独立1级缓存以及一个共享2级缓存的处理器核。
(文献:S. Gochman, et al.: Introduction to Intel Core Duo Processor Architecture. Intel Technology Journal, vol. 10, no. 2, 2006)
7.3. 指令获取
在PM的指令获取方式与PPro,P2及P3中的相同(参考第61页),有一个重要改进。在一个预测的跳转后获取指令更高效,且不会被16字节边界延迟。表6.1(第62页)里的时延不适用于PM,每时钟周期有一个跳转是可能的。出于这个原因,对齐例程入口与循环入口不再重要。在PM上对齐代码的唯一原因是提升缓存效率。
指令仍然在最多16字节的IFETCH块中获取(第61页)。PM每时钟周期最多可以获取一个IFETCH块。在一个预测的跳转后的第一个IFETCH块通常在第一条指令处开始。这与之前的处理器无法确定第一个IFETCH块位置不同。下一个IFETCH块将在离开不超过16字节的最后一条指令边界处开始。因此,通过查看汇编器的输出列表,你可以预测所有IFETCH块的位置。假设第一个IFETCH块在一个跳转目标标记处开始。向前16字节找第二个IFETCH块。如果那里不是指令边界,那么回退到最接近的指令边界。这是第二个IFETCH开始的地方。知道IFETCH块在那有助于提升解码速度,如下面解释的那样。
7.4. 指令解码
在PM上的指令解码方式与PPro,P2及P3上相同,如第63页解释的那样。有三个并行的解码器:D1,D2与D2。解码器D0可以处理任何指令。D1与D2可以仅处理产生一个μop、不超过8字节且不超过一个前缀的指令。
在同一时钟周期解码三条指令是可能的,如果它们包含在同一个IFETCK块中,且第二、第三条指令满足去往D1与D2的条件。
某些复杂指令需要多个时钟周期解码:
解码器的最大输出是每时钟周期6个μop。如果避免了上述问题且根据4-1-1模式调度指令,使每第三条指令产生4个μop,且后两条指令每个产生1个μop,可以获得这个速度。产生4个μop的指令将进入解码器D0,后两条指令将进入D1与D2。每条指令生成μop数的列表,参考手册4“指令表”,并使用“μops fused domain”下列出的值。在一个IFETCH块中的第一条指令必须去往D0。如果根据4-1-1模式,这条指令预定给D1或D2,那么这个模式被破坏,损失一个时钟周期。
为每时钟周期解码输出6个μop来安排代码是不必要的,因为后面阶段的吞吐率仅是每时钟周期3个μop。例如,一个2-1-2-1模式每时钟周期将产生3个μop。但建议致力于一个高于3个μop的平均输出,因为当一条预定给D1或D2的指令落入IFETCH块的开头时,可能会损失一个时钟周期。
如果解码速度是关键的,那么可以通过减小代码大小,减少单个μop指令落入IFETCH块开头的可能性。关于优化代码大小,参考手册2“优化汇编例程”。一个可能更有效的策略是,根据第78页解释的方法,确定每个IFETCH块在那里开始,然后调整指令长度,使得仅预定给解码器D0的指令落入IFETCH块的开头。如何使指令代码更长,参考手册2“优化汇编例程”的章节“为对齐目的,使指令更长”。这个方法是乏味的,仅应该在解码是瓶颈时使用。例如:
; Example 7.1. Arranging IFETCH blocks
LL: movq mm0, [esi+ecx] ; 4 bytes long
paddd mm0, [edi+ecx] ; 4 bytes long
psrld mm0, 1 ; 4 bytes long
movq [esi+ecx], mm0 ; 4 bytes long
add ecx, 8 ; 3 bytes long
jnz LL ; 2 bytes long
这个循环计算两组整数的平均。循环有六条分别产生一个μop的指令。前四条指令都是4字节长。如果假设第一个IFETCH块在LL:处开始,那么第二个IFETCH块将在这之后16字节处开始,即ADD ECX, 8的开头。前三条指令将在一个时钟周期里解码。第四条指令将在第二个时钟周期里被单独解码,因为在第一个IFETCH块里没有别的指令。最后两条指令将在第三个时钟周期里解码。整个解码时间是循环每次迭代三个时钟周期。可以通过例如向第四条指令添加一个DS段前缀来改进。这使得这条指令长了一个字节,因此,现在第一个IFETCH块在第四条指令结束前结束。现在最后三条指令在同一个IFETCH块里,因此它们将分别同时在D0,D1与D2里解码。现在解码时间对六条指令仅是2个时钟周期。整个执行时间从每次迭代3个时钟周期减为2个,因为对速度没有什么限制,除了可能的缓存不命中。
7.5. 循环缓冲
PM有一个4x16字节、保存预解码指令的循环缓冲。在指令获取是瓶颈的小循环里,这是一个优势。解码器可以重用最多64字节的已获取指令。这意味着在不超过64字节且16字节对齐的循环里,指令获取不是瓶颈。如果循环不超过49字节,循环的对齐不是必要的,因为这个大小的循环可以放进4*16字节,即使在最坏的不对齐情形下。
7.6. 微操作融合
流水线中的寄存器重命名(RAT)与回收(RRF)阶段是最大吞吐率每时钟周期3个μop的瓶颈。为了扩大这些瓶颈,设计者将在之前处理器中分解为两个μop的一些操作结合起来。他们称之为μop融合。融合的操作在流水线的多数地方共享一个μop,且在重排缓冲(ROB)中共享一个项。但这个ROB项代表要由两个不同执行单元完成的两个操作。融合的ROB项被分发到两个不同的执行端口,但作为一个单元回收。
μop融合技术仅适用于两种类型的组合:内存写操作与读-修改操作。
内存写操作涉及内存地址的计算与数据传输。在之前的处理器上,这两个操作被分解为两个μop,其中端口3关心地址计算,而端口4关心数据传输。在大多数写内存的PM指令里,这两个μop融合在一起。内存读操作仅要求一个μop(端口2),就像之前处理器那样。
第二种可以融合的操作是读-修改操作。例如,指令ADD EAX, [mem32]涉及两个操作:第一个操作(端口2)从[mem32]读,第二个操作(端口0或1)加上读到EAX的值。在之前的处理器上,这样的指令被分解为两个μop,但在PM上可以被融合在一起。这适用于许多工作在通用寄存器、浮点栈寄存器及MMX寄存器上的读-修改指令,但不适用于工作在XMM寄存器上的读-修改指令。
读-修改-写操作·,比如ADD [mem32], EAX不能融合读与写μop,但融合了写所需的两个μop。
μop融合的例子:
; Example 7.2. Uop fusion
mov [esi], eax ; 1 fused uop
add eax, [esi] ; 1 fused uop
add [esi], eax ; 2 single + 1 fused uop
fadd qword ptr [esi] ; 1 fused uop
paddw mm0, qword ptr [esi] ; 1 fused uop
paddw xmm0, xmmword ptr [esi] ; 4 uops, not fused
addss xmm0, dword ptr [esi] ; 2 uops, not fused
movaps xmmword ptr [esi], xmm0 ; 2 fused uops
正如你看到的,在Pentium M里,封装加法(PADDW)读-修改指令可被融合,如果目标是64位MMX寄存器,但如果目标是128位XMM寄存器,就不行。后者要求每个读64位的两个μop,以及每个加64位的另两个μop。ADDSS指令不能融合,即使它仅使用XMM的低半部。没有涉及XMM寄存器的读-修改指令可被融合,但XMM内存写指令可以融合,如上例所示。
Core Solo/Duo有更多XMM指令的μop融合机会。一条128位XMM指令,在64位执行模式里,由两个或更多64位μop处理。在解码器及流水线早期阶段中,根据7.2章节引用的文章,两个64位μop可以被“叠”在一起。不清楚μop的“融合”与“层叠”是否有显著差别。这个机制的结果是,在Core Solo/Duo解码器中XMM指令的吞吐率增加,但执行单元里没有。
PM的重排缓冲(ROB)被重新设计,使每项最多可以有三个输入依赖,而之前的设计仅允许两个。例如,指令MOV [ESI+EDI], EAX及ADD EAX, [ESI+EDI]都有三个输入依赖,就指令所有部分可以执行前,EAX,ESI与EDI必须就绪而言。进入执行单元的非融合μop仍然仅有两个输入依赖。MOV [ESI+EDI], EAX被分解为一个依赖ESI与EDI的地址计算μop,以及一个依赖地址计算μop输出与EAX的储存μop。类似的,ADD EAX, [ESI+EDI]被分解为一个依赖ESI与EDI的读μop,以及一个依赖读μop输出与EAX的ADD μop。没有融合的μop仅能有两个输入依赖。例如,指令ADC EAX, ECX与CMOVE EAX, EBX都有三个输入依赖:EAX,EBX与标记。因为这些指令都不能融合,它们每个都必须产生两个μop。
μop融合有几个好处:
如果程序可以从这些好处获益,优先使用可以产生融合μop的指令。如果一个或多个执行单元是唯一的瓶颈,那么μop融合不重要,因为在发送给执行单元时,融合的μop被分解为两个μop。
手册4“指令表”中的表显示了由PM中每个指令产生融合与非融合μop。“μop融合域”列展示了由解码器产生的μop数,其中一个融合μop算作一个。“μop非融合域”列展示了去往每个执行端口的μop数。在执行单元处融合μop被分解,因此一个融合内存写μop同时列在端口3与端口4下面,一个融合读-修改μop同时列在端口2与端口0或1下面。产生融合μop的指令是那些在“μop融合域”下列出的数字小于在“μop非融合域”下列出数字之和的指令。