vLLM:推理吞吐与尾延迟的资源真相
推理平台的真正挑战,不在于极限吞吐,而在于如何用资源契约守住尾延迟的底线。
在线推理平台最容易掉进一个“看起来很合理、结果很不稳定”的陷阱:把吞吐(tokens/s、QPS)拉满之后,尾延迟(p95/p99)反而开始失控;更糟的是,这种失控常常不是线性恶化,而是突然抖动、周期性爆炸、或者在某些请求形态/租户组合下才出现。
vLLM 之所以值得单独一章讨论,是因为它把“吞吐—尾延迟冲突”的核心机制暴露得非常清楚:并发与批处理把 GPU 利用率推高的同时,也把显存(KV cache,Key-Value Cache)与调度队列变成了系统的“弹性瓶颈”。当共享/隔离策略变化时,这个瓶颈会被放大或被抑制,最终决定平台的可预测性。
本章将从 vLLM 的并发与显存行为出发,回答三个工程问题:
- 为什么吞吐优化天然会冲击尾延迟?冲击发生在哪些资源环节?
- 平台应该如何定义“资源请求”与“验收指标”,让容量与体验可讨论、可评审?
- 哪些配置组合最容易引发系统性抖动(jitter),以及如何提前规避?
吞吐与尾延迟:不是权衡口号,而是队列与显存的物理后果
在在线推理场景中,提升“吞吐”通常有三种主要手段:
- 提高并发:即同时活跃的请求数量(更多 sequences / requests)。
- 加大批处理:将更多 token 计算合并进一次或更少次数的 GPU kernel 调用(continuous batching,连续批处理)。
- 提高占用率:尽量让 GPU 计算单元持续忙碌,减少空洞和上下文切换。
这些手段的共同副作用是:请求进入 GPU 的等待时间上升,且等待时间的方差变大(队列效应)。尾延迟通常在以下两种情况下爆炸:
- 短请求被长请求“拖住”:混合工作负载下,批处理与调度更倾向于追求整体吞吐,导致短请求在队列里等待“拼批”或被长请求占用算力窗口。
- 显存接近水位线:KV cache 越逼近显存上限,越容易触发保守策略(拒绝、回退、offload、或者更频繁的调度切片),从而产生非线性的延迟抖动。
因此,在推理平台中,吞吐与尾延迟的矛盾,往往不是“算法优化 vs 用户体验”的抽象冲突,而是两个非常具体的资源事实:
- 调度队列的排队时间(谁先算、谁后算、如何拼批)
- KV cache 的驻留与碎片(显存水位、可用块、回收/分配成本)
vLLM 的工程价值在于:它把这两件事做成了“可控旋钮”,也就意味着——你把旋钮拧到极致时,系统会以尾延迟或稳定性作为代价支付账单。
vLLM 的关键资源模型:并发、KV cache 与“水位线管理”
理解 vLLM 的资源本质,抓住以下两点即可:
并发不是免费:每个活跃序列都要占 KV cache
对于大多数 Transformer 推理而言,KV cache(Key-Value Cache) 是决定显存曲线的主项。直观来看:
- 并发越高 → 同时活跃的序列越多 → KV cache 占用越大
- 上下文越长 / max_model_len 越大 → 单序列 KV cache 上限越高
- 输出越长 → 序列更久才结束 → KV cache 驻留时间更长
这意味着平台在讨论“QPS 能跑多少”之前,必须先回答一个更硬的问题:在给定显存与模型配置下,你能稳定承载多少“活跃 token”。吞吐的上限通常不是算力,而是显存与调度策略共同限定的“并发安全区”。
连续批处理提升吞吐,但会把尾延迟变成“队列治理问题”
vLLM 的 continuous batching(连续批处理)会不断把新 token 合入批次,从而提高 GPU 利用率。但连续批处理天然引入一个事实:
- 更大的批次通常意味着更高的平均吞吐
- 但也意味着单个请求更可能等待更久才能进入下一次计算窗口
因此,尾延迟治理的本质是"不要让队列变成黑箱"。你需要明确哪些请求优先、如何限制长尾请求对系统的占用、以及何时应该牺牲一点吞吐换取稳定性。
下图展示 vLLM 的资源模型以及吞吐与尾延迟的物理后果。左侧展示吞吐优化的三种手段(提高并发、加大批处理),代价是尾延迟风险(队列等待时间上升、显存接近水位线)。中间蓝色区域展示 KV cache 作为显存硬边界的三条规则:并发越高 KV cache 占用越大、上下文越长单序列上限越高、输出越长驻留时间越长。黄色区域展示 continuous batching 将尾延迟变成队列治理问题。右侧虚线框总结两个资源瓶颈:调度队列排队时间、KV cache 驻留与碎片。底部核心洞察:vLLM 把队列与显存做成了可控旋钮,拧到极致时以尾延迟或稳定性支付账单。
平台应该怎么定义资源请求:从"GPU 张数"走向"可运营的推理配额"
Kubernetes 的原生资源模型让我们习惯于“请求 1 张 GPU”,但在线推理的真实瓶颈并不等价于“1 张卡”。如果平台只提供“GPU=1”这一维度,团队会通过提高并发/批处理去榨干吞吐,最终把尾延迟与抖动成本转嫁给平台。
更可运营的做法是:把推理资源请求拆成三层配额语义(不一定都暴露给用户,但平台需要内部具备):
下图展示三层资源配额语义。第一层(蓝色区域):硬资源(GPU + 显存安全余量),强调显存 headroom 不是浪费而是尾延迟稳定性的保险。第二层(黄色区域):软配额(并发与上下限),包括最大并发序列数和最大上下文/输出长度,如果只控制 QPS 不控制上下文会得到平时很好偶尔爆炸的系统。第三层(绿色区域):体验配额(SLO 绑定的可用吞吐 - Goodput),定义为在满足指定尾延迟 SLO 的前提下系统能持续输出的有效吞吐,如果 tokens/s 上去但 Goodput 没上去说明只是把抖动转移给用户。底部核心理念:从 GPU 张数走向可运营的推理配额,让资源请求与验收具备可讨论可评审的基础。
硬资源:GPU + 显存安全余量(headroom)
- GPU:依然以整卡(或 MIG slice)作为硬隔离单元最可靠。
- 显存 headroom:不要把显存用到“刚好满”。平台需要设定一个保守水位线,例如只允许 vLLM 使用显存的某个比例,其余留作碎片、峰值波动、驱动/通信开销与异常回退空间。这不是“浪费”,而是尾延迟稳定性的保险。
软配额:并发与上下限(Admission Control)
平台建议明确以下两个“准入阈值”:
- 最大并发序列数(max concurrent sequences):超过就排队/拒绝,而不是让系统进入不可预测区。
- 最大上下文长度与最大输出长度(prompt/output cap):它们决定 KV cache 的最坏情况上界。
如果你只控制 QPS,不控制上下文与输出长度,你会得到一个“平时很好、偶尔爆炸”的系统。
体验配额:SLO 绑定的“可用吞吐”(Goodput)
吞吐指标最好不要只看 tokens/s,而要引入 Goodput:在满足指定尾延迟 SLO 的前提下,系统能持续输出的有效吞吐。 这会迫使所有优化回到同一个目标:既要快、也要稳。
验收指标怎么定:别只测“平均”,要把尾延迟拆开测
为了让平台具备可运营性,在线推理的验收建议至少包含四组指标,且每组都要给出 p50/p95/p99(或至少 p95/p99):
在介绍各项指标前,先说明其作用和意义。
- TTFT(Time To First Token,首 token 延迟):直接反映“队列与调度”是否健康。吞吐拉满时,TTFT 往往最先恶化。
- TPOT(Time Per Output Token,输出 token 间隔):反映生成阶段的稳定性。共享环境中,TPOT 抖动通常意味着算力争用、带宽争用或调度切片不稳定。
- Throughput(tokens/s 与 Goodput):
- tokens/s 反映吞吐能力
- Goodput 反映“在 SLO 下的吞吐”。如果 tokens/s 上去了但 Goodput 没上去,说明你只是把抖动转移给用户。
- 可靠性:拒绝率、OOM/回退率、重试率。尾延迟失控往往伴随拒绝、OOM、或回退策略触发。验收必须把这些作为“失败”而不是“另一个状态”。
实操建议:验收用例必须覆盖至少三种负载形态:
- 短 prompt + 短输出(典型聊天)
- 长 prompt + 短输出(RAG/长上下文检索)
- 短 prompt + 长输出(长文生成)
- 以及"混合形态"作为压测常态。
下图展示验收指标体系。顶部四个关键指标:TTFT(首 token 延迟)直接反映队列与调度健康、吞吐拉满时最先恶化;TPOT(输出 token 间隔)反映生成阶段稳定性、抖动意味着算力带宽争用;Throughput 包含 tokens/s 和 Goodput(SLO 下的吞吐),tokens/s 上但 Goodput 不上说明只是把抖动转移给用户;Reliability(可靠性)包括拒绝率、OOM/回退率、重试率,尾延迟失控往往伴随这些事件。中间展示四种验收负载形态:短 prompt+ 短输出(聊天)、长 prompt+ 短输出(RAG)、短 prompt+ 长输出(长文生成)、混合形态(压测常态)。底部绿色区域说明为什么要看 p50/p95/p99。底部核心原则:吞吐优化的收益是平均意义上的,尾延迟是最坏情况的,如果验收不看 p99 会把问题留给生产。
最容易引发系统性抖动的配置组合(以及为什么)
下面列出一些在 vLLM + Kubernetes 场景中最常见的“看似合理、实则高风险”的组合。它们的共同特点是:把系统推到了“显存水位线 + 队列效应”的非线性区间。
在介绍每种配置前,先说明其风险和表现。
把显存利用率设得过满 + 提高并发
- 典型表现:平时很稳,一旦某些请求变长或并发上冲,就出现 TTFT 飙升、TPOT 抖动、甚至 OOM。
- 原因:KV cache 分配与碎片会在高水位时变得脆弱,任何波动都会触发“分配失败/回退/拒绝”链式反应。
- 规避策略:设定显存 headroom,宁可牺牲一点峰值吞吐,也要保证“高峰不崩”。
混合长短请求但不做队列隔离或优先级
- 典型表现:短请求的 p99 被长请求拖到不可用;用户感知为“突然卡住”。
- 原因:continuous batching 在追求整体吞吐时,可能让短请求等待更久以换取更大的批次,长请求又延长了序列驻留时间。
- 规避策略:至少做到“按模型/租户/请求类型”分队列;必要时分开部署两套参数(高吞吐池 vs 低延迟池)。
上下文长度上限过大且不做准入
- 典型表现:少量超长上下文请求就能让整个服务抖动。
- 原因:超长 prompt 直接把 KV cache 推到最坏情况上界,导致其他请求在显存/调度上被挤压。
- 规避策略:对外暴露明确的 context cap;超长上下文走单独服务池或异步通道。
多租户共享同一张卡,但隔离手段不足
- 典型表现:单个租户的突刺会影响所有租户;TPOT 抖动明显。
- 原因:算力、显存带宽、PCIe/CPU/网络都可能成为干扰路径。没有足够隔离时,推理服务会出现“看不见的邻居”。
- 规避策略:
- 强隔离场景优先 MIG(Multi-Instance GPU,MIG)或至少整卡独占
- 共享场景必须配合严格准入(并发、上下文、输出)和监控/计量,否则就是“谁先抢到算谁的”
盲目追求更大 batch / 更高并发,而不绑定 SLO
- 典型表现:压测报告很好看,但线上体验很差。
- 原因:吞吐优化的收益是平均意义上的,尾延迟是最坏情况的;如果验收不看 p99,你会把问题留给生产。
- 规避策略:把 Goodput 作为核心 KPI:SLO 达标前提下的吞吐,才是平台应该交付的吞吐。
一套可落地的“推理资源契约”:让团队与平台可对齐
为了让推理平台可运营,建议把“资源请求与验收”固化成一份契约(可以写进平台文档或服务模板),至少包含:
- 模型与服务形态:模型版本、精度、并行策略(如有)
- 用户侧限制:最大上下文、最大输出、并发配额、速率限制
- 平台侧保障:GPU/MIG 规格、显存 headroom、隔离策略、调度队列策略
- SLO 指标:TTFT/TPOT 的 p95/p99 目标,允许的拒绝率/错误率
- 验收用例:短/长/混合负载的固定脚本与基线阈值
- 容量结论:在 SLO 下的 Goodput(可用吞吐)与可承载并发区间
这样做的收益是:当某个团队说“我们要更高 QPS”,平台可以回答“在保持 p99 TTFT=… 的前提下,你可以提高并发到 X;如果你要更高,需要牺牲尾延迟或迁移到高吞吐池”,而不是陷入“感觉应该可以”的争论。
总结
vLLM 把在线推理的资源真相讲得很直白:
- 吞吐来自并发与批处理,但代价是队列等待与尾延迟风险。
- 显存(KV cache)是并发的硬边界,水位线决定稳定性。
- 共享与隔离策略会放大或抑制这种边界:隔离不足时,抖动是系统性问题,不是“调参没调好”。
- 平台要交付的不是“峰值 tokens/s”,而是“在 SLO 下的可用吞吐(Goodput)”。
在后续实验章节中,我们会把这些结论落到可复现实验:如何用不同的共享/隔离方案(整卡、MIG、共享数据平面等)去跑同一组 vLLM 负载,并用 TTFT/TPOT/p99 与 Goodput 给出可验证的取舍曲线。这样,你的 GPU 平台能力才能从“能跑”升级为“可预测、可验收、可治理”。