Intel, AMD及VIA CPU的微架构(28)

9.16. Sandy Bridge与Ivy Bridge中的瓶颈

指令获取与预解码

指令获取与解码非常类似于之前的处理器,仍然是一个瓶颈。幸运的是,新的μop缓存降低了解码器的压力。

μop缓存

新的μop缓存有了显著的提高,因为在代码的关键部分适合μop缓存时,它消除了指令获取与解码的瓶颈。现在,很容易得到每时钟周期4条指令的最大吞吐率(或者宏融合的5条),即使对更长的指令。

程序员应该小心在CPU密集代码中经济地使用μop缓存。适合μop缓存的循环与不适合μop缓存的循环之间的性能差异相当可观,如果平均指令长度超过4字节。

在旧的P4/NetBurst处理器中,μop缓存有点类似追踪缓存(参考第39页),具有某些相同的弱点。地址与数据操作数需要超过32位储存的指令可能在μop缓存中使用额外的空间,且需要额外一个时钟周期载入。优化代码避免这些弱点是可能的,但这不可能由任何人来完成,而是最专业的汇编程序员。

对我而言,为什么处理器没有在代码缓存中标记指令边界是一个谜。这将消除指令长度解码的瓶颈,因而消除μop缓存的需要。AMD处理器这样做,旧的Pentium MMX也同样。

寄存器读暂停

这个旧的瓶颈,自Pentium Pro起困扰Intel处理器,最终被消除了。在我的测量中没有检测到这样的暂停。

执行端口与执行单元

执行端口与执行单元的能力相当高。许多μop可在2个或3个执行端口间选择,每个单元每时钟周期可处理一个完整的256位向量操作。因此,如果指令平均分配在端口之间,执行端口的吞吐率不是一个严重的瓶颈。

如果代码产生许多去往相同执行端口的μop,执行端口会是平均。

执行时延与依赖链

执行时延通常是低的。大多数整数ALU操作仅有一个时钟周期的时延,即使对256位向量操作。执行时延仅在长的依赖链中是关键的。

部分寄存器访问

在写寄存器一部分后,读整个寄存器有一个惩罚。使用MOVZXMOVSX8位或16位内存操作数读入一个32位寄存器,而不是使用寄存器更小的部分。

回收

在我的所有测试中,都没有观察到μop的回收成为一个瓶颈。

分支预测

分支预测器没有专门识别循环。带有许多分支循环的预测逊色于之前的处理器。分支历史模式表与分支缓冲可能比之前的处理器大,但误预测仍然常见。对适合μop缓存的代码,误预测惩罚要更小。

内存访问

Sandy Bridge有两个内存读端口,而之前的处理器仅有一个。这是一个重要的改进。在最大内存带宽利用率时,缓存库冲突相当常见。

Ivy Bridge预取指令有一个严重的问题,它极慢。

多线程

大多数关键资源在线程间共享。这意味着在多线程应用程序中,瓶颈变得更加关键。

文献

"Intel 64 and IA-32 Architectures Optimization Reference Manual". Intel 2011.

10. Haswell与Broadwell流水线

在之前设计之上,Haswell有几个重要的改进。数据缓存的带宽加倍到每时钟周期两次读及一次写,每次最多32字节。执行单元数从6增加到8。大多数执行单元有每时钟周期一整个256位向量的带宽。

Haswell目前有2 – 18个核,大多数版本每个核能运行两个线程。大多数关键资源在运行在同一核上的线程间共享,如第119页对Sandy BridgeIvy Bridge处理器所述。

Haswell是第一个支持AVX2指令集的处理器,它可以处理256位向量的整数指令。它还是第一个支持融合乘加(FMA)指令的处理器。

BroadwellHaswell14 nm微缩制程(die shrink),仅在执行单元上有少数改进。

​​​​​​​10.1. 流水线

流水线类似于之前的设计,但有更多的改进。它为每时钟周期4条指令的吞吐率而设计。

根据下面第132页列出文献,每个核有一个192项的重排缓冲,保留站有60项,寄存器文件有168整数寄存器与168向量寄存器。

流水线的所有部分在那些可以在每个核上运行两个线程的CPU模式中共享。在两个线程在同一个核上运行时,每个线程得到一半的总吞吐率。

​​​​​​​10.2. 指令获取与掩码

在单线程应用程序中,指令获取单元每时钟周期可以获取最多16字节的代码。

4个解码器,它们可以处理每时钟周期最多产生4μop的指令,以第108页所描述的Sandy Bridge的方式。

具有任意前缀的指令在一个时钟周期内解码。对重复前缀没有惩罚。

长度改变前缀的惩罚与Sandy Bridge相同(参考第108页)。带有一个立即数,使用操作数大小前缀的算术与逻辑指令,如add ax, 1234在解码器中有2 – 3个时钟周期的惩罚,不管是否对齐。这适用于在3264位模式中,使用一个16位立即数作为操作数的算术与逻辑指令。移动指令没有长度改变前缀的惩罚。

​​​​​​​10.3. μop缓存

来自Sandy Bridgeμop缓存(参考第109页),在Haswell与Broadwell中保留了相同的大小与结构。μop缓存最重要的影响是。吞吐率不再受限于小代码循环每时钟周期16字节的获取速度。适合μop缓存的代码片段可以每时钟周期最多32字节的速率发布。当平均指令长度超过4字节时,这是一个大的优势。

Sandy Bridge μop缓存的局限与弱点也适用于Haswell与Broadwell。细节参考第109页。

​​​​​​​10.4. 回路缓冲

处理器有只是从μop队列回收μop的回路缓冲。回路缓冲很少用掉队列的所有56项,但最多30个μop,或者有时最多40个μop的小循环将从回路缓冲受益。μop队列动态地在线程间共享,因此如果两个线程运行在同一个核上,每个仅得到一半的大小。回路缓冲给出每时钟周期4个μop的稳定吞吐率,不管小循环的指令长度。

概括来说,流水线有三个不同的来源,依赖于关键循环的大小:

  • 回路缓冲用于最多30 – 40条指令的小循环。吞吐率是每时钟4μop,没有指令长度限制。
  • μop缓存用于最多大约1000条指令的循环。吞吐率是每时钟最多4条指令或32字节代码。
  • 获取及解码单元用于不在μop缓存的指令。吞吐率是每时钟最多4条指令或16字节代码。

融合指令对(参见下面)在μop缓存及回路缓冲中算作一个。每时钟周期两个融合非采用分支,从回路缓冲或μop缓存获取每时钟周期6条指令的最大吞吐率是可能的。

在μop 3个来源间,分支误预测惩罚可能不同,但我不能证实这样一个差异,因为测量的误差很高。在所有3个情形里,测的的误预测惩罚在16到20个时钟周期间。​​​​​​​

10.5. 微操作融合

与之前处理器相同的方式使用μop融合。某些在执行单元中需要两个μop的指令可以使用μop融合技术,从解码器到保留站,将这两个μop保持为一个,以节省流水线带宽。然后,保留站将发布两个μop到两个不同的端口。大多数内存写指令以及大多数带有内存操作数的算术与逻辑指令使用μop融合,不管寄存器的大小。进一步解释参考第80页与94页。

解码器每时钟周期可以处理4条μop融合指令。

在手册4“指令表”的表格中,可以查到哪些指令使用μop融合。使用μop融合的指令在“非融合域”下列出的μop数要大于“融合域”下列出的μop数。

​​​​​​​10.6. 宏操作融合

Haswell与Broadwell可以像之前处理器那样,将两条指令融合为一个μop(参考第95页)。

在某些情形里,解码器将1条算术或逻辑指令与后续的条件跳转指令融合为1个计算分支μop。计算分支μop在执行单元处不分解为2个μop,而是由端口0或6处的分支单元作为1个μop执行。

CMPADDSUB指令可与有符号及无符号分支指令融合。INCDEC可与有符号分支指令融合,而TESTAND指令可与所有的分支指令(包括无用的组合)融合,如第111页表9.2所示。

第一条指令可以有一个立即数或内存源操作数,但不能是两者。它不能有内存目标操作数。它不能有RIP相对取址操作数。JECXZLOOP指令不能融合。

不像之前的处理器,即使跨越16字节代码边界,也可以融合。两个可融合对可在同一个时钟周期里解码。

要利用宏操作融合,程序员应该将任何可融合算术指令与后续的跳转指令放在一起,而不是在之间安排其他指令。所有4个解码器都支持宏操作融合。

​​​​​​​10.7. 栈引擎

HaswellBroadwell有一个类似于Sandy Bridge的栈引擎,如第112页所述。在栈操作,像pushpopcallreturn,穿插着明确访问栈指针的指令,如rsp, 8mov eax, [rsp+16]时,一个额外的栈同步μop被自动插入。

​​​​​​​10.8. 寄存器分配与重命名

所有整数、浮点、MMXXMMYMM、标记以及还可能段寄存器可以重命名。浮点控制字也可以重命名。

寄存器重命名由重排缓冲以及调度器控制。没有观察到寄存器分配与重命名成为瓶颈。

依赖性的特殊情形

将一个寄存器置零的常见方式是与自身XOR,或减去自己,如XOR EAX, EAX。处理器知道,如果两个输入操作数是同一个寄存器,特定的指令与寄存器的之前值无关。这个寄存器在寄存器分配阶段置零,无需使用执行单元,也无需等到寄存器之前的值可用。

以下指令可以这个方式将一个寄存器置零,仿佛与自身XOR或减去自身:XORSUBPXORXORPSXORPD以及PSUBxxxPCMPGTxx的各个版本。

带有v前缀的指令行为相同。不使用执行单元,吞吐率是每时钟周期4个置零操作。

这对所有32位及64位通用寄存器以及所有128位与256位向量寄存器都奏效。它不能作用于8位与16位寄存器,因为仅寄存器部分被置零。它部分作用于64mmx寄存器:寄存器被置零,无需等待之前的值,但它使用执行单元(以解决作为浮点栈寄存器与mmx寄存器的双重使用)。

PCMPEQxx指令的所有版本可以将一个寄存器置为全1,无需等待该寄存器的之前值。不过,它要使用一个执行单元。

以下指令没有两个输入操作数是同一个寄存器的特殊情形:CMPSBBANDNPANDNANDNPSANDNPDCMPEQPSCMPEQPD

无需执行单元的指令

上述通过,比如XOR EAX, EAX指令,将寄存器置零的特殊情形,在寄存器分配阶段处理,无需使用任何执行单元。

少数其他指令也无需使用任何执行单元来处理。这些是CLCFXCHNOP(包括长nop),但不包括FNOP

移动指令的消除

大多数寄存器寄存器移动在寄存器分配阶段,以与Ivy Bridge相同的方式消除,如113页所述。

超过80%的可能情形中,大多数消除在通常会成功。串接的移动也可被消除。

所有32位及64位通用寄存器以及所有128位与256位向量寄存器都可能消除移动。8位与16位寄存器不可能,64mmx寄存器也不可能。

不像Ivy BridgeHaswellBroadwell不能消除零扩展移动。但隐含的零扩展移动可以消除,如MOV EAX, EBX(零扩展到RAX),及VMOVAPS XMM0, XMM1(零扩展到YMM0)。

到自身的移动永远不会被消除。例如mov eax, eax不会被消除。

一个消除的移动有0时延,不使用任何执行单元。但确实消耗解码器的带宽。

​​​​​​​10.9. 执行单元

处理器有若干访问8个执行端口的执行单元。这给出了理论上执行单元里每时钟周期8μop的最大吞吐率。不过,整个设计的典型吞吐率是每时钟周期4条指令。因此,即使使用μop融合,在临时爆发以外,保持所有执行端口繁忙,是不可能的。

许多执行单元是有备份的,因此对一个特定的μop找到一个空闲的单元可能性总是很大。有4个整数ALU,因此最常见的整数操作可以每时钟周期4条指令的吞吐率执行。有3个端口可以处理整数向量操作。两个端口可以处理浮点向量操作。两个端口可以处理分支。两个端口可以处理内存读操作,一个端口可以处理内存写。

很奇怪,仅有一个端口用于浮点加法,但两个端口用于浮点乘法。

8个端口及它们的常见操作在表10.1列出。

端口

操作

时延

0

整数与向量算术、逻辑、偏移

1

0

浮点乘法

Haswell5Broadwell3

0

浮点FMA与长双精度乘法

5

0

整数向量乘法

5

0

除法

可变

0

分支

1 – 2

1

整数与向量算术、逻辑

1

1

浮点加法

3

1

整数乘法

3

1

浮点乘法

Haswell5Broadwell3

1

浮点FMA

5

2

载入,地址生成

 

3

载入,地址生成

 

4

 

5

整数与向量算术、逻辑

1

5

向量组合(permute

1(如果跨通道,3

5

AES加密

7

6

整数算术、逻辑、偏移

1

6

分支

1 - 2

7

写地址生成

 

表10.1. Haswell与Broadwell中的执行单元

所有向量执行单元有完整的256位能力,除了除法、平方根及加密。一个256位单元不能分解并同时用于2个128位操作。

整数向量操作的时延与通用寄存器中的操作相同。这使得在用完通用寄存器时,将XMM寄存器用于整数操作成为可能。虽然,更少的执行单元支持向量操作。

融合乘法与加法

Haswell与Broadwell有两个可以处理类型为a = b * c + d融合乘加(FMA)指令的执行单元。

一条FMA指令使用单条指令与单个μop执行一个乘法与一个加法或减法。这可以提升乘法与加法组合经常出现的浮点代码的性能。吞吐率是每时钟周期两条FMA指令,时延为5。

Intel一开始设计FMA指令带有4个操作数(FMA4),但后来改变计划为设计带有3个操作数(FMA3),其中重用其中一个输入寄存器为输出寄存器是必须的,即a = b * c + a。

一个μop可以有多少输入依赖?

所有之前有乱序执行能力的Intel处理器,有μop不能有超过2个输入依赖的设计局限。我从所有具有超过两个输入依赖的指令都被分解为至少两个μop的事实,得出这个结论。最常见的例子有:

; Example 10.1. Instructions with three input dependencies

mov [eax+ebx],e cx                            ; depends on 3 registers

adc eax, ebx                                        ; depends on 2 registers and carry flag

cmovz eax,e bx                                   ; depends on 2 registers and zero flag

pblendvb xmm1, xmm2, xmm0       ; depends on 3 registers

在Haswell及所有之前的Intel处理器上,这些指令都被分解为两个μop。在Haswell中,融合乘加指令的引入,使得消除μop两个输入依赖的限制变得必要。因此,FMA指令是Intel处理器上第一批使用超过两个输入依赖的μop的指令。可能需要重新设计乱序机制以允许带有3个输入依赖的μop。

通过对进位加法,借位减法,及通用寄存器上的条件移动,使用带3个输入依赖的μop,Broadwell稍微更进一步地采取了这个重新设计。在Broadwell上,所有的内存写与混合指令仍然分解为两个μop。

读写带宽

有两个相同的内存读端口(端口2与3)及一个写端口(端口4)。这些端口都被扩展到256位。这使得每时钟周期,与任何最多256位寄存器的两次内存读及一个内存写,成为可能。所有写操作需要在端口2,3或7上的地址计算。

数据旁路时延

如第99页所示,执行单元被分为域,当整数域中一条指令的输出用作浮点域中一条指令的输入时,有时有一个时钟周期的时延,反之亦然。例如:

; Example 10.2. Data bypass delays

addps xmm0, xmm1

por xmm0, xmm2         ; 1 clock delay

mulps xmm0, xmm3    ; 1 clock delay

可以通过更合适的ORPS替换POR指令,避免例子10.2里的时延。

不过,在Haswell与Broadwell上,这样的时延更少。在以下情形里,我没有找到这样的时延:

  • 当一条浮点布尔指令,比如ORPS,与整数数据一起使用时。
  • 在使用一个错误类型的移动指令时,如MOVPS或MOVDQA。
  • 在使用一个错误类型的混排指令时,如SHUFPS或PFHUFD。

不过,在一条浮点混合指令用于整数数据时,如BLENDPS,我确实找到了时延。

像MOVD这样在通用寄存器与向量寄存器间移动数据的指令,仅有1时钟周期时延且没有额外的旁路时延。类似的,从向量寄存器移动数据到整数标记寄存器的指令,比如COMISS与PTEST没有额外的旁路时延。

256位向量

所有向量执行单元有完全的256位吞吐率,除了除法、平方根与加密。

用于YMM寄存器上所有操作的256位数据通道被分为2个128位通道(lane)。所有在这2个通道间移动数据的指令都有3时钟周期的时延,其他移动指令仅有1时钟周期的时延,例如:

; Example 10.3. Moving data between 128-bit lanes

vextracti128 xmm0,ymm1,1     ; move from upper to lower lane, 3 clocks

vextracti128 xmm0,ymm1,0     ; move in lover lane only, still 3 clocks

vmovdqa xmm0,xmm1              ; same operation in 0 - 1 clocks

在例子10.3中第二条指令有3时钟周期的时延,即使所有数据待在较低的通道,因为VEXTRACTI128指令有通道间移动数据的可能性。可以通过使用VMODDQA指令来避免这个时延,在这个情形里它做相同的事情,但不会跨通道移动数据。

如章节9.12 所述,混用256位VEX代码与128位非VEX代码有严重的惩罚。所有在状态B(修改)与状态C(保存)间的转换有70时钟周期的时延。这个时延在32位模式与64位模式里是相同的,即使保存的寄存器数不一样。可以通过将所有非VEX向量指令替换为V前缀版本,或者在一条包含256位向量代码的函数调用另一个可能使用非VEX代码的函数,或返回到可能使用非VEX代码的一个函数时,发布一条VZEROUPPER指令,来避免这个时延。

256位寄存器的两个半部不视为无关的,除了非期望的状态C(保存),如章节9.12所述。将256位执行单元分解为两个128位单元来同时执行两个128位操作是不可能的。

混用不同时延的μop

某些更旧的处理器,在具有不同时延的μop发布到同一个执行端口时,有一个回写冲突,如第102页所述。在Haswell与Broadwell上,我没有找到这样问题的证据。部分通过标准化时延,如表10.1所示,部分通过多个回写通道,这个问题被解决了。

下溢与次正规数(subnormal

当浮点操作接近下溢时,出现次正规数。在某些情形里,次正规数的处理代价非常高,因为次正规结果由微代码异常处理的。

在一个正规值上操作给出一个次正规结果的所有情形里,Haswell与Broadwell有大约124个时钟周期的惩罚。正规值与次正规值间的乘法有一个类似的惩罚,不管结果是否正规。正规值与次正规值相加没有惩罚,不管结果如何。上溢、下溢、无穷或非值结果没有惩罚。

如果在MXCSR寄存器里,同时设置了“flush-to-zero”与“denormals-are-zero”模式,次正规值的惩罚可以避免。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值