机器基础
机器基础
数据手册报告的是峰值吞吐量;实际工作负载几乎从未达到这一水平。要缩小这一差距,就需要知道哪种物理限制最先约束执行:延迟、内存带宽、算术强度,还是能耗。本附录汇集了具有持久适用性的硬件模型和数量级数值,为全书的单节点分析提供依据,并给出可用于核验或扩展各章估算的数学基准。这些模型假定读者具备计算机体系结构的本科基础,尤其是对存储层次结构的了解。
如何使用本附录
本附录旨在用作参考。在诊断性能问题时,可利用本附录将模糊的症状(“运行很慢”)转化为具体的约束(“批量大小为一时受内存限制”),进而选择真正能够改善性能的调节手段。
此处的约定遵循全书统一的符号体系(例如,\(B\) 专用于表示批量大小,\(\text{BW}\) 专用于表示带宽)。
- 检查可行性是否合理:首先查阅 section 1.1 中的数量级数值。
- 诊断主导性能上限:使用 section 1.2.1 中的 Roofline 模型,判断工作负载是受计算限制还是受内存限制。
- 分析扩展极限:使用 section 1.2.3 中的阿姆达尔定律和古斯塔夫森定律,理解为何增加加速器可能无法缩短训练时间。
- 选择合适的精度:使用 section 1.4.1,从系统权衡的角度分析 FP32、BF16/FP16 与 INT8。
- 查阅相关章节的论述:完整论述见 硬件加速、模型训练 和 模型服务。
尽管现代硬件涵盖多种加速器架构、互连拓扑和封装方案,但此处详述的物理定律和瓶颈模型均适用于它们。
应知数值
正如 Jeff Dean 的“每位程序员都应知道的延迟数值”1 影响了一代系统工程师,这些参考数值也为机器学习系统设计提供了必不可少的数量级直觉。尽管绝对数值和比值会随技术和工作负载而变化,但层次关系更为持久。应牢记这些关系,并用具体数值检验估算是否合理。
Systems Perspective 1.1: 最重要的三个数值
- 能耗比:在所引用的 45 nm 参考数据中,一次 32 位 DRAM 读取的能耗约为一次 FP16 乘法能耗的 581×。这说明了算术强度的重要性。
- 训练状态内存占用:模型权重(FP16,2 bytes)+ 梯度(FP16,2 bytes)+ 主权重(FP32,4 bytes)+ 自适应矩估计(Adam)的优化器状态(8 bytes)。每个参数总计占用 16 bytes,因此,一个参数量为 7B 的模型,仅启动训练就需要 112 GB。
- 光纤传播极限:光在光纤中的传播速度约为 200 km/ms。表中约 40 ms 的跨美国往返延迟包含可削减的开销,但传播时间无法通过优化消除。
参考关系
这些关系既包含物理或算术界限,也包含特定技术的测量结果;只有这些界限是不变的。
光速带来的固有开销
Table 1 给出了近似的往返延迟基准。其中的传播分量无法削减。
| 距离 | 往返延迟 | 影响 |
|---|---|---|
| 同一数据中心 | ~1 ms | 分布式训练可行 |
| 横跨美国境内 | ~40 ms | 缩减剩余的请求时间预算 |
| 跨大西洋 | ~60 ms | 进一步缩减请求时间预算 |
| 跨太平洋 | ~100 ms | 数据局部性至关重要 |
能耗层次结构
Table 2 量化了数据搬移与计算的能耗成本对比——这是算术强度主导机器学习性能优化的根本原因。2
| 关系 | 参考比值 | 来源与解释 |
|---|---|---|
| DRAM 访问与 FP16 乘法 | ~581× | Horowitz 的 45 nm 测量;导线电容随距离增大而增大 |
| FP32 与 INT8 能耗 | ~18× | Horowitz 的 45 nm 测量;位宽决定开关能耗 |
| FP32 与 FP16 能耗 | ~3.4× | Horowitz 的 45 nm 测量;较窄的运算位宽可降低开关能耗和数据通路能耗 |
| L1 SRAM 与寄存器 | ~5× | 片上层次结构的示例性参考值;取决于与运算单元的距离 |
存储层次结构
Table 3 展示了存储层次结构为何存在不均衡:封装内邻近位置之间的访问开销可能仅相差几倍,而片外、存储设备和网络层级则会带来数量级的跃升。
| 关系 | 比值 | 这种差异持续存在的原因 |
|---|---|---|
| 加速器高带宽内存与寄存器 | 延迟约为寄存器的 1000× | 片上与片外的区别 |
| SSD 与寄存器 | 延迟约为寄存器的 300,000× | 电气访问与机械/闪存访问的区别 |
| 网络与本地内存 | 延迟约为本地内存的 16× | 光速 + 交换开销 |
| 加速器内存带宽与 CPU\(\leftrightarrow\)加速器链路带宽 | 带宽约为链路带宽的 52× | 架构投入的优先级 |
扩展规律
Table 4 汇集了决定训练和推理所需内存与计算量的算术关系。3
| 规则 | 公式 | 示例 |
|---|---|---|
| 推理内存(FP16) | 2 字节\(\times\)参数量 | 7B 参数 → 14 GB |
| 推理内存(INT8) | 1 字节\(\times\)参数量 | 7B 参数 → 7 GB |
| 训练内存(Adam) | 16 字节\(\times\)参数量 | 7B 参数 → 112 GB |
| 推理 FLOPs(Transformer) | 每个词元约需 2\(\times\)参数量 | 7B 模型 → 约 14 GFLOPs/词元 |
| 训练 FLOPs | 约 6\(\times\)参数量\(\times\)词元数 | 用 1T 词元训练 7B 模型 → \(4 \times 10^{22}\) FLOPs |
延迟预算示例
延迟预算取决于应用特定的安全要求、用户预期和服务级别目标。Table 5 列出的是示例性目标,而非普遍适用的限制。
| 应用 | 预算 | 约束 |
|---|---|---|
| 自动制动 | <10 ms | 以 100 km/h 行驶时,10 ms = 28 cm 的行驶距离 |
| 语音助手 | <100 ms | 人类对“即时”的感知 |
| 网页搜索 | <200 ms | 用户耐心阈值 |
| 视频流播放 | <1 s | 缓冲容忍度 |
| 批量训练 | 数小时至数天 | 吞吐量比延迟更重要 |
当前硬件参考值(约 2024 年)
这些数值反映了当前一代硬件的情况。可将其用于粗略估算,并根据当前硬件重新核验。
内存延迟与带宽
Table 6 提供了存储层次结构中具有代表性的延迟和带宽参考值。
| 层级 | 延迟 | 带宽 |
|---|---|---|
| 寄存器 | ~0.3 ns | — |
| L1 缓存 | ~1 ns | — |
| L2 缓存 | ~4 ns | — |
| GPU HBM3 | ~300 ns | 3.4 TB/s |
| PCIe Gen5(CPU\(\leftrightarrow\)GPU) | ~1000 ns | 64 GB/s |
| CPU DRAM | ~100 ns | 50 GB/s |
| InfiniBand(网络) | ~5000 ns | 50 GB/s |
| NVMe SSD | ~100000 ns | 7 GB/s |
计算吞吐量
Table 7 比较了数据中心 GPU 和移动端 NPU 具有代表性的峰值吞吐量与功率。
| 平台 | FP16/BF16 | INT8/FP8 类 | 功率 |
|---|---|---|---|
| 数据中心 GPU(H100) | 989 TFLOP/s | 1979 TFLOP/s(FP8 峰值) | 700 W |
| 数据中心 GPU(A100) | 312 TFLOP/s | 624 TOPS | 400 W |
| 移动端 NPU | — | 35 TOPS | 3–5 W |
Roofline 转折点
Table 8 定义了用于判断工作负载受内存限制还是受计算限制的算术强度阈值。
| 加速器 | 脊点 | 含义 |
|---|---|---|
| A100 (FP16) | 153 FLOP/byte | 低于此值 → 受内存限制 |
| H100 (FP16) | 295 FLOP/byte | 达到计算受限状态的门槛更高 |
Systems Perspective 1.2: 术语说明:GPU 与加速器
了解这些数值只是第一步。真正有力的是简洁的模型,它们能够揭示:对于当前瓶颈,哪个数值至关重要。Roofline 模型将原始硬件规格转化为可用于指导实践的性能上限,从而开启这一诊断过程。
计算的物理基础
TFLOP/s、TB/s、功率预算等原始硬件规格是性能推理的必要条件,但并不充分。如果没有简洁的分析模型,工程师就无法区分计算受限与内存受限的工作负载,也无法预测将 GPU 数量增加一倍是否能使训练时间减半。本节的模型将这些区别转化为可量化的检验。
Roofline 模型
Roofline 模型 (Williams et al. 2009) 给出了工作负载在指定硬件上所能达到的运行速度上限。这个上限取决于工作负载先耗尽计算能力还是内存带宽。
每种操作都有一个算术强度:执行的计算量与从内存传输的字节数之比。矩阵乘法具有较高的算术强度,因为每个载入的元素都会被重复使用多次。整流线性单元(ReLU)等逐元素操作的算术强度较低,因为每次操作都要载入一个数,执行一次计算,然后将其写回。如 figure 1 所示,每种工作负载都受内存带宽或计算吞吐量的限制,而其算术强度决定了它先触及哪个上限。
\begin{tikzpicture}[font=\small\sffamily, scale=1.1]
\tikzset{
Axis/.style={line width=1.0pt, draw=GrayLine, ->, >=Latex},
Guide/.style={dashed, draw=GrayLine!60, line width=0.6pt},
Label/.style={text=TextBlack, align=center, font=\footnotesize\sffamily},
Dot/.style={circle, fill=#1, draw=white, line width=0.5pt, minimum size=5pt, inner sep=0pt}
}
\draw[step=0.5, gray!15, very thin] (0,0) grid (6,4);
\draw[Axis] (0,0) -- (6,0) node[right,align=left,text=TextBlack,font=\fontsize{8pt}{10}\sffamily] {算术强度\\ (FLOP/byte)};
\draw[Axis] (0,0) -- (0,4.2) node[above, text=black,font=\fontsize{8pt}{9}\sffamily] {性能 (FLOP/s)};
\draw[BlueLine, line width=2pt] (0,0) -- (3,3);
\draw[OrangeLine, line width=2pt] (3,3) -- (5.8,3);
\node[Label, text=BlueLine, rotate=45, anchor=south, yshift=2pt] at (1.5, 1.5) {\textbf{内存受限}};
\node[Label, text=OrangeLine, anchor=south, yshift=2pt] at (4.4, 3) {\textbf{计算受限}};
\draw[Guide] (3,0) -- (3,3);
\node[Dot=TextBlack] at (3,3) {};
\node[below, font=\scriptsize\sffamily, text=TextBlack] at (3,0) {脊点};
\end{tikzpicture}脊点决定了硬件的计算能力与内存带宽之间的平衡。如果工作负载的算术强度低于该点,则其受内存限制(倾斜区域);如果高于该点,则其受计算限制(水平区域)。 \[\begin{gather*} \text{Arithmetic Intensity} = \frac{\text{FLOPs}}{\text{bytes accessed}} \qquad\qquad \text{Ridge Point} = \frac{\text{Peak FLOP/s}}{\text{Memory Bandwidth}} \end{gather*}\]
Systems Perspective 1.3: 批大小控制算术强度
- 浮点运算量:\(2 \times B \times d_{\text{in}} \times d_{\text{out}}\)(乘加运算)
- 字节数:传输 \(X\)、\(W\) 和 \(Y\) 需要约 \(s_{\text{elem}}(B d_{\text{in}} + d_{\text{in}}d_{\text{out}} + B d_{\text{out}})\) 字节。
将 \(B\) 增加一倍会使浮点运算量增加一倍,而当权重占据主要传输量时,权重传输量几乎保持不变,因此算术强度会提高。这就是批处理通常有助于推理服务的原因,不过能否达到计算上限,还取决于激活值传输量、缓存行为、形状、精度和脊点。
具体示例:A100 分析
考虑一块 NVIDIA A100 GPU,其 FP16 Tensor Core 性能为 312 TFLOP/s,HBM2e 带宽为 2.04 TB/s。脊点为 312 TFLOP/s/2.04 TB/s = 153 FLOP/byte(Tera 前缀相消,得到 FLOP/byte)。
两种常见操作分别位于该脊点的两侧。对两个大小为 4096 乘 4096 的方阵执行通用矩阵乘法(GEMM)时,算术强度约为 1365 FLOP/byte,因此 1365 FLOP/byte > 153 FLOP/byte,该操作受计算限制。对 FP16 张量执行逐元素 ReLU 时,每进行 1 次比较,需要读取 2 字节并写入 2 字节(合计 4 字节),因此算术强度仅为 \(1/4 =\) 0.25 FLOP/byte。由于 0.25 FLOP/byte \(\ll\) 153 FLOP/byte,该操作严重受内存限制,实际性能仅约为峰值 TFLOP/s 的 0.16 percent。这种对比解释了现代框架为何要融合操作。将 ReLU 直接与前面的 MatMul 内核融合,可以避免将中间结果写入内存,从而有效节省内存带宽。
量纲分析
Roofline 模型有助于诊断瓶颈位于何处。然而,在应用任何性能方程之前,都必须验证其是否具有物理意义。量纲分析提供了这种合理性检验:任何有效方程都必须满足量纲齐次性,即每一项都必须具有相同的单位。如果不满足这一条件,方程就存在错误。
对于串行执行的各个阶段,考虑 机器学习系统铁律 中介绍的机器学习系统铁律(原理 原则(英文原版)): \[ T = \frac{D_{\text{vol}}}{\text{BW}} + \frac{O}{R_{\text{peak}} \cdot \eta_{\text{hw}}} + L_{\text{lat}} \]
通过确认每一项的单位都归结为时间(秒),可以验证其正确性: \[ T [s] = \underbrace{ \frac{D_{\text{vol}} [\text{bytes}]}{\text{BW} [\text{bytes/s}]} }_{\text{seconds}} + \underbrace{ \frac{O [\text{FLOPs}]}{R_{\text{peak}} [\text{FLOP/s}] \cdot \eta_{\text{hw}} [1]} }_{\text{seconds}} + \underbrace{ L_{\text{lat}} [s] }_{\text{seconds}} \]
- 数据项:\(\frac{\text{bytes}}{\text{bytes/s}} = \text{bytes} \times \frac{\text{s}}{\text{bytes}} = \mathbf{s}\)
- 计算项:\(\frac{\text{FLOPs}}{\text{FLOP/s}} = \text{FLOPs} \times \frac{\text{s}}{\text{FLOP}} = \mathbf{s}\)
- 延迟项:单位已经是秒。
该方程在物理上是一致的。可将这一方法应用于任何系统方程:如果量纲不匹配,公式就是错误的。“浮点运算量”和“带宽”的单位不同,因此不能直接相互权衡。任何此类权衡都必须通过时间进行换算,而这正是机器学习系统铁律所量化的关系。
一旦方程在量纲上成立,接下来的问题就是其中各时间项如何随设备数量变化。Section 1.2.3 用两个互补的界限回答了这一问题。
Amdahl 定律与 Gustafson 定律
并行化是扩展机器学习规模的主要手段,但其极限取决于工作负载如何扩展。这两条定律刻画了并行计算中的基本矛盾。Amdahl 定律体现了悲观的视角,约束一个固定任务最多能够加速多少(优化延迟)。Gustafson 定律体现了乐观的视角,约束在相同时间内能够完成多少更多的工作(优化吞吐量)。
强扩展(Amdahl 定律)
强扩展衡量增加处理器后,固定规模问题的运行速度能提高多少。
Amdahl 定律 (Amdahl 1967) 指出,加速比受任务串行部分的限制。4 如果任务中有比例为 \(s\) 的部分是串行的(无法并行化),而比例为 \(p = 1-s\) 的部分可以并行化,那么使用 \(n\) 个处理器时的最大加速比为: \[ \text{Speedup}(n) = \frac{1}{s + \frac{1-s}{n}} \]
当 \(n \to \infty\) 时,\(\frac{1-s}{n} \to 0\),加速比收敛于 \(1/s\)。
为了解 Amdahl 定律如何发挥作用,假设一个训练步骤中,5 percent 是串行开销(例如 Python 全局解释器锁(GIL)、内核启动延迟),而 95 percent 是可并行化的矩阵运算:
- 当 \(n=1\) 时,加速比为 1。
- 当 \(n=\) 8 时,加速比为 1/(0.05 + 0.95/8) ≈ 5.9×。
- 当 \(n \to \infty\) 时,加速比上限为 1/0.05 = 20×。
无论增加多少加速器,这个固定工作负载的加速比都无法超过 20×。
弱扩展(Gustafson 定律)
弱扩展衡量增加处理器并保持运行时间不变时,问题规模能够增大多少。5
这正是大语言模型的实际情况。使用 1,000 个加速器,并不是为了在几毫秒内用小数据集训练一个模型,而是为了在合理时间内,使用大 1,000\(\times\) 的数据集进行训练。
对于 \(n\) 个处理器,令 \(s\) 为扩展后的工作负载在这些处理器上测得的执行时间中,串行部分所占的比例。Gustafson 定律 (Gustafson 1988) 对这种“规模扩展加速比”建模: \[ \text{Scaled Speedup}(n) = n - s(n - 1) \]
由于并行部分随 \(n\) 线性增长,而 \(s\) 保持不变,因此随着问题规模增大,弱扩展仍可保持较高效率。
采用相同的 5 percent 串行开销(\(s\) = 0.05),Gustafson 定律给出了截然不同的结果:
- 当 \(n=1\) 时,加速比为 1。
- 当 \(n=\) 8 时,规模扩展加速比为 8 − 0.05 \(\times\) (7) = 8 − 0.35 = 7.65×。
- 当 \(n=\) 1000 时,规模扩展加速比为 1000 − 0.05 \(\times\) (999) ≈ 950×。
在弱扩展中,效率保持较高水平,因为有效工作量(训练模型)随规模增长,远远超过固定开销。
同样的尺度分析视角将模型规模、数据规模、硬件数量和利用率归结为一个训练时间估计值。
Napkin Math 1.1: 训练时间方程
数学表达式: \[ T \approx \frac{6 \cdot P \cdot D}{N_{\text{GPU}} \cdot R_{\text{peak}} \cdot \eta_{\text{hw}}} \]
变量:
- 训练 FLOP 系数:系数 6 来自前向传播的 \(2P\text{ FLOP/token}\) 和反向传播的 \(4P\text{ FLOP/token}\)(其中激活梯度计算为 \(2P\),权重梯度计算为 \(2P\)),因此,处理 \(D\) 个训练词元共需 \(6PD\text{ FLOP}\)。
- \(P\):模型参数数量。
- \(D\):训练词元数量。
- \(N_{\text{GPU}}\):GPU 数量。
- \(R_{\text{peak}}\):单个加速器的峰值 FLOP/s。
- \(\eta_{\text{hw}}\):硬件利用率,在此训练估计中通常为 30%–50%。
示例:使用 1 个 A100(312 TFLOP/s),以 40 percent 的利用率,在 20B 个词元上训练一个具有 1B 个参数的模型。 \(\text{Total FLOPs} = 6 \times 1 \times 10^{9} \times 2 \times 10^{10} = 1.2 \times 10^{20} \text{ FLOPs}\)。 \(\text{Throughput} = 1 \times (3.12 \times 10^{14}) \times 0.40 \approx 1.248 \times 10^{14} \text{ FLOP/s}\)。 \(T = \frac{1.2 \times 10^{20}}{1.248 \times 10^{14}} \approx 961,538.5 \text{ seconds} \approx 16,025.6 \text{ minutes}\)。
系统洞见:在这些假设下,训练需要 961,538.5 seconds(≈ 16025.6 minutes,或约 11.1 days);该估计明确表明,模型规模、数据规模、加速器的有效吞吐量和利用率都是可调控的因素。
利特尔定律
在稳定推理系统的容量规划中,利特尔定律 (Little 1961) 描述了长期平均并发量(\(Q_{\text{req}}\))、平均到达率(\(\lambda_{\text{arr}}\))和平均系统停留时间(\(T_{\text{lat}}\))之间的关系:6 \[ Q_{\text{req}} = \lambda_{\text{arr}} \times T_{\text{lat}} \]
要理解其实际应用,可以考虑以 50 ms 的平均延迟持续维持 1,000 QPS 的吞吐量。该定律表明,系统必须支持 1000 \(\times\) 0.05 s = 50 个并发请求。
这可以用于确定工作线程池的规模,但内存需求还需要额外假设。如果系统中的每个请求都同时持有 1 GB 的设备状态,则平均请求数量意味着需要 50 GB 的设备内存;而排队中的请求也可能驻留在主机内存中,或共享批处理状态。如果 24 个设备槽位每个都被持续占用 50 ms,则其吞吐量估算值为 \(N_{\text{max}}/T_{\text{lat}} = 24/0.05 = 480 \text{ QPS}\);利特尔定律本身并不设定物理上限。
这些基于物理约束的模型——Roofline、阿姆达尔、古斯塔夫森和利特尔模型——能够诊断瓶颈位于何处。然而,要将这些诊断转化为可执行的优化措施,还需要理解造成这些约束的具体硬件结构:缓存、内存总线和互连。
计算机体系结构基础
某款 GPU 宣称可达到 1,000 TFLOP/s,但一个内核实际仅达到 30 TFLOP/s。这种差距可能源于数据移动、并行性不足、同步、启动开销或软件效率低下。物理规律设定了理论性能边界,而计算机体系结构则定义了决定实际工作负载能够多接近这些边界的硬件机制。以下讨论涵盖影响系统设计的延迟、带宽和能耗权衡。
每位程序员都应了解的延迟
建立系统直觉的第一步是理解距离的代价。Table 9 量化了处理器等待来自存储层次结构不同层级的数据所需的时间。如果访问寄存器如同拿起手边的铅笔,那么从高带宽内存(HBM)取数就如同步行穿过办公室,而从磁盘取数则如同飞往月球。
| 组件 | 延迟(ns) | 周期数(近似) | 相对“距离” |
|---|---|---|---|
| 寄存器 | ~0.3 ns | 1 个周期 | 10 seconds |
| L1 缓存 | ~1 ns | 3–4 个周期 | 33.3 seconds |
| L2 缓存 | ~4 ns | 12 个周期 | 2.2 minutes |
| HBM3(GPU 内存) | ~300 ns | 1,000 个周期 | 2.8 hours |
| NVLink(GPU-GPU) | ~500 ns | 1,500 个周期 | 4.6 hours |
| PCIe(CPU-GPU) | ~1000 ns | 3,000 个周期 | 9.3 hours |
| InfiniBand(网络) | ~5000 ns | 15,000 个周期 | 1.9 days |
| SSD(NVMe) | ~100000 ns | 300,000 个周期 | 38.6 days |
AI 硬件速查表(现代参考)
延迟衡量等待第一个字节所需的时间;带宽衡量随后能传输多少字节。Table 10 提供了进行粗略 Roofline 计算所需的常数。这些数值代表了当前机器学习时代的“标准算力单位”。
| 规格 | NVIDIA H100(SXM) | Google TPU v5p | 对系统的影响 |
|---|---|---|---|
| BF16 峰值 | 989 TFLOP/s | 459 TFLOP/s | “速度上限”(\(R_{\text{peak}}\)) |
| 内存带宽 | 3.35 TB/s | 2.76 TB/s | “管道宽度”(\(\text{BW}\)) |
| HBM 容量 | 80 GB | 95 GB | 最大模型规模(\(P\))/批大小(\(B\)) |
| 片上 SRAM | 50 MB L2 | 128 MB VMEM/芯片 | 对算子融合至关重要 |
| 互连 | 900 GB/s (NVLink) | 1200 GB/s(芯片间互连,ICI) | 决定模型并行的扩展能力 |
存储层次结构
计算机系统采用层次结构,是因为没有任何单一技术能够同时提供高容量和低延迟。Figure 2 展示了这种权衡:当数据移动位于关键路径上时,将数据保留在金字塔较高层级(寄存器/缓存)可以降低访问延迟。
\scalebox{0.8}{%
\begin{tikzpicture}[line cap=round, line join=round, font=\sffamily\small]
%—parameters ---
\def\H{6.2} % triangle height
\def\W{4.6} % half triangle width
%—levele (down to up) ---
\def\yone{1.5}
\def\ytwo{3}
\def\ythree{4.5}
%—macro: calculate the half-width at the height y and write it in the macro \tmp
\newcommand{\halfwidthat}[2]{%
\pgfmathsetmacro#2{\W*(1-#1/\H)}%
}
% ---calculate the required widths ---
\halfwidthat{0}{\wzero}
\halfwidthat{\yone}{\wone}
\halfwidthat{\ytwo}{\wtwo}
\halfwidthat{\ythree}{\wthree}
%—centri po visini (x=0 zbog simetrije) ---
\pgfmathsetmacro{\ycA}{0.5*(0+\yone)}
\pgfmathsetmacro{\ycB}{0.5*(\yone+\ytwo)}
\pgfmathsetmacro{\ycC}{0.5*(\ytwo+\ythree)}
\pgfmathsetmacro{\ycD}{0.46*(\ythree+\H)}
%up to down
\filldraw[fill=RedFill, draw=RedLine, line width=1pt]
(-\wthree,\ythree) -- (\wthree,\ythree) -- (0,\H) -- cycle;
\filldraw[fill=YellowFill, draw=YellowLine, line width=1pt]
(-\wtwo,\ytwo) -- (\wtwo,\ytwo) -- (\wthree,\ythree) -- (-\wthree,\ythree) -- cycle;
\filldraw[fill=BlueFill, draw=BlueLine, line width=1pt]
(-\wone,\yone) -- (\wone,\yone) -- (\wtwo,\ytwo) -- (-\wtwo,\ytwo) -- cycle;
\filldraw[fill=GreenFill, draw=GreenD, line width=1pt]
(-\wzero,0) -- (\wzero,0) -- (\wone,\yone) -- (-\wone,\yone) -- cycle;
% ---text ---
\node[font=\sffamily\bfseries\small,text=GreenD] at (0,\ycA) {存储(SSD/磁盘)};
\node[font=\sffamily\bfseries\small,text=BlueLine] at (0,\ycB) {HBM/DRAM};
\node[font=\sffamily\bfseries\small,text=YellowLine] at (0,\ycC) {L1/L2/L3 缓存};
\node[font=\sffamily\bfseries\small,text=RedLine] at (0,\ycD) {寄存器};
%
\coordinate(D)at($(\W,0)+(0.65,0)$);
\coordinate(L)at($(-\W,0)+(-0.65,0)$);
\coordinate(V)at($(0,\H)+(0,0)$);
\path[green](D)|-coordinate(D1)(V);
\path[green](L)|-coordinate(L1)(V);
%
\draw[->,>=Latex,line width=1pt,draw=black!40](D)--
node[align=center,right]{速度更快\\ 延迟更低}(D1);
\draw[->,>=Latex,line width=1pt,draw=black!40](L1)--
node[align=center,left]{容量更大\\ 成本更低}(L);
%—outline ---
%\draw[thick] (-\W,0) -- (0,\H) -- (\W,0) -- cycle;
\end{tikzpicture}}存储层次结构是机器学习系统的根本物理约束。Table 11 汇总了整个层次结构的物理特性——延迟、带宽和能耗。
| 层级 | 技术 | 延迟 | 带宽 | 能耗参考值(每 32 位) |
|---|---|---|---|---|
| 寄存器 | 触发器 | ~0.3 ns | — | — |
| L1 缓存 | SRAM | ~1 ns | — | — |
| L2 缓存 | SRAM | ~4 ns | — | — |
| 内存(本地) | HBM3 | ~300 ns | 3350 GB/s | 640 pJ |
| 互连 | NVLink 4.0 | ~500 ns | 900 GB/s | — |
| 主机链路 | PCIe Gen5 | ~1000 ns | 64 GB/s | — |
| 系统内存 | DDR5 | ~100 ns | 50 GB/s | — |
| 网络(互连网络) | InfiniBand NDR | ~5000 ns | 50 GB/s | — |
| 存储(本地) | NVMe SSD | ~100000 ns | 7 GB/s | — |
各层的能耗揭示了数据移动为何主导现代系统设计。
Systems Perspective 1.4: 数据移动的高昂代价
带宽与延迟
带宽(吞吐量)和延迟(时延)是不同的约束。对于通过有效带宽为 \(\text{BW}\) 的链路发送的数据量 \(D_{\text{vol}}\),若固定路径延迟为 \(L_{\text{lat}}\),并假设延迟开销与串行化开销不重叠,则总传输时间为: \[ T = L_{\text{lat}} + \frac{D_{\text{vol}}}{\text{BW}} \]
交叉点是两项相等时的传输数据量: \[ D_{\text{vol,cross}} = L_{\text{lat}} \times \text{BW} \]
当传输数据量低于 \(D_{\text{vol,cross}}\) 时(例如小型请求或控制消息),延迟占主导。当传输数据量高于该值时(例如加载权重),带宽占主导。
考虑通过一条带宽为 10 Gb/s、请求/响应基础延迟为 10 ms 的链路发送数据。交叉点对应的数据量为 12.5 MB,因此,哪一项占主导取决于传输数据量位于该阈值的哪一侧。
- 受延迟限制的数据包(1 KB):
- 传输时间:1 KB \(\times\) 8/10 Gb/s ≈ 0.8 μs。
- 总时间 ≈ 10 ms + 0.8 μs ≈ 10 ms。
- 结果:固定路径延迟和协议延迟占主导;传播延迟是其中的一个组成部分。
- 受带宽限制的检查点(1 GB):
- 传输时间:\(1\text{ GB} \times 8/10\text{ Gbps} \approx 800\text{ ms}\)。
- 总时间 \(\approx 10\text{ ms} + 800\text{ ms} = 810\text{ ms}\)。
- 结果:基础延迟可以忽略;带宽是瓶颈。
架构决定数据移动的速度,而数值精度直接控制必须移动的数据量。将精度从 FP32 降低到 FP16,会使每个参数的字节数减半;在硬件、内核和模型准确率允许的情况下,有效带宽可接近翻倍。要理解这些权衡,需要进一步考察硬件如何表示数值。
数值表示
统计学刻画数据分布;数值表示决定系统如何存储这些数值。在机器学习系统中,精度选择(FP32、BF16 或 INT8)直接体现了统计保真度与硬件吞吐量之间的权衡。
浮点格式比较
FP32 和 FP16 等 IEEE 754 格式,以及 BF16 和 FP8 等面向 AI 的格式,在动态范围(可表示数值的跨度)和精度(该范围内数值的粒度)之间采用了不同的权衡。Table 12 汇总了主要格式及其使用场景,figure 3 则直观展示了各格式的位分配。
| 格式 | 位数 | 指数 | 尾数 | 动态范围 | 典型使用场景 |
|---|---|---|---|---|---|
| FP32 | 32 | 8 | 23 | \(\sim 10^{-38}\) 至 \(10^{38}\) | 训练(全精度)、基准推理 |
| FP16 | 16 | 5 | 10 | 规格化数:\(6.10 \times 10^{-5}\)–\(6.55 \times 10^{4}\) | 训练(通常使用损失缩放)、推理 |
| BF16 | 16 | 8 | 7 | 与 FP32 相同的规格化数指数范围 | 训练,通常无须损失缩放 |
| FP8 | 8 | 4 或 5 | 3 或 2 | 因具体格式而异 | 在支持的硬件上进行训练/推理 |
| INT8 | 8 | 不适用 | 不适用 | -128 至 127 | 量化后的推理 |
\begin{tikzpicture}[font=\small\sffamily, scale=0.9, every node/.append style={scale=0.9}]
\tikzset{
BitBox/.style={draw=white, line width=0.8pt, minimum height=0.6cm, align=center, font=\scriptsize\bfseries\sffamily, text=white},
Label/.style={text=TextBlack, font=\small\bfseries\sffamily, anchor=east}
}
% Colors
\definecolor{SignColor}{HTML}{D9534F} % Red
\definecolor{ExpColor}{HTML}{5BC0DE} % Blue
\definecolor{MantColor}{HTML}{F0AD4E} % Orange
\definecolor{IntColor}{HTML}{5CB85C} % Green
% FP32
\node[Label] at (-0.2, 3) {FP32(32 位)};
\node[BitBox, fill=SignColor, minimum width=0.3cm] (fp32_s) at (0.15, 3) {S};
\node[BitBox, fill=ExpColor, minimum width=2.4cm, right=0pt of fp32_s] (fp32_e) {指数(8)};
\node[BitBox, fill=MantColor, minimum width=6.9cm, right=0pt of fp32_e] (fp32_m) {尾数(23)};
% BF16
\node[Label] at (-0.2, 2) {BF16(16 位)};
\node[BitBox, fill=SignColor, minimum width=0.3cm] (bf16_s) at (0.15, 2) {S};
\node[BitBox, fill=ExpColor, minimum width=2.4cm, right=0pt of bf16_s] (bf16_e) {指数(8)};
\node[BitBox, fill=MantColor, minimum width=2.1cm, right=0pt of bf16_e] (bf16_m) {尾数(7)};
\node[right=0.2cm of bf16_m, font=\scriptsize\sffamily, text=gray] {与 FP32 范围相同};
% FP16
\node[Label] at (-0.2, 1) {FP16(16 位)};
\node[BitBox, fill=SignColor, minimum width=0.3cm] (fp16_s) at (0.15, 1) {S};
\node[BitBox, fill=ExpColor, minimum width=1.5cm, right=0pt of fp16_s] (fp16_e) {指数(5)};
\node[BitBox, fill=MantColor, minimum width=3.0cm, right=0pt of fp16_e] (fp16_m) {尾数(10)};
% INT8
\node[Label] at (-0.2, 0) {INT8(8 位)};
\node[BitBox, fill=IntColor, minimum width=2.4cm] (int8) at (1.2, 0) {整数(8)};
% Grid/Scale markers (approximate)
\draw[gray!30, dashed] (0, -0.5) -- (0, 3.5);
\draw[gray!30, dashed] (9.6, -0.5) -- (9.6, 3.5);
\node[below, font=\scriptsize\sffamily, text=gray] at (0, -0.5) {第 31/15/7 位};
\node[below, font=\scriptsize\sffamily, text=gray] at (9.6, -0.5) {第 0 位};
\end{tikzpicture}除了位宽,指数和尾数之间的位分配也决定了每种格式能够表示的数值范围。
在这些格式中,BF167 值得特别关注 (Wang and Kanwar 2019; Kalamkar et al. 2019)。它与 FP16 同属 16 位存储格式,但在训练中的行为不同,因此仅凭标称位宽无法预测实际运行负担。下面从系统视角说明位分配与内存流量、数值稳定性和部署效率之间的联系。
Systems Perspective 1.5: 动态范围之墙
数值格式的选择是机器学习系统铁律(原理 原则(英文原版))的直接应用。将精度从 FP32 降低到 BF16 或 FP16,可使数据量项减半,从而有可能使受内存限制的工作负载吞吐量翻倍。然而,16 位格式的类型决定了工程复杂度:
- 动态范围(指数):BF16 保留了 FP32 的八位指数,因此具有相同的规格化数指数范围 (Kalamkar et al. 2019)。
- 精度(尾数):FP16 的尾数为 10 位,比 BF16 的 7 位更长,因此对于其范围内的数值可提供更高的精度。然而,其五位指数提高了下溢风险。因此,FP16 训练通常采用损失缩放,即在运行过程中将梯度乘以一个较大的常数,使更多数值保持可表示 (Micikevicius et al. 2017)。
- 能效:INT8 运算的能效可显著高于对应的浮点运算,因为它们移动的数据更少,并使用更简单的整数算术路径。对于内存、延迟或功耗预算严格受限的神经网络部署,将推理转换为 INT8 是一种主要手段 (Jacob et al. 2018; Krishnamoorthi 2018)。
整数量化
量化将连续的浮点值映射为离散整数,通常为 INT8。关键挑战在于选择如何将浮点数范围映射为整数。主要有两种方法。
对于对称量化,令 \(s_{\text{quant}} = \max |x|\),其中最大值在校准范围内取值,并假设 \(x\) 被裁剪到 \([-s_{\text{quant}}, s_{\text{quant}}]\)。有符号 INT8 编码为: \[ x_{\text{int}} = \text{round}\left(\frac{x}{s_{\text{quant}}} \times 127\right) \] 这一映射适用于以零为中心的权重分布。
非对称量化通过在缩放前平移范围,处理不以零为中心的分布(常见于 ReLU 之后,因为 ReLU 仅产生非负值)。令 \(x_{\min}\) 和 \(x_{\max}\) 为校准范围的边界,将归一化跨度设为 \(s_{\text{quant}} = x_{\max} - x_{\min}\),并将 \(x\) 裁剪到 \([x_{\min}, x_{\max}]\)。一种常见的无符号 8 位映射为: \[ x_{\text{uint8}} = \text{round}\left(\frac{x - x_{\min}}{s_{\text{quant}}} \times 255\right) \]
对称量化与非对称量化之间的选择取决于张量的分布,并会对准确率产生可测量的影响。
总结
机器侧诊断始于单位、上限和工作集。参考数值用于确定数量级;Roofline 模型和机器学习系统铁律用于判断约束来自运算量、字节数还是延迟;阿姆达尔定律、古斯塔夫森定律和利特尔定律用于检验有关加速比、扩展性和并发性的论断;量纲分析则用于发现不可能成立的结果。存储层次结构和互连展示了这些限制如何表现为延迟与带宽约束,而数值表示则说明了动态范围、精度和量化如何改变存储需求、能耗、吞吐量和准确率。
应将峰值规格视为上限,而非性能预测。将测量值与相关上限进行比较,然后将剩余差距追溯到利用率、数据移动、运行时开销、架构或软件。当粗略估算明确列出假设和单位,并清楚指向下一步应进行的测量时,它就具有实用价值。
Footnotes
Jeff Dean:Google 高级研究员,也是 Google 分布式系统基础设施的架构师之一,其参与设计的系统包括 MapReduce、BigTable 和 TensorFlow。他最初于 2010 年前后与 Peter Norvig 一同提出的延迟数值,已成为系统工程师的经典参考。随着硬件演进,这些数值多年来不断更新,但延迟的层次关系始终非常稳定;Colin Scott 的交互式可视化展示了不同硬件世代的延迟层次关系 (Scott 2012)。↩︎
能耗层次结构的数据来源:DRAM 和算术运算的数值来自 Horowitz (2014)(45 nm)。L1 SRAM/寄存器比值是层次结构的示例性参考值,并非 Horowitz 的测量结果。重点列出的比值比较的是一次 32 位 DRAM 访问与一次 16 位浮点乘法。绝对数值和比值都会随工艺、电路、存储技术及操作数位宽而变化;导线距离有助于解释为何数据搬移的成本始终很高。↩︎
训练内存(Adam):每个参数 16 字节的规则假定使用 Adam 进行混合精度训练。ZeRO 优化通过在加速器之间对优化器状态进行分片,可以减少每个加速器的内存占用,但所有加速器的内存总占用仍约为 16\(\times\)参数量。↩︎
Gene Amdahl(1922–2015):IBM 的传奇计算机架构师,曾担任 System/360 的首席架构师。后来,他创立了 Amdahl 公司,在大型机市场与 IBM 竞争。↩︎
John Gustafson:计算机科学家,以并行计算方面的研究和提出 Unum(通用数)格式而闻名。他的定律直接回应了将 Amdahl 定律应用于超大规模计算时,人们所认为的那些“极限”。↩︎
约翰·利特尔:麻省理工学院的校级教授(Institute Professor),也是运筹学领域的先驱。他于 1961 年证明的这一定律是排队论的基础,广泛应用于从制造业到计算机网络分析等领域。↩︎
BF16(brain floating point 16,脑浮点 16):BF16 最初随 Google TPUv2 推出,随后被 Intel、Arm 和 NVIDIA 采用,成为各类加速器生态中常见的训练格式。随着它的普及,硬件、内核和分布式训练库对该格式的支持成为可移植性问题,而不再只是特定厂商的功能。↩︎