G.2 深入分析向量性能
对于长向量,chime 近似已经相当准确。不过,还有一种开销,影响远比指令发射限制更大。
chime 模型忽略的最重要开销,是向量启动时间。启动时间来自向量操作的流水线延迟,主要取决于所用功能单元的流水线深度。它使一个编队的实际执行时间超过一个 chime。由于我们假设各编队在时间上不重叠,启动时间还会推迟后续编队的执行。当然,相邻编队中的指令,不是在某个功能单元上存在结构冲突,就是存在数据依赖,因此不重叠的假设是合理的。
完成一个编队的实际时间,由向量长度与启动时间之和决定。如果向量长度无限大,启动开销就能被摊薄;但向量长度有限,启动开销便会显现,下面的例子说明了这一点。
例题 假设功能单元的启动开销如图 G.2 所示。给出各编队可以开始执行的时间,以及总共需要的周期数。向量长度为 64 时,该时间与 chime 近似相比如何?
解答 图 G.3 按编队给出答案,假定向量长度为 。一个棘手的问题是:我们认为什么时刻才算整个向量指令序列完成?这决定了 SV 的启动时间是否可见。这里假设后续指令不能放入同一编队,而且前面已经假定编队之间不重叠。因此,总时间是最后一个编队中最后一条向量指令完成所需的时间。这是一种近似:最后一条向量指令的启动时间,在一些指令序列中可见,在另一些序列中则可能不可见。为简单起见,我们始终将它计入。
向量长度为 64 时,每个结果所需时间为 个时钟周期,而 chime 近似为 4 个时钟周期。计入启动开销后,执行时间是原来的 1.16 倍。
译注: 上式沿用原书的 4.65;精确计算为 4.65625,通常保留两位小数应为 4.66。这里不把原书截取的近似值静默改成另一数值。
| 单元 | 启动开销(周期) |
|---|---|
| 加载和存储单元 | 12 |
| 乘法单元 | 7 |
| 加法单元 | 6 |
| 编队 | 开始时间 | 第一个结果时间 | 最后一个结果时间 |
|---|---|---|---|
1. LV | 0 | 12 | |
2. MULVS.D LV | |||
3. ADDV.D | |||
4. SV |
为简单起见,我们通常使用 chime 近似估算运行时间;只有需要更精确的性能结果,或要说明某项改进的收益时,才计入启动时间。对长向量这一常见情形,启动开销的影响并不很大。本附录后面还会讨论降低启动开销的方法。
指令的启动时间来自执行该指令的功能单元的流水线深度。如果要保持每时钟周期产生一个结果的启动速率,则有:
例如,若一个操作需要 10 个时钟周期,那么要达到每周期启动一个操作的速率,就必须使用 10 级流水线。因此,流水线深度取决于操作的复杂度和处理器的时钟周期时间。功能单元的流水线深度差异很大,常见为 2–20 级;使用最频繁的单元通常为 4–8 个时钟周期。
对于 VMIPS,我们采用与 Cray-1 相同的流水线深度,尽管更现代的处理器往往具有更大的延迟,尤其是加载延迟。所有功能单元都采用完全流水化设计。按第 4 章的设定,浮点加法的流水线深度为 6 个时钟周期,浮点乘法为 7 个时钟周期。在 VMIPS 上,和大多数向量处理器一样,使用不同功能单元的独立向量操作可以在同一编队中发射。
除了启动开销,还要考虑执行 strip-mining 循环的开销。这种开销来自重新启动向量指令序列、设置向量长度寄存器(VLR);在编队与其他指令不重叠的假设下,它实际上增加了向量启动时间。如果一个编队的这项开销为 10 个周期,那么每 64 个元素的有效开销就增加 10 个周期,即每元素增加 0.15 个周期。
由一系列编队组成的 strip-mining 循环,其运行时间主要取决于两个因素:
- 循环中的编队数,它决定 chime 数。我们用 表示以 chime 为单位的执行时间。
- strip-mining 中每组编队的开销。它由对每个数据块执行 strip-mining 标量代码的开销 ,以及各编队的向量启动开销 组成。
第一次设置向量指令序列时,还可能有一项固定开销。近来的向量处理器已将它降得很低,因此这里忽略不计。
利用这些组成部分,可以表示一组向量指令处理长度为 的向量所需的总时间,记为 :
译注: 原式使用普通方括号。结合后面把 200 个元素分成 4 段的例题,这里的方括号表示所需分段次数,即对 向上取整;不是向下取整。
、 和 的取值,都取决于编译器和处理器。指令的寄存器分配与调度,既会影响哪些指令被放入一个编队,也会影响各编队的启动开销。
为简单起见,VMIPS 的 采用常数。根据对 Cray-1 向量执行的多种测量,取 。乍看之下,这个值似乎过小:每次循环都需要设置向量起始地址与步长、递增计数器,并执行循环分支。但实际上,这些标量指令可以与向量指令完全或部分重叠,从而尽量减少这些辅助操作占用的时间。当然, 取决于循环结构;不过,与向量代码对 和 的影响相比,这种依赖关系较弱。
| 操作 | 启动代价(周期) |
|---|---|
| 向量加法 | 6 |
| 向量乘法 | 7 |
| 向量除法 | 20 |
| 向量加载 | 12 |
例题 在 VMIPS 上执行向量操作 需要多长时间?其中 为标量,向量 、 的长度均为 200。
解答 假定 、 的地址最初分别位于 Ra、Rb 中, 位于 Fs 中;还要记住,MIPS 及 VMIPS 的 R0 始终为 0。由于 ,strip-mining 循环的第一次迭代处理 8 个元素,之后每次处理 64 个元素。每个向量下一段起始字节地址的增量,为本段向量长度的 8 倍。因此,第一段之后地址寄存器递增 ,后续各段之后递增 。向量共有 字节;将下一段地址与初始地址加 1600 比较,即可判断是否完成。实际代码如下:
循环中的三条向量指令存在依赖,必须放入三个编队,因此 。代入基本公式:
是以下三项之和:
- 向量加载的 12 个时钟周期启动时间;
- 乘法的 7 个时钟周期启动时间;
- 存储的 12 个时钟周期启动时间。
因此:
总执行时间为:
计入全部启动开销后,每元素执行时间为 个周期,而 chime 近似为 3 个周期。G.4 节将进一步允许不同编队重叠执行。
图 G.5 给出前例 在不同向量长度下的每元素开销与实际执行速率。按 chime 计数的模型,每元素需要 3 个时钟周期;两种开销在极限情况下还会为每元素增加 0.9 个时钟周期。

图内术语:Clock cycles=时钟周期;Vector size=向量长度;Total time per element=每元素总时间;Total overhead per element=每元素总开销。
译注: 图注原写“F-6 页”,对应例题实际位于本附录 G-6 页。正文“极限情况下增加 0.9 个周期”也与本节参数不一致:按 、、,长向量的每元素开销极限为 个周期。
指令启动的流水化与多通道
增加通道数可以提高峰值性能,却不会改变启动延迟。因此,让一条向量指令的启动与此前向量指令的收尾重叠,以减少启动开销,就变得十分关键。最简单的情况是两条指令访问不同的向量寄存器。例如:
一种实现方式是:第一条向量指令的最后一个元素进入浮点加法流水线后,紧接着让第二条指令的第一个元素进入。为降低控制逻辑复杂度,一些向量机要求两条派发到同一向量单元的指令之间,保留一段恢复时间,即空转时间(dead time)。图 G.6 用单条向量流水线的时序图,同时说明启动延迟与空转时间。
下面的例题说明空转时间对可达到的向量性能有何影响。
例题 Cray C90 有两条通道,但发往同一功能单元的任意两条向量指令之间,即使没有数据依赖,也必须有 4 个时钟周期的空转时间。最大向量长度为 128 个元素时,这会使可达到的峰值性能降低多少?如果通道数增至 16,又会降低多少?
解答 最大长度为 128 的向量分配到两条通道,需占用向量功能单元 64 个时钟周期。空转时间又增加 4 个周期的占用,使峰值性能降至无空转时间时的 。如果增至 16 条通道,最大长度的向量指令只需占用功能单元 个周期,空转时间使峰值性能降至无空转时间时的 。后一种情况下,向量单元的忙碌时间永远不可能超过总时间的三分之二!

图内术语:Start-up latency=启动延迟;First/Second vector instruction=第一/第二条向量指令;Dead time/Dead cycle=空转时间/空转周期;Element=元素;R、X1–X3、W 分别表示读寄存器、三个执行级和写寄存器。
当多条指令可能读写同一个向量寄存器,或某些指令可能因不可预测的原因停顿——例如向量加载遇到存储体冲突——时,指令启动的流水化就更加复杂。但随着通道数与流水线延迟不断增加,允许指令启动完全流水化,也就愈加重要。