前两篇分别讲了 Prefill / Decode 两阶段,以及 KV Cache 如何用「空间换时间」避免重复计算。理解机制之后,下一步是:怎么衡量一个推理系统好不好?
线上服务不会只看「能不能出字」,而要看首字有多快、后续有多顺、整体要等多久、吞吐能不能撑住、有多少请求真正落在 SLA 内,这些指标如下:TTFT、ITL / TPOT、Total Latency、Time To Publish、Throughput、Goodput。
它们既是压测与基准测试的核心口径,也是选型框架、调 batch、做 SLO 时最常用的尺子,下文除了对指标的解释还会谈两部分内容:哪些因素在影响它 以及 通常解决方案是怎么样的。
为什么需要这些指标?
同一个模型,换一套服务配置,体感可以差很多:有的系统首字很快但后续卡顿,有的吞吐很高但大量请求超时。没有统一指标,就很难回答:
- 用户感知的「快」到底是首字快,还是整段回复快?
- 吞吐上去之后,延迟有没有被牺牲?
- 看起来很忙的 GPU,是否在产出「有效」请求?
先记住两阶段的「物理性格」,后面所有影响因素都绕不开它:
| 阶段 | 算力特征 | 主要瓶颈 | 直接对应指标 |
|---|---|---|---|
| Prefill | 一次吃完整 prompt,偏 compute-bound | Attention / GEMM 算力、长序列 O(N²) | TTFT、部分 Total Latency |
| Decode | 每步只进 1 个 token,偏 memory-bound | 读 KV Cache 的 HBM 带宽 | ITL / TPOT、大部分 Total Latency |
下面按「用户先感知什么 → 系统整体怎样 → 有效容量如何」展开。
TTFT:首 Token 延迟
TTFT 全称 Time to First Token,指模型输出 第一个 token 所花费的时间。
在标准流水线里,TTFT ≈ 排队等待 + Prefill + 采样出首 token,Prefill 要在一次(或若干次 chunk)前向里,为 prompt 的每个 token 算 Q/K/V,并完成整段 Attention;prompt 越长,Attention 越接近平方级变贵,TTFT 通常越高。
对聊天、补全、Agent 工具调用来说,TTFT 决定用户「有没有感觉到它开始动了」。
什么影响 TTFT?
Prefill 要跑通整段 prompt 的前向:每一层的 QKV 投影、全序列 Attention、FFN,再经 LM Head 采样出首 token,同时把 K/V 写入 KV Cache,图中高亮部分影响 TTFT。
影响因素
| 因素 | 原理 |
|---|---|
| Prompt 长度 | Prefill Attention 约 O(N²),长 system / RAG / 多轮历史直接拉长首字 |
| 模型规模与层数 | 每层都要跑一遍 Attention + FFN;参数越大,Prefill FLOPs 越多 |
| 排队 / 调度 | Continuous Batching 下,若 Decode 占满 GPU,新请求 Prefill 可能被推迟 |
| 与 Decode 抢资源 | Prefill 与 Decode 同卡混跑时,大 Prefill 会打断 Decode,反过来也被 Decode 挤占 |
| Attention 实现 | 朴素 Attention 大量 HBM 往返;未用 FlashAttention 时 Prefill 更慢 |
| 前缀是否重复计算 | 相同 system / 工具定义每次都重新 Prefill,等于把固定成本反复付 |
| 并行通信开销 | Tensor Parallel 在 Prefill 有 AllReduce;切分不合理时通信吃掉算力收益 |
| 权重量化 / 精度 | 低精度可加速 GEMM,但反量化或内核未优化时未必线性变快 |
优化解法
- FlashAttention / 高效 Attention 内核:降低 Prefill 的 HBM 流量,直接砍 TTFT。
- Chunked Prefill:把长 prompt 切块做 Prefill,避免一次超大 kernel 饿死其他请求,也更易与 Decode 交错调度。
- Prefix / Prompt Caching:跨请求复用公共前缀的 KV(OpenAI / Claude Prompt Caching、vLLM APC),公共 system 只 Prefill 一次。
- Prefill–Decode 分离(Disaggregation) :Prefill 与 Decode 分到不同机器/池,互不抢占,专治「首字被 Decode 拖死」。
- 调度优先级:给短 prompt / 交互流量更高 Prefill 优先级;限制单次 Prefill 占用的 token budget。
- 模型侧减负:更小蒸馏模型、GQA/MQA(略减 Prefill KV 写带宽)、合理的 context 裁剪与摘要。
ITL / TPOT:Token 间隔
ITL 全称 Inter Token Latency,很多材料也叫 TPOT(Time Per Output Token)。
Prefill 产出第一个输出 token 后,模型进入 Decode:每次只生成一个新 token,再把它拼回 context,继续预测下一个。Decode 阶段里,两个连续输出 token 之间的时间间隔 就是 ITL。
TTFT = 生成第一个输出 token 所需的时间ITL = 每个下一个输出 token 之间的时间间隔
示例
继续用熟悉的 prompt:
The capital of France is
Prefill 读完整段 prompt,生成第一个输出 token:
Paris
生成 Paris 的耗时就是 TTFT。
进入 Decode 后,context 变为:
The capital of France is Paris
再生成下一个 token:
.
从 Paris 到 . 之间的间隔就是 ITL。
再看更长一点的输出:
Prompt: AI is useful because
模型可能生成:
it helps humans solve problems faster.
逐 token 对应关系:
第一个输出 token: it → TTFT下一个 token: helps → ITL下一个 token: humans → ITL下一个 token: solve → ITL下一个 token: problems → ITL下一个 token: faster → ITL下一个 token: . → ITL
一句话:TTFT 看「多快开始说话」,ITL 看「说下去有多顺」。
什么影响 ITL?
Decode 每步只为 当前 1 个 token 算 Q/K/V 与 FFN,但 Attention 必须从 KV Cache 读出整段历史——高亮的「读 KV + Attention」是 ITL 的主因(memory-bound)。
影响因素
Decode 每步通常只算 当前 1 个 token 的 Q/K/V,但 Attention 要读出 整段历史 KV。算力往往吃不满,HBM 带宽才是主瓶颈——这就是 ITL 对「缓存有多大、并发读多少」极敏感的原因。
| 因素 | 原理 |
|---|---|
| 已生成 / 上下文长度 | KV Cache 随序列长度线性涨,每步要多读一截历史 K/V |
| 并发 batch 大小 | 同一步 Decode 的请求越多,同时读的 KV 越多,带宽竞争越狠 |
| KV head 数(MHA vs GQA/MQA/MLA) | MHA 的 KV 最大;GQA/MQA/MLA 直接缩小每步要搬的字节数 |
| 有无 KV Cache | 无 cache 时每步重算全部历史,复杂度从约 O(N) 回到 O(N²) |
| KV 精度 | FP16 KV 占带宽大;FP8/INT4 等可减流量,但有反量化开销 |
| 显存碎片 / 换页 | KV 放不下或 Offload 到 CPU 时,PCIe 往返直接拉高单步延迟 |
| 采样与 logits 处理 | 大 vocab、复杂采样、额外分类头会叠一点 CPU/小算子开销 |
| 投机解码是否命中 | 草稿模型猜错时要回滚,ITL 抖动变大 |
优化解法
- KV Cache(标配) :Decode 只 append 新 K/V,把单步从平方级降到近似线性。
- GQA / MQA / MLA:从架构上少存、少读 KV(Llama、Qwen、DeepSeek 等主流路线)。
- KV 量化 / PagedAttention:减 footprint、减碎片,让更大 batch 仍装得下、读得动。
- FlashDecoding / 优化 Decode kernel:专攻「单 token × 长 KV」的 memory-bound 路径。
- Speculative Decoding(投机解码) :用小模型起草、大模型一次验证多 token,有效提高「每秒可见 token」。
- 控制并发与 batch:用
max_num_seqs/ 显存水位限制 Decode 并行度,避免 ITL 被吞吐挤爆。 - Tensor Parallel:大模型把层内算力摊到多卡,缩短单步墙钟时间(通信成本要算清楚)。
Time To Publish:用户真正看到的首字
Chip Huyen 在《AI Engineering》里指出:TTFT / TPOT 从 模型视角 和 终端用户视角 可能不一致,带 CoT、工具调用、隐藏推理的模型,会先生成用户不可见的中间 token;Time To Publish 指终端用户 真正看到第一个可见 token 的时间。
什么影响 Time To Publish?
模型内部仍走完整 Decoder 栈;差别在于:多段 Decode 产出的 token 对用户不可见(thinking / tool call),要等到「可见答案」才算 Publish。高亮的是拖长「用户首字」的路径。
影响因素
| 因素 | 原理 |
|---|---|
| 隐藏推理 / CoT 长度 | 模型侧 TTFT 已结束,但用户仍在等「可展示」内容 |
| 工具调用往返 | 先出 tool call → 执行 → 再继续生成,可见首字被拉远 |
| 输出过滤 / 安全审核 | 缓冲整段或整句再下发,等于人为推迟 Publish |
| 流式协议与前端渲染 | SSE/WebSocket 缓冲、UI 批处理也会加几十到几百毫秒 |
| 推理预算(thinking tokens) | Test-time compute 越大,不可见前缀越长 |
优化解法
- 流式输出可见 token:推理过程可后台跑,但尽快开始下发用户可见文本。
- 分离「思考预算」与「回答预算」:限制 hidden CoT / tool 轮次,并单独监控。
- 边想边展示(若产品允许):展示阶段性结论或工具状态,降低「假死」感。
- 指标拆分上报:同时报模型 TTFT 与 Time To Publish,避免只优化内部数字、忽略体验。
Total Latency:端到端延迟
Total Latency 是从提交 prompt,到模型生成 全部 输出 token 的总时间;对用户来说,就是「等到完整回复」的时间。
它与 TTFT、TPOT 的关系可以写成:
Total Latency = TTFT + TPOT × (第一个 token 之后的输出 token 数量)
或:
Total Latency = TTFT + TPOT × (生成的 token 总数 - 1)
比较不同系统时,延迟更建议看 p50 / p99:长尾才是线上体验的杀手。
什么影响 Total Latency?
Total Latency = 整次 Prefill(所有层) + Decode 循环 × (L_gen−1)。图中两条高亮链路一起决定端到端时间;输出越长,右边 Decode 环占比越大。
影响因素
| 因素 | 原理 |
|---|---|
| TTFT 与 TPOT 本身 | 公式上就是两者的线性组合 |
| 输出长度 | Decode 步数近似等于生成 token 数,长回答线性拉长总时间 |
| EOS / max_tokens 策略 | 模型啰嗦或不及时停,等于强制多跑许多 Decode 步 |
| 请求排队与重试 | 端到端还含网关、排队、失败重试,不只是 GPU 内核时间 |
| 多阶段流水线 | RAG 检索、多 Agent、多模型串联会叠加在 Total 上 |
| 长尾干扰 | 超长 prompt、超大 batch、偶发换页,主要抬高 p99 |
优化解法
- 分别优化 TTFT 与 TPOT(见上两节),再限制平均输出长度。
- 流式交互:Total Latency 不变时,用流式改善体感;产品上往往比「等整段」更重要。
- 提前停止 / 长度约束:
max_tokens、stop sequences、拒答短路。 - 端到端链路治理:检索缓存、并行工具调用、超时熔断,避免 GPU 之外的时间主导。
- 按百分位做 SLO:用 p99 Total Latency(或分位数 Goodput)指导扩容,而不是只看均值。
Throughput:吞吐
Throughput(吞吐) 指单位时间内完成的请求数(或 token 数),例如每秒完成多少请求、每秒生成多少 token。它回答:「系统忙不忙、干了多少活?」
注意口径要说清:是 request/s 还是 output token/s——二者优化手段不完全相同。
什么影响 Throughput?
吞吐看的是 单位时间里,多少请求能并行穿过同一套 Transformer 算子。瓶颈常在:权重/KV 的 HBM 复用效率、Attention/FFN 的 batch 维度,以及 KV Cache 还能塞多少并发序列。
影响因素
| 因素 | 原理 |
|---|---|
| Batch / 并发度 | Decode memory-bound,适当加大 batch 可摊薄权重读取、提高 GPU 利用率 |
| 静态 vs 连续批处理 | 静态 batch 被最长序列拖死;Continuous Batching 按 iteration 调度,空洞更少 |
| KV 显存能否装下 | 装不下就无法提高并发;碎片化会进一步压低有效 batch |
| Prefill/Decode 混部比例 | 过多长 Prefill 会降低 Decode 迭代频率,token/s 下跌 |
| 模型与精度 | 更小模型、权重量化通常提高 token/s;质量要另测 |
| 多卡并行效率 | TP/PP/EP 切分不当,通信占比过高,吞吐不升反降 |
| 输入输出长度分布 | 长短请求混杂时,调度策略决定有效吞吐 |
优化解法
- Continuous Batching(Orca / vLLM 等) :iteration 级调度,吞吐相对静态 batch 可有数量级提升。
- PagedAttention:近乎消除 KV 预留浪费,同等显存塞进更多并发。
- Chunked Prefill + 智能调度:在拉高吞吐的同时保护 TTFT。
- 量化(权重 / KV) :腾出显存与带宽,换更高 batch。
- 水平扩展与合适并行策略:复制实例提 request/s;单实例内用 TP 装大模型。
Goodput:有效吞吐
工程里常先定 SLO(Service Level Objective),例如:
95% 的 API 请求应在 2 秒内完成
或对 LLM:
TTFT ≤ 2s,且 TPOT ≤ 50ms,且 Total Latency ≤ 3s
Goodput 指的是:满足 SLO 的 每秒请求数。它回答:「干了多少 有效 的活?」
假设系统每秒处理 100 个请求,其中:
80 个满足 SLO20 个超时或超延迟
则:
Throughput = 100 requests/secondGoodput = 80 requests/second
对照:
Throughput = 完成的总工作量Goodput = 在 SLO 范围内完成的有效工作量
什么影响 Goodput?
Goodput 不对应某一个算子,而是 整条 Transformer 延迟路径 + SLO 过滤器。Attention / FFN / KV 导致的 TTFT·ITL 长尾,一旦跌出 SLO,该请求就不计入 Goodput。
影响因素
| 因素 | 原理 |
|---|---|
| SLO 松紧 | 同一套延迟分布,SLO 越严,Goodput 越低 |
| 延迟分布形态 | 均值好看但长尾重时,Goodput 会被 p99 拖垮 |
| 过载 | 队列爆炸 → TTFT/ITL 全面恶化 → 大量请求跌出 SLO |
| 只追 Throughput | 激进 batch 抬高吞吐的同时抬高延迟,Goodput 可能下降 |
| 流量尖刺与长短请求 | 突发长 Prefill 会短暂「毒化」整池延迟 |
| 重试风暴 | 失败重试放大负载,有效成功数反而下降 |
优化解法
- 以 Goodput / SLO 达成率为优化目标,而不是裸 Throughput。
- 准入控制(Admission Control) :队列过长时快速失败或降级,保护已在途请求的 SLO。
- 优先级与隔离:交互流量与离线批跑分队列 / 分池。
- 自动扩缩容:按 p95 TTFT、队列长度触发,而不是只看 GPU 利用率。
- 过载降级:缩短 max_tokens、关闭重推理、切小模型,换「更少但合格」的成功请求。
Latency vs Throughput:经典权衡
计算机科学里到处是 tradeoff,LLM 服务也不例外。
小 batch:延迟友好,吞吐偏低
Batch size = 1
请求几乎不用排队等别人,单请求延迟往往更低,但 GPU 容易吃不饱:
Latency: 低Throughput: 低GPU 利用率: 偏低
大 batch:吞吐上去,延迟变差
Batch size = 32
GPU 一次吃很多请求,系统吞吐和利用率通常更好,但单个请求可能在队列里等更久:
Throughput: 高GPU 利用率: 高Latency: 升高
归纳:
小 batch size → 低延迟,低吞吐大 batch size → 高吞吐,高延迟
所以 Throughput 可能具有误导性:冲高吞吐很容易以牺牲延迟为代价。
用 Goodput 看「有效容量」
仍用 SLO「每个请求应在 3 秒内完成」比较两种配置。
配置 A:保守 batching
总吞吐 = 80 requests/second满足 SLO = 78 requests/secondGoodput = 78 requests/second
请求略少,但几乎都在延迟目标内。
配置 B:激进 batching
总吞吐 = 120 requests/second满足 SLO = 70 requests/secondGoodput = 70 requests/second
吞吐更高,但大量请求超时,对终端用户而言,配置 A 往往更好:在可接受延迟内,成功请求反而更多。
Goodput 度量的是推理系统的 有效容量:不只看干了多少活,而看在可接受延迟内干了多少活。
一个好的推理系统,是在满足延迟 SLO 的同时,尽量把吞吐(进而 Goodput)做上去的系统。
总结
最终模型推理关注两层即可:一层是 用户体感(TTFT、ITL、Total Latency、Time To Publish),一层是 系统有效产能(Throughput vs Goodput)。
| 指标 | 主要回答的问题 | 关键影响因素 | 业界常用方案 |
|---|---|---|---|
| TTFT | 首字有多快?(偏 Prefill) | Prompt 长、排队、抢 Decode、重复前缀 | FlashAttention、Chunked Prefill、Prefix Cache、PD 分离 |
| ITL / TPOT | 后续吐字有多顺?(偏 Decode) | KV 大小、并发读带宽、注意力结构 | KV Cache、GQA/MLA、KV 量化、投机解码 |
| Time To Publish | 用户看到首字要多久? | 隐藏 CoT、工具调用、下发缓冲 | 流式可见输出、限制思考预算、指标拆分 |
| Total Latency | 完整回复要多久? | TTFT + TPOT×输出长度 | 双侧优化 + 限长 + 流式体感 |
| Throughput | 单位时间干了多少活? | Batch、连续批处理、显存能否 concurrent | Continuous Batching、PagedAttention、量化 |
| Goodput | 多少活落在 SLO 内? | 延迟长尾、过载、只追吞吐 | 准入控制、隔离、按 SLO 扩缩容 |
参考
(1)pub.towardsai.net/llm-inferen…
(2)Chip Huyen,《AI Engineering》
(3)www.anyscale.com/blog/contin…
(4)vllm.ai/blog/2023-0…
(5)developer.nvidia.com/blog/master…