推理优化:快和省的三层功夫
这一章讲四件事: 先分清两种瓶颈——计算受限,和内存带宽(数据搬运的速度)受限,预填充和解码恰好各占一种,这决定了优化的方向; 指标怎么量——延迟要拆成 TTFT/TPOT,「每秒能吐多少」要配上 goodput,利用率要用 MFU/MBU 而不是厂商面板; 模型层优化——推测解码、KV 缓存与注意力变体、内核与编译器; 服务层优化——连续批处理、预填充-解码解耦、提示词缓存、并行化。 它在全书的位置:适配技术(提示、RAG、微调、数据)到本章收尾,下一章把所有组件拼成完整架构。
1. 顶层全景:三种瓶颈,三层优化
推理 = 用训练好的模型对输入算出输出。书里给的三层优化框架,配了一个射箭比方:模型层优化是磨箭,硬件层优化是练射手,服务层优化是完善整个流程1:
请求 → [推理服务:接收/路由/批处理/缓存] → [推理服务器:模型跑在芯片上] → 响应
↑ 服务层(不改模型) ↑ 模型层 + 硬件层(可能改行为)
图说:模型层优化可能改变输出质量(不同提供商的同一模型,基准成绩就有差异),
服务层优化按书里的要求「不应改变输出质量」,只挪资源[^2]。
动手优化前先做诊断:你的系统卡在哪一种瓶颈上。
2. 两种瓶颈:快不起来只有两种原因
计算受限:完成时间由计算量决定——典型例子是密码破解,要做的数学运算太多2。内存带宽受限(简称带宽受限):完成时间受限于数据搬运速度——内存到处理器之间的传输跟不上3。
一个术语陷阱书里特意挑明:系统背景的人说「内存受限」指带宽不够,AI 背景的人说「内存受限」指容量不够(就是「CUDA out of memory」那种 OOM)4。读优化文献时先对齐口径。
判断方法靠算术强度:每访问 1 字节内存执行了几次运算。算术强度高 → 计算受限(加芯片算力有效);低 → 带宽受限(加带宽有效),性能分析工具能画出「屋顶图」直观显示5。关键结论6:
| 阶段 | 干什么 | 瓶颈 | 为什么 |
|---|---|---|---|
| 预填充 | 并行处理输入 token | 计算受限 | 一次算多少取决于算力 |
| 解码 | 逐个生成输出 token | 带宽受限 | 每步都要把整个模型的权重搬一遍 |
Stable Diffusion 这类图像生成是计算受限,自回归语言模型的解码是带宽受限7。由于两个阶段瓶颈不同,生产上常把它们拆到不同机器上各跑各的(§6.2)8。
3. 指标:延迟、吞吐、利用率,各有一个坑
3.1 延迟拆开量:TTFT 与 TPOT
总延迟一个数不够用,自回归生成要拆成两段9:
- TTFT(time to first token,首个 token 时间):对应预填充阶段,用户希望聊天机器人瞬时响应,长文档摘要则可以等;
- TPOT(time per output token,每输出 token 时间):对齐人类阅读速度即可——快速阅读者读一个 token 约 120 ms,所以 TPOT 在 120 ms 左右(每秒 6~8 个 token)就够大多数应用,做得更短用户也感知不到10。
整体延迟 = TTFT + TPOT × 输出 token 数。还有一个 Anyscale 的实验数字帮你分配资源:100 个输入 token 对延迟的影响,大约等于 1 个输出 token11。两个坑12:一是 CoT/智能体场景里模型生成了不展示给用户的中间步骤,用户感知的首 token 时间远长于模型的 TTFT,有团队为此用「首个 token 发布时间」(time to publish)单列这个口径;二是平均值会说谎——10 个请求里 9 个 100 ms 左右、1 个 3000 ms,平均被拉到 390 ms,看起来比典型情况慢得多;要看百分位(把所有请求按快慢排队后的名次)——p50 就是队伍正中间那个的成绩,通常看 p50/p90/p95/p99。
3.2 吞吐量与 goodput:成本怎么算
每秒能吐出多少个输出 token,行话叫吞吐量(TPS)。
并发(同一时刻挤进来许多请求)的能力,看每分钟请求数(RPM,基础模型一个请求动辄数秒,RPS 不够细)13。吞吐量直接决定成本,书里给了一笔完整账14:
假设:算力成本 2 美元/小时,吞吐量 100 TPS
解码:每 100 万输出 token ≈ 5.56 美元
若每请求平均 200 个输出 token,1000 个请求 ≈ 1.11 美元
预填充:每分钟 100 个请求 → 1000 个请求 ≈ 0.33 美元
── 1000 个请求总成本 ≈ 1.44 美元 ──
图说:分词器不同 token 数就不同,跨服务比较请用「每次请求的成本」[^16]。
延迟与吞吐是一对权衡:LinkedIn 的总结是,愿意牺牲 TTFT 和 TPOT 的话,吞吐量翻倍甚至翻三倍并不罕见15。所以又有了 goodput(有效吞吐量):每秒满足 SLO(软件级目标,如「TTFT ≤ 200 ms 且 TPOT ≤ 100 ms」)的请求数——每秒完成 10 个请求、只有 3 个达标,goodput 就是 316。
3.3 利用率:别信 nvidia-smi 的百分比
nvidia-smi 显示的「GPU 利用率」只统计「芯片处于活跃状态」的时间占比——一块每秒能做 100 次运算的芯片,每秒只做 1 次照样显示 100%17。真指标有两个18:
MFU(模型每秒浮点运算利用率)= 实际吞吐量 ÷ 峰值算力下的理论吞吐量。MBU(模型带宽利用率)= 实际使用的内存带宽 ÷ 理论带宽。MBU 可以当场复算,这是本章的主走查(数全部出自书里)19:
模型:70 亿参数,FP16(每参数 2 字节),实测吞吐 100 TPS
实际内存带宽 = 参数量 × 字节数 × 吞吐量
= 7×10⁹ × 2 × 100 = 1400 GB/s
芯片:A100-80GB,理论带宽 2 TB/s
MBU = 1400 GB/s ÷ 2000 GB/s = 70%
图说:顺带看定量化的价值——带宽公式里每参数字节数是乘数,
8 位权重直接把带宽消耗砍半(这就是第 10 章量化在本章的回报)。
瓶颈类型和利用率指标对得上号:计算受限的工作负载 MFU 高、MBU 低;带宽受限的反之20。还要防「峰值 FLOP/s 包装」营销——厂商用特殊形状的稀疏矩阵刷峰值数字,用户实际很难跑出高 MFU21。参照系:训练任务 MFU 超过 50% 通常算表现良好;预填充的 MFU 天然高于解码22。
4. 硬件底座:加速器、内存三层、电费
- CPU vs GPU:CPU 少量高性能核心(高端消费级 64 核),GPU 几千个小核心——因为神经网络的运算 90% 以上是矩阵乘法,高度可并行23。已部署系统里推理成本可占 ML 总成本高达 90%,所以推理专用芯片(Apple Neural Engine、AWS Inferentia、Meta MTIA)成为趋势24;
- 内存三层:片上 SRAM(超过 10 TB/s,但容量一般不超过 40 MB)→ GPU 专用 HBM(256 GB/s~1.5 TB/s,消费级卡 24~80 GB)→ CPU DRAM(25~50 GB/s,容量可到 1 TB)25。离计算单元(芯片里真正干活的运算部件)越近越快也越小——许多优化(比如 FlashAttention)本质就是在跟这个层次结构博弈;
- 功耗:A100 有 540 亿个晶体管,H100 有 800 亿;一块 H100 以峰值性能跑一年约 7000 kWh——参照物:美国普通家庭一年约 10,000 kWh,一块芯片约等于一个家庭的七成用电26。
5. 模型层优化:磨箭
5.1 压缩老三样与「输出比输入贵」
压缩三件套里,量化(第 10 章)和蒸馏(第 12 章)已讲;剪枝是把不重要的参数置零或移除——「彩票假设」证明某些网络剪掉 90% 以上非零参数不损准确率,但实践中不常见:实现难、提升相对小、硬件未必利用得了稀疏性27。最受欢迎的压缩手段至今是权重量化,但它有物理极限——每个数值不能低于 1 位28。为什么解码值得花这么大力气?因为一个输出 token 的成本大约是输入 token 的 2~4 倍,而且自回归逐个生成,100 个 token 要等 10 秒(按每 token 100 ms)29。
5.2 推测解码:让小模型打草稿
推测解码的直觉:响应里很多 token 其实很好猜,不必每次都动用大模型30。流程四步(走查)31:
① 草稿模型(快而弱)一口气生成 K 个候选 token:xt+1 … xt+K
② 目标模型(你要的那个)并行验证这 K 个
③ 从左到右接受最长的一致前缀,比如前 j 个
④ 目标模型补生成 1 个 token(xt+j+1),回到 ①
结果:最差也产出 1 个;最好产出 K+1 个,其中 K 个是白赚的
它成立靠三条32:验证可并行(把「解码特征」变成了「预填充特征」);简单 token 弱模型也能猜对;解码本来带宽受限、有空闲的 FLOP 可以免费验证。K 越大验证次数越少但接受率越降,要试;代码这类高结构文本接受率高。实证:DeepMind 给 Chinchilla-70B 配了个 40 亿参数的同架构草稿模型——草稿 1.8 ms/token 对目标 14.1 ms/token,整体延迟降低一半以上且不损质量;PyTorch 里 50 行代码就能实现,vLLM、TensorRT-LLM、llama.cpp 都已内置33。
两个变体34:带参考的推理——输出与输入大量重叠时(检索问答、改代码、多轮对话——来回聊很多次的那种),草稿 token 直接从输入里复制,不要草稿模型,实测 2 倍提速;并行解码——干脆同时生成 x(t+1)…x(t+k)(给定「the cat sits」,不管下一个是 on 还是 under,再下一个多半是 the),Lookahead 用雅可比方法迭代验证,Medusa 给原模型加多个解码头、树状注意力选最优序列,NVIDIA 宣称在 H200 上让 Llama 3.1 提速 1.9 倍。
5.3 KV 缓存与注意力优化
生成下一个 token 要用之前所有 token 的键值向量;KV 缓存把算过的存起来复用,避免每步重算(训练时不需要——训练里整个序列已知,可一次并行算完)35。但缓存本身吃内存。书里的算例,一字不差可复算36:
KV 缓存内存 = 2 × B × S × L × H × M(字节)
B 批次大小, S 序列长度, L 层数, H 模型维度, M 每数值字节数
Llama 2-13B(40 层,维度 5120),批次 32,序列 2048,FP16:
2 × 32 × 2048 × 40 × 5120 × 2 字节 ≈ 54 GB
对照:5000 亿参数模型、批次 512、上下文 2048 → KV 缓存 3 TB,是权重本身的 3 倍[^39]
注意力优化的三板斧37:
-
重新设计注意力(要动架构,只能训练/微调时做):局部窗口注意力(只看邻近 1000 token,1 万 token 序列的缓存降到 1/10)、跨层注意力(相邻层共享键值,三层共享 = 1/3)。
多查询注意力,以及把查询头分成小组、组内共享键值的分组查询(GQA,注意力的一个改法)属于同一族。Character.AI 的组合拳:平均对话 180 条消息的负载下,三招并用把 KV 缓存压到 1/20 以下;
-
管理 KV 缓存:vLLM 的 PagedAttention 把缓存切成非连续内存块(像操作系统分页),减少碎片;再加 KV 量化、自适应压缩、选择性保留;
-
写内核:不改设计不改存储,改计算本身的代码——FlashAttention 把多个算子融合成一次计算,是为 A100 设计的,到 H100 就有了 FlashAttention-3。内核 = 为特定芯片优化的专用代码,写它要懂内存层次,CUDA/Triton/ROCm 是常用语言38。
5.4 编译器把这一切串起来
模型代码要在特定硬件上跑,得先「底层化」(lowering)成硬件兼容的形式,干这活的工具叫编译器(torch.compile、TVM、MLIR、XLA、TensorRT 内置编译器)39。PyTorch 团队给 Llama-7B 做过四步走查:torch.compile → 量化 INT8 → 量化 INT4 → 加推测解码,吞吐量逐级上升40。许多公司把专有内核当商业机密——跑得又快又省的内核就是竞争优势41。
6. 服务层优化:不碰模型,只挪资源
6.1 批处理:三班车
批处理的比喻书里用得很完整:单独处理每个请求是人手一辆车,批处理是挤公交——载得多,但可能每人都慢一点42。三班车43:
| 方式 | 规则 | 缺陷 |
|---|---|---|
| 静态批处理 | 坐满才发车 | 第一个请求要等最后一个到齐 |
| 动态批处理 | 坐满或到点发车(如 100 ms 窗口) | 发车时批次可能不满,浪费算力 |
| 连续批处理 | 有人下车立刻补人上车 | 实现复杂(Orca 2022 提出) |
前两种还有个对 LLM 特别痛的问题:必须整批完成才返回——同批一个请求生成 10 个 token、另一个要 1000 个,短请求干 等长请求。连续批处理让完成的响应立即返回、空位立即补新请求,是现代推理服务器的标配44。
6.2 预填充-解码解耦与提示词缓存
§2 说过预填充(计算受限)和解码(带宽受限)瓶颈不同,塞在同一块 GPU 上会互相争抢:新查询带来的预填充任务会挤占现有解码任务的算力,TPOT 被拉长。解法是把两步分配到不同实例——论文证实这能在满足延迟要求的同时显著提高吞吐,中间状态的传输开销在现代集群(NVLink 高速互联)里并不显著。实例配比:长输入优先保 TTFT 时预填充:解码取 2∶1~4∶1;短输入优先保 TPOT 时取 1∶2~1∶145。
提示词缓存利用提示词里的重叠片段(系统提示词、长文档、对话历史)只处理一次46。账很直白:系统提示词 1000 token、每天 100 万次调用,每天省下约 10 亿个重复输入 token 的处理47。厂商已跟进:Gemini 缓存 token 便宜 75%(另收存储费);Anthropic 宣称最高省 90% 成本、降 75% 延迟——上下文越长省得越多48。
6.3 并行化:四种拆法
资源不够、模型太大时按维度拆49:
- 副本并行:多复制几份模型,最容易,同时服务更多请求;多大规模的模型配多大内存的卡是个装箱问题(40 GB 的卡放三个 13B 还是一个 34B?);
- 按张量(矩阵就是二维张量)并行:把一次矩阵乘法切到多卡,推理最常用,能跑大模型还能降延迟,代价是通信开销;
- 流水线并行:模型分段接力(训练常用);因为有额外通信,延迟敏感的推理场景一般避开它;
- 上下文并行 / 序列并行:长输入按位置拆到多机,或按算子(注意力/前馈)拆到多机。
书里的小结给了选用经验:影响最广的四件套是量化(适用广)、张量并行(降延迟+跑大模型)、副本并行(易实现)、注意力优化(Transformer 专用)50。