# 4.8 谬误与陷阱
<!-- source: p0383 -->

从程序员的角度看，DLP 是仅次于 ILP 的最容易利用的并行性；从体系结构设计者的角度看，它也可能是最简单的一类。不过，其中仍有许多谬误与陷阱。

## 谬误：GPU 作为协处理器，处于不利地位

主存与 GPU 存储器相分离固然有缺点，但与 CPU 保持一定距离也有优势。

例如，PTX 的存在，部分原因就在于 GPU 具有 I/O 设备的性质。编译器与硬件之间的这一间接层，让 GPU 体系结构设计者拥有远大于系统处理器设计者的灵活性。我们往往很难事先知道，一项体系结构创新能否获得编译器和程序库的良好支持，以及它对应用是否重要。有时，一种新机制可能只在一两代产品中有用，随后便随着 IT 世界的变化而不再重要。PTX 允许 GPU 设计者探索性地尝试创新，并在效果不佳或重要性下降时，在后续几代中将其移除，从而鼓励了实验。对于系统处理器，纳入新特性所需的论据自然要充分得多，<!-- source: p0384 -->因而能进行的实验也少得多：发布二进制机器码，通常就意味着这一体系结构今后的所有代际都必须支持这些新特性。

PTX 的价值已有实例证明：不同代的体系结构曾彻底改变硬件指令集，从类似 x86 的面向存储器的形式，改为类似 RISC-V 的面向寄存器的形式，并把地址宽度加倍至 64 位，却没有破坏 NVIDIA 的软件栈。

## 陷阱：只关注向量体系结构的峰值性能，忽略启动开销

早期存储器—存储器型向量处理器，例如 TI ASC 和 CDC STAR-100，启动时间很长。对于某些向量问题，向量长度必须超过 100，向量代码才会比标量代码快！由 STAR-100 派生的 CYBER 205，执行 DAXPY 的启动开销为 158 个时钟周期，使向量与标量性能持平所需的向量长度明显增加。如果 Cray-1 与 CYBER 205 的时钟频率相同，那么在向量长度超过 64 以前，Cray-1 都更快。实际上，Cray-1 的时钟频率也更高，尽管 205 推出的时间更晚，因此两者性能交叉时的向量长度超过了 100。

## 陷阱：提升向量性能，却没有相应提升标量性能

这种不平衡是许多早期向量处理器的问题，也是 Cray 计算机的设计者 Seymour Cray 改写既有规则的领域。许多早期向量处理器的标量单元相当慢，启动开销也很大。即使在今天，向量性能较低、但标量性能较好的处理器，也可能胜过峰值向量性能更高的处理器。良好的标量性能能够压低各种开销，例如 strip mining，并减轻 Amdahl 定律的影响。

一个很好的例子是，比较一台高速标量处理器和一台标量性能较低的向量处理器。Livermore Fortran 内核包含 24 个科学计算内核，各自的可向量化程度不同。图 4.37 给出了两个不同处理器在这套基准上的性能。尽管向量处理器的峰值性能更高，但以调和平均值衡量时，较低的标量性能使它慢于高速标量处理器。

今天，这种危险的另一种表现，是只提高向量性能——例如增加通道数——却不提高标量性能。这种短视也会把计算机引向不平衡的设计。

下一个谬误与此密切相关。

<!-- source: p0385 -->

| 处理器 | 所有循环中的最低速率（MFLOPS） | 所有循环中的最高速率（MFLOPS） | 全部 24 个循环的调和平均值（MFLOPS） |
| --- | ---: | ---: | ---: |
| MIPS M/120-5 | 0.80 | 3.89 | 1.85 |
| Stardent-1500 | 0.41 | 10.08 | 1.72 |
{#fig-4.37 .wide .expand num="4.37" caption="两个不同处理器运行 Livermore Fortran 内核的性能测量。MIPS M/120-5 与 Stardent-1500（原名 Ardent Titan-1）都使用 16.7 MHz 的 MIPS R2000 芯片作为主 CPU。Stardent-1500 使用向量单元执行标量浮点运算，其标量性能按最低速率衡量，约为使用 MIPS R2010 浮点芯片的 MIPS M/120-5 的一半。对于高度可向量化的循环，向量处理器的最高速率超过后者的 2.5 倍。不过，以全部 24 个循环的调和平均值衡量总性能时，Stardent-1500 较低的标量性能抵消了较高向量性能的优势。"}

## 谬误：不提供足够的存储器带宽，也能获得良好的向量性能

从 DAXPY 循环与 Roofline 模型中可以看到，存储器带宽对所有 SIMD 体系结构都十分重要。DAXPY 每次浮点运算需要 1.5 次存储器访问，这也是许多科学计算程序的典型比例。即使浮点运算完全不花时间，Cray-1 也无法提高所用向量指令序列的性能，因为它受存储器限制。当编译器通过分块改变计算，使数据能够保留在向量寄存器中时，Cray-1 的 Linpack 性能出现了飞跃。这种方法降低了每 FLOP 对应的存储器访问次数，使性能提高到接近原来的两倍！对于原先需要更高带宽的循环，此时 Cray-1 的存储器带宽便足够了。

## 谬误：GPU 的存储器性能不足时，只要增加线程就行

GPU 使用大量 CUDA 线程来隐藏访问主存的延迟。如果各 CUDA 线程的存储器访问十分分散，或彼此缺少关联，存储器系统响应每个独立请求的速度就会越来越慢。最终，即使线程很多，也无法掩盖这些延迟。因此，要让“增加 CUDA 线程”的策略奏效，不仅需要大量 CUDA 线程，这些线程的存储器访问还必须具有良好的局部性。
