先问:这个“忙”究竟测到了什么?
你接手一个模型服务,监控显示 GPU 利用率接近 95%,用户还是在等。最容易下的结论是“卡不够,扩容”。但这个百分比并没有告诉你:算子在做矩阵乘法,还是在等显存中的数据?CPU 是否在准备下一批输入?一个请求是否先在队列里耗掉了两秒?先画执行路径,再决定该换哪一个变量。
GPU 是由很多执行单元组成的设备,适合对大量数据执行相似运算。CPU 通常负责控制流程、数据准备和提交工作;设备上的 kernel 是一段真正执行计算的程序。输入可能从存储进入主机内存,再通过主机到设备的传输到达显存。kernel 从显存读取数据、计算、写回结果。不同内存层与互联有不同的带宽和时延。设备有工作在执行,不等于每个执行单元都在高效计算。
这一课不要求你编写 CUDA。你要学会沿这条路径提出可以检验的解释:是数据晚到了,是传输串在关键路径上,是访存量太大,还是很多小 kernel 的启动间隙占了时间?第 01 课的端到端时间是问题的入口,下面的设备时间才解释其中一段。
一条时间线,胜过几个平均百分比
先看一个刻意串行的教学例。CPU 准备 5ms,传输 5ms,GPU 执行 100ms,响应整理 5ms,共 115ms。这里没有重叠,所以可以相加;在真实异步流水线里,不能直接把各阶段总时间相加作为端到端时延,要找关键路径。
例 B 的下一步应是检查解码、存储读取、预处理或数据队列。你可以预取输入来测试假设,但预取增加主机内存占用,也可能把瓶颈移到存储。例 A 则要进入 kernel:同样 100ms 的设备段,可以主要受计算能力限制,也可以主要受显存带宽限制。只看到长条,仍不能宣布“计算瓶颈”。
用计算量和访存量,给解释加一道约束
把一个 kernel 的计算量记作 F(FLOPs),在所研究的存储层实际搬运的字节记作 D。算术强度 I=F/D,单位是 FLOP/byte。如果同精度下的计算峰值是 P,带宽是 B,理想时间下界为 max(F/P, D/B)。同一思想写成绩效上界就是 min(P, B×I),常被称为 roofline 模型。
注意两个词:同精度和理想下界。不能把 FP32 的任务拿去除以另一种精度的宣传峰值;D 也不是简单读取模型文件的大小,而是相关存储层的流量。kernel 启动、同步、分支、占用率不足与通信都可能使实际时间更长。这个估算帮助排除不合理解释,不是性能预测器。
把例 A 拆开
虚构设备的峰值为 100 TFLOP/s,显存带宽为 1 TB/s,即 1,000 GB/s。任务执行 2×10¹² FLOPs,搬运 100 GB:
- 算术强度:2×10¹² ÷ 10¹¹ = 20 FLOP/byte。
- 计算时间下界:2 ÷ 100 = 0.02s,即 20ms。
- 访存时间下界:100 ÷ 1,000 = 0.1s,即 100ms。
- 理想下界取较大者,得到 100ms。此模型中先降低流量比提高峰值算力更有希望。
现在把流量减少到 10 GB,计算量保持不变。访存下界变为 10ms,计算下界仍为 20ms,总下界变为 20ms。再把流量减到 5 GB,下界仍是 20ms:瓶颈已经转移。真实优化后如果实际仍是 60ms,你应寻找这个模型未包含的限制,而不是把观测强行改成 20ms。
先预测,再改一个变量
默认值来自上面的虚构算例。先写下将流量改为 10 GB 的预测,再计算;其余三个条件保持不变。
对照预测与反例
流量 10 GB 时下界 20ms;只把峰值从 100 提到 200 TFLOP/s,仍为 100ms。流量改变必须有可测机制,例如数据复用提高、临时张量减少或算子融合;不能只在计算器中改数字后宣布代码已优化。
时间之外,还要算“同时活着”的内存
带宽回答“搬得多快”,容量回答“能不能放下”。一个推理服务的显存可能同时容纳权重、生成请求的 KV、临时工作区和运行时开销。训练还会有梯度、优化器状态和激活,第 03 课会拆开计算。不能只算权重,就断言模型可以稳定运行。
以 7×10⁹ 参数、每参数 2 bytes 的权重为例,理论权重是 14 GB,约 13.04 GiB。这里 GB=10⁹ bytes,GiB=2³⁰ bytes。假设可供实验使用的容量是 22 GiB,KV 为 2 GiB,工作区与运行时预留为 3 GiB,则账面剩余约 3.96 GiB。这不是某型号显卡的实测容量,也不是该模型的部署保证。你还要观察峰值、动态 shape、缓存增长和分配碎片。
发生 OOM 时,先定位它在哪个阶段出现。加载权重就失败,和请求增多后 KV 增长导致失败,是不同问题。增加并发可能提高利用率,也可能吃掉余量;减小单次批量可能缓解容量问题,但未必提高吞吐。优化动作必须对应你观察到的限制。
如果卡住,按证据缩小范围
| 现象 | 先查什么 | 有边界的试验 |
|---|---|---|
| GPU 空闲间隙长 | CPU 与输入队列、数据读取时间 | 固定样本,比较预处理后缓存与原输入 |
| kernel 长,带宽项更大 | 相关内存层流量、缓存与数据复用 | 固定计算量,减少一次中间读写 |
| 很多极短 kernel 与间隙 | 提交、同步和设备执行时间线 | 减少一次同步,再核对正确性和端到端时间 |
| 只在长请求或大 batch OOM | 峰值内存、shape 与请求生命周期 | 分别只改长度或 batch,记录出现阈值 |
有 GPU 的选做路线:固定模型、输入、精度、设备与版本,先做 warmup,再用相同统计窗口采集端到端时延和 Profiler 时间线。异步设备操作的计时必须明确同步或使用设备事件,CPU 发起调用所用的时间不能直接当作 kernel 时间。没有 GPU 时,就在本课的教学 trace 与计算模型中练习归因,并把报告标为机制实验。
把解释写成一份能被推翻的记录
交付一个三段记录:①观察边界和基线;②你认为最主要的限制及依据;③只改一个变量时的预测、实际结果和反证条件。浏览器计算与虚构 trace 只能证明你会解释模型,不证明某块 GPU 会获得对应收益。
- 复算与反例。把带宽从 1,000 改为 500 GB/s,保持其他默认值不变。写出新下界,再解释为何提高峰值算力不能解决这个例子的访存限制。
- 迁移到陌生任务。一个 kernel 为 8×10¹² FLOPs、20 GB 流量,设备条件仍为 100 TFLOP/s 与 1,000 GB/s。选“降低流量”还是“提高同精度算力”作为首先验证的方向?列出实际观测可能与模型不同的两个原因。
- 建立内存账。将上例 KV 从 2 改为 6 GiB,其余预算不变。解释为何账面超出约 0.04 GiB 已经不能按原假设运行;不要把只差一点当成可以忽略。
完成后再看参考与验收
带宽减半:访存下界 200ms,总下界 200ms。陌生 kernel:计算 80ms,访存 20ms,先验证计算项;启动开销、并行不足、真实流量/峰值不匹配都可能让实测更慢。内存账:13.04+6+3≈22.04 GiB,超出 22 GiB 假设预算;必须改变并发/长度/表示或容量,并测峰值,不能依靠四舍五入过关。
怎样算完成:单位与观察范围清楚;预测可复算;能指出一个错误解释;明确什么证据会推翻当前假设。自由文本需要你按这些条件自检。
带着一个问题读官方说明
NVIDIA · GPU Performance Background:先找 arithmetic intensity 与 memory/compute limited 的判断。读前写下你认为提高算力会有效的条件;读后用本课两个 kernel 解释边界,再列出该模型没有覆盖的执行开销。无需把全文的架构参数背下来。
下一步:训练多出来的状态是什么?
你现在会把时间与内存分开,也知道一个权重大小数字不足以证明能跑。训练除了权重,还要保存梯度、优化器和激活;第 03 课会从一个实际更新步说明这些状态为什么存在,以及减小 batch 与梯度累积各改变什么。
留下本课的产出
本课产出:一份瓶颈假设与反证记录。写下条件、证据、结果和仍不确定的事。保存不代表自动通过,下面的状态由你按验收条件自检。
记录只保存在当前浏览器。