跳转到主要内容

G.6 融会贯通:向量处理器的性能

本节考察向量处理器的性能指标,以及这些指标能够反映哪些处理器特征。要确定处理器执行一个向量问题的性能,必须同时考虑启动开销与持续执行速率。报告向量处理器执行某个循环的性能时,最简单、也最好的方法,是给出该向量循环的执行时间。不过,人们往往以 MFLOPS——每秒百万次浮点运算——而非执行时间来表示向量循环性能。我们用 RnR_n 表示处理长度为 nn 的向量时达到的 MFLOPS。只要浮点运算次数的计算口径一致,用 TnT_n(时间)或 RnR_n(速率)衡量就是等价的。无论采用哪一种,都应计入开销。

本节从不同角度分析 VMIPS 执行 DAXPY 循环(见第 4 章)的性能。我们仍使用 G.2 节建立的公式计算向量循环执行时间,同时考察如何根据算出的时间,以不同方式衡量性能。本节对 TloopT_{\mathrm{loop}} 采用常数,会引入少量误差,这里忽略不计。

向量性能的度量

向量长度对处理器性能影响很大,因此除了时间与 MFLOPS,还经常采用与长度有关的指标。这些指标在不同处理器之间往往相差很大,值得比较。不过请记住,比较两台处理器的相对速度时,最终关心的始终是时间。三个最重要的长度相关指标是:

  • R∞R_\infty:无限长向量的 MFLOPS。它可以用于估计峰值性能,但实际问题的向量长度有限,所承受的开销代价更大。
  • N1/2N_{1/2}:达到 R∞R_\infty 一半所需的向量长度。这是衡量开销影响的良好指标。
  • NvN_v:使向量方式快于标量方式所需的向量长度。它既反映开销,也反映标量与向量的相对速度。

下面看看 VMIPS 执行 DAXPY 问题时的这些指标。采用链接时,DAXPY 内循环可以组织为图 G.10 所示的编队,假定 Rx 和 Ry 保存起始地址。

访存指令链接的运算指令编队
LV V1,RxMULVS.D V2,V1,F0编队 1:链接执行加载和乘法
LV V3,RyADDV.D V4,V2,V3编队 2:链接执行第二次加载和加法
SV Ry,V4编队 3:存储结果
图 G.10 DAXPY 代码的内循环,按采用链接的编队组织。

回顾包含 nn 个元素的向量循环执行时间公式:

Tn=[nMVL]×(Tloop+Tstart)+n×Tchime T_n=\left[\frac{n}{\mathrm{MVL}}\right]\times(T_{\mathrm{loop}}+T_{\mathrm{start}})+n\times T_{\mathrm{chime}}

链接使循环能够在 3 个 chime 中完成;由于只有一条存储器流水线,不能再少,因此 Tchime=3T_{\mathrm{chime}}=3。如果 TchimeT_{\mathrm{chime}} 足以完整反映性能,那么循环的浮点运算速率就应为时钟频率的 2/32/3,因为每次迭代执行两次浮点运算。仅根据 chime 数,不计 strip-mining 和启动开销时,500 MHz 的 VMIPS 可达到 333 MFLOPS。

有几种办法可以继续提高性能:增加向量加载—存储单元;允许编队重叠,以降低启动开销的影响;通过向量寄存器分配减少所需加载次数。本节考察前两项扩展。最后一项优化确实用于与 VMIPS 颇为接近的 Cray-1,使性能提高了 50%。减少加载次数需要过程间优化,习题 G.6 将讨论这种变换。分析前两项扩展之前,先来看看计入开销后的实际性能。

VMIPS 执行 DAXPY 的峰值性能

首先要确定真正的峰值性能 R∞R_\infty,因为它必然不同于理想的 333 MFLOPS。暂时仍采用简化假设:只有前一编队中的全部指令完成,后一编队才能开始;后面再取消这一限制。在该假设下,向量序列的启动开销就是各条指令启动时间之和:

Tstart=12+7+12+6+12=49 T_{\mathrm{start}}=12+7+12+6+12=49

在性能公式中代入 MVL=64\mathrm{MVL}=64、Tloop=15T_{\mathrm{loop}}=15、Tstart=49T_{\mathrm{start}}=49、Tchime=3T_{\mathrm{chime}}=3,并假设 nn 不是 64 的整数倍,可得处理 nn 个元素的时间:

Tn=[n64]×(15+49)+3n≤(n+64)+3n=4n+64 \begin{aligned} T_n&=\left[\frac{n}{64}\right]\times(15+49)+3n\\ &\le(n+64)+3n\\ &=4n+64 \end{aligned}

实际持续执行时,每次迭代需要超过 4 个时钟周期,而不是忽略开销时理论上的 3 个 chime。差异主要来自每 64 个元素的启动开销:启动需要 49 个周期,循环控制开销则为 15 个周期。

对于 500 MHz 时钟,现在可以计算 R∞R_\infty:

R∞=lim⁡n→∞(每次迭代的运算数×时钟频率每次迭代的时钟周期数) R_\infty=\lim_{n\to\infty}\left(\frac{\text{每次迭代的运算数}\times\text{时钟频率}}{\text{每次迭代的时钟周期数}}\right)

分子与 nn 无关,因此:

R∞=每次迭代的运算数×时钟频率lim⁡n→∞(每次迭代的时钟周期数) R_\infty=\frac{\text{每次迭代的运算数}\times\text{时钟频率}}{\lim_{n\to\infty}(\text{每次迭代的时钟周期数})} lim⁡n→∞(每次迭代的时钟周期数)=lim⁡n→∞Tnn=lim⁡n→∞4n+64n=4 \lim_{n\to\infty}(\text{每次迭代的时钟周期数}) =\lim_{n\to\infty}\frac{T_n}{n} =\lim_{n\to\infty}\frac{4n+64}{n}=4 R∞=2×500 MHz4=250 MFLOPS R_\infty=\frac{2\times500\ \mathrm{MHz}}{4}=250\ \mathrm{MFLOPS}

不计启动开销、仅由向量功能单元结构决定的峰值性能,是这个结果的 1.33 倍。实际上,这项基准测试中峰值性能与持续性能的差距还会更大!

VMIPS 在 Linpack 基准测试上的持续性能

Linpack 基准测试对一个 100×100100\times100 矩阵执行高斯消元。因此,向量长度从 99 逐渐减至 1;长度为 kk 的向量会使用 kk 次。所以,平均向量长度为:

∑i=199i2∑i=199i=66.3 \frac{\sum_{i=1}^{99}i^2}{\sum_{i=1}^{99}i}=66.3

现在可以用长度为 66 的向量,较准确地估计 DAXPY 的性能:

T66=2×(15+49)+66×3=128+198=326 T_{66}=2\times(15+49)+66\times3=128+198=326 R66=2×66×500326 MFLOPS=202 MFLOPS R_{66}=\frac{2\times66\times500}{326}\ \mathrm{MFLOPS}=202\ \mathrm{MFLOPS}

忽略启动开销的峰值数值,是按实际向量长度估计的持续性能的 1.64 倍。实际上,Linpack 还有相当一部分代码不能向量化。虽然在向量化之前,这部分代码占执行时间不到 20%,但以浮点运算速率计,它的性能不到向量代码的十分之一。因此,根据 Amdahl 定律,整体性能会明显低于只分析内循环得到的估计。

由于向量长度显著影响性能,比较向量机时常使用 N1/2N_{1/2} 和 NvN_v。

例题 对 500 MHz 的 VMIPS,如果只考虑 DAXPY 内循环,N1/2N_{1/2} 是多少?

解答 把 R∞R_\infty 作为峰值速率,需要求出达到约 125 MFLOPS 的向量长度。假定测量处理 N1/2N_{1/2} 个元素,从 MFLOPS 公式出发:

MFLOPS=N1/2 次迭代执行的浮点运算数N1/2 次迭代所需的时钟周期数×时钟周期数秒×10−6 \mathrm{MFLOPS}=\frac{N_{1/2}\text{ 次迭代执行的浮点运算数}}{N_{1/2}\text{ 次迭代所需的时钟周期数}} \times\frac{\text{时钟周期数}}{\text{秒}}\times10^{-6} 125=2N1/2TN1/2×500 125=\frac{2N_{1/2}}{T_{N_{1/2}}}\times500

化简,并假设 N1/2<64N_{1/2}<64,此时执行时间为 64+3n64+3n,得到:

TN1/2=8N1/264+3N1/2=8N1/25N1/2=64N1/2=12.8 \begin{aligned} T_{N_{1/2}}&=8N_{1/2}\\ 64+3N_{1/2}&=8N_{1/2}\\ 5N_{1/2}&=64\\ N_{1/2}&=12.8 \end{aligned}

因此 N1/2=13N_{1/2}=13:在 VMIPS 上,长度为 13 的向量就能使 DAXPY 循环达到约一半的峰值性能。

例题 使向量操作快于标量操作的向量长度 NvN_v 是多少?

解答 同样知道 Nv<64N_v<64。标量方式执行一次迭代的时间,可估为 10+12+12+7+6+12=5910+12+12+7+6+12=59 个周期。其中 10 是循环控制开销的估计,比 strip-mining 循环的开销稍小。上一题已经得到,在 n≤64n\le64 时,这个循环以向量方式运行需要 Tn=64+3nT_n=64+3n 个时钟周期。因此:

64+3Nv=59NvNv=[6456]Nv=2 \begin{aligned} 64+3N_v&=59N_v\\ N_v&=\left[\frac{64}{56}\right]\\ N_v&=2 \end{aligned}

对 DAXPY 循环,只要向量至少有两个元素,向量方式就快于标量方式。这个门槛低得令人意外。

增强版 VMIPS 的 DAXPY 性能

DAXPY 和许多向量问题一样,受存储器限制。因此,增加访存流水线可以提高性能。这正是 Cray X-MP 及其后继处理器与 Cray-1 的主要体系结构差异。Cray X-MP 有三条存储器流水线,而 Cray-1 只有一条;X-MP 的链接也更加灵活。这会怎样影响性能?

例题 如果为 VMIPS 再增加两条存储器流水线,DAXPY 的 T66T_{66} 会是多少?

解答 有三条存储器流水线时,所有指令都能放入同一编队,只需一个 chime。启动开销不变,因此:

T66=[6664]×(Tloop+Tstart)+66Tchime T_{66}=\left[\frac{66}{64}\right]\times(T_{\mathrm{loop}}+T_{\mathrm{start}})+66T_{\mathrm{chime}} T66=2×(15+49)+66×1=194 T_{66}=2\times(15+49)+66\times1=194

三条存储器流水线将持续执行所需周期数从 326 降至 194,带来约 1.7 倍的加速。注意 Amdahl 定律的影响:按 chime 数衡量的理论峰值速率提高至原来的 3 倍,但整体持续性能只提高至原来的 1.7 倍。

另一项改进是允许不同编队重叠,并让标量循环控制操作与向量指令重叠。这要求在前一向量操作尚未完成时,后一操作就可以开始使用功能单元,因此指令发射逻辑会更加复杂。这种重叠可以消除第一个编队之外各编队单独产生的启动开销,并隐藏循环控制开销。

要尽可能隐藏 strip-mining 开销,就需要让不同分段的循环实例重叠,使同一编队的两个实例,以及可能存在的两组标量代码同时执行。这需要使用第 3 章所述避免 WAR 冒险的技术;不过,由于不允许对同一个向量元素进行重叠读写,因此可以避免复制。这项技术称为 tailgating,Cray-2 曾使用过它。另一种办法是像第 3 章那样,展开外层循环,用不同寄存器组创建多个向量指令序列实例,前提是寄存器足够多。

让编队和标量循环控制操作尽可能重叠后,每个向量序列只会显现一次启动与循环开销,不再取决于编队数或各编队的指令数。这样,采用向量寄存器的处理器既能在短向量上获得较低的启动开销,也能在很长的向量上达到较高峰值性能。

例题 若为 VMIPS 再增加两条存储器流水线,并允许 strip-mining 与启动开销完全重叠,DAXPY 的 R∞R_\infty 和 T66T_{66} 分别是多少?

解答

R∞=lim⁡n→∞(每次迭代的运算数×时钟频率每次迭代的时钟周期数) R_\infty=\lim_{n\to\infty}\left(\frac{\text{每次迭代的运算数}\times\text{时钟频率}}{\text{每次迭代的时钟周期数}}\right) lim⁡n→∞(每次迭代的时钟周期数)=lim⁡n→∞Tnn \lim_{n\to\infty}(\text{每次迭代的时钟周期数})=\lim_{n\to\infty}\frac{T_n}{n}

由于开销只显现一次,Tn=n+49+15=n+64T_n=n+49+15=n+64。于是:

lim⁡n→∞Tnn=lim⁡n→∞n+64n=1 \lim_{n\to\infty}\frac{T_n}{n}=\lim_{n\to\infty}\frac{n+64}{n}=1 R∞=2×500 MHz1=1000 MFLOPS R_\infty=\frac{2\times500\ \mathrm{MHz}}{1}=1000\ \mathrm{MFLOPS}

增加访存流水线、采用更灵活的发射逻辑,使峰值性能达到原来的 4 倍。不过,T66=130T_{66}=130,所以对较短向量,持续性能约提高至原来的 326/130=2.5326/130=2.5 倍。

本节考察了几种向量性能指标。只根据 TchimeT_{\mathrm{chime}},就能计算理论峰值性能:

每次迭代的浮点运算数×时钟频率Tchime \frac{\text{每次迭代的浮点运算数}\times\text{时钟频率}}{T_{\mathrm{chime}}}

计入循环开销后,可以计算无限长向量的峰值性能 R∞R_\infty,以及长度为 nn 的向量的持续性能 RnR_n:

Rn=每次迭代的浮点运算数×n×时钟频率Tn R_n=\frac{\text{每次迭代的浮点运算数}\times n\times\text{时钟频率}}{T_n}

还可以据此求出 N1/2N_{1/2} 和 NvN_v,从另一角度观察向量启动开销,以及向量方式与标量方式的速度比。采用多种向量处理器性能指标,有助于理解应用实际可能获得的性能范围。

表格阅读