Part 2 · Serving & Cost 服务与成本

2.1 Latency Metrics 延迟指标体系:先量对,再优化

难度 ★★☆ 阅读 ~14 min 面试出现频率 高 前置 1.3 Continuous Batching
Core Concept 核心概念

「延迟」这个词在 LLM 服务里没有意义

传统 Web 服务的延迟是一个数:请求进去,响应出来,中间的时间。LLM 服务不是——它流式地产出结果,用户在第一个字出现时就已经开始体验了。把整个过程压成一个「响应时间」,会同时掩盖两类完全不同的问题,并且指向完全相反的优化手段。

所以必须拆成三个指标,它们对应推理的两个阶段(见 1.1):

指标含义由什么决定用户感受
TTFT
time to first token
从发出请求到收到第一个 tokenprefill 阶段:输入长度(计算量约 O(n²))、排队时间、缓存命中「它反应快不快」
——等待感的全部来源
TPOT
time per output token
后续每个 token 的平均间隔
(也称 ITL,inter-token latency)
decode 阶段:模型大小、显存带宽、batch 大小「它打字快不快」
——流畅度
E2E latency整个请求的总时长≈ TTFT + TPOT × (输出 token 数 − 1)只有非流式场景才直接可感

这个分解的实用价值在于:它把「慢」这个模糊抱怨翻译成了可定位的工程问题。用户说「慢」,如果是 TTFT 高,你该去看输入长度、排队和缓存;如果是 TPOT 高,你该去看模型大小、batch 配置和硬件带宽——两条路毫无交集,走错方向就是几周的浪费。

一次流式请求的时间构成 排队 prefill 预填充 tok1 tok2 tok3 tok4 … tokN TTFT — 用户盯着空白屏幕的时间 TPOT — 每个后续 token 的间隔 E2E latency ≈ TTFT + TPOT × (N − 1)
图 1 · 三个指标的关系。注意排队时间被算进 TTFT——在高负载下,TTFT 的恶化往往来自排队而非计算,这一点在只测单请求延迟的压测里完全看不见。

指标要配 SLA,SLA 要配场景

不同产品形态对三个指标的敏感度差异极大,用同一套阈值管理所有服务是常见的错误:

流式对话:TTFT 决定「有没有反应」,是第一优先级;TPOT 只需快过用户的阅读速度即可,再快用户也感知不到——这是一条重要的止损线,说明 TPOT 的优化存在收益归零点。举例:若目标是每秒吐 20 个 token,TPOT 只需 ≤ 50ms,做到 20ms 并不会让体验更好,但可能让你的 batch 配置付出吞吐代价。

语音对话:TTFT 的要求严苛得多,因为人类对话中的沉默极难忍受;这类场景常常值得为压低 TTFT 牺牲相当多的吞吐。

离线批处理:TTFT 和 TPOT 都无所谓,唯一指标是吞吐(以及由此决定的成本)。此时应该把 batch 开到显存允许的上限。

Agent 内部调用:单步的 TTFT 会被乘以循环轮数放大(见 4.2)。一个 8 轮的 agent,每轮多 300ms,用户就多等 2.4 秒——所以 agent 场景对 TTFT 的敏感度远高于它看起来的样子。

根本取舍:延迟与吞吐不可兼得

这是 Part 2 全部内容的地基。增大 batch 会摊薄权重读取成本,吞吐上升;但批内每一步要处理的数据更多,且每条请求都要等整批一起走完这一步,单请求延迟上升。反过来,batch=1 延迟最优,但 GPU 大部分算力在空转,成本高到不可接受。

batch size / 并发 → 吞吐 throughput(趋于饱和) 单请求延迟 latency(加速上升) 拐点:吞吐已近饱和, 继续加 batch 只换来延迟恶化 ← 有效工作区:吞吐还在涨 无效区:只涨延迟 →
图 2 · 取舍曲线与拐点。示意图,非实测数据——真实拐点位置取决于模型、硬件与请求长度分布,必须自己压测得到。但结构是普适的:吞吐有饱和上限,延迟没有。工程上的任务是找到那个拐点,并让系统运行在它左侧。

还有一个更隐蔽的取舍在 batch 内部:新请求的 prefill 与在途请求的 decode 争抢同一次迭代(1.3 已提到)。优先 prefill,新用户的 TTFT 好看、老用户的 TPOT 卡顿;优先 decode 则相反。TTFT 与 TPOT 在调度层面直接互为代价——这正是下面案例要解决的问题。

用 goodput 而不是 throughput 做决策。「每秒 5000 token」听起来很好,但如果其中一半请求的 TTFT 已超出 SLA,这些吞吐是无效的。goodput(有效吞吐)= 单位时间内满足 SLA 的请求数。用它做容量决策,可以避免「指标很好但用户在骂」的经典困境。

Case Study 真实案例

Sarathi-Serve:不牺牲延迟地提高吞吐

Taming Throughput-Latency Tradeoff in LLM Inference (OSDI 2024)

Georgia Tech 与 Microsoft Research India 的团队直指上文那个调度冲突:要提高吞吐就得增大 batch,但新请求的 prefill 一旦入批就会停住(stall)正在进行的 decode,导致 TPOT 抖动。

他们的方案是 chunked-prefill + stall-free scheduling:把一个 prefill 请求切成大小接近的若干块,每次迭代只处理一块,并与在途的 decode 拼在同一批里执行——新请求可以加入批次而不暂停任何正在进行的解码。这样既拿到了大 batch 的吞吐,又不让 TPOT 出现尖刺。

2.6×
Mistral-7B / 单张 A100,服务容量提升(vs vLLM)
3.7×
Yi-34B / 两张 A100,最高提升
5.6×
Falcon-180B + 流水线并行,端到端服务容量最高提升

条件:三组数字分别对应不同模型与硬件配置,基线均为 vLLM,衡量的是在满足延迟约束前提下的服务容量——这正是上文 goodput 的思路,而不是裸吞吐。

这个案例的方法论价值大于数字本身:当你面对一个「A 和 B 不可兼得」的取舍时,先别急着在曲线上选点,去检查这条曲线是不是可以被整体推动。此处的关键洞察是——prefill 之所以阻塞 decode,是因为它被当成一个不可分割的原子操作;一旦允许它被切分,冲突的前提就消失了。

来源:Agrawal, Kedia, Panwar, Mohan, Kwatra, Gulavani, Tumanov, Ramjee, "Taming Throughput-Latency Tradeoff in LLM Inference with Sarathi-Serve", USENIX OSDI 2024. 数字引自论文;示意图为本书重绘。
Tooling Sidebar 工具层
怎么把这些指标真的量出来(会过期,正文不依赖本区块) 截至 2026-10

压测工具:vLLM 自带 vllm bench serve 命令,可指定请求到达率与数据集,直接输出 TTFT / TPOT / E2E 的分位数;第三方工具可选 NVIDIA 的 AIPerf(genai-perf 的后继,后者已宣布弃用)等,提供类似能力;早期常用的 llmperf 仓库已于 2025-12 归档。关键是选一个能报分位数而不只报均值的工具。若后续轮次命中前缀缓存,TTFT 可能偏低:重复压测前清理缓存,或记录并解释命中率。

务必用真实长度分布:用等长请求压测会系统性地高估性能——连续批处理与分页的收益、prefill 阻塞的代价,全都取决于长度方差。把线上真实的输入/输出长度分布采样出来做压测输入。

务必测排队:单并发下测出的 TTFT 只包含计算时间。把并发压到目标负载的 1×、1.5×、2× 分别测,观察 TTFT 的 P99 在哪个点开始非线性上升——那就是你的容量拐点(2.4 会用到这个数)。

线上监控口径:TTFT、TPOT 至少各留 P50/P95/P99,并按输入长度分桶(如 <1K、1–8K、>8K)。不分桶的话,长请求的正常慢会污染整体分位数,掩盖真正的异常。

商业 API 场景:你无法控制对方的 batch 配置,但同样要测——把 TTFT/TPOT 作为供应商的 SLA 监控项,它们的波动是 6.5 降级策略的触发信号。

Quiz 自测

章末自测

估算题一个流式问答服务:TTFT P95 = 800ms,TPOT P95 = 40ms,平均输出 500 token。用户的 E2E 体感是多久?如果把模型换成一个小一半、TPOT 降到 25ms 但 TTFT 升到 1.1s 的方案,值得吗?

当前:E2E ≈ 0.8 + 0.04 × 499 ≈ 0.8 + 20.0 ≈ 20.8 秒。

新方案:E2E ≈ 1.1 + 0.025 × 499 ≈ 1.1 + 12.5 ≈ 13.6 秒。总时长减少 35%。

但这道题的陷阱是「值得吗」不能只看 E2E。需要追问三件事:

① 用户真的会读完 500 个 token 吗?如果多数用户扫两眼就走,那么 TTFT 恶化 300ms 是所有人都要付的代价,而 TPOT 的改善只有读完的人才享受得到。这种情况下新方案可能是净负面。

② TPOT 40ms 是否已经快过阅读速度?40ms/token 约合每秒 25 个 token,通常已快于人的阅读速度——那么把它降到 25ms 属于用户感知不到的优化,收益接近于零。

③ 模型小一半,质量退化了多少?这才是真正的成本,必须用 eval 集量化(6.4)。

结论:多半不值得。这道题想训练的判断是——E2E 是一个会误导人的聚合指标,优化它之前先问用户实际感知的是哪一段。

概念题为什么增大 batch size 几乎总能提高吞吐,却不总能提高 goodput?

因为吞吐只数「产出了多少 token」,而 goodput 只数「满足 SLA 的请求」。增大 batch,每一步处理的数据变多,单请求的延迟随之上升;当延迟越过 SLA 阈值,这些请求产出的 token 在业务上等于零——吞吐曲线还在涨,goodput 已经开始掉头向下。

实践含义:容量规划应该以 goodput 峰值对应的 batch 配置为准,而不是吞吐峰值。两者往往不在同一点上,且吞吐峰值总是在更大的 batch 处——只盯吞吐会系统性地把系统配置到 SLA 违约区。

场景判断监控显示 TTFT 的 P50 是 400ms 但 P99 飙到 9 秒,而 TPOT 的 P50/P99 都很平稳。问题最可能出在哪?

TPOT 平稳说明解码本身没问题——模型、硬件、batch 配置都健康。问题被锁定在 TTFT 独有的那几个成分上:排队或超长输入。

排队(更常见):负载已越过容量拐点,请求在队列里等待。特征是 P99 的恶化与 QPS 高峰在时间上重合,且和请求内容无关。

超长输入:少数请求输入极长,prefill 计算量按 O(n²) 增长。特征是慢请求与长输入一一对应,与 QPS 无关。

怎么区分:把 TTFT 按输入长度分桶。若各桶内的 P99 都高 → 排队问题,需扩容或限流;若只有长输入桶高 → 计算问题,需 chunked prefill、输入长度上限或长短请求分流。

反例警示:如果此时有人提议「换个更小的模型」,那是在治 TPOT 的病——方向完全错了,而且会白白损失质量。

面试题改编「你们的 LLM 服务延迟多少?」——这个问题该怎么回答才算专业?

直接报一个数字是不合格的。合格的回答要包含四个要素:

① 拆指标:报 TTFT 和 TPOT,而不是笼统的「延迟」,并说明是否包含排队。

② 报分位数:P50 和 P99 至少各一个。均值在长尾分布上几乎没有信息量。

③ 给条件:什么模型、什么输入长度分布、什么并发水平下测的。同一套系统在 10 QPS 和 100 QPS 下是两个完全不同的数字。

④ 关联 SLA:这个数字对应的 goodput 是多少,即在这个延迟目标下系统能承载多少负载。

一个好的回答样例:「TTFT P50 约 400ms、P99 约 1.2s,TPOT P99 约 45ms;在输入 P95 为 4K token、并发 60 的负载下测得;SLA 是 TTFT P99 < 2s,当前 goodput 余量约 40%。」——这个回答同时展示了指标体系、测量素养和容量意识。