Continuous Batching 调度策略
Continuous Batching(连续批处理)是 vLLM 实现高吞吐推理的关键调度策略,它打破了传统 Static Batching 的限制,使得新请求可以在任意迭代步加入批次,已完成请求可以立即释放资源。
Static Batching 的局限
传统 Static Batching 的工作方式:
- 收集一批请求直到批次满或超时
- 整批执行直到所有请求完成生成
- 短请求必须等待长请求,造成 GPU 空闲
假设批次中有 1 个需要 500 Token 的请求和 7 个需要 50 Token 的请求,Static Batching 需要等待最长的请求完成,GPU 利用率仅约 25%。
Continuous Batching 原理
Continuous Batching 在每个迭代步进行调度决策:
- Prefill 阶段:新请求加入批次,计算其 Prompt 的 KV Cache
- Decode 阶段:已有请求各生成一个 Token
- 驱逐:已完成的请求立即从批次中移除
- 补充:从等待队列中选取新请求填补空位
python
# Continuous Batching 调度伪代码
class ContinuousBatchScheduler:
def __init__(self, max_num_seqs=256):
self.max_num_seqs = max_num_seqs
self.running = [] # 正在运行的请求
self.waiting = [] # 等待队列
def schedule(self, block_manager):
# 1. 移除已完成的请求
self.running = [r for r in self.running if not r.finished]
# 2. 释放完成请求的 KV Cache
for req in self.running:
if req.finished:
block_manager.free(req.id)
# 3. 从等待队列补充新请求
while len(self.running) < self.max_num_seqs and self.waiting:
req = self.waiting[0]
if block_manager.can_allocate(req):
block_manager.allocate(req.id, req.num_tokens)
self.running.append(req)
self.waiting.pop(0)
else:
break # 显存不足,停止补充
return self.running调度策略对比
| 策略 | 空闲等待 | 显存利用率 | 实现复杂度 |
|---|---|---|---|
| Static Batching | 高 | 低 | 低 |
| Dynamic Batching | 中 | 中 | 中 |
| Continuous Batching | 低 | 高 | 高 |
调度优先级
vLLM 默认采用 FCFS(先来先服务)策略。对于需要 SLA 保证的场景,可以配置优先级调度,确保高优先级请求优先获得计算资源。
Preemption 机制
当显存不足以接纳新请求时,vLLM 支持 Preemption(抢占)策略:
- Recomputation:抢占低优先级请求,释放其 KV Cache,后续重新计算
- Swap:将低优先级请求的 KV Cache 换出到 CPU 内存
python
# 配置抢占策略
from vllm import LLM
llm = LLM(
model="meta-llama/Llama-2-7b-hf",
preemption_mode="recompute", # 或 "swap"
)性能影响
Continuous Batching 带来的性能提升:
- 吞吐量:相比 Static Batching 提升 2-4x
- 延迟:P99 延迟降低 30-50%
- GPU 利用率:从 40-60% 提升至 80-95%
注意事项
Continuous Batching 在 Prefill 和 Decode 混合执行时,Prefill 的计算量远大于 Decode,可能导致 Decode 请求的延迟抖动。vLLM 通过 Chunked Prefill 机制缓解此问题。