AI Infra · Inference & Acceleration

大模型推理与加速:
沿着一次请求从输入走到输出

把一次请求拆开来看:文字或图片先经 Tokenizer 变成序号,Embedding 查成向量,再流过几十个 Transformer 层完成推理计算,采样环节按输出的浮点数挑出下一个序号,最后由 Detokenizer 拼回文本——回答就是这样一个 token 接一个 token 产生的。AI 基建横跨硬件、算子、显存、调度与架构多个层次,本文把镜头对准推理层,沿着这条从输入到输出的路径,逐站看每个环节慢在哪、可以怎么优化,以及每项技术背后的原始论文。

AUTHOR
周
周龙超
超周到的程序员
AUTHOR · 作者
分享主题 LLM 推理与部署加速 参考文献 43 篇(含 arXiv)
24×
PagedAttention / vLLM 的单卡吞吐提升:较 HuggingFace 最高 24×、较 Orca / FasterTransformer 2–4×(Kwon et al., SOSP 2023)
36.9×
Orca 提出 iteration 级连续批处理,同等时延下吞吐较 FasterTransformer 最高提升 36.9×(Yu et al., OSDI 2022)
7.4×
DistServe 将 Prefill 与 Decode 解耦后,同等资源下可服务请求数最高提升 7.4×(或 SLO 收紧 12.6×)(Zhong et al., OSDI 2024)
20×
FlashAttention 注意力显存占用最高降低约 20×(随序列长度线性增长)、端到端训练提速 2–4×(Dao et al., NeurIPS 2022)

全景地图:一次请求的完整流水线与 AI 基建八层

图片 视觉编码器 ViT 图像 embedding 汇入 Embedding 输入 文本 · 图片 Tokenizer 文本转序号 Embedding ID 转向量 Transformer Layer Transformer 主干计算 ×L Logits Sampling 按浮点分布 挑选序号 Detokenizer 序号转文本 ID一批浮点数 一批浮点数ID 输出 文本 公共前缀识别 查表 · 位置编码 KV Cache·PagedAttention FlashAttention·量化 温度/TopP·投机·约束 流式拼接输出 选中的 ID 作为下一步输入,循环往复 并发请求 · 横切所有环节 Continuous Batching Chunked Prefill PagedAttention RadixAttention PD 分离 灰色胶囊 = 各环节优化手段;底部长条 = 并发请求层,横切流水线所有环节(详见「并发请求」一章);图片经视觉编码器在 Embedding 环节汇入
图 1一次推理的完整路径:输入(文本或图片)依次经过 Tokenizer、Embedding、Transformer 主干计算、Logits Sampling、Detokenizer 变成输出;采样选中的 ID 会回灌为下一步的输入。每个环节下方的灰色胶囊列出该环节的代表性优化手段;底部的「并发请求」长条横切所有环节,是服务端调度与架构层的独立话题,与主干流水线正交。
硬件 / 互联HARDWARE
GPU A10 · 24GBA100 / H100 / GB200 业界参照GDDR6 600GB/s(A10)HBM3/3e 3.35TB/sPCIe Gen4 A10 机内通道NVLink + NVSwitch 机内全互联RDMA / IB 跨机直访CPU DRAM / SSD
▲ 自底向上调用 自顶向下驱动 ▼
并行 / 通信PARALLEL
TP 张量并行PP 流水并行DP 数据并行EP 专家并行SP 序列并行NCCL 集合通信AllReduce / AllGatherAll-to-All
算子 / 编译KERNEL
FlashAttention 1/2/3Tiling 分块在线 Softmax算子融合FP8 / 低精度CUDA Graph 减启动开销TensorRT / 编译器
显存管理MEMORY
KV CachePagedAttention 分页GQA / MQA 减 KV 头MLA 潜变量压缩KV 量化 KIVIKV 驱逐 H2O / SnapKV
模型架构MODEL
Dense / MoETransformer 主干Mamba / SSM 状态空间线性注意力Hybrid 混合架构滑动窗注意力长上下文架构
调度 / 服务SCHEDULING
Continuous BatchingChunked PrefillRadixAttention 前缀复用SLO / 优先级调度抢占与换出
系统架构ARCHITECTURE
PD 分离DistServeMooncakeSplitwiseNVIDIA Dynamo
解码算法DECODING
Speculative Decoding自投机 Early-ExitMedusaEAGLE 1/2/3MTPLookahead约束解码
图 2AI 基建自底向上可分为八个层次:硬件 / 互联、并行 / 通信、算子 / 编译、显存管理、模型架构、调度 / 服务、系统架构与解码算法。我们团队硬件以 A10 为主(无 NVLink、走 PCIe,不支持 FP8),本文镜头对准推理层:沿输入→输出的路径逐站展开,硬件与算子只交代与推理直接相关的结论。

为什么推理加速值得做:两大根本矛盾

过去做后端,我们优化的是 QPS、RT、连接池与缓存;转到 AI Infra 后,优化对象变成了大模型推理(Inference)本身。训练是“一次性把题算完”,而推理是 7×24 小时在线、要同时服务海量用户、且逐字(token)生成的过程。它决定了两件最花钱的事:用户的等待时延与GPU 的持有成本。

从成本结构看,训练是一次性的大额投入,而推理成本随请求量线性增长、永不停机——在一个模型服务的整个生命周期里,推理的累计算力开销往往远超训练;同一张 GPU 卡能同时服务多少用户、每个 token 摊多少成本,全部取决于推理层的效率。这就是推理加速“值得做”的定量理由:每一点吞吐与时延的改善,都会直接折算进 GPU 账单。

大模型推理慢、贵,根子上是两个绕不开的矛盾:

两大矛盾对所有业务都成立,但不同业务痛的点不一样:在线聊天最在意 TTFT 与 ITL;IDE 代码补全输出短、追求极低 ITL,是投机解码的最佳猎场;RAG 与长文档问答痛在长 Prefill 的 TTFT;Agent 多轮调用共享大量 system prompt,前缀命中率决定成本;离线批处理没有时延约束,只看吞吐与每 token 成本。读后面每章都可以带着这个问题:我的业务属于哪一类,这项优化为我解决什么?

生成的 token(以问答「北京今天天气怎么样?」为例) 今天 北京 天气 晴朗 适合 前向 #1读全部权重 前向 #2读全部权重 前向 #3读全部权重 前向 #4读全部权重 前向 #5读全部权重 单次前向的时间构成(Decode) ≈23ms≈23ms ≈23ms≈23ms ≈23ms 橙色 = 有效计算(<5%);灰色 = 等待权重 / KV 从显存搬到计算单元(>95%)。7B FP16 权重约 14 GiB,每步都要完整读一遍 5 次前向 × 读 14 GiB 权重 ≈ 搬运 70 GiB 数据,有效产出只有 5 个 token —— 算术强度仅 1–2 FLOPs/B,A10 脊点约 208,远落在带宽斜坡上 GPU 算力利用率 个位数~12% 串行依赖 + 等显存,算力大量闲置 优化点① KV Cache:不重复算历史(03 章) 优化点② 拼 batch:摊薄权重读取(04 章) 优化点③ 投机解码:一次出多个(05 章)
图 3原生自回归解码:5 个 token 触发 5 次串行前向;每步时间几乎全花在等待权重 / KV 搬运上,GPU 算力利用率长期只有个位数。底部三个优化点对应后续第 03、04、05 章的优化主线。
本文的一条主线

沿着输入→输出的流水线,每个环节都有各自的优化抓手:输入侧识别公共前缀(RadixAttention);主干计算侧用 KV Cache 消除重复计算、FlashAttention 减少访存、量化与 KV 压缩减少搬运字节;并发侧用 Continuous Batching、分页与 PD 分离把 GPU 喂满;采样侧用投机解码一次前向产出多个 token、用约束解码保证格式。它们大多无损、可叠加。

01

输入与 Tokenizer:文字和图片变成 ID

TOKENIZER

旅程第一站,是把人能看懂的输入变成模型能处理的整数序号(token ID)。这一站不占 GPU、却决定了后续所有计算的粒度:同一个意思,切出来的 token 越少,算得越快、KV Cache 也越小。

流水线位置导航 · POSITION输入Tokenizer文本转序号EmbeddingID 转向量Transformer主干计算 ×LSampling挑选序号Detokenizer序号转文本输出当前环节 · Tokenizer(文本转序号)

Tokenizer:文本怎样切成序号

主流大模型使用 BPE(Byte-Pair Encoding,字节对编码)类分词器:它在语料上预先统计“哪两个相邻片段最常一起出现”,把它们逐步合并成一个子词,最终形成一张 3–15 万大小的词表。分词时把句子从左到右 greedily 匹配词表,输出一串 ID;遇到没见过的词,就拆成已知的更小片段(极端情况退到字节),因此不会出现 OOV。

对中文业务还有一个容易忽略的量化事实:不同词表对中文的切分粒度差异很大。早期以英文为主的词表里一个汉字可能要拆成 2–3 个 token,而面向中文扩容过的词表(Qwen、GLM 等)通常在 1 个汉字 1 个 token 上下——同一句话的 token 数可以差到 1.5–2×。token 数直接决定 Prefill 算力、KV Cache 体积和按 token 计费的账单,所以评估一个模型是否适合自己的业务,用真实业务语料实测“每千字 token 数”,是比看参数量更实在的口径。

原始文本 unbelievable idea unbelievableĠidea 81230516191842 BPE 分词要点 ① 从最小的字符 / 字节单元开始 ② 每轮合并语料中频率最高的相邻对 ③ 重复合并,直到词表上限 ④ 未登录词拆成已知子词,不会 OOV 特殊 token(如 <|im_start|>)整体保留 中文多按单字 / 子字切,数字常逐位切
图 4BPE 把文本切成子词序列、再查成 ID(ID 为示例值)。前缀 Ġ 表示词前空格,是字节级 BPE 的特殊标记。分词在 CPU 上完成,不占 GPU。

图片怎样进来:视觉编码器与图像 token

多模态模型里,图片不是直接进入 Transformer:它先被切成小块(patch),由一个视觉编码器(ViT)编码、再经投影层对齐维度,变成一组图像 embedding,与文本 token 的向量拼接成同一条序列,之后走的是完全相同的推理路径。分辨率越高、图像 token 越多(常见一张图数百到上千 token),算力与 KV Cache 开销随之增长。

图像 token 的数量是可以自己算出来的:以 Qwen2-VL 式的动态分辨率为例,ViT 按 14×14 像素切 patch、再经 2×2 合并,图像 token 数约为 $\frac{H}{28}\times\frac{W}{28}$(不同模型的 patch 与合并参数不同)——一张 1024×1024 的图约 1300 token,4K 分辨率则上万。视频则是“帧采样 × 每帧 token”,几秒视频轻松吃掉数千 token。多模态场景的显存规划,第一步就是把这条公式代进第 3 章的 KV 公式里。

图片 像素输入 切 patch 视觉编码器 ViT + 投影层 输出图像向量 图图图 文文 拼接为统一序列 图像 embedding 与文本向量拼接后,进入同一套 Transformer,没有第二条推理路径 图像 token 越多,算力与 KV Cache 开销越大:多模态场景的成本主要来自这里
图 5图片接入路径:像素 → patch 网格 → ViT + 投影 → 图像 embedding,与文本 token 拼接。之后的 KV Cache、批处理、采样等优化对图像 token 同样生效。
这一环节怎么优化

① 分词在 CPU 异步执行、与 GPU 前向重叠,别让字符串处理阻塞推理;② 公共前缀(system prompt、多轮历史)在分词阶段即可识别,为 RadixAttention 的前缀复用铺路(第 6 章);③ 为结构化输出 / 工具调用预留的特殊 token 应加进词表,避免在采样阶段被拆碎(第 4 章)。

还有一个常踩的坑在分词之前:对话模板(Chat Template)。多轮对话并不是直接拼接文本,而是按模型约定的模板(如 <|im_start|>user … <|im_end|>)拼成一条带角色标记的字符串再分词——模板用错(换模型没换模板、系统提示位置拼错),模型不会报错、只会静默变笨;模板不一致还会让前缀缓存完全失效。上线前拿官方模板逐字节比对,是零成本的质量保险。

02

Embedding:ID 转向量,位置编码就位

EMBEDDING

第二站很短,但它是“离散符号”进入“连续计算世界”的入口:ID 序列被查成一批浮点数向量,之后 GPU 才能大展拳脚。

流水线位置导航 · POSITION输入Tokenizer文本转序号EmbeddingID 转向量Transformer主干计算 ×LSampling挑选序号Detokenizer序号转文本输出当前环节 · Embedding(ID 转向量)

Embedding:一次近乎免费的查表

模型自带一张 Embedding 矩阵(词表大小 × 隐藏维度,如 15 万 × 4096),每个 ID 对应矩阵的一行;所谓 embedding,就是按 ID 把对应行取出来、拼成向量序列,再乘一个缩放系数。它是显存 gather 操作、没有矩阵乘,开销在整条路径中几乎可以忽略。图像 embedding 也在这一站与文本向量汇合。

ID 1528873619 Embedding 矩阵(查找表) token 向量(一批浮点数) 位置信息(RoPE 旋转位置编码)在进入注意力前作用于 Q/K;图像 embedding 在此与文本向量汇合 Embedding 常与输出投影 lm_head 共享权重(weight tying);lm_head 是采样前的一次大矩阵乘,与本站的“免费查表”形成对比
图 6Embedding 就是按 ID 从权重矩阵取行、拼成向量序列;自注意力本身对位置无感,因此还要叠加位置编码(RoPE)。这一站是 gather、不是 GEMM,开销极小。

位置编码:给向量装上“坐标”

自注意力只看内容相关性、对 token 的先后顺序天然无感(打乱输入,注意力照样能算)。因此必须显式注入位置信息:主流做法是 RoPE(旋转位置编码),在每个注意力层对 Q、K 按位置做一次与相对距离相关的“旋转”,让注意力打分天然带有相对位置信息,且能外推到训练时没见过的长度。RoPE 之外还有 ALiBi 等替代方案(不给向量做旋转,而是给注意力打分直接加上与距离线性相关的偏置,外推性好但表达力略弱),不过当前主流模型几乎清一色采用 RoPE。

长上下文外推:PI、NTK 与 YaRN。RoPE 的“外推”其实有限——拿按 4K 训练的模型直接跑 128K,注意力打分会迅速退化。工程上靠三类位置编码改造把窗口拉长:位置插值(PI)把超长位置线性压缩回训练范围,配合少量微调即可;NTK-aware 插值改为非均匀缩放旋转频率、压低频保高频,短文本几乎无损;YaRN 在此基础上按维度分频段处理并引入注意力温度校正,成为长上下文模型(Qwen、GLM 等)的主流配置。它们的共同点:不改模型结构、只动 RoPE 的“坐标系”,是部署长上下文时成本最低的改造。

RoPE 与 KV Cache 的耦合。旋转是施加在 Q、K 上的,因此缓存进 KV Cache 的 K 已经带上了位置信息——这带来两个直接后果:① 前缀复用(6.2 RadixAttention)要求命中的前缀位置编号完全一致,否则缓存的 K 与新请求的坐标系对不上;② DeepSeek MLA(3.2)之所以要在潜向量之外单独缓存一份 RoPE 部分,正是因为位置分量无法和内容一起被低秩压缩。同理,用 PI / YaRN 改坐标系扩上下文时,存量 KV 也必须一并重算。

这一环节怎么优化

Embedding 本身没有提速空间,关键是别让它阻塞串行路径:查表可与前一步的采样 / 分词在流水线上重叠,RoPE 可融合进 QKV 投影算子。需要记住的是反方向的事实——采样前的 lm_head 输出投影是词表维度的大矩阵乘,在大 batch、大词表下开销可观,是第 4 章要面对的对象。

03

Transformer 主干计算:KV Cache、FlashAttention、量化

TRANSFORMER · KERNEL

向量序列进入模型主体——$L$ 个结构相同的 Transformer Block,这是整条路径上算力、显存带宽与优化手段最密集的一环。本章先用 3.1 建立背景、诊断 Decode 为什么慢;再沿三条优化主线展开:3.2 KV Cache 家族(少算、放得下、装得少,能复用见 6.2)、3.3 FlashAttention、3.4 量化。

流水线位置导航 · POSITION输入Tokenizer文本转序号EmbeddingID 转向量Transformer主干计算 ×LSampling挑选序号Detokenizer序号转文本输出当前环节 · Transformer 主干计算 ×L
03 Transformer 主干计算 · 先诊断瓶颈,再沿三条优化主线开方 3.1 背景与 瓶颈诊断 推理只有前向 QKV 结构 Prefill/Decode 两节拍 显存账 GPU 存储层级 显存墙 Roofline 诊断 → 结论:Decode 落在带宽斜坡(Memory-Bound),提速不能靠堆算力 诊断顺序:前向数据流 → 两节拍 → 显存账 / 存储层级 → Roofline 落点 3.2 KV Cache 家族 围绕「历史 K/V」 的三层手段 ① 少算 历史 K/V 只算一次 注意力 O(n²)→O(n) KV Cache 本体 ② 放得下 PagedAttention 切 block + 页表映射 碎片 60–80%→个位数 ③ 装得少 GQA/MQA 减头 · MLA 降维 H2O/SnapKV 逐出 KIVI 位宽量化见 3.4 3.3 Flash Attention 分块 HBM→SRAM,片上融合 N×N 中间矩阵不落地,HBM 访存 O(N²)→O(N) 3.4 量化 BF16→FP8 / INT8 / INT4 搬运字节减半 ~ 1/4(有损,需校准) 主线分工:3.2 让历史 K/V 不重算、好摆放、体积小;3.3 让注意力中间矩阵不落地;3.4 让每个字节更便宜——彼此正交、可叠加。 另一条思路是减少前向「次数」(一次前向出多个 token):投机解码家族,见第 04 章。
图 703 章结构地图:3.1 背景与瓶颈诊断(两节拍 + 显存账 + Roofline);3.2 KV Cache 家族三层手段(少算 / 放得下 / 装得少;跨请求前缀复用属于并发请求一章,不在本图);3.3 FlashAttention;3.4 量化。

背景:推理只有前向,Decode 为什么慢

推理只有前向:权重固定,不断预测下一个 token

前向传播(Forward)是数据从 Embedding 一路算到 logits 的过程,训练和推理都有;反向传播(Backward)从损失 Loss 倒推梯度、更新权重,只在训练中存在。推理阶段权重固定、只有前向、不断预测下一个 token,不更新任何参数——这也是为什么推理优化的抓手集中在“怎么搬数据、怎么调度”,而不需要保存反向用的中间状态。

30 秒回顾 Transformer 与 QKV

向量序列流过 $L$ 个结构相同的 Transformer Block(多头自注意力 MHA + 前馈网络 MLP/FFN,配 LayerNorm 与残差),最后由输出 Linear 层投影成词表维度的 logits。注意力做的事,是让当前 token 的 Query 与所有历史 token 的 Key 打分、再按权重取 Value:

$$\mathrm{Attention}(Q,K,V)=\mathrm{softmax}\!\left(\frac{QK^{\top}}{\sqrt{d_k}}\right)V$$

其中 $Q$ 是当前 token 在“问什么”,$K$ 是每个历史 token 的“标签”,$V$ 是每个历史 token 的“内容”。多头(Multi-Head)只是把 Q/K/V 切成多组并行计算后拼回。

容易被忽略的是另一半参数:MLP/FFN。每个 Block 里注意力只占约 1/3 参数,剩下约 2/3 在两层(或 SwiGLU 三层)的前馈网络里——Decode 每步“搬权重”,大头其实是搬 MLP。现代模型的主流做法是把 MLP 换成 MoE(Mixture of Experts,混合专家):把一个大 FFN 拆成数十到数百个“专家”,每个 token 由路由器只激活其中少数几个(如 Qwen3-30B-A3B 总参 30.5B、每 token 仅激活 3.3B)。MoE 对推理的含义是显存按总参数装、带宽与算力按激活参数算:模型必须完整常驻显存,但每个 token 只需读取被激活专家的权重——Decode 访存大减、TPS 天花板抬高(6.1 会按此修正口径),代价是专家路由引入 All-to-All 通信与负载均衡问题(见 7.3 趋势)。

每个 token 独立过路由器 token 向量token 向量token 向量 Router 路由器 对全部专家打分,选 top-k 专家池(全部常驻显存) E1 E2 ✓ E3 E4 E5 E6 E7 ✓ E8 橙色:本 token 激活的专家(如 128 选 8) 显存:全部专家常驻 → 按总参数装 带宽 / 算力:只读激活专家 → 按激活参数算 不同 token 可激活不同专家;注意力层不受影响,KV Cache 照常
图 8MoE 路由:每个 token 经 Router 打分只激活少数专家(橙),其余专家(灰)本轮不读取;模型按总参数占显存、按激活参数耗带宽与算力——Qwen3-30B-A3B 为 128 专家选 8,DeepSeek-V3 为 256 选 8。

两个节拍:Prefill 与 Decode

Prompt L 个 token ① Prefill 一次性并行前向 算全部 K/V 并缓存 Compute-Bound KV Cache 逐层 K / V 缓存 随生成不断追加 ↓ ② Decode 输入 1 个新 token 读 KV,出下一个 token Memory-Bound 循环:每步追加 K/V,直到结束 token 流式输出
图 9推理两阶段:Prefill 一次性并行处理整个 prompt 并生成 KV Cache(算力受限);Decode 逐 token 自回归生成、反复读取 KV(带宽受限),每步把新 K/V 追加进缓存。

为什么 Decode 每次只输入 1 个 token?不是硬件强制,而是自回归逻辑 + KV Cache 设计的自然结果:Prefill 已把全部历史 K/V 存好,Decode 时“新信息”只有刚生成的那 1 个 token,所以本轮的 Q 只有 1 个;它用这 1 个 Q 去和缓存里全部历史 K/V 打分,采样出下一个 token,再把这 1 个 token 的 K/V 追加进缓存。第 $N$ 个 token 无法提前知道第 $N+1$ 个,只能逐步进行。

对比项Prefill(提示填充)Decode(逐 token 生成)
输入整个 prompt($n$ 个 token)一次进每次仅 1 个新 token
并行度高,所有 token 并行矩阵乘低,序列维度并行度为 1
瓶颈类型Compute-Bound 算力受限,利用率可达 ~95%Memory-Bound 带宽受限,利用率可低至 ~12%
主要开销大量 GEMM 矩阵运算反复读取全部权重 + 全部 KV Cache
对应指标决定 TTFT(首 token 时延)决定 ITL(token 间时延)
优化抓手FlashAttention、张量并行、量化PagedAttention、减 KV 访存、PD 分离、投机解码
关键认知

Prefill 和 Decode 的瓶颈完全不同——一个拼算力、一个拼带宽。这是后面所有优化(包括 PD 分离)成立的根本原因。

显存占用:模型权重、KV Cache 与激活

$$\text{GPU 显存总占用}\ \approx\ \underbrace{P\times b}_{\text{模型权重}}+\underbrace{\mathrm{KV}_{\text{bytes}}}_{\text{KV Cache}}+\underbrace{A}_{\text{激活/中间张量}}+\text{碎片}$$

先看清 GPU 的存储层级

GPU 不是一块“均匀的大内存”,而是一个越靠近计算核心越快、但容量越小的层级结构。硬件细节不是本文重点,只需记住结论:数据在不同层级之间搬运的速度,往往才是推理的真正瓶颈。

寄存器 Register 数十 TB/s · 256KB/SM 片上 SRAM(L1 / Shared Memory) TB/s 级 · ~228KB/SM HBM3 / GDDR6 显存 H100 HBM3 3.35TB/s · 80GB | A10 GDDR6 600GB/s · 24GB 主机 DRAM(DDR5) ~50 GB/s(0.05 TB/s)· 数百 GB–2TB NVMe SSD / 远端存储 ~7 GB/s · 数 TB(需经 CPU/PCIe 搬运) 快 / 小 慢 / 大 离计算核心越近 离计算核心越远
图 10GPU 存储层级:片上寄存器/SRAM 带宽极高但以 KB 计;HBM / GDDR6 是模型权重与 KV Cache 的主阵地(H100 HBM3 3.35TB/s、80GB,A10 GDDR6 600GB/s、24GB);再往下到主机 DRAM、SSD,带宽断崖式下降。

显存墙:算得快,搬得慢

当一次计算的算术强度(Arithmetic Intensity,每搬运 1 字节能做多少次浮点运算,FLOPs/Byte)很低时,计算单元会大量空转等数据,这就是 Memory-Bound。Decode 阶段每个 token 要把全部权重 + 全部 KV Cache 从 HBM 读一遍,却只做约 2 倍参数量的运算,算术强度极低,因此被显存带宽死死卡住。

Roofline:一张图判断瓶颈在算力还是带宽

1101001000 1101001000 算术强度 I(FLOPs / Byte,对数轴) 性能(TFLOPS,对数) 算力屋顶 989 TFLOPS 带宽屋顶 = 3.35 TB/s × I 脊点 I*≈295 Decode I≈1.5 落在斜坡:带宽受限 Prefill I 大 贴近屋顶:算力受限
图 11Roofline 模型(Williams et al., 2009):斜屋顶由显存带宽决定(性能 = β·I),平屋顶由峰值算力决定,二者交点为“脊点”。H100 脊点 ≈ 989/3.35 ≈ 295 FLOPs/B。Decode 落在斜坡(带宽受限,约 5 TF/s 量级),Prefill 贴近平顶(算力受限)。
A10 口径:FP16 Tensor Core 稠密峰值约 125 TFLOPS、GDDR6 带宽 600 GB/s,脊点 ≈ 125 / 0.6 ≈ 208 FLOPs/B——Decode 同样落在斜坡,结论不变;A10 无 FP8 硬件,FP8 屋顶不适用。
用 Roofline 指导优化

Decode 想提速,要么减少搬运字节(量化、压缩/驱逐 KV、减 KV 头);要么提高算术强度(多请求拼 batch,让一次搬运服务更多 token,第 6 章);Prefill 想提速则靠 FlashAttention、张量并行与低精度。

KV Cache 家族:少算、分页、压缩与逐出

如果只记住一个推理优化,那就是 KV Cache。本节把所有围绕 KV Cache 的手段收为一个家族,按四个问题展开:① 少算——历史 K/V 只算一次;② 放得下——PagedAttention 分页管理;③ 装得少——GQA/MQA、MLA 与运行时逐出(KIVI 位宽量化见 3.4);④ 能复用——RadixAttention 前缀树(见 6.2)。

① 少算:把历史 K/V 存起来,注意力计算 O(n²) → O(n)

它的思想极其朴素:把已经算过的历史 token 的 K、V 存起来,新 token 只算自己的 Q,直接复用旧 K/V,从而把每步“重算全部历史”的开销砍掉。

无 KV Cache:每步重算全部历史 O(n²) 1 2 3 4 5 第 $k$ 步要算 $k$ 个 token,总量 $1+2+\dots+n$ 有 KV Cache:每步只算 1 个新 token O(n) 1 1 1 1 1 历史 K/V 直接复用,每步恒定只算新 Q
图 12KV Cache 把单请求解码的注意力计算量从 $O(n^2)$ 降到 $O(n)$:左图每步计算量随序列线性增长,右图每步恒定。代价是要用显存存放历史 K/V,序列越长占用越高。

KV Cache 的数据流动:HBM、SRAM 与一次写入

把 KV Cache 放到硬件层面看,它改变的是「数据在 HBM 与片上 SRAM 之间怎么流动」。没有缓存时,每生成一个新 token,都要把它之前所有 token 的 K/V 重新算一遍(生成第 k+1 个 token 时,重算的是之前的 1~k 个):权重反复从 HBM 读进 SRAM、算出的 K/V 用完即弃,下一步全部重来。有了缓存,Prefill 阶段权重只需流式读一遍,算出的 K/V 作为结果写回 HBM 的 KV Cache 区、只写一次;Decode 每生成一个 token,只算自己的 Q/K/V,把之前已缓存 token 的 KV 块从 HBM 读进 SRAM,在片上完成 $QK^\top$、softmax、乘 V,再把新 token 的 K/V 作为增量追加写回。另外要严格区分两层含义:「缓存随生成变长」说的是已缓存的有效内容逐步增多;而物理显存是按块管理的——旧式方案按最大生成长度连续预留一整段,PagedAttention(②)则切成固定页、按需分配、写满再取新页,空间总是先备好、内容再逐步写入。

无 KV Cache:每步重算全部历史 每生成一个新 token,都把它之前所有 token 的 K/V 重算一遍 北京 重算: 今天 天气 重算: 今天 北京 晴朗 重算: 今天 北京 天气 生成 n 个 token 共重算 n(n−1)/2 次 K/V → O(n²); 每步还要把全部权重重新从 HBM 搬进 SRAM 有 KV Cache:一次写入,反复读取 每个 token 的 K/V 只算一次、只写一次;之后每步只读缓存 Prefill 今天 北京 天气 并行算好,写缓存 Decode 晴朗 只算自己的 Q/K/V;读缓存 3 个 (之前的 1~3 个),追加写 1 个 Decode … 读缓存 4 个,再追加写 1 个 历史 K/V 永不重算 n 个 token 共写 n 次 → 计算 O(n); 代价是 HBM 上要常驻这份缓存 HBM 上的 KV Cache 区:「变长」的是有效内容,物理空间按块管理 今天 北京 天气 晴朗 … 空闲 空闲 空闲 已写入:随 Decode 只增不改(只追加、不重算) 空闲块:按需分配,写满再取新页 旧式方案按最大生成长度连续预留(碎片 60–80%);PagedAttention 切成固定页、按需分配——空间先备好,内容逐步写入
图 13以「今天、北京、天气、晴朗」为例看 KV Cache 的数据流:无缓存时,每生成一个新 token 都要重算它之前所有 token 的 K/V(生成第 k+1 个时,重算之前的 1~k 个);有缓存时,Prefill 把已有 token 的 K/V 一次写入 HBM 的 KV Cache 区,Decode 每步只读缓存、并把新 token 的 K/V 追加写入。注意「缓存随生成变长」指有效内容逐步增多;物理显存按块管理——旧式方案按最大长度连续预留,PagedAttention 按需分页。它与 FlashAttention 正交:一个让历史「不重算」,一个让中间矩阵「不落地」。

注意区分两件正交的事:KV Cache 让历史 K/V「不重算」(省计算,代价是占用 HBM);FlashAttention(3.3)让注意力的中间矩阵「不落地」——读进 SRAM 的 KV 块在片上算完、结果只写回一次(省访存)。二者叠加,才构成 Decode 的完整数据流。

为什么只缓存 K、V,从不缓存 Q?

这来自注意力结构的根本不对称:

一句话:K/V 面向过去、可复用;Q 面向当下、一次性。所以 KV Cache 里永远只有 K 和 V。

KV Cache 显存,一个公式算清

$$\mathrm{KV}_{\text{bytes}}=2\times n_{\text{kv}}\times d_{\text{head}}\times L\times s\times b_{\text{size}}\times \underbrace{b}_{\text{每元素字节}}$$

② 放得下:PagedAttention,像操作系统管内存一样管 KV

缓存本身该怎么在显存里摆放?传统做法给每个请求按最大生成长度连续预留一整段显存。但请求实际生成长度未知,预留多了形成内部碎片、预留少了直接 OOM;再加上预留的显存无法在请求间灵活共享,浪费惊人(vLLM 论文测得浪费 60–80%)。

PagedAttention 借鉴 OS 虚拟内存分页:把 KV Cache 切成固定大小的 Block(页,如每块 16 个 token);逻辑上连续的 KV,物理上可以分散存放,由页表记录映射;按需分配、用完即回收。更进一步,相同前缀(系统提示、公共 few-shot、beam/采样分叉)的 KV 块可以在多个请求间共享(写时复制),几乎消灭碎片。

传统:连续预留 红色=内部碎片 浪费 60–80% 分页:逻辑连续 / 物理分散 共享前缀块 物理块池(按需分配)
图 14上:连续预留产生大量内部碎片;下:PagedAttention 把 KV 切成固定块、页表映射,物理块按需分散分配,相同前缀块(橙)跨请求共享。显存浪费从 60–80% 降到个位数。
vLLM
Efficient Memory Management for Large Language Model Serving with PagedAttention
Kwon et al. · UC Berkeley · SOSP 2023 · PagedAttention 与 vLLM,吞吐较 HF/传统方案数倍提升
arXiv:2309.06180

③ 装得少(结构侧):减头 GQA / MQA

先从模型结构入手,减少每个 token 要缓存的 K/V。

KV Cache 大小正比于 KV 头数,于是模型侧最直接的演进就是减少 KV 头、让多个 Q 头共享同一组 K/V:

MHA · 每 Q 独享 KV GQA · 多 Q 共享(主流) MQA · 全局共享 1 组 QQQQ QQQQ QQQQ KVKVKVKV KVKVKV KV Cache 最大(基准 1.0×) Llama3-70B:Q 64 / KV 8,缓存降至 1/8 KV 最小,质量略损
图 15MHA 中每个 Q 头有专属 K/V;GQA 将多个 Q 头分组共享 K/V(Llama2/3、Qwen 主流);MQA 所有 Q 头全局共用 1 组 K/V。KV Cache 只与 KV 头数有关。
MQA
Fast Transformer Decoding: One Write-Head is All You Need
Noam Shazeer · 2019 · 首次提出多查询注意力,所有 Q 共享一组 K/V
arXiv:1911.02150
GQA
GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints
Ainslie et al. · Google · EMNLP 2023 · 分组查询注意力,质量与 MHA 接近、KV 按组数缩减,可从 MHA 检查点低成本转换,现为 Llama/Qwen 等主流标配
arXiv:2305.13245
易错三连

① GQA 必须用 KV 头数而非 Q 头数;② 不要漏 K+V 的系数 2;③ KV Cache 显存 ≠ 模型权重显存,二者要分开估算再相加。

③ 装得少(维度侧):MLA 多头潜在注意力

GQA/MQA 减的是 KV 头的数量;DeepSeek 提出的 MLA(Multi-head Latent Attention)走另一条路——把 K、V 向量本身通过低秩投影压缩成更小的潜向量(latent),缓存里只存这个潜向量,Decode 时再投影还原。KV 头数量不变,但每个 token 缓存体积大幅下降。

原始高维 K/V 每头 128 维 GQA:直接存这一份 压缩 Down 潜向量 c_KV 低维(如 128 → 更小) ↑ KV Cache 实际只存这一份 还原 Up 投影还原 K/V Decode 时算注意力 精度近无损 不同模型实测(BF16): Qwen3-30B GQA ≈ 96KB/字 V2 MLA:较 MHA −93.3% 128K 上下文 KV ≈ 8.4GB
图 16MLA:Prefill 用可学习投影把高维 K/V 编码为低维潜向量,缓存只存潜向量;Decode 再投影还原。压缩/还原矩阵随模型一起训练,注意力精度近无损(DeepSeek-V2/V3)。
MLA
DeepSeek-V2: A Strong, Economical, and Efficient Mixture-of-Experts Language Model
DeepSeek-AI · 2024 · 首次提出 MLA 多头潜在注意力,配合 MLA 大幅压缩 KV Cache
arXiv:2405.04434

同属“结构侧装得少”的前沿方向还有稀疏注意力:DeepSeek 的 NSA 与月之暗面的 MoBA 让注意力只计算被选中的 KV 块、而非全部历史,把长上下文的注意力开销从 $O(n^2)$ 压到近线性,且设计为可训练、近无损——可以理解为“把 KV 驱逐(3.2)直接做进模型结构”的版本,二者均已进入新一代长上下文模型(趋势见 7.3,论文见参考文献)。工程上还有一个更温和的中间档:滑窗注意力(SWA)及其与全局注意力的层间混合——大部分层只看最近 W 个 token、每隔几层插一层全局注意力(Mistral 首开,Gemma 2/3 的 5:1 局部/全局交替即此路线;Qwen3-Next 等则把部分层换成线性注意力),KV 占用随窗口封顶、实现简单,是中长上下文模型的实用默认。

三个主流模型,代入公式实算

模型(BF16)结构:层数 / Q头·KV头 / d单 token KV典型上下文 KV 占用
Qwen3-30B-A3B48 层 / 32 · 4 / 128(GQA)$2{\times}48{\times}4{\times}128{\times}2$ = 96 KiB16K ≈ 1.5 GiB;128K ≈ 12 GiB
Llama3-70B80 层 / 64 · 8 / 128(GQA)$2{\times}80{\times}8{\times}128{\times}2$ = 320 KiB32K ≈ 10 GiB;128K ≈ 40 GiB
DeepSeek-V2(MLA)60 层 / kv_lora_rank 512(潜变量)只存 512 维潜向量 + RoPE 部分128K ≈ 8.4 GiB,较 MHA 压缩 93.3%
口径:KiB/GiB 按 1024 换算;KV 为单请求、单并发,线上还要乘以并发 batch。Qwen3-30B 为 MoE(总参 30.5B、单 token 激活 3.3B),但 KV Cache 只与注意力结构有关,与 MoE 无关。

③ 装得少(运行时):KV 逐出 H2O 与 SnapKV

结构改动需要重新训练模型;不训练模型,也能在运行时主动丢 KV——这就是逐出(eviction)。

KV Cache 随序列线性膨胀,长上下文下甚至超过权重。但注意力本身是高度稀疏的:实证表明,生成时 95% 以上的历史 token 几乎从未被高权重关注,真正反复被“看”的只有少数“关键 token”(Heavy Hitters)加上最近的一小段。这给了我们主动丢弃低价值 KV的空间(属有损压缩,需控制比例以保质量)。

注意力权重(生成某 token 时对历史的关注) 少数关键 token >95% 历史 token 注意力权重接近 0,驱逐它们对结果影响很小 H2O:边生成、边驱逐 Heavy Hitters 保留 Recent 窗口 每步按累计注意力淘汰低分 KV KV 显存恒定(bounded),适合长生成 训练免费、即插即用 SnapKV:Prefill 末一次压缩 Prefill 观察窗口投票 压缩 Decode 长 prompt 一次瘦身,近无损 适合 RAG / 长文档,可配合 prompt 缓存
图 17上:注意力稀疏、只有少数关键 token 被反复关注;左下 H2O 在生成中持续保留 Heavy Hitter + 最近窗口、KV 总量恒定;右下 SnapKV 用 prefill 末尾观察窗口投票、在生成前一次性压缩。

H2O:Heavy-Hitter Oracle · 边生成边驱逐。

H2O 观察到一个规律:历史贡献(累计注意力得分)高的 token,未来也更可能被需要。它在每步维护一个固定大小的 KV 池,保留“累计注意力最高的 Heavy Hitter”加“最近窗口”,其余直接驱逐,从而把 KV Cache 变成有上界的滚动窗口,训练免费、可直接嵌入任意引擎。

H2O
H2O: Heavy-Hitter Oracle for Efficient Generative Inference of Large Language Models
Zhang, Zhang, Mirzadeh et al. · NeurIPS 2023 · 边生成边驱逐,KV 显存大幅下降、质量可控
arXiv:2306.14048

SnapKV:生成前「一次看对眼」。

SnapKV 利用 prefill 末尾若干 query 位置(观察窗口)对各历史 KV 的注意力作为投票,选出对当前任务真正重要的 KV 位置,在 Decode 开始前一次性批量压缩。它特别契合长 prompt / RAG 场景——大量无关文档块可以在生成前就被丢掉,且因为依据的是真实任务相关的投票,压缩近无损。

SnapKV
SnapKV: LLM Knows What You Are Looking for Before Generation
Li et al. · NeurIPS 2024 · 观察窗口加权投票 + 生成前一次压缩
arXiv:2404.14469
对比H2OSnapKVKIVI(见 3.4)
压缩手段整段丢弃低价值 KV整段丢弃(投票选块)保留全部、降低每元素位宽
时机Decode 中持续驱逐Prefill 末、Decode 前一次全程量化
判据累计注意力 Heavy Hitter + 最近末尾观察窗口注意力投票key 按通道 / value 按 token
最适合超长生成、流式对话长 prompt / RAG通用、与其他方法叠加

Attention Sink:为什么开头几个 token 永远丢不得。驱逐类方法有一个共同的实证基础:softmax 要求注意力权重归一,模型会把大量“无处安放”的注意力倾倒给序列最开始的几个 token(Attention Sink)——它们被关注的强度远超其语义重要性,一旦被驱逐,输出立刻崩坏。StreamingLLM 利用这一点给出极简方案:只保留最前 4 个 sink token + 最近滑动窗口,KV 有界、无需微调,即可在远超训练长度的流式输入上稳定生成。它既是所有驱逐策略“特殊照顾开头”的物理直觉,也是无限流式对话(直播字幕、持续监控等场景)的经典解法。

StrLLM
Efficient Streaming Language Models with Attention Sinks
Xiao et al. · MIT · ICLR 2024 · Attention Sink 现象 + 滑动窗口,KV 有界的无限流式生成
arXiv:2309.17453
KV Cache 家族一览

少算(缓存复用)→ 放得下(PagedAttention)→ 装得少(GQA/MQA、MLA、H2O/SnapKV;位宽量化 KIVI 见 3.4)→ 能复用(RadixAttention 见 6.2)。现代引擎里这几件事通常同时开启,且与 3.3/3.4 正交可叠加。

FlashAttention:用分块把 N×N 矩阵“焊”在片上

先把标准注意力的完整计算写全(与 3.1 的公式一致):

$$\mathrm{Attention}(Q,K,V)=\mathrm{softmax}\!\left(\frac{QK^{\top}}{\sqrt{d_k}}\right)V$$

拆成三步看:$S=\dfrac{QK^{\top}}{\sqrt{d_k}}$(打分,别忘了除以 $\sqrt{d_k}$ 做缩放,防止分数随维度变大把 softmax 推进饱和区)、$P=\mathrm{softmax}(S)$(归一化成注意力权重)、$O=PV$(按权重加权求和 Value)。标准实现要把 $S$、$P$ 这些 $N\times N$ 中间矩阵反复写回 HBM 再读出,访存量是 $O(N^2)$。FlashAttention 的做法是算子融合 + 分块(Tiling)+ 在线 Softmax(Online Softmax):把 Q/K/V 分块搬进片上 SRAM,在片上连续完成 $QK^\top$、softmax、乘 V,用流式方式逐块更新 softmax 归一化因子,最终输出 $O$ 只写回 HBM 一次,HBM 访存降到 $O(N)$。

HBM · 慢、容量大 Q K V O(写一次) softmax 归一化因子 不落地 HBM,片上流式更新 分块读入 O 仅写回一次 SRAM · 快、容量小 Q 块 K 块 / V 块循环遍历 片上算 QKᵀ · softmax · PV online softmax 逐块累积 数学结果与标准注意力完全一致(exact / 无损),只改变计算顺序与访存路径;HBM 访存由 O(N²) 降为 O(N)
图 18FlashAttention 的 Tiling:Q/K/V 分块载入 SRAM,片上融合完成注意力与在线 softmax,避免 N×N 矩阵落地 HBM。它与 KV Cache 正交——一个优化 Prefill 的算子访存,一个优化 Decode 的重复计算。
FA1
FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness
Dao et al. · NeurIPS 2022 · IO 感知、分块 + 在线 softmax
arXiv:2205.14135
FA2
FlashAttention-2: Better Parallelism and Work Partitioning
Tri Dao · 2023 · 约 2× FA1,减少非 matmul 开销
arXiv:2307.08691
FA3
FlashAttention-3: Fast and Accurate Attention with Asynchrony and Low-precision
Shah, Bikshandi, Zhang, Thakkar, Ramani, Dao · NeurIPS 2024 · Hopper TMA 异步、Warp 特化、FP8
arXiv:2407.08608

同层的另一件武器:CUDA Graph 消除启动开销

FlashAttention 优化的是单个算子内部的访存;Decode 还有一层更“机械”的开销——每步前向要发起成百上千次 kernel launch,每次 launch 本身有数微秒的 CPU→GPU 开销,而 Decode 的每个 kernel 只算几十微秒,小 batch 下 launch 开销能占单步耗时的两三成(这正是 6.1 实测低于理论天花板的原因之一)。CUDA Graph 把 Decode 的整条计算图一次性“录制”下来,之后每步整体回放:CPU 只发一次启动指令,launch 开销几乎归零,vLLM、SGLang、TensorRT-LLM 均已默认对 Decode 开启。它与 FlashAttention、量化正交,是“不改算法、只改执行方式”的纯系统优化——代价是计算图形状(batch、序列长度档位)必须固定,引擎需要按档位预录多份。

量化:直接减少要搬运的字节

Decode 是 Memory-Bound,把权重/KV 从 BF16 降到 FP8/INT8/INT4,直接把搬运字节减半甚至减到 1/4,提速立竿见影;代价是引入微小精度损失(属有损优化,需校准或少量微调)。

技术作用对象方法要点典型精度
SmoothQuant权重 + 激活把激活的离群值平滑迁移到权重,实现 W8A8 训练后量化INT8,近无损
GPTQ仅权重利用二阶 Hessian 信息逐层一键量化,误差补偿W4 / W8
AWQ仅权重识别并保护约 1% 显著权重通道(缩放而非直接裁剪)W4,部署最广
KIVIKV CacheKey 按通道、Value 按 token 分块非对称量化,无需调参2bit 近无损
FP8权重 + 激活Hopper / Blackwell 原生支持,需校准或 QATFP8(E4M3)
AWQ
AWQ: Activation-aware Weight Quantization for On-Device LLM Compression
Lin et al. · MLSys 2024
arXiv:2306.00978
KIVI
KIVI: A Tuning-Free Asymmetric 2bit Quantization for KV Cache
Zhang et al. · ICML 2024
arXiv:2402.02750
A10 等 Ampere 卡没有 FP8 硬件,优先选择 INT8(SmoothQuant,A10 INT8 Tensor Core 可直接加速)或 W4(AWQ / GPTQ);FP8 仅在 Hopper / Blackwell 上可用。
量化的两条边界

① W4A16 只省搬运、不省计算:权重以 4bit 存储、计算前反量化回 FP16,只有配套专用 kernel(Marlin、ExLlamaV2 等)才能把“省带宽”兑现为真实加速,且 batch 调大、负载转入 Compute-Bound 后收益递减;② 只有激活也量化(W8A8 / FP8)才能吃到低精度 Tensor Core 的计算加速——A10 上的 SmoothQuant INT8 即属此类,而纯 W4 方案的价值主要在省显存与带宽。先用 Roofline(3.1)判瓶颈,再决定量化谁。

04

采样:从 logits 到下一个 ID

SAMPLING

Transformer 最后一层输出的隐藏向量,先经 lm_head 输出投影变成词表维度的一批浮点数(logits),采样环节要回答:这批浮点数对应哪个 token ID。本章先在 4.1 建立 logits→ID 的完整背景(采样旋钮),再讲两个优化方向:限制候选的约束解码(4.2),以及把「一次前向只出一个 ID」变成「一次前向出多个 ID」的投机解码家族(4.3)。

流水线位置导航 · POSITION输入Tokenizer文本转序号EmbeddingID 转向量Transformer主干计算 ×LSampling挑选序号Detokenizer序号转文本输出当前环节 · Sampling(logits 选 ID)

背景:logits 怎样变成一个 ID

确定性方法(贪心)直接取 logit 最大的 ID;随机方法先做 softmax 得到概率分布再按分布采样,并用一组旋钮调节随机性与重复度。

先补一笔容易被低估的账:lm_head 本身是词表维度的大矩阵乘(隐藏维 × 词表,如 4096 × 15 万)。Decode 阶段每个 token、每个请求都要过一次,大词表模型在 batch 较大时 lm_head 可占单步耗时的 10–30%。工程上的应对包括:词表维度做张量并行、lm_head 与采样融合为一个 kernel。这也回答了 02 章留下的悬念——同一张矩阵,Embedding 查表几乎免费,lm_head 全量投影却不便宜。

温度 T:改变分布尖锐度 T=0.5 集中T=1 原始T=2 平坦 Top-K / Top-P:截断候选 Top-K=3 Top-P≈0.9(前4个累计) 候选按概率排序,截断后重新归一化再采样
图 19左:温度对 softmax 的影响,$p_i\propto e^{z_i/T}$,T 越低越确定、越高越发散;右:Top-K 固定只在前 K 个候选采样,Top-P(nucleus)按累计概率动态截断——候选少而尖时少取、多而平时多取。
策略做法 / 效果典型场景
贪心 Greedy每步取 logit 最大的 token,确定性、可复现,但容易重复、缺乏多样性事实问答、评测
Beam Search同时保留 B 条累计概率最高的部分序列,最后选最优;质量高但开销大、不利于流式翻译/摘要(离线)
温度 Temperature采样前缩放 logits,控制创造性与随机性对话/创作
Top-K / Top-P限制采样候选范围,过滤长尾低质 token,二者常一起用通用聊天默认
Min-P按最高概率的比例动态截断(候选 = $p \ge \text{min\_p}\times p_{\max}$),分布尖时少取、平时多取,高温度下比 Top-P 更稳开源社区新默认(llama.cpp 等)
存在惩罚 Presencetoken 一旦出现就给其 logit 减一个固定值,鼓励引入新话题长文防单调
频率惩罚 Frequency按 token 已出现次数成比例减 logit,抑制重复与复读防重复(代码慎用)
惩罚项在 softmax 之前加到 logit 上(取值常为 −2~2,正值抑制、负值鼓励);代码生成场景一般关闭频率惩罚,以免合法的重复变量名被误伤。

约束解码:把非法 ID 提前屏蔽

做后端最常需要模型输出 JSON、代码、函数调用参数等有严格格式的结果。但自由采样可能在任意位置混入非法字符(缺括号、多余逗号、枚举值乱写),下游解析直接失败。约束解码(Constrained / Guided Decoding)在每一步采样前,根据目标文法(JSON Schema、正则、BNF)判断当前状态下哪些 token 合法,把非法 token 的 logit 屏蔽为 $-\infty$,只在合法集合里采样,从机制上保证输出 100% 可解析。

文法编译为状态机(FSM / PDA) START状态0 { key"..." :value } {合法key:合法value 下一步 logit:非法 token 屏蔽为 −∞ 合法 合法 非法 token:logit = −∞,概率 0
图 20约束解码:文法先编译成状态机(上),每步根据当前状态确定合法 token 集合;在 logit 层把非法位置屏蔽(下),采样只在合法集合中进行。输出从“概率上像”升级为“机制上必然合法”。
工具原理 / 能力典型集成
Outlines把 JSON Schema / 正则 / 文法编译成有限状态,提供 logit processor,支持 PDA自研服务、HuggingFace 生态
XGrammar高性能结构化生成引擎:文法即时编译、掩码缓存、jump-forward 跳过强制 token,开销极低SGLang、MLX、vLLM 系
GBNFllama.cpp 的类 BNF 文法格式,本地/端侧约束生成llama.cpp、Ollama
GCD文法约束解码,用 FST 在结构化任务上免微调约束输出(学术源头之一)研究 / 基线
OUTLINES
Efficient Guided Generation for Large Language Models
Willard & Louf · 2023 · Outlines 的状态机 / 正则引导生成
arXiv:2307.09702
XGrammar
XGrammar: Flexible and Efficient Structured Generation Engine for LLMs
Dong et al · 2024 · 高效文法编译与掩码执行
arXiv:2411.15100
GCD
Grammar-Constrained Decoding for Structured NLP Tasks without Finetuning
Geng et al. · EMNLP 2023 · 文法约束解码早期系统工作
arXiv:2305.13971
两个工程提醒

① 当合法 token 恰好是模型很不想要的 token 时,约束会“硬逼”模型走低概率路径,可能导致重复或质量下降——应让提示词与目标格式对齐、并设置合理停止条件;② 约束解码与投机解码、连续批处理不冲突,但需要在验证阶段同样施加约束掩码。

投机解码:一次前向,确认多个 Token

前面的优化都在“喂饱”串行解码;投机解码家族更激进——先让一个便宜的“猜测者”一口气写出多个候选 ID,再让大模型用一次并行前向统一验证,对的直接采纳、错的丢弃重算。如果猜测足够准,就相当于用一次前向的时间换出了多个 token,且输出分布与大模型完全一致(无损)。

两阶段:Draft 猜测 → Target 验证

猜测者 Draft 小模型 / 头 / 自身 快速、连续写 k 个 The ✓ cat ✓ sat ✓ on ✗ k 个候选(单链或树) 大模型 Target 一次并行前向验证全部 逐位置接受 / 拒绝 采纳前缀 拒绝处重采样后重来 关键:验证是并行的(一次前向处理 k 个候选),成本≈一次普通 decode,却可能一次确认多个 token
图 21Draft→Verify:猜测者快速连续写出 k 个候选,大模型用一次并行前向逐位置校验;接受最长正确前缀,在第一个错误位置按目标分布重采样,然后进入下一轮。

为什么能“无损”:拒绝采样的数学保证

设目标模型分布为 $p(x)$、猜测者分布为 $q(x)$。对候选 token $x$,以概率

$$P(\text{接受}\,x)=\min\!\left(1,\frac{p(x)}{q(x)}\right)$$

决定接受;若被拒绝,则从修正分布 $r(x)\propto\max\big(0,\,p(x)-q(x)\big)$ 中重采样。可以证明:无论 $q$ 准不准,最终被接受的 token 分布严格等于目标分布 $p$。直觉上:猜测者认为概率不低于目标的 token($q\ge p$)必然被接受;猜测者高估的 token 会以相应概率被拒、并“修正”回目标分布。猜测越准,接受长度越长、加速越多;猜得再差也只是退回普通解码,不会改变结果。

SPEC-DEC
Fast Inference from Transformers via Speculative Decoding
Leviathan, Kalman, Matias · Google/DeepMind · ICML 2023 · 投机解码开山论文,小模型起草 + 大模型验证,证明无损并给出复杂度分析
arXiv:2211.17192
SPEC-SAMP
Accelerating Large Language Model Decoding with Speculative Sampling
Chen et al. · DeepMind · 2023 · 独立提出投机采样(拒绝采样),在对话模型上约 2× 加速
arXiv:2302.01318

接受率的实战经验:温度越高,草稿与目标分布差异越大、接受率越低,高温创作场景收益会缩水;代码、数学、格式化输出(JSON/SQL)等“高可预测”任务接受长度最长——IDE 补全最先吃到投机红利正因此。评估时按自家任务的温度与内容分布实测接受率,别直接搬论文数字。

自投机:用模型自己的“浅层”当草稿

不想维护一个独立小模型,可以让目标模型自身在中间层提前输出(Early Exit / 早退):用浅层快速起草,再走完整网络验证。只需少量微调让浅层具备预测能力,权重完全共享、部署简单,属于“自投机(Self-Speculative)”。

D&V
Draft & Verify: Lossless Large Language Model Acceleration via Self-Speculative Decoding
Zhang et al. · 2023 · 自投机解码代表作,浅层起草 / 深层验证,无损
arXiv:2309.08168

Medusa:给大模型加几个“候选头”

Medusa 在冻结的目标模型上新增若干轻量解码头(Medusa Heads):头 1 预测下一个位置、头 2 预测下两个位置……各头给出 top 候选,组合成一棵候选树;再用 Tree Attention(为每条候选路径构造对应因果掩码)在一次前向里验证整棵树。它不需要逐步自回归起草,配合典型采样(typical sampling)与前缀树,典型加速 2.2–3.6×。

Target 主干 (冻结,权重共享) Medusa 头 1(+1 位)头 2(+2 位)头 3(+3 位)头 4(+4 位) 候选树(top 候选组合) Tree Attention:一次前向、按路径掩码验证整棵树
图 22Medusa:冻结主干 + 多个新增候选头,对未来 k 个位置各提 top 候选并组合成树;Tree Attention 用自定义因果掩码在一次前向中验证所有路径,命中即一次输出多个 token。
MEDUSA
Medusa: Simple LLM Inference Acceleration Framework with Multiple Decoding Heads
Cai et al. · CMU · ICML 2024 · 多头 + 候选树 + Tree Attention,工程上易插拔
arXiv:2401.10774

EAGLE:在“特征层”而不是 token 层起草

Medusa 直接猜 token,EAGLE 更进一步:把目标模型中层的隐藏特征与已采样 token 一起喂给一个轻量自回归头,在“特征序列”上做预测,天然贴合目标模型的内部表示,接受率显著更高。三代演进:

EAGLE
EAGLE: Speculative Sampling Requires Rethinking Feature Uncertainty
Li et al. · Microsoft · ICML 2024
arXiv:2401.15077
EAGLE-2
EAGLE-2: Faster Inference with Dynamic Draft Trees
Li et al. · EMNLP 2024
arXiv:2406.16858
EAGLE-3
EAGLE-3: Scaling up Inference Acceleration via Training-Time Test
Li et al. · 2025 · Training-Time Test,支持推理模型,开源加速比领先
arXiv:2503.01840

MTP:把“多 token 预测”做进模型本体

DeepSeek-V3 的 MTP(Multi-Token Prediction,多 token 预测)把投机能力直接做进模型:主模型之外串联若干 MTP 模块,每个模块接收上一位置的隐藏状态 + token embedding,预测下一个 token;训练时每个模块都作为额外的多任务预测损失(还能提升主模型质量),推理时这些模块自然形成一条候选链,再由主模型一次并行验证。DeepSeek-R1 推理即用 MTP 加速。

主模型输出 h、token MTP 模块 1预测 +1 token MTP 模块 2预测 +2 token h+embh+emb 候选链一次并行验证 接受前缀,无损 训练:MTP 作为多任务损失,同时提升主模型;推理:链式起草
图 23MTP:主模型与 MTP 模块串联,沿“隐藏状态 + token”链式预测多个未来 token;训练即多任务学习、推理即候选链。相比外挂草稿,它与模型一体化、无额外模型维护成本(DeepSeek-V3/R1)。
DSv3
DeepSeek-V3 Technical Report
DeepSeek-AI · 2024 · MTP 多 token 预测模块的设计、训练损失与推理用法(另含 61 层 MLA、FP8 训练、MoE 细节)
arXiv:2412.19437

Lookahead Decoding:零训练、零小模型的 Jacobi 并行

Lookahead Decoding 完全不引入额外模型、也不做任何微调,而是用 Jacobi 迭代把“串行解码”变成对一个 2D 窗口的并行猜测:每条“猜测分支”在窗口内同时尝试多个未来位置(等价于并行 Jacobi 解码),每条“N-gram 分支”从历史维护的 n-gram 池里取候选序列验证、命中后回填池子。一次前向可能确认多个 token,未命中也无损退回。

2D Jacobi 窗口(并行猜测) Thecatsat N-gram 池 The cat sat ... a dog loves ... chasing after ... 确认的 token 直接输出 窗口向前滑动,继续猜测 Guess 分支 Jacobi 并行;无需训练 / 小模型
图 24Lookahead:2D 窗口内 Guess 分支做 Jacobi 并行解码、N-gram 分支验证历史 n-gram 并回填;绿色为一次确认的 token。开箱即用、完全无损,适合不能改模型的部署场景。
LOOKAHEAD
Break the Sequential Dependency of LLM Inference Using Lookahead Decoding
Fu et al. · Together AI · ICML 2024 · Jacobi 迭代 + n-gram 池,零训练并行解码
arXiv:2402.02057

同属零训练路线的还有 REST(Retrieval-Based Speculative Decoding):草稿不从模型自身产生,而是从外部语料库检索与当前上下文匹配的 n-gram 片段作为候选,特别适合代码补全、RAG 等输出大量复用已有文本的场景。

同族还有一个更轻的分支 Prompt Lookup Decoding:草稿不去外部语料库检索,而是直接在当前请求的 prompt 里匹配 n-gram——代码编辑(输出大量重复上下文)、RAG 摘要(答案抄自检索文档)场景命中率极高,实现只需几十行、几乎零额外开销,常作为投机家族里“先试这个”的起步项。

家族全对比与选型

方法猜测者来源需要训练候选形态典型加速输出质量
经典 Spec独立小模型否(用现成小模型)单链2–3×无损
自投机自身浅层早退少量微调单链1.5–2×无损
Medusa新增多个候选头训练新增头候选树2.2–3.6×无损
EAGLE特征起草头训练头(2 动态树 / 3 TTT)动态树3–6×无损
MTP模型原生 MTP 模块随主模型一起训练链~1.8–2×无损
LookaheadJacobi 自身并行完全不需要2D 窗口 + n-gram1.5–2×无损
REST语料库检索 n-gram完全不需要检索候选链1.6–2.4×无损
加速比为各论文/开源报告的典型量级,强依赖于任务(代码/数学等高可预测任务更高)、草稿与目标的匹配度、batch 与硬件;请以自家业务实测为准。
什么时候不该开投机解码

投机的本质是用空闲算力换带宽:小 batch、带宽受限时,一次验证 k 个候选几乎不比验证 1 个贵,加速立竿见影;但 batch 一大,系统转向 Compute-Bound,验证整棵候选树的额外算力开始挤占吞吐,高并发场景开投机反而可能拉低 Goodput。线上常见策略是按负载动态开关:低峰开投机追时延,高峰关掉保吞吐(vLLM / SGLang 均支持按并发阈值自动禁用);batch 偏大时也应缩短草稿长度、用小候选树。

能否修改 / 训练目标模型? 有匹配的小模型? 经典 Spec Decoding Lookahead(零训练) 训练预算 / 可加结构? Medusa / EAGLE MTP(原生) 自投机(早退) 全部无损;结构化输出再叠加约束解码;推理(reasoning)模型优先 EAGLE-3 / 原生 MTP 高并发服务端:投机与 PD 分离、Continuous Batching 可叠加使用
图 25选型决策:先看“能不能改模型”,再看训练预算与可加结构;不能改就走 Lookahead / 经典投机,能改则按改动量选自投机、Medusa/EAGLE 或原生 MTP。
05

Detokenizer:把 ID 拼回文字,送到用户眼前

DETOKENIZE & STREAM

采样产出的 ID 是机器内部表示,用户要看到的是文字。Detokenizer 是 Tokenizer 的逆过程:按 ID 查表得到 token 字符串,再按 BPE 的合并规则把它们拼接还原。这一站计算量极小(仍是 CPU 侧的字符串操作),但有几个直接影响体验的工程细节。

流水线位置导航 · POSITION输入Tokenizer文本转序号EmbeddingID 转向量Transformer主干计算 ×LSampling挑选序号Detokenizer序号转文本输出当前环节 · Detokenizer(ID 拼回文本)
采样产出的 ID 81230516191842 逆查表 ID → token 字符串 unbelievableĠidea BPE 拼接还原 Ġ → 空格 Ċ → 换行 字节回退处理多语言 unbelievable idea 作为 chunk 流式推给前端 · 遇到停止符(如 <|im_end|>)立即结束、停止符本身不输出 · 投机解码一次确认多个 ID 时,批量 detokenize 后再统一推送
图 26Detokenizer:ID 逆查表得到子词字符串,按 BPE 规则拼接(Ġ 还原为空格、Ċ 还原为换行),还原出的文本以 chunk 流式推送;停止符不输出,多语言经字节回退正确还原。

三个工程细节

还有两类“输出形态”的细节:一是工具调用 / Function Calling——其参数本质是一段受约束的 JSON(4.2),流式解析要按增量 JSON 的方式边收边校验,而不是攒到完整再 parse,否则工具执行要等到输出结束才能启动;二是内容安全审核通常挂在这一环节——流式场景采用“缓冲若干 token 送审、命中风险立即截断”的策略,审核窗口会额外增加首 chunk 延迟,需要在体验与合规之间取平衡。

多模态输出

多模态模型的输出可以是文本、图像、音频或工具调用:文本走上述 Detokenizer;图像/音频由对应的扩散/声码器等生成模块产出,再与文本片段按时间线/布局混合呈现。无论输出形态如何,“模型产出内部表示 → 解码为用户可感知内容 → 流式交付”的结构是一致的。

06

并发请求:批处理、前缀复用与 PD 分离

SERVING & SCHEDULING

前面五章跟踪了一个请求从输入到输出的完整旅程;服务端真正面对的是成千上万个请求同时处在流水线的不同环节:有的在 Prefill、有的在 Decode、有的刚结束。这一层横切所有环节,核心命题是调度粒度、显存组织与架构解耦,目标是把 GPU 吞吐打满。本章先看静态做法的痛点与天花板(6.1),再讲机内调度手段(6.2),最后是架构级的 PD 分离(6.3)。

流水线位置导航 · POSITION输入Tokenizer文本转序号EmbeddingID 转向量Transformer主干计算 ×LSampling挑选序号Detokenizer序号转文本输出并发请求 · 覆盖流水线每一个环节Continuous Batching · PagedAttention · Chunked Prefill · RadixAttention · PD 分离
06 并发请求 · 多请求同时处在流水线的不同环节 多请求持续到达 全局调度器(每 iteration 重排) GPU 资源池 6.1 背景 痛点·天花板·指标 Static Batching 三痛点 木桶效应 · 无法动态加入 按最大长度预留→碎片 带宽反推 TPS 天花板 7B/A10 ≈ 43 TPS(理论) 实测 ~30,差距即优化空间 服务指标体系 TTFT · ITL · TPS Goodput(SLO 达标吞吐) 6.2 机内优化 调度器统一驱动 Continuous Batching iteration 级进出 GPU 持续满载(ORCA) Chunked Prefill 长 prompt 切块 与 Decode 交错护 ITL RadixAttention 基数树 + 最长前缀匹配 公共前缀只算一次 6.3 架构解耦 PD 分离 Prefill 池 → KV 高速迁移(NVLink / RDMA / NIXL)→ Decode 池 两池硬件、卡数、并行策略独立扩缩容;P/D 吞吐差约 140 倍,混部双输 DistServe · Mooncake · Splitwise · NVIDIA Dynamo 脉络:机内手段把单 GPU 打满;P/D 矛盾无法调和时走向架构解耦——目标:Goodput 最大化、每 token 成本最小化
图 2706 章结构地图:6.1 背景(Static Batching 痛点、TPS 天花板、指标体系);6.2 机内优化(Continuous Batching / Chunked Prefill / RadixAttention);6.3 架构解耦(PD 分离)。

背景:并发处理时 GPU 算力的浪费

最简单的批处理是凑齐一批请求、同时开始、等整批都生成完再返回、再放下一批。它有三个致命问题:

Static Batching Continuous iteration 级调度 最慢者决定整批 空槽无法复用 不同颜色 = 不同请求;谁完成就立刻在该槽位放入新请求 每个 iteration 重新组 batch,GPU 几乎不空闲 吞吐显著高于静态批处理(业界主流默认)
图 28Static Batching(上)整批同进同出,短请求空等最慢者、槽位浪费;Continuous Batching(下)把调度粒度从“整批”细化到每个 iteration(一次前向),请求完成后立即在空槽插入新请求,GPU 持续满载。

浪费之外,再算清单卡的理论天花板,才知道优化空间有多大。

STEP 1 · 权重
7B × 2B ≈ 14 GiB
FP16/BF16 常驻显存,A10 24GB 可容纳
→
STEP 2 · 带宽
600 GB/s
A10 GDDR6 峰值(H100 为 3.35TB/s)
→
STEP 3 · 读一遍
14 / 600 ≈ 23 ms
单 token 最短访存时间
→
STEP 4 · 理论上限
1 / 23ms ≈ 43 TPS
batch=1、纯读权重
理论 vs 现实

A10 上 7B 单请求实测约 30 TPS 量级(随上下文变长继续下降)——因为还要读取随序列增长的 KV Cache、kernel launch / 调度开销、带宽利用率不可能 100%;同口径 H100 理论天花板约 238 TPS。理论值的意义是给出“天花板”:当实测离天花板很远时,说明还有调度或访存可挖;batch 拼大后吞吐可逼近“带宽×算术强度”的整体上限。

MoE 口径:上面按“每步读全部权重”估算;MoE 模型每步只读激活专家的权重,如 Qwen3-30B-A3B 单 token 约读 3.3B × 2B ≈ 6.6 GiB,A10 上的理论天花板相应抬到 ~90 TPS 量级——代价是 30.5B 总参数仍需完整装进显存(单机放不下就要多卡并行分摊,见 6.3)。

谈优化前还要先定义「好」:下面这套指标贯穿本章与第 3 章。

指标含义主要相关阶段/手段
TTFTTime To First Token,发出请求到看到第一个字的时间,最影响“快不快”的第一印象Prefill;FlashAttn / PD 分离
ITLInter-Token Latency,相邻 token 间隔,决定流式输出是否“卡顿”Decode;Chunked Prefill / 减 KV
TPOTTime Per Output Token,每输出 token 平均耗时(含首字口径差异)Decode
TPS / 吞吐系统每秒生成 token 数(聚合所有请求),决定单卡能扛多少用户Batching / 分页 / PD 分离
TBTTime Between Tokens,更严格的逐 token 间隔 SLO(如 P99 < 某阈值)调度 / Chunked Prefill
TTLTTime To Last Token,完整响应时延(非流式场景)全链路
Goodput在 SLO(TTFT/ITL)达标前提下的有效吞吐,比裸吞吐更有意义PD 分离的核心优化目标
现象 / 问题看哪个指标对应手段
第一个字等太久TTFT ↑FlashAttention、Prefill 并行、PD 分离、前缀缓存
打字机一卡一卡ITL P99 尖刺Chunked Prefill、Continuous Batching、压缩 KV
单卡扛不住并发TPS / Goodput ↓Continuous Batching、PagedAttention、PD 分离
长上下文 OOM显存占用MLA、KV 量化、KV 驱逐、分页
JSON / 代码格式非法格式合规率约束解码(Outlines / XGrammar / GBNF)

机内优化:动态拼批、切块调度与前缀复用

6.1 的痛点对应三类机内手段:调度粒度细化到 iteration(Continuous Batching)、长 Prefill 切块交错(Chunked Prefill)、公共前缀缓存复用(RadixAttention);显存摆放问题(PagedAttention)已在 3.2 讲过。

Continuous Batching(ORCA 首创)把调度单位从「整批」细化到每个 iteration(一次前向):完成的请求立刻离开、空槽插入新请求,GPU 持续满载(时序对比见上图)。

ORCA
Orca: A Distributed Serving System for Transformer-Based Generative Models
Yu et al. · OSDI 2022 · 首次提出 iteration-level 调度与 selective batching,是连续批处理的源头
USENIX OSDI'22

Continuous Batching 还有一个高负载下绕不开的问题:KV Cache 不够装时怎么办。当并发请求的 KV 总量逼近显存上限,调度器必须抢占(Preemption)一部分请求腾地方——vLLM 提供两种策略:重计算(Recompute),丢弃被抢占请求的全部 KV、轮到时重跑 Prefill,实现简单但时延开销大;换出(Swap),把 KV 临时搬到 CPU 内存、轮到时再搬回,省算力但吃 PCIe 带宽。工程上通常配合优先级队列与最大并发上限,把抢占压成偶发事件——频繁抢占本身就是“该扩容或上 PD 分离(6.3)”的信号。

Prefill 是 Compute-Bound,一个几万 token 的长请求会连续占用 GPU 多个 iteration,期间正在 Decode 的请求得不到调度,ITL 出现尖刺、用户明显卡顿。Chunked Prefill 把长 Prefill 切成固定小块(chunk),与 Decode 请求在 iteration 级别交错执行,必要时还把多个 prefill 块拼进同一 batch,兼顾首 token 时延与吞吐。chunk 大小是关键参数:切得太小,Prefill 效率下降、调度开销上升;切得太大,ITL 尖刺依旧。vLLM 默认 chunk 约 2048 token(长 prompt 场景可调大到 4096–8192),正确姿势是固定业务流量、按 TTFT 与 ITL P99 实测调参。

无 Chunked:长 Prefill 独占 Prefill(连续数百 iteration) Decode 请求全程排队 → ITL 尖刺、卡顿 Chunked:切块与 Decode 交错 P=Prefill块 D=Decode ITL 保持平滑
图 29Chunked Prefill:长 Prefill 被切成 chunk(橙)与 Decode(青)在 iteration 级交错,避免长请求阻塞解码;现代推理引擎(vLLM / SGLang / TensorRT-LLM)均已默认开启。
SARATHI
Sarathi: Efficient LLM Inference by Pipelining Token Computation (Chunked Prefill)
Agrawal et al. · 2023 · 首次提出 chunked prefill + decode 流水
arXiv:2308.16369
SARATHI-SERVE
Sarathi-Serve: Chase Away Prefix Queue Under Chunked Prefills
Agrawal et al. · OSDI 2024 · Stall-free 调度,延迟与吞吐协同
arXiv:2403.02310

多轮对话、Agent、共享系统提示、few-shot 场景里,大量请求共享相同前缀。SGLang 提出 RadixAttention:用一棵 基数树(Radix Tree)维护所有处理过的 prompt,新请求自动做最长前缀匹配,命中的 KV 直接复用、未命中部分才算;缓存紧张时按 LRU 驱逐最少使用的叶子。它与 PagedAttention 是搭档——分页管“块怎么放”,基数树管“哪些前缀能复用”。

ROOTThecapitalof Britain → LondonFrance → ParisChina → Beijing 青色节点:公共前缀 一次计算、多请求复用 叶子可 LRU 驱逐
图 30RadixAttention 基数树:“The capital of”作为公共前缀只算一次并被三个请求共享;分叉部分各自计算。多轮对话 / Agent / 公共 system prompt 场景命中率极高。
SGLANG
SGLang: Efficient Execution of Structured Language Model Programs
Zheng et al. · NeurIPS 2024/2025 · RadixAttention 自动前缀复用 + 结构化生成运行时
arXiv:2312.07104

一个工程提醒:RadixAttention 的收益完全取决于前缀命中率——多轮对话、共享 system prompt、Agent 场景命中率常超过 50%;但如果业务里每条 prompt 都互不相同(如独立文档的离线批处理),前缀缓存只会白占显存,此时应调低缓存容量甚至关闭。上线前用真实流量统计命中率(vLLM / SGLang 均暴露 prefix cache hit rate 指标),比盲目开启更重要。

架构级优化:PD 分离

当 Prefill 与 Decode 的资源矛盾在机内无法靠调度调和时,必然走向架构解耦。

为什么必须分离:两阶段资源诉求相差约 140 倍。

同一个模型、同一块卡,Prefill 与 Decode 的吞吐差距是数量级的。以 20 层模型在消费级卡上的实测为例(视频作者数据):Prefill 约 14,000 token/s,Decode 约 100 token/s,相差约 140 倍。更麻烦的是二者对硬件的偏好相反:

把两阶段混部(Co-located)在同一批 GPU 上,会互相干扰:长 Prefill 拖慢 Decode 的 ITL,Decode 又占着算力让 Prefill 排队,资源配比无法同时满足两边,整体利用率与 SLO 双输。

吞吐差距(20 层模型实测) Prefill ~14,000 tok/s Decode ~100 tok/s ≈ 140× 集群利用率(示意) 混部 Co-located ~40%(互相干扰) PD 分离 Disaggregated ~85% 两类资源独立扩缩容、各跑最适合的阶段 Mooncake:吞吐 +75%、TTFT −25%
图 31左:Prefill/Decode 吞吐差约两个数量级;右:混部互相干扰、利用率低,PD 分离后两类资源独立配比、利用率与 SLO 同时改善(数值为论文/实测量级,具体随模型与硬件变化)。

怎么做:两池拆分,KV 迁移。

核心是把两阶段拆到不同 GPU 池、各自独立扩缩容:请求先进 Prefill 池完成提示计算与首 token,生成的 KV Cache 通过高速互联整体搬运/复制到 Decode 池,再由 Decode 池完成后续自回归生成并流式返回。难点不在“拆”,而在KV Cache 的高效迁移与全局调度。

全局调度器 / 路由(SLO 感知) Prefill GPU 池 高 FLOPs 配置,算力优先 KV 迁移层 RDMA / NVLink GPUDirect / NIXL 页对齐、零拷贝传输 Decode GPU 池 大显存 / 高带宽配置 推送 KV 流式输出 token
图 32PD 分离架构:全局调度器把请求导向 Prefill 池,KV 经高速迁移层送到 Decode 池独立生成。两池硬件型号、卡数、并行策略可完全不同,按各自瓶颈独立扩缩容。

两池内部的并行策略也可以各自选。推理常用的并行方式有三种:张量并行(TP)把每层权重切到多卡、每层两次 AllReduce——Decode 带宽受限时,TP 让单卡搬运量除以 N(图 32),是 Decode 池最常用的手段,代价是通信随卡数上升(A10 无 NVLink、走 PCIe,一般不超过 2–4 卡);流水并行(PP)按层切分、卡间只传激活,通信少但调度复杂,推理用得少;专家并行(EP)是 MoE 专属,专家分到不同卡、token 经 All-to-All 路由。Prefill 池偏算力、常配较大 TP;Decode 池偏带宽与显存,TP / EP 按 KV 容量与带宽目标定——这就是图 31 里“两池并行策略可完全不同”的具体含义。

一层权重矩阵 W(按列切成 4 块) GPU 0 · 列块 1/4GPU 1 · 列块 2/4 GPU 2 · 列块 3/4GPU 3 · 列块 4/4 各算部分和各算部分和 各算部分和各算部分和 AllReduce 各部分和跨卡相加 完整输出 每层 2 次 AllReduce(注意力投影后、MLP 后);Decode 带宽受限时单卡权重搬运量 ÷ N,代价是通信开销随卡数上升
图 33张量并行(TP):把一层权重按列切到 4 张卡,各卡只存、只读自己的分块并算部分和,再经 AllReduce 合并出完整输出。Decode 池常用 TP 压低单卡搬运量;无 NVLink 的卡(如 A10)通信走 PCIe,TP 规模不宜大。

三篇奠基论文,三种侧重。

DistServe
DistServe: Disaggregating Prefill and Decoding for Goodput-optimized LLM Serving
Zhong et al. · UC Berkeley / UChicago · OSDI 2024 · 用 Goodput(SLO 达标吞吐)论证分离的必要性,两阶段独立部署、独立并行策略,Goodput 提升数量级
arXiv:2401.09670
Mooncake
Mooncake: A KVCache-centric Disaggregated Architecture for LLM Serving
Qin et al. · Moonshot AI / 清华大学 · FAST 2025 最佳论文(期刊扩展版 ACM TOS 2025)· 以 KV Cache 为中心,Prefill/Decode 分离 + 跨 GPU/CPU/SSD 的全局 KV Cache Pool 与 KV Agent 调度;集群吞吐 +75%、TTFT −25%(Kimi 生产验证,已开源)
arXiv:2407.00079
Splitwise
Splitwise: Efficient Generative LLM Inference Using Phase Splitting
Patel, Choukse et al. · Microsoft · ISCA 2024 · 把 Prefill/Decode 拆到最具性价比的异构硬件上(强算力卡跑 Prefill、大显存/低成本卡跑 Decode),以最小化每 token 成本,KV 经 RDMA 迁移
arXiv:2311.18677
方案核心主张KV Cache 处理最适合回答的问题
DistServe按 Goodput 重配资源,两阶段独立扩缩Prefill→Decode 高速直传“怎样让 SLO 达标吞吐最大?”
MooncakeKVCache-centric,全局多级缓存 + 全局调度跨 GPU/CPU/SSD 的 KV Pool,可复用、可落盘“超大规模多租户、多轮对话怎么省?”
Splitwise异构硬件按阶段分工,成本最优跨机 RDMA 迁移“怎样把每 token 成本打到最低?”

关键工程问题:KV Cache 怎么搬。

通道典型带宽适用范围特点
NVLink / NVSwitch~900 GB/s(H100)同一节点内 GPU 之间最快,P/D 同机时首选
RDMA(RoCE / InfiniBand)400/800 Gb/s(约 50–100 GB/s)跨节点、跨 P/D 池绕过 CPU、零拷贝,生产主流
GPUDirect (Storage/Memory)随 PCIe/NICGPU 直访对端显存 / 存储避免经主机内存中转
PCIe / TCP 回退较低无高速互联时兜底实现简单、延迟高

工程上还会做:KV 块页对齐、压缩后再传(FP8)、传输与计算重叠、只传增量,把迁移成本摊到流水线里,使它不成为新瓶颈。另一个配套手段是 KV 分层 offload:热 KV 留在 GPU 显存,温 KV 下沉 CPU 内存,冷 KV(如多轮对话的历史轮次)落 SSD——Mooncake 的全局 KV Pool 正是这一思路的集群化,让显存只装“正在用”的部分。

GPU HBM · 热 KV 正在 Decode 的请求 · TB/s 级带宽 · 最贵 CPU 内存 · 温 KV 被抢占 / 等待调度的请求 · 百 GB/s 级 SSD · 冷 KV 多轮历史、跨会话复用 · GB/s 级 · 最便宜 换出 / 换回落盘 / 加载 命中即免重算 Prefill Mooncake 把这套分层 做成集群级 KV Pool
图 34KV 分层 offload:热 KV 留 GPU 显存、温 KV 下沉 CPU 内存、冷 KV 落 SSD;请求重新激活时命中即免于重跑 Prefill。层级越往下带宽越低、成本越省——Mooncake 的 KV Pool 正是这一思路的集群化。

前沿:PD 分离正在「产品化、标配化」。

NVIDIA Dynamo(2025 GTC 开源)

NVIDIA 推出的开源分布式推理编排框架,原生支持 Prefill/Decode 分离、GPU 异构与 SLO 感知路由,并配套 NIXL(NVIDIA Inference Xfer Library)做跨机 KV Cache 的异步零拷贝传输,与 TensorRT-LLM / vLLM 等引擎解耦。这标志着 PD 分离从“论文最佳实践”变成开箱即用的标准架构;vLLM、SGLang、Mooncake 也都已内置 disaggregated 模式。

两章手段如何叠加

机内优化(6.2)把单块 GPU 打满,架构解耦(6.3)让两类资源独立扩缩;它们与第 3 章的 KV Cache 家族、FlashAttention、量化全部可叠加——最终目标都是 SLO 达标前提下最大化 Goodput、最小化每 token 成本。

07

总结、落地清单与前沿趋势

SUMMARY & OUTLOOK

一句话串起全文

沿着一次请求走一遍:文字或图片经 Tokenizer(及视觉编码器)变成 ID 序列,Embedding 查表层把 ID 变成向量,Transformer 层在 Prefill/Decode 两节拍里完成推理计算,lm_head 把隐藏向量变回 logits,采样选出下一个 ID,Detokenizer 把 ID 拼回文字流式输出,再把选中的 ID 喂回入口循环往复。每个环节的优化都有明确指向:输入侧异步分词、识别公共前缀;计算侧KV Cache、GQA/MLA、FlashAttention、量化与 KV 驱逐;调度侧Continuous Batching、PagedAttention、Chunked Prefill、RadixAttention 与 PD 分离;采样侧温度/Top-P 旋钮、约束解码与投机解码家族。它们大多无损、可叠加,最终目标是在 SLO 达标前提下最大化 Goodput、最小化每 token 成本。

$$\text{每 token 成本}\ \approx\ \frac{\text{GPU 时价}\times\text{卡数}}{\text{SLO 达标下的 Goodput(token/s)}\times 3600}$$

这条公式是全文所有优化的最终折算口径:Goodput 越高,每 token 成本越低——PD 分离的资源重配、Splitwise 的异构分工、KV 压缩换来的更高并发,最终都在这条公式上见效。

工程落地 Checklist(按流水线环节)

输入与 Embedding

  • 分词放 CPU 异步执行、与 GPU 计算重叠
  • 多模态控制图像 token 数量,评估 KV 开销
  • 实测业务语料的每千字 token 数(词表差异可达 1.5–2×)
  • 对话模板逐字节比对官方版本,避免静默降质
  • 特殊 token 入词表
  • Embedding 查表无需专门优化,RoPE 位置编码确认开启
  • 超长上下文场景确认 YaRN / NTK 外推配置

推理加速

  • 优先选 GQA / MLA 架构,从源头减小 KV
  • 开启 FlashAttention(Prefill)与 KV Cache(Decode)
  • 权重 INT8 / AWQ(A10 无 FP8)、KV 上 KIVI(INT8)
  • Decode 开启 CUDA Graph,消除 kernel launch 开销
  • MoE 模型:显存按总参数规划、带宽按激活参数估算
  • 长上下文叠加 H2O / SnapKV 驱逐

并发与调度

  • Continuous Batching 取代静态批处理
  • PagedAttention 分页,设合理 block(如 16)
  • Chunked Prefill 平衡 TTFT/ITL;RadixAttention 缓存前缀
  • 高负载上 PD 分离,KV 走 RDMA/NVLink/NIXL

采样与输出

  • 按决策图选投机方案(EAGLE/MTP/Lookahead)
  • 按负载动态开关投机解码:低峰追时延、高峰保吞吐
  • 结构化输出上约束解码(Outlines/XGrammar/GBNF)
  • 特殊 token 过滤,避免在多字节字符中间切流
  • 监控 TTFT / ITL P99 / TPS / Goodput 与 KV 命中率
压测口径:TTFT / ITL / TPS 建议用 vllm bench serve、NVIDIA GenAI-Perf 等基准工具、在接近真实的流量分布(prompt 长度、输出长度、并发曲线)下测量;平均值会掩盖长尾,SLO 相关指标一律看 P95 / P99。Goodput 的测法:固定 SLO 阈值、逐步加并发,达标率跌破线时的吞吐即 Goodput。

推理引擎怎么选

全文提到的引擎能力正在收敛(趋势 7),但侧重仍有差异,选型先看业务画像:

引擎背景强项更适合
vLLMUC Berkeley 开源,PagedAttention 发源地生态最广、功能最全(分页 / 前缀 / PD / 投机 / LoRA),社区迭代快通用在线服务的默认首选
SGLangRadixAttention 发源地前缀复用与结构化生成最强(XGrammar 原生),复杂 Agent 工作流友好多轮对话 / Agent / 高前缀复用
TensorRT-LLMNVIDIA 官方算子级极致优化,FP8 与新硬件支持最快,性能上限最高追求极限性能的 N 卡全栈部署
llama.cpp社区 C++ 实现CPU / 消费级 GPU / 端侧全覆盖,GGUF 量化生态本地、边缘与嵌入式场景
常见反模式(各章边界条件的汇总)

① 盲目调大 batch:吞吐升了、TTFT/ITL 劣化,Goodput 反而下降(6.1);② 高并发开投机解码:系统已是 Compute-Bound,额外验证算力拖低吞吐(4.3);③ Compute-Bound 场景做权重量化:W4A16 只省带宽,瓶颈不在带宽时无收益(3.4);④ 前缀各异的业务开 RadixAttention:命中率≈0 还白占显存(6.2);⑤ KV 驱逐不看 Attention Sink:丢掉开头几个 token,输出立刻崩坏(3.2)。共同教训:先用 Roofline 与指标判瓶颈,再选优化手段。

前沿趋势(2025–2026)

  1. PD 分离“标配化”:NVIDIA Dynamo + NIXL、vLLM/SGLang/Mooncake 全部内置,分离架构成为大规模部署默认项。
  2. KV Cache 成为一等公民:跨节点/跨层级(GPU/CPU/SSD)池化、持久化、按需复用甚至单独计费,系统设计从“以计算为中心”转向“以 KV 为中心”。
  3. 推理(Reasoning / 思考)模型普及:长 thinking 阶段让 Prefill/Decode 比例剧变,Chunked Prefill、PD 配比与面向推理模型的投机解码(EAGLE-3 等)成为新重点。
  4. MoE / EP 推理性价比:大参数、小激活(如 Qwen3-30B-A3B),需要 expert 并行、All-to-All 通信与专家负载均衡配套。
  5. 测试时计算(Test-Time Compute):多路径采样、树/并行验证、自评估与搜索进入推理引擎,天然依赖 Tree Attention 与批量验证能力。
  6. 硬件与精度:GB200 NVL72 把整机做成一个 NVLink 域、HBM3e 与 FP8/FP4 普及,带宽与互联持续抬高 Roofline 屋顶。
  7. 引擎能力收敛:vLLM、SGLang、TensorRT-LLM 在分页、前缀、PD、投机上能力趋同,竞争转向调度智能、异构支持与端到端成本。
  8. 稀疏与混合注意力进入主线模型:NSA / MoBA 等可训练稀疏注意力与 SWA 混合层(Gemma 2/3、Qwen3-Next 等)把长上下文注意力成本压到近线性,"装得少"正从运行时手段变成模型结构的默认项(3.2)。
一个整体判断

这是一个“系统工程 + 算法理解”双密集的方向:并发、缓存、调度、内存管理的系统经验几乎都能平移(分页=虚拟内存、Continuous Batching=动态线程池、PD 分离=读写分离、前缀树=连接复用),但必须建立在对 Transformer 计算特征与论文方法的理解之上——这也是按“一次请求从输入到输出”梳理全篇的原因。

建议的读论文顺序

  1. 打地基:Transformer → FlashAttention
  2. 显存与服务:vLLM/PagedAttention → SGLang → Sarathi-Serve
  3. 架构:DistServe → Mooncake → Splitwise
  4. 解码算法:Speculative Decoding → Medusa → EAGLE(→2→3)→ DeepSeek-V3 MTP → Lookahead
  5. 压缩与长上下文:StreamingLLM → H2O → SnapKV → YaRN → XGrammar

参考文献

  1. Attention Is All You NeedVaswani et al. · NeurIPS 2017 · Transformer 原始论文arXiv:1706.03762
  2. Fast Transformer Decoding: One Write-Head is All You Need (MQA)Shazeer · 2019arXiv:1911.02150
  3. GQA: Training Generalized Multi-Query Transformer Models from Multi-Head CheckpointsAinslie et al. · EMNLP 2023arXiv:2305.13245
  4. FlashAttention: Fast and Memory-Efficient Exact Attention with IO-AwarenessDao et al. · NeurIPS 2022arXiv:2205.14135
  5. FlashAttention-2: Faster Attention with Better Parallelism and Work PartitioningDao · 2023arXiv:2307.08691
  6. FlashAttention-3: Fast and Accurate Attention with Asynchrony and Low-precisionShah et al. · NeurIPS 2024arXiv:2407.08608
  7. Orca: A Distributed Serving System for Transformer-Based Generative ModelsYu et al. · OSDI 2022 · iteration-level 调度源头USENIX OSDI'22
  8. Efficient Memory Management for LLM Serving with PagedAttention (vLLM)Kwon et al. · SOSP 2023arXiv:2309.06180
  9. Sarathi: Efficient LLM Inference by Pipelining Token ComputationAgrawal et al. · 2023 · Chunked PrefillarXiv:2308.16369
  10. Sarathi-Serve: Chase Away Prefix Queue Under Chunked PrefillsAgrawal et al. · OSDI 2024arXiv:2403.02310
  11. SGLang: Efficient Execution of Structured Language Model Programs (RadixAttention)Zheng et al. · NeurIPS 2024/2025arXiv:2312.07104
  12. DistServe: Disaggregating Prefill and Decoding for Goodput-optimized LLM ServingZhong et al. · OSDI 2024arXiv:2401.09670
  13. Splitwise: Efficient Generative LLM Inference Using Phase SplittingPatel, Choukse et al. · ISCA 2024arXiv:2311.18677
  14. Mooncake: A KVCache-centric Disaggregated Architecture for LLM ServingQin et al. · FAST 2025 最佳论文 / ACM TOS 2025arXiv:2407.00079
  15. NVIDIA Dynamo:开源分布式推理编排框架(含 NIXL KV 传输)NVIDIA · 2025 · PD 分离产品化github.com/ai-dynamo/dynamo
  16. Fast Inference from Transformers via Speculative DecodingLeviathan, Kalman, Matias · ICML 2023arXiv:2211.17192
  17. Accelerating LLM Decoding with Speculative SamplingChen et al. · DeepMind · 2023arXiv:2302.01318
  18. Medusa: Simple LLM Inference Acceleration Framework with Multiple Decoding HeadsCai et al. · ICML 2024arXiv:2401.10774
  19. EAGLE: Speculative Sampling Requires Rethinking Feature UncertaintyLi et al. · ICML 2024arXiv:2401.15077
  20. EAGLE-2: Faster Inference of Language Models with Dynamic Draft TreesLi et al. · EMNLP 2024arXiv:2406.16858
  21. EAGLE-3: Scaling up Inference Acceleration via Training-Time TestLi et al. · 2025arXiv:2503.01840
  22. Break the Sequential Dependency of LLM Inference Using Lookahead DecodingFu et al. · ICML 2024arXiv:2402.02057
  23. DeepSeek-V2: A Strong, Economical, and Efficient MoE Language Model (MLA)DeepSeek-AI · 2024arXiv:2405.04434
  24. DeepSeek-V3 Technical Report (MTP / MLA / FP8)DeepSeek-AI · 2024arXiv:2412.19437
  25. Draft & Verify: Lossless LLM Acceleration via Self-Speculative DecodingZhang et al. · 2023arXiv:2309.08168
  26. REST: Retrieval-Based Speculative DecodingAnkner et al. · 2023 · 用检索 n-gram 数据起草arXiv:2311.08252
  27. H2O: Heavy-Hitter Oracle for Efficient Generative Inference of LLMsZhang et al. · NeurIPS 2023arXiv:2306.14048
  28. SnapKV: LLM Knows What You Are Looking for Before GenerationLi et al. · NeurIPS 2024arXiv:2404.14469
  29. KIVI: A Tuning-Free Asymmetric 2bit Quantization for KV CacheZhang et al. · ICML 2024arXiv:2402.02750
  30. GPTQ: Accurate Post-Training Quantization for Generative Pre-trained TransformersFrantar et al. · ICLR 2023arXiv:2210.17323
  31. SmoothQuant: Accurate and Efficient Post-Training Quantization for LLMsXiao et al. · ICML 2023arXiv:2211.10438
  32. AWQ: Activation-aware Weight Quantization for On-Device LLM CompressionLin et al. · MLSys 2024arXiv:2306.00978
  33. Grammar-Constrained Decoding for Structured NLP Tasks without FinetuningGeng et al. · EMNLP 2023arXiv:2305.13971
  34. Efficient Guided Generation for Large Language Models (Outlines)Willard & Louf · 2023arXiv:2307.09702
  35. XGrammar: Flexible and Efficient Structured Generation Engine for LLMsDong et al. · 2024arXiv:2411.15100
  36. Roofline: An Insightful Visual Performance Model for Multicore ArchitecturesWilliams, Waterman, Patterson · CACM 2009ACM DOI
  37. Efficient Streaming Language Models with Attention Sinks (StreamingLLM)Xiao et al. · ICLR 2024 · Attention Sink + 滑动窗口,KV 有界的无限流式生成arXiv:2309.17453
  38. Extending Context Window of Large Language Models via Positional Interpolation (PI)Chen et al. · Meta · 2023 · 位置插值扩上下文arXiv:2306.15595
  39. YaRN: Efficient Context Window Extension of Large Language ModelsPeng et al. · ICLR 2024 · 分频段 RoPE 外推,长上下文主流方案arXiv:2309.00071
  40. Native Sparse Attention: Hardware-Aligned and Natively Trainable Sparse Attention (NSA)Yuan et al. · DeepSeek-AI · 2025 · 可训练稀疏注意力arXiv:2502.11089
  41. MoBA: Mixture of Block Attention for Long-Context LLMsLu et al. · Moonshot AI · 2025 · 块混合稀疏注意力(Kimi 长上下文方案)arXiv:2502.13189
  42. Mistral 7BJiang et al. · Mistral AI · 2023 · 滑窗注意力(SWA)进入主流模型的开端arXiv:2310.06825
  43. Min P Sampling: Balancing Creativity and Coherence at High TemperatureNguyen et al. · 2024 · 按比例动态截断的采样方法arXiv:2407.01082
滚轮缩放 · 拖拽平移 · 双击复位 · ESC 退出