# G.6 融会贯通：向量处理器的性能
<!-- source: p1108 -->

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

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

## 向量性能的度量

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

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

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

| 访存指令 | 链接的运算指令 | 编队 |
| --- | --- | --- |
| `LV V1,Rx` | `MULVS.D V2,V1,F0` | 编队 1：链接执行加载和乘法 |
| `LV V3,Ry` | `ADDV.D V4,V2,V3` | 编队 2：链接执行第二次加载和加法 |
| `SV Ry,V4` | | 编队 3：存储结果 |
{#fig-G.10 .wide .expand num="G.10" caption="DAXPY 代码的内循环，按采用链接的编队组织。"}

回顾包含 \(n\) 个元素的向量循环执行时间公式：

$$
T_n=\left[\frac{n}{\mathrm{MVL}}\right]\times(T_{\mathrm{loop}}+T_{\mathrm{start}})+n\times T_{\mathrm{chime}}
$$

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

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

## VMIPS 执行 DAXPY 的峰值性能

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

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

在性能公式中代入 \(\mathrm{MVL}=64\)、\(T_{\mathrm{loop}}=15\)、\(T_{\mathrm{start}}=49\)、\(T_{\mathrm{chime}}=3\)，并假设 \(n\) 不是 64 的整数倍，可得处理 \(n\) 个元素的时间：

$$
\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_\infty\)：

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

分子与 \(n\) 无关，因此：

$$
R_\infty=\frac{\text{每次迭代的运算数}\times\text{时钟频率}}{\lim_{n\to\infty}(\text{每次迭代的时钟周期数})}
$$

$$
\lim_{n\to\infty}(\text{每次迭代的时钟周期数})
=\lim_{n\to\infty}\frac{T_n}{n}
=\lim_{n\to\infty}\frac{4n+64}{n}=4
$$

$$
R_\infty=\frac{2\times500\ \mathrm{MHz}}{4}=250\ \mathrm{MFLOPS}
$$

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

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

Linpack 基准测试对一个 \(100\times100\) 矩阵执行高斯消元。因此，向量长度从 99 逐渐减至 1；长度为 \(k\) 的向量会使用 \(k\) 次。所以，平均向量长度为：

<!-- source: p1111 -->

$$
\frac{\sum_{i=1}^{99}i^2}{\sum_{i=1}^{99}i}=66.3
$$

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

$$
T_{66}=2\times(15+49)+66\times3=128+198=326
$$

$$
R_{66}=\frac{2\times66\times500}{326}\ \mathrm{MFLOPS}=202\ \mathrm{MFLOPS}
$$

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

由于向量长度显著影响性能，比较向量机时常使用 \(N_{1/2}\) 和 \(N_v\)。

**例题** 对 500 MHz 的 VMIPS，如果只考虑 DAXPY 内循环，\(N_{1/2}\) 是多少？

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

$$
\mathrm{MFLOPS}=\frac{N_{1/2}\text{ 次迭代执行的浮点运算数}}{N_{1/2}\text{ 次迭代所需的时钟周期数}}
\times\frac{\text{时钟周期数}}{\text{秒}}\times10^{-6}
$$

$$
125=\frac{2N_{1/2}}{T_{N_{1/2}}}\times500
$$

化简，并假设 \(N_{1/2}<64\)，此时执行时间为 \(64+3n\)，得到：

$$
\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}
$$

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

<!-- source: p1112 -->

**例题** 使向量操作快于标量操作的向量长度 \(N_v\) 是多少？

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

$$
\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 的 \(T_{66}\) 会是多少？

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

$$
T_{66}=\left[\frac{66}{64}\right]\times(T_{\mathrm{loop}}+T_{\mathrm{start}})+66T_{\mathrm{chime}}
$$

$$
T_{66}=2\times(15+49)+66\times1=194
$$

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

<!-- source: p1113 -->

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

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

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

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

**解答**

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

$$
\lim_{n\to\infty}(\text{每次迭代的时钟周期数})=\lim_{n\to\infty}\frac{T_n}{n}
$$

由于开销只显现一次，\(T_n=n+49+15=n+64\)。于是：

$$
\lim_{n\to\infty}\frac{T_n}{n}=\lim_{n\to\infty}\frac{n+64}{n}=1
$$

$$
R_\infty=\frac{2\times500\ \mathrm{MHz}}{1}=1000\ \mathrm{MFLOPS}
$$

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

<!-- source: p1114 -->

本节考察了几种向量性能指标。只根据 \(T_{\mathrm{chime}}\)，就能计算理论峰值性能：

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

计入循环开销后，可以计算无限长向量的峰值性能 \(R_\infty\)，以及长度为 \(n\) 的向量的持续性能 \(R_n\)：

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

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