Agent 工程学习站 做棵大树 出品 · beatree.cn 免费 · 无需登录 · 进度存本机
I 版 · 大模型原理 第 05 章 Inference Optimization
I · 大模型原理 05 / 22 深入

推理优化与成本杠杆

Inference Optimization
预计阅读 30 分钟
难度 深入
关键词 量化 · 投机解码
本机状态 未读

把推理成本打下来有四把锤子:量化、分页注意力、投机解码、连续批处理。它们各自砸的是不同的钉子——有的是显存带宽,有的是显存容量,有的是串行依赖,有的是硬件利用率。搞混了就会在错误的地方使劲。

编辑部注 · 本章难度较高,建议在读完前四章后再读。重点是「每个手段针对哪个瓶颈」,而不是记名词。

前面四章建立了三个事实:注意力是平方的、KV Cache 是线性的但占显存、解码是逐步采样的。这一章回答工业界最关心的问题——在这些约束下,怎么把单位 token 的成本压下去。

一、先分清两个阶段,再谈优化

推理分两个阶段,它们的瓶颈完全不同,因此优化手段也不同。这是本章最重要的一张表。

Prefill(处理输入)Decode(逐 token 生成)
做什么把整段输入一次算完,建立 KV Cache每步生成一个 token,读取已有缓存
并行度高(n 个位置可并行)低(第 t 步必须等第 t−1 步)
主要瓶颈算力(FLOPs)显存带宽(Memory Bandwidth)
典型表现首 token 延迟(TTFT)高吞吐(tokens/s)受限,GPU 利用率低
对应手段算子融合、FlashAttention、分块计算量化、连续批处理、投机解码
一个反直觉的事实

Decode 阶段的 GPU 利用率通常只有个位数百分比。原因不是算力不够,而是每生成一个 token,都要把整个模型权重和 KV Cache 从显存搬到计算单元一次。数据搬运的时间远超计算时间——这就是为什么 decode 阶段是「显存带宽受限」而非「算力受限」。

结论:针对 decode 的优化,本质上都是在减少搬运量或增加搬运的并行度。量化减少搬运量,连续批处理让一次搬运服务更多请求,投机解码让一次搬运产出更多 token。

二、四把锤子

四把锤子,四个钉子 ① 量化 Quantization 把 fp16 → fp8 / int8 / int4,权重与缓存都变小 钉子:显存容量 + 搬运量  → TTFT 与吞吐同时改善 代价:精度损失,长链推理与大数运算上更明显 常见档位:W8A8 较安全,W4 需配合分组量化与校准 ② PagedAttention 把 KV Cache 切成固定大小的块,按需分配、不要求连续 钉子:显存碎片  → 可并发请求数显著提升 代价:寻址开销、实现复杂度上升 附带收益:支持前缀共享(多个请求共用相同前缀的块) ③ 连续批处理 Continuous Batching 不等整批做完,某个请求一结束就立刻补入新请求 钉子:硬件空转  → 吞吐成倍提升,是性价比最高的一刀 代价:单请求延迟可能变差(被大 batch 拖累) 这是现代推理框架(vLLM 等)的默认能力 ④ 投机解码 Speculative Decoding 小模型先一次性起草 k 个 token,大模型一次前向并行验证 钉子:串行依赖  → 一次搬运产出多个 token 代价:需额外模型或额外计算;加速比取决于接受率 接受率低时可能反而更慢(净亏损) 选型决策:从你实际卡住的地方出发 模型装不下 / 显存不够    → 量化(优先 W8A8,再考虑 W4) 并发上不去、显存碎片严重  → PagedAttention 吞吐不够、GPU 利用率低   → 连续批处理(先查这一项) 单请求延迟高、输出长    → 投机解码 / MLA 类结构优化
图 1 四种手段针对的瓶颈完全不同。面试中被问到「怎么优化推理成本」时,先问清瓶颈在哪(显存、并发、吞吐、还是延迟),再给手段——不问瓶颈直接列表,是最容易被追问到答不上来的答法。

三、MLA:结构层面的一次改写

除了上面四个「工程手段」,还有一条更彻底的路:改注意力本身的结构

DeepSeek 提出的 MLA(Multi-head Latent Attention)思路是:不直接缓存完整的 K/V,而是把它们压到一个低维的潜在向量里缓存,用的时候再投影回来。

用前面学过的语言描述:它压的是 KV Cache 这张「线性增长的账单」的系数。

  • 常规 MHA:缓存量 ∝ n_layers × n_heads × head_dim
  • GQA:把 n_heads 换成更小的 n_kv_heads,系数降到 1/8 左右
  • MLA:缓存的是压缩后的潜在向量,维度远小于 n_kv_heads × head_dim,系数进一步下降

这解释了为什么在同样的显存预算下,采用 MLA 的模型能支撑显著更长的上下文与更高的并发。这也是「结构创新本身就是成本创新」的最好例子。

优化的第一步不是选手段,是量瓶颈。没有测量就上手段,等于在黑暗中拧螺丝。本刊编辑部

四、动手:测一次,而不是猜一次

下面这段代码用纯 Python 模拟三种 decode 策略的「搬运次数」,用来建立数量级直觉。真实基准请用专业压测工具,但模型足够说明问题。

decode_cost_model.pypython
"""
用「显存搬运次数」估算 decode 成本。
核心假设:decode 每步都要把权重搬运一遍(带宽受限),
所以「总搬运量 = 步数 × 权重体积」,优化就是减少这两个因子。
"""
def weight_gb(n_params_b: float, bytes_per: int = 2) -> float:
    """权重体积:参数量 × 每参数字节数"""
    return n_params_b * 1e9 * bytes_per / 1024**3
def naive_decode(n_params_b, out_tokens, bytes_per=2):
    """朴素逐 token:每一步搬一次全量权重"""
    w = weight_gb(n_params_b, bytes_per)
    return out_tokens * w, out_tokens     # (总搬运 GiB, 前向次数)
def continuous_batching(n_params_b, out_tokens, concurrent, bytes_per=2):
    """连续批处理:一次搬运服务 concurrent 个请求(假设长度对齐)"""
    w = weight_gb(n_params_b, bytes_per)
    steps = out_tokens                          # 步数不变
    # 但每一步服务 concurrent 个请求,等于摊薄了单位 token 的搬运量
    return steps * w, steps * concurrent
def speculative(n_params_b, out_tokens, accept_rate, draft_tokens=4, bytes_per=2):
    """投机解码:小模型起草 k 个,大模型一次前向验证"""
    calls = out_tokens / (1 + draft_tokens * accept_rate)
    return calls * weight_gb(n_params_b, bytes_per), calls
def quantized(n_params_b, out_tokens, bytes_per, baseline_bytes=2):
    """量化:直接按字节比例缩小搬运量"""
    return naive_decode(n_params_b, out_tokens, bytes_per)
print('=== 同一个 70B 模型,输出 512 token,fp16 ===')
base, calls = naive_decode(70, 512)
print(f'朴素 decode          搬运 {base:8.1f} GiB 前向 {calls:.0f} 次')
b, c = continuous_batching(70, 512, concurrent=16)
print(f'连续批处理 ×16       搬运 {b:8.1f} GiB 有效产出 {c:.0f} token')
b, c = speculative(70, 512, accept_rate=0.7)
print(f'投机解码 接受率 0.7  搬运 {b:8.1f} GiB 前向 {c:.0f} 次  ← 少了一半以上的搬运')
b, _ = quantized(70, 512, bytes_per=1)
print(f'fp8 量化             搬运 {b:8.1f} GiB  ← 直接减半')
实操任务
  • 把上面三个函数组合起来(量化 + 连续批处理 + 投机解码),算出叠加后的搬运量,理解为什么这几种手段可以叠加而不会互相抵消;
  • accept_rate 从 0.9 降到 0.2,观察投机解码何时变成负优化;
  • 用一句话回答:为什么连续批处理能提升吞吐但可能恶化单请求延迟?

五、自测

本章自测本章为深入级别,答对 2/3 即可
Q1Decode 阶段的 GPU 利用率通常很低,根本原因是什么?
B。这是理解所有 decode 优化的钥匙:瓶颈是搬运,不是计算。由此推出——量化(少搬)、连续批处理(一次搬服务多个请求)、投机解码(一次搬产出多个 token),三条路都在减少「单位 token 的搬运量」。
Q2PagedAttention 主要解决什么问题?
D。传统实现要求每个请求的 KV Cache 占一段连续显存,长度不确定时只能按最大长度预留,浪费严重。分页方案把缓存切成固定块、按需分配、物理上不要求连续,碎片问题随之消失,可并发数大幅上升。它不改复杂度,改的是显存利用率。
Q3MLA 这类结构优化的核心收益是什么?
C。MLA 缓存的是压缩后的潜在向量而非完整 K/V,等于把 KV Cache 这条线性账单的系数改小了。注意它同样不解决 n² 的算力问题——算力问题是另一条技术线(稀疏注意力 / 线性注意力)要处理的。

六、小结

手段针对的瓶颈主要收益主要代价
量化显存容量 + 搬运量装得下、跑得快精度损失
PagedAttention显存碎片并发数提升实现复杂度、寻址开销
连续批处理硬件空转吞吐成倍提升单请求延迟可能变差
投机解码串行依赖单请求延迟下降低接受率时反而变慢
MLA 类结构优化KV Cache 系数长上下文 + 高并发训练与实现复杂度

这一章的最终落点是那个决定性的问题:当一个成本问题出现时,你的第一反应是「换个更强的模型」,还是「先量一下瓶颈在哪」? 面试官几乎一定会用这个问题来区分这两类人——而这道题,正好是下一部分(Harness 工程)的入口。

面试官会怎么问

共 3 条 · 其中 1 条高频 · 先自己答一遍,再展开对照

如果让你把推理成本砍一半,你会动哪几个杠杆? 高频
按"先看瓶颈、再选手段"的顺序来,不要一上来就报技术名词。
① 先量瓶颈:是显存不够(并发上不去)、还是首 token 延迟高、还是吞吐被批处理限制?三个瓶颈对应完全不同的杠杆。
② 缓存与上下文侧(几乎无质量损失,优先级最高):提升前缀缓存命中率、裁剪工具结果、压缩历史。
③ 调度侧:连续批处理提高批内利用率、分页管理 KV Cache 降低碎片。
④ 模型侧:量化(FP8/INT8)、分级路由(简单任务走小模型)。
⑤ 采样侧:投机解码用小模型起草、大模型验证。
顺序很关键:能在上下文层解决的问题,不要动到模型精度。

追问链

  1. 每一档的代价是什么?
  2. 怎么验证优化没有伤害质量?
加分点:把"无质量损失的手段优先"作为排序原则说出来,是这道题真正的考点。
量化的代价到底是什么?
量化把权重(以及激活)从高精度浮点压到更低比特,换到的收益是显存占用下降、带宽压力下降、吞吐上升。代价有三个方面:
① 精度损失,且不是均匀的——对数值敏感的环节(长链推理、精确算术、结构化输出)更容易先崩;
② 长尾能力退化,平均分可能只掉一点,但某些能力会明显下滑,所以必须看分项而不是看总分;
③ 工程复杂度,不同量化格式对推理引擎、算子支持、硬件的要求不同,实际部署时"能不能跑"和"跑得快不快"是两回事。
结论:量化必须配评测,用平均分判断量化是否可行是危险的
投机解码(speculative decoding)为什么能加速?
核心是利用"验证比生成便宜"这个不对称性:用一个小而快的草稿模型一次并行猜出 k 个 token,再让大模型一次前向并行验证这 k 个 token,接受其中连续正确的部分,遇到第一个不被接受的就从那之后重来。
因为大模型的一次前向是并行的,验证 k 个 token 的成本接近验证 1 个,所以只要草稿的接受率够高,就能在一次前向上推进多个 token,端到端提速。
前提条件:草稿模型的分布要与目标模型足够接近(否则接受率低、反而更慢),且任务可预测性较强(代码补全、格式化输出这类收益最明显,自由创作收益有限)。
加分点:说清"接受率决定收益、分布不匹配会反噬"这个前提,说明你理解适用边界。
答这类题的通用结构

结论先行(一句话给出取舍)→ 机制(为什么是这样,涉及哪条链路)→ 代价或边界(这么做放弃了什么)→ 你的实践或数字(真实场景里怎么落地)。 只讲机制不讲代价,是背题;只讲取舍没有数字,是空谈。

本章收尾 · Wrap up
掌握度自评
点一下给自己打分;低于 3 分建议加入复习队列
个人笔记 · Notes