上一章 采样、温度与确定性 同一个问题问两次得到不同答案,这不是 bug,是解码策略的必然结果。但在 Agent 里,不确定性会直接变成"工具参数写错了"。这一章讲清每个采样参数到底在动什么。 I · 大模型原理 · 第 04 章 · 入门 · 约 20 分钟
Agent 工程学习站 做棵大树 出品 主站 beatree.cn 免费 · 无需登录 · 进度存本机
I 版 · 大模型原理 ◆ 第 05 章 ◆ Inference Optimization
I · 大模型原理 05 / 22 深入

推理优化与成本杠杆

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

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

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

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

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

推理分Prefill 和 Decode 两个阶段(Prefill / Decode),它们的瓶颈完全不同,因此优化手段也不同。这是本章最重要的一张表。Prefill 阶段的主要指标是 首 token 时延(TTFT),它卡在算力上。

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 四种手段针对的瓶颈完全不同。面试中被问到「怎么优化推理成本」时,先问清瓶颈在哪(显存、并发、吞吐、还是延迟),再给手段——不问瓶颈直接列表,是最容易被追问到答不上来的答法。
Prefill 与 Decode:两个阶段的瓶颈不同 左:一次性算完、可并行 右:逐 token、强串行。优化要分别下手。 Prefill(处理输入) n 个位置同时算(高并行) 瓶颈:算力 FLOPs 指标:TTFT 高 Decode(逐 token) t1 t2 t3 t4 第 t 步必须等 t−1(强串行) 瓶颈:显存带宽(搬权重) 指标:吞吐低、GPU 利用率个位数 判据:问「这一步卡在算还是搬」 Prefill 卡在算力 → 用算子融合 / FlashAttention / 分块计算把一次前向算便宜。 Decode 卡在带宽 → 用量化(少搬)、连续批处理(一次搬服务多请求)、投机解码(一次搬产多 token)。 两者是不同瓶颈,不能用同一把锤子:给 prefill 上量化,对 TTFT 帮助有限;给 decode 上分块,治不了带宽。
图 2 Prefill 一次算完、n 个位置可并行,瓶颈是算力;Decode 逐 token、强串行,瓶颈是显存带宽。把两个阶段混为一谈,是推理优化最常见的方向性错误。
手段 × 瓶颈:该用哪把锤子 ✓ = 该手段针对此瓶颈;先定位瓶颈列,再找对应的 ✓ 显存容量 显存碎片 显存带宽 串行/吞吐 量化 ✓ ✓ PagedAttention ✓ 连续批处理 ✓ 投机解码 ✓ FlashAttn·融合 ✓ 用法:先量瓶颈列,再读对应行的 ✓ 显存装不下 → 量化;碎片严重 → PagedAttention;GPU 空转 → 连续批处理;串行依赖 → 投机解码;一次前向贵 → FlashAttn/融合。 这些手段可叠加:量化 + 连续批处理 + 投机解码,各自打不同的钉子。
图 3 把「手段」和「瓶颈」摆成矩阵,对应关系一目了然。面试里被问「怎么优化成本」,先定位瓶颈列再给手段;直接列名词而不说针对什么,几乎一定会被追问到答不上来。

2.1 量化:先砍显存与带宽

量化(Quantization)把权重从 fp16 降到 fp8 / int8 / int4,权重体积和 KV Cache 一起变小,于是「装得下」「搬得动」。它同时打两根钉子:显存容量(装下更大模型)和显存带宽(decode 时搬得少)。什么时候该先上量化:模型装不下、或 decode 带宽瓶颈时。什么时候别指望它:prefill 是算力瓶颈,量化对 TTFT 帮助有限;且低比特(W4)会带来精度损失,长链推理、大数运算、代码生成上更明显。安全的起点是 W8A8,W4 要配合分组量化 + 校准才能用。

2.2 PagedAttention:治显存碎片

PagedAttention 把 KV Cache 切成固定大小的块、按需分配、不要求连续,像操作系统分页管理内存那样。它打的是「显存碎片」这根钉子:传统连续分配会让显存里布满无法利用的空洞,能并发的请求数被碎片锁死。附带收益是前缀共享——多个请求共用相同前缀时,可以复用同一批块,这也是下一章 前缀稳定性(Prefix Stability) 在推理框架侧的落点。代价是实现复杂度上升、有寻址开销。

2.3 连续批处理:性价比最高的一刀

连续批处理(Continuous Batching) 不等整批做完,某个请求一结束就立刻补入新请求,让 GPU 始终有活干。它打的是「硬件空转」这根钉子——decode 阶段单请求的 GPU 利用率常是个位数,批处理把它拉高数倍,吞吐成倍提升,是性价比最高的一刀,现代推理框架(vLLM 等)已默认支持。代价是单请求延迟可能变差(被大 batch 拖累),所以对强延迟敏感的场景要控制 batch 上限。

2.4 投机解码:拿算力换串行

投机解码(Speculative Decoding) 让一个小模型(草稿模型)先一次性起草 k 个 token,大模型一次前向并行验证,验证通过的 token 直接采用。它打的是「串行依赖」这根钉子:decode 每步都要等上一步,投机解码让一次前向「搬运」产出多个 token。代价是需要额外的小模型或额外计算,加速比取决于「接受率」——小模型猜得越准,越划算;接受率低时可能反而更慢(净亏损)。它本质上是用「多算一点」换「少等一点」,适合输出长、延迟敏感的场景。

这四把锤子可以叠加(量化 + 连续批处理 + 投机解码,各打各的钉子),但叠加前先量清瓶颈——给 prefill 上量化治不了算力,给 decode 上分块治不了带宽。

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

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

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

顺带把四把锤子的发力点一次性说清:量化 同时压显存与搬运量,PagedAttention 消灭显存碎片(Memory Fragmentation),连续批处理 填平硬件空转,投机解码 绕开串行依赖——而后两者的提速分别受 接受率(Acceptance Rate) 与 显存带宽(Memory Bandwidth) 制约;此外还有 FlashAttention 与 算子融合(Operator Fusion) 这类「让单次计算更便宜」的手段。

用前面学过的语言描述:它压的是 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,观察投机解码何时变成负优化;
  • 用一句话回答:为什么连续批处理能提升吞吐但可能恶化单请求延迟?

五、常见误区与追问

5.1 误区:Decode 慢是因为算力不够

错在哪:直觉上「生成慢 = 算得慢」。为什么自然:我们习惯用算力衡量深度学习瓶颈。正确做法:Decode 每步只生成一个 token,计算量极小,但每步都要把整个模型权重和 KV Cache 从显存搬到计算单元一次——时间花在搬运上,不是计算上。判据:看 GPU 利用率,若只有个位数百分比却算力充足,说明瓶颈是显存带宽而非算力;优化方向是「少搬 / 一次搬服务多请求 / 一次搬产多 token」,而不是堆算力。

5.2 误区:优化推理成本,先上量化最稳妥

错在哪:量化几乎人人会提,便当成默认第一步。为什么自然:它名字最熟、上手最容易。正确做法:应该先测瓶颈:GPU 利用率低、吞吐上不去 → 先看连续批处理(性价比最高的一刀);显存装不下 → 才上量化;碎片严重 → PagedAttention。判据:没量就上手段等于在黑暗中拧螺丝;被问「先优化哪个」时,先反问「你卡在显存、并发、吞吐还是延迟」,再给手段。

5.3 误区:投机解码总是比朴素 decode 快

错在哪:把「并行验证多个 token」想成必然加速。为什么自然:一次前向验证 k 个草稿,听起来省了 k−1 次前向。正确做法:加速的前提是草稿被大模型接受;接受率低时,草稿白白计算,反而净亏损。判据:设草稿数 k、接受率 r,等效前向次数 ≈ 步数 /(1 + k·r);当 r 很低(如 0.2 配 k=4 时等效约 1/1.8≈0.56 步/ token 的节省并不稳),需实测;接受率掉到阈值以下就关掉投机解码。

5.4 误区:PagedAttention 把注意力的平方复杂度降下来了

错在哪:看到「优化注意力」就联想到降复杂度。为什么自然:名字带 Attention,容易望文生义。正确做法:PagedAttention 只解决 KV Cache 的显存碎片——把缓存切成固定块、按需分配、物理不连续,从而提升可并发请求数;它不改 O(n²) 的注意力计算复杂度。判据:被问「PagedAttention 改了哪个公式因子」时,答案必须是显存利用率,而不是 n²;降复杂度是稀疏/线性注意力与 FlashAttention 的事。

5.5 误区:Prefill 和 Decode 用同一套优化手段

错在哪:把「推理优化」当成一张扁平清单随机挑。为什么自然:清单上都是「优化手段」,容易混用。正确做法:Prefill 瓶颈在算力(n 个位置可并行,卡在 FLOPs),Decode 瓶颈在显存带宽(强串行,卡在搬权重)。判据:问「这一步卡在算还是搬」——Prefill 上量化对 TTFT 帮助有限,Decode 上分块计算治不了带宽;手段必须对准阶段。

5.6 误区:FlashAttention 是推理专属或训练专属的优化

错在哪:按使用场景给它贴标签。为什么自然:常听人在「训练加速」语境下提到它。正确做法:FlashAttention 是训练与推理通用的注意力 IO 优化——通过分块与算子融合,减少中间结果的显存读写,不改变注意力语义。判据:它改的是「注意力怎么算更省 IO」,不是「注意力算什么」;在任何受显存带宽制约的阶段(prefill 尤甚)都能用。

六、自测

本章自测本章为深入级别,答对 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 工程)的入口。

八、参考与延伸

本章机制部分只讲到「够用」为止。想往下深挖,下面按「先看图、再看代码、最后读论文」的顺序排好了。全站不做原文转载,这里只登记链接与「为什么值得读」。

先看图(建立直觉)

再看代码(动手实现)

最后读论文(对齐一手定义)

◇ 面试官会怎么问

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

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

追问链

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

追问链

  1. 它和连续批处理是什么关系?
  2. 如果碎片不严重但吞吐上不去,你会先查什么?
加分点:加分点:说清它改的是显存利用率而非复杂度,并给出「收益大 / 小」的判据,而不是一刀切推荐。
投机解码:草稿模型一次起草 k=4 个 token,接受率 r=0.7 大概能拿到多少加速?r 掉到 0.2 时还能上吗? 高频
按简化模型,等效前向次数约为原来的 1/(1 + k·r)。
r=0.7、k=4 时约为 1/3.8 ≈ 26%,也就是大约 3~4 倍的搬运量节省;r=0.2、k=4 时只有 1/1.8 ≈ 56%,而且草稿模型自身的计算与调度开销还没算进去,实际很可能净亏损。
判据:上线前先实测接受率;r 低于 0.5 左右就别上,或者先换一个与目标模型分布更接近的草稿模型。
加速比还强依赖任务类型——代码补全、格式化输出这类高可预测任务收益最大,自由创作收益有限。

追问链

  1. 接受率低是草稿模型的问题还是任务的问题?怎么区分?
  2. 除了另训一个草稿模型,还有别的「一次前向产出多个 token」的办法吗?
加分点:加分点:把接受率当成「上不上的门槛」而不是事后指标,并主动补一句草稿模型自身开销会吃掉收益。
线上看到「吞吐上不去、GPU 利用率只有个位数」,你会先上量化还是先上连续批处理?
先上连续批处理。
GPU 利用率只有个位数说明硬件在空转,根因是「一批请求要等最慢的那个结束才换下一批」;连续批处理让某个请求一结束就立刻补入新请求,直接填满空转,是性价比最高的一刀。
量化解决的是另一类问题——显存装不下或搬运量太大:它减少每次搬运的字节数,对 decode 的带宽瓶颈有效,但对「批内没填满」这种调度问题无能为力。
判据:先问瓶颈是「算」「搬」还是「排」——排队问题用调度解,搬运问题用量化解,算力问题用融合与分块解。

追问链

  1. 连续批处理会不会让单请求延迟变差?为什么?
  2. 如果显存也不够、利用率也低,这两件事能一起做吗?
加分点:加分点:先定位瓶颈再给手段,并能说清连续批处理恶化单请求延迟的原因(被大 batch 拖累)。
答这类题的通用结构

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

本章收尾 · Wrap up
掌握度自评
点一下给自己打分;低于 3 分建议加入复习队列
个人笔记 · Notes
    下一章 Harness 是什么 "除模型本身以外的所有工作,都属于 Harness 的范畴"——这句话划出的地到底有多大?这一章给出一张完整的 Harness 能力地图,后面七章都挂在这张图上。 II · Harness 工程 · 第 01 章 · 入门 · 约 24 分钟