用户在聊天框里输入一句话,几秒后屏幕开始逐字出现回答。看起来像一次普通的函数调用,实际上这段旅程跨越了分词器、Transformer、CUDA Kernel、KV Cache、请求调度器和网络服务层。
本文不按模型组件逐个介绍,而是跟随一个 Token 从进入系统到离开 GPU。沿着这条数据流,可以自然解释 Prefill 与 Decode、KV Cache、Continuous Batching,以及 TTFT 和 TPOT 为什么会成为推理系统最重要的指标。
图 1:Prefill 与 Decode 共享模型结构,却具有完全不同的执行形状和性能瓶颈。
理解整条链路时,最好把系统看成三个相互耦合的状态机,而不是一条只会向前的流水线:
| 状态机 | 保存什么 | 何时变化 |
|---|---|---|
| 模型状态 | 权重、编译图、并行配置 | 加载或热更新时 |
| 请求状态 | Token 序列、采样参数、KV Cache、停止条件 | 每生成一个 Token |
| 调度状态 | Waiting/Running 队列、Token Budget、Block 水位 | 每个推理迭代 |
模型前向只负责把输入状态推进一步;生产推理的复杂性来自数百个请求状态机共享同一份模型状态,并由调度状态决定谁在下一轮获得计算和显存。
一、Tokenization 与离散输入表示
模型不能直接处理字符串。请求首先经过 Tokenizer,被转换为一串整数 ID:
1 | "AI Infra 很有意思" |
现代 LLM 通常使用子词或字节级分词。Tokenizer 会影响:
- 同一句话被切成多少 Token;
- 上下文窗口实际能容纳多少文字;
- 推理计费和吞吐统计;
- 多语言场景中的输入效率;
- Prefix Cache 能否命中完全一致的 Token 前缀。
因此,服务端判断两个 Prompt 是否拥有相同前缀时,比较的不是肉眼看到的字符串,而是分词后的 Token 序列。
二、Embedding、位置编码与输入张量
每个 Token ID 都会在 Embedding 表中查到一个 维向量。假设 Batch 为 ,序列长度为 ,隐藏维度为 ,进入 Transformer 前的张量形状是:
Embedding 本身不包含顺序信息,所以还需要位置编码。当前 Decoder-only 模型常使用 RoPE,将位置信息施加到 Q、K 上。这样 Attention 的内积不仅与内容有关,也能感知相对位置。
这一阶段看似简单,却决定了后续所有 Kernel 的形状。如果不能熟练跟踪 (Batch, Sequence, Hidden),就很难分析 Attention 的显存、GEMM 的尺寸或张量并行的切分位置。
三、Decoder Block 的计算路径

图 2:Decoder-only 模型从 Embedding 开始,经过重复的 Decoder Block,最终由 LM Head 产生 Logits。
一个典型的 Pre-Norm Decoder Block 可以抽象成:
1 | x |
3.1 Q、K、V 从哪里来
输入 分别乘以投影矩阵:
拆成多头后,Q 的形状通常是:
GQA 模型的 K、V 则是:
Attention 的核心公式为:
其中 是 Causal Mask,它让当前位置只能看到自己和之前的 Token。
3.2 FFN 为什么不能忽略
Attention 负责 Token 之间的信息混合,FFN 则对每个 Token 独立做非线性变换。SwiGLU 通常包含 Gate、Up 和 Down 三个大矩阵乘,是参数量和计算量的重要组成部分。
从系统角度看,一个 Decoder Block 不是单个大 Kernel,而是一组 GEMM、Norm、激活、位置编码和 Attention Kernel。推理框架需要尽量减少中间张量读写和 Kernel Launch 次数。
四、Prefill 与 Decode 两阶段执行模型
自回归模型每次只生成一个新 Token,但处理输入 Prompt 和生成后续 Token 是两种完全不同的工作负载。
4.1 Prefill:一次处理完整 Prompt
假设 Prompt 有 个 Token,Prefill 会让这 个位置一起通过所有 Transformer 层。
特点是:
- GEMM 的矩阵尺寸较大;
- GPU 并行度高;
- 计算量随 Prompt 长度快速增长;
- 通常更接近 Compute Bound;
- 结束时生成所有历史 Token 的 K、V。
用户最关心的 Prefill 指标是 TTFT,也就是从请求进入系统到收到第一个输出 Token 的时间。
4.2 Decode:每轮只新增一个 Token
Decode 第 轮只计算一个新位置的 Q、K、V。新 Q 要与之前所有 Token 的 K 做 Attention,然后产生下一个 Token 的概率分布。
此时矩阵乘的 维通常很小,GPU 每轮却仍要读取大量模型权重和历史 KV。Decode 因而常表现为 Memory Bound:算术单元还没吃饱,显存带宽已经成为瓶颈。
| 特性 | Prefill | Decode |
|---|---|---|
| 每次处理 Token 数 | 整个 Prompt | 每请求通常 1 个 |
| 主要瓶颈 | 计算 | 权重与 KV 读取 |
| 用户指标 | TTFT | TPOT/ITL |
| 优化重点 | 高效 Attention、Chunking | Batching、量化、CUDA Graph |
五、为什么需要 KV Cache
如果没有 KV Cache,第 轮生成时必须重新计算前面 个 Token 的 K 和 V。整个生成过程会产生大量重复工作。
KV Cache 的策略很直接:
1 | Prefill: 计算 Prompt 所有 K/V → 写入缓存 |
为什么不缓存 Q?因为历史 Token 的 Q 只用于它们各自产生输出。生成当前 Token 时,需要的是当前 Q 与全部历史 K、V,历史 Q 不再参与计算。
KV Cache 用显存换取了计算,但也带来了新的系统问题:
- 长上下文让缓存线性增长;
- 并发请求的长度不同;
- 请求随时到达和结束;
- 连续预留容易产生碎片;
- 相同系统提示词会被反复计算。
这些问题分别催生了 PagedAttention、Continuous Batching 和 Prefix Cache。
六、Logits 处理与采样解码
最后一层隐藏状态经过 Final Norm 和 LM Head,得到词表大小的 Logits。Softmax 将其转换为概率分布,但系统不一定选择概率最大的 Token。
6.1 Temperature
- :分布更尖锐,输出更确定;
- :分布更平坦,输出更多样。
6.2 Top-K
只保留概率最高的 K 个候选,再归一化采样。它限制候选数量,但没有考虑概率质量。
6.3 Top-P
按概率从高到低排序,保留累计概率达到 的最小集合。分布很确定时候选少,分布不确定时候选会自动增多。
采样出新 Token 后,它会被追加到请求序列,重新进入下一轮 Decode,直到遇到结束符、长度限制或停止条件。
6.4 一个最小自回归循环
下面的伪代码刻意省略推理框架的调度细节,只保留模型状态如何跨轮次延续:
1 |
|
这段代码展示了 Prefill/Decode 的语义边界,但不适合直接作为生产服务:每个请求单独循环会产生小 Kernel 和频繁 Launch,也无法让完成的请求即时退出 Batch。vLLM、SGLang 等引擎优化的正是“多个这样的状态机如何共享一次模型执行”。
七、多请求推理与迭代级调度
如果 GPU 每次只为一个请求生成一个 Token,Decode 的小矩阵很难利用完整算力。推理引擎会把多个请求的当前 Token 拼成一个 Batch,让一次权重读取服务更多请求。
传统 Static Batching 要等整个 Batch 中所有请求都完成,短请求会被长请求拖住。Continuous Batching 则在每个 Decode 迭代重新组织 Batch:
1 | 完成的请求退出 |
Batch 越大,通常吞吐越高,但每个请求等待调度和执行的时间也可能增加。因此推理系统不是只追求 Tokens/s,而是在吞吐、TPOT 和尾延迟之间做平衡。
八、用指标描述这段旅程
8.1 TTFT
Time To First Token,包括排队、Tokenization、调度和 Prefill。长 Prompt、排队拥塞和冷启动都会抬高 TTFT。
8.2 TPOT / ITL
Time Per Output Token 或 Inter-Token Latency,反映回答逐字输出是否流畅,主要受 Decode 阶段影响。
8.3 E2E Latency
一个近似关系是:
短回答可能由 TTFT 主导,长回答则更容易由 TPOT 主导。
8.4 Throughput 和 Goodput
Throughput 统计单位时间完成的请求或生成的 Token。Goodput 则只统计满足延迟 SLO 的有效工作。一个系统可能吞吐很高,但大量请求超过 TPOT 或 TTFT 限制,此时 Goodput 仍然很低。
九、建议实验
可以用同一模型做四组测试:
- 固定输出长度,改变 Prompt 长度,观察 TTFT;
- 固定 Prompt,改变输出长度,观察 E2E 延迟;
- 改变并发数,记录吞吐和 P99 TPOT;
- 对比单请求 Hugging Face 推理与 vLLM Continuous Batching。
记录结果时不要只写平均值,至少保留 P50、P95、P99 和实际 Prompt 长度分布。
十、技术 Q&A
Q1:为什么历史 Token 的 Q 不需要进入 KV Cache?
某个历史位置的 Q 只用于计算该位置自己的 Attention 输出。生成新 Token 时,需要的是“当前新位置的 Q”与“所有历史位置的 K/V”。历史 Q 不会再次参与后续位置的计算,缓存它只会增加显存而不会减少必要计算。
Q2:Prefill 和 Decode 使用同一个模型,为什么瓶颈不同?
Prefill 同时处理 个位置,线性层形成较大的矩阵乘,数据复用和 Tensor Core 利用率较高;Decode 每请求每轮只有一个新位置,GEMM 的 Token 维很小,却仍要读取几乎全部层权重和历史 KV,因此算术强度低,通常更受 HBM 带宽和 Launch 延迟限制。
Q3:为什么增大 Batch 能提高 Decode 吞吐,却可能恶化 TPOT?
单请求 Decode 读取一次模型权重只服务一个 Token;把多个请求组成 Batch 后,同一轮权重读取可以为多个 Token 计算摊销,GEMM 的有效尺寸也变大。但 Batch 过大使单轮执行时间增加,新请求和已经处于 Decode 阶段的请求都要等待更久,所以吞吐上升不保证 TPOT 和尾延迟同步改善。
Q4:应如何用 TTFT、TPOT 和 Goodput 评价推理引擎?
TTFT 描述排队和 Prefill 后用户多久看到首 Token,TPOT 描述后续输出的流畅度,吞吐量描述系统总产出。Goodput 只统计同时满足 TTFT/TPOT SLO 的请求,可避免系统通过无限扩大 Batch 换取表面吞吐。测试时还应使用输入与输出长度分桶、并发扫描和真实到达 Trace;固定 Shape 无法暴露长 Prefill 干扰、动态补位、KV 碎片和 P99 排队时间。
十一、系列导航
上一篇:大模型训练与推理的显存模型
下一篇:GPU 性能分析方法
十二、参考资料
论文
- Attention Is All You Need:Transformer 和 Scaled Dot-Product Attention。
- LLaMA 2:现代 Decoder-only 模型的结构实例。
- GQA: Training Generalized Multi-Query Transformer Models:Query Head 与 KV Head 解耦。
- Efficient Memory Management for LLM Serving with PagedAttention:KV Cache 管理和 vLLM。
- Orca: A Distributed Serving System for Transformer-Based Generative Models:Iteration-level Scheduling。
- The Curious Case of Neural Text Degeneration:Top-P 采样。
- Fast Inference from Transformers via Speculative Decoding:并行验证多个候选 Token。
文档
- vLLM Documentation:离线推理、在线服务和引擎配置。
- NVIDIA NIM LLM Benchmarking Metrics:TTFT、ITL、吞吐等指标定义。
- The Illustrated Transformer:建立 Transformer 数据流直觉。
- AIInfraGuide:Tokenization、Decoder-only、Prefill/Decode 与 KV Cache 的中文资料,MIT License。