背景问题:LLM Serving 的瓶颈不只是模型慢
大模型在线推理看似只是一个简单接口——用户发起请求,服务端返回 token。但在生产环境里,真正消耗成本的是 GPU 计算周期、KV Cache 显存、请求排队时间和长尾输出长度 之间的组合问题。
传统推理服务习惯沿用常规深度学习推理的批处理方式:先收集一批请求组成 batch,然后一起执行。这个方法在图像分类、Embedding 或固定长度模型里很有效,因为每个样本的计算形状相对稳定。但文本生成是 自回归解码——每个请求的输入长度不同,输出长度也不同,有的请求几步就结束,有的会持续生成几百个 token。
如果服务端仍按”整批请求一起开始、一起等待”的方式工作,GPU 会被两类浪费拖垮:
- 短请求提前完成后,batch 槽位不能立刻被新请求使用;
- 为对齐不同序列长度,系统可能处理大量 padding 或等待最慢请求。
Continuous Batching 的价值就在这里:它把调度粒度从”请求级”下沉到”解码迭代级”,让服务端在每一轮生成时重新整理正在运行的请求。
核心原理:从 Request-Level Scheduling 到 Iteration-Level Scheduling
Continuous Batching 也常被称为 iteration-level scheduling 或 in-flight batching。ORCA 论文把这个问题讲得很清楚:生成式 Transformer 的每一步解码都是一次迭代,调度器不必把一个请求从开始到结束都固定在同一个 batch 里,而是可以在每个迭代边界重新决定当前 batch 由哪些请求组成。
简化后的执行流程如下:
- 请求进入队列,先执行 prefill,生成首个 KV Cache;
- 调度器选择一批可运行请求进入 decode batch;
- 模型执行一次 decode forward pass,通常生成每个序列的下一个 token;
- 已完成请求退出,释放对应 KV Cache 和 batch 槽位;
- 新请求或等待中的请求补位进入下一轮 decode;
- 重复步骤 3–5,直到请求结束或达到限制。
这个机制的核心不是”把 batch_size 调大”,而是 让 batch 在运行过程中持续变化。系统不再等待一整批请求全部结束,而是在每个 token 生成周期上做调度。这样 GPU 更少处于空转状态,请求也不必等待一个固定 batch 生命周期结束后才能进入执行阶段。
Hugging Face Text Generation Inference 明确把 continuous batching 列为提升总吞吐的核心能力;TensorRT-LLM 则使用 in-flight batching 这个名称,强调动态管理 context 与 generation 阶段,以提高 GPU 利用率并降低排队等待。
工程落地:不能只打开一个开关
在 vLLM、TGI、TensorRT-LLM 这类推理服务里,Continuous Batching 通常已被内置,但生产落地仍然要围绕几个控制面做配置。
1. 区分 Prefill 和 Decode
Prefill 阶段处理完整 prompt,计算量通常和输入长度相关;decode 阶段每轮生成少量 token,但会反复执行。Continuous Batching 最直接优化的是 decode 阶段的槽位利用率,但 prefill 太重时仍然会压垮 TTFT。
线上不能只看 tokens/s,还要拆开看:
| 指标 | 构成 |
|---|---|
| TTFT | queue waiting + prefill scheduling + first token generation |
| TPOT | decode scheduling + model forward + sampling + streaming overhead |
如果长 prompt 很多,单纯提升 decode batch 效率并不能解决首 token 慢的问题,可能还需要:
- Prefix cache 加速 prefill;
- Prefill/Decode 分离部署;
- 请求限流或 prompt 长度分级。
2. 控制最大并发序列数,而不是盲目调大 Batch
Continuous Batching 会让更多请求同时驻留在 GPU 上,但每个请求都需要 KV Cache。并发序列数越高,显存压力越大。生产参数通常要围绕以下指标做压测:
serving_tuning:
max_running_requests: 64
max_total_tokens: 32768
max_input_tokens: 8192
max_output_tokens: 1024
admission_control: true
overload_policy: "queue_or_reject"
这段配置不是某个框架的固定格式,而是容量规划思路:限制正在运行的请求数、限制总 token 预算、限制单请求最大输入输出长度,并定义过载策略。
3. 和流式输出配合
Continuous Batching 常和 streaming 一起使用。流式输出可以降低用户感知等待时间,但也会引入网络 flush、连接管理和客户端消费速度差异。服务端需要防止慢客户端拖住资源,常见做法包括:
- 输出缓冲上限;
- 连接超时;
- 取消请求回收;
- 客户端断开后的 KV Cache 释放。
适用场景:高并发、长输出、GPU 利用率不足
Continuous Batching 适合以下场景:
- 多用户共享同一模型服务,QPS 波动明显;
- 输出长度差异大,静态 batch 容易被长请求拖住;
- GPU 利用率不稳定,但请求排队时间较高;
- 在线 Chat、代码补全、Agent 后端、批量内容生成等持续生成 token 的业务;
- 已经使用 vLLM、TGI、TensorRT-LLM 等支持动态调度的推理框架。
它不适合被理解为”所有延迟问题的通用解”。如果系统瓶颈是网络传输、上游业务串行调用、模型加载慢、CPU tokenizer 饱和,或者请求量太低导致 GPU 本来就很空,Continuous Batching 的收益会变小。
常见误区
误区一:吞吐提升就等于用户体验提升
吞吐提升通常意味着单位 GPU 可以处理更多 token,但用户体验更依赖 TTFT、TPOT 和尾延迟。如果为追求吞吐把并发序列数推得过高,短请求可能排队更久,P95/P99 延迟反而变差。
误区二:Batch 越大越好
更大的 batch 可以提高 GPU 利用率,但也会增加显存占用、调度复杂度和单轮迭代时间。最优点通常来自压测,而不是经验值。需要按模型大小、prompt 长度、输出长度、GPU 型号和业务 SLO 做组合测试。
误区三:忽略 KV Cache 的显存增长
Continuous Batching 让更多请求同时运行,KV Cache 占用会随着输入长度和已生成 token 增长。没有 admission control 的系统很容易在流量高峰触发 OOM,导致服务整体抖动。
误区四:只看平均值
LLM Serving 的问题经常藏在长尾里。平均 tokens/s 好看,不代表用户请求稳定。上线前必须看 P95/P99 TTFT、TPOT、排队时间和取消率。
上线检查清单
容量与压测
- 使用真实 prompt 长度分布,而不是只用短 prompt;
- 覆盖短输出、长输出、混合输出三类请求;
- 分别测试低 QPS、中等 QPS、接近饱和 QPS;
- 记录最大可持续 tokens/s,而不是只看峰值;
- 单独测试流式输出和非流式输出。
关键监控指标
| 指标 | 含义 |
|---|---|
request_queue_time_seconds | 请求排队等待时间 |
llm_ttft_seconds | 首 token 延迟 |
llm_tpot_seconds | 每输出 token 平均延迟 |
running_requests | 正在运行的请求数 |
waiting_requests | 等待中的请求数 |
gpu_utilization | GPU 利用率 |
kv_cache_usage_ratio | KV Cache 使用率 |
prefill_tokens_per_second | Prefill 阶段吞吐 |
decode_tokens_per_second | Decode 阶段吞吐 |
request_abort_total | 取消请求总数 |
request_oom_total | OOM 次数 |
其中 KV Cache 使用率 和 waiting_requests 特别重要。前者决定服务是否接近显存风险,后者反映调度器是否已经进入拥塞状态。
过载保护
- 限制最大输入 token;
- 限制最大输出 token;
- 为不同业务设置不同优先级或队列;
- 对超长请求单独路由;
- 超过阈值时拒绝请求,而不是让所有请求一起变慢;
- 客户端断开后立即取消生成并释放资源。
选型建议
如果目标是快速上线通用 LLM 服务,vLLM 和 TGI 都是常见选择。TGI 强调 continuous batching、streaming、Prometheus metrics 和 OpenTelemetry tracing;vLLM 生态则围绕 PagedAttention、调度、KV Cache 管理和高吞吐服务持续迭代。TensorRT-LLM 更偏向 NVIDIA GPU 上的深度优化,in-flight batching、paged attention、量化和多 GPU 推理能力更适合对性能有强要求的部署。
实际选型时,不建议只问”哪个框架最快”。更合理的问题是:
- 我的业务更看重 TTFT 还是总吞吐?
- 请求长度分布是否稳定?
- 是否需要 OpenAI-compatible API?
- 是否需要多 GPU / 多节点?
- 团队能否维护 TensorRT-LLM 这类更底层的优化栈?
- 是否已经有监控体系接入 Prometheus / OpenTelemetry?
常见问题 (FAQ)
Continuous Batching 会改变模型输出吗?
通常不会。它改变的是服务端调度方式,不改变模型权重和采样逻辑。但如果上线时同时调整了采样参数、最大输出长度、停止条件或框架版本,输出仍可能出现差异,所以灰度验证仍然必要。
它和 PagedAttention 是什么关系?
两者解决的问题不同但经常一起出现。Continuous Batching 解决请求调度与 GPU 利用率问题;PagedAttention 解决 KV Cache 显存管理问题。没有高效 KV Cache 管理,持续增加运行中请求会更容易遇到显存瓶颈。
小流量服务是否需要 Continuous Batching?
低 QPS 场景收益有限,因为 GPU 本来就没有足够请求可批处理。但保留这个能力仍有价值:当流量突然上升或多个租户共享服务时,它可以减少 GPU 空转和排队抖动。
参考资料
- ORCA: A Distributed Serving System for Transformer-Based Generative Models — USENIX OSDI 2022
- Hugging Face Text Generation Inference Documentation
- Hugging Face Inference Endpoints - TGI Engine
- NVIDIA TensorRT-LLM Overview
- NVIDIA TensorRT-LLM Batch Manager
- BentoML: Static, Dynamic and Continuous Batching
- vLLM Metrics Design