Appearance
调度系统 — 概念
连续批处理(Continuous Batching)
传统静态批处理中,一个 batch 内所有请求必须全部完成才能开始新 batch。Continuous Batching 打破了这个限制:
关键优势:
- 更低的延迟:新请求无需等待整个 batch 完成
- 更高的吞吐:GPU 始终保持满载
- 更公平的调度:短请求不会被长请求阻塞
Chunked Prefill
长 prompt 的 prefill 可能非常耗时(占用大量计算资源和显存)。Chunked Prefill 将 prefill 分成多个 chunk:
好处:
- 降低 TTFT:不需要一次性处理完整个 prompt
- 混合调度:prefill 和 decode 可以在同一轮迭代中混合执行
- 显存友好:每次只需分配部分 prompt 的 KV 缓存
Preemption 策略
当显存不足以容纳所有活跃请求的 KV 缓存时,调度器会触发 preemption:
1. Re-computation(重新计算)
直接丢弃被抢占请求的 KV 缓存块。当请求恢复时,重新计算 prefill。适用于抢占时间短的场景。
2. Swapping(交换到 CPU)
将 KV 缓存块从 GPU 交换到 CPU 内存。恢复时再交换回来。适用于抢占时间较长的场景。
3. 异步抢占多帧丢弃
当使用推测解码或流水线并行时,被抢占请求可能还有多个在途(in-flight)的输出帧。旧的实现使用布尔标志只能丢弃一帧,新的计数器机制逐帧正确排空:
discard_latest_async_tokens (bool) → async_tokens_to_discard (int counter)调度器将 async_tokens_to_discard 设为 num_output_placeholders,异步调度器每帧递减计数器,确保所有在途帧被正确排空后才恢复请求。
4. KV Connector 延迟释放
KV Connector 的异步操作可能在请求从调度队列移除后仍在进行。has_finished_requests() 现在额外检查 self.requests 中是否还有等待 KV connector 延迟清理的请求,确保调度器不会过早认为"无请求"。异步调度 + PD KV consumer 场景下,请求完成时其 KV 块可能仍被在途(in-flight)的 step 引用,调度器现在将这些块的释放延迟到所有在途 step 完成后才执行,避免提前释放导致的 use-after-free。
前缀缓存与混合注意力
Marconi 式混合缓存准入
针对混合 KV 缓存(如含 Mamba / 线性注意力层),HybridKVCacheCoordinator 计算 num_uncached_common_prefix_tokens(其他 group 缓存了更长前缀,表明存在跨请求的未缓存公共前缀)。调度器据此优先把 prefill token 数截断为该公共前缀长度(按 block_size 对齐),加速公共前缀入缓存。
部分前缀缓存原语(Partial Prefix Cache)
BlockPool 新增 block→hash 反向索引(cached_block_hashes_by_block)与部分块提升机制,支持同一缓存块从 partial 状态提升为 full,为异构 block_size 与跨 group 缓存复用铺路。
稀疏保留间隔(Retention Interval)
对 sliding-window KV(DeepSeek V4)与 Mamba / 线性注意力,新增 VLLM_PREFIX_CACHE_RETENTION_INTERVAL:按固定间隔保留稀疏缓存检查点块(须为 block_size 倍数),而非密集缓存全部 token,节省显存。Mooncake 连接器同步支持该保留间隔。
请求队列与优先级
调度器维护多个请求队列:
| 队列 | 用途 |
|---|---|
| waiting | 等待 prefill 的新请求 |
| running | 正在 decode 的活跃请求 |
| swapped | 被交换到 CPU 的请求 |
调度优先级:
- 处理 swapped 队列(恢复被抢占的请求)
- 继续 running 队列的 decode
- 从 waiting 队列添加新 prefill
队列准入(server 级背压)
max_num_seqs 限制的是单个引擎/DP rank 内的并发;v0.26.1rc0 后新增 server 级准入控制(#49445):--max-num-queued-reqs(未完成请求总数上限)与 --max-num-queued-tokens(prefill 中请求的 prompt token 总量上限,可视为 TTFT QoS 阀门)在 API server 进程判定(AsyncLLM.check_admission(),计数来自前端 OutputProcessor,无需引擎往返),超限返回 HTTP 503 让 LB 换实例重试——调度器自身无感知。容量建议 data_parallel_size × max_num_seqs + 期望队列深度(#55124)。详见 topics/serving/concepts。
调度器本体的近期修复:PRIORITY 策略静默跳过请求(#49206)、P/D 分离下的抢占竞态(#50297)、num_output_placeholders 抢占下溢(#48245)、spec decode 不再把 batch pad 到 max_model_len(#53962)、推测解码 token 预算自适应(#51725,Kimi K3 DSpark 场景 TTFT 降约 60%)。
调度决策流程
调度配置参数
| 参数 | 默认值 | 含义 |
|---|---|---|
max_num_seqs | 128(SchedulerConfig);API/LLM 上下文下因 GPU 而异(如 H100 上 1024) | 最大并发序列数 |
max_num_batched_tokens | 2048(SchedulerConfig);API/LLM 上下文下因 GPU 而异(H100 上可达 8192/16384) | 每轮迭代最大 token 数 |
max_model_len | 模型默认 | 最大序列长度 |
enable_chunked_prefill | True | 是否启用 chunked prefill |
scheduling_policy | "fcfs" | 调度策略(先来先服务/优先级) |
watermark | 0.0 | KV cache 水位线(占比)。>0 时接纳 waiting/preempted 请求会保留该比例空闲块作缓冲,减少显存紧张时频繁抢占-驱逐抖动;不影响已在运行的 decode |
prefill_schedule_interval | 1 | DP 部署下每 N 步才 admit 新 prefill,跨 rank 对齐节奏,避免某些 rank 长时间做 prefill 拖慢集合通信 |
max_num_queued_reqs | None(不限制) | server 级未完成请求上限(API server 准入,#49445) |
max_num_queued_tokens | None(不限制) | server 级 prefill token 总量上限(API server 准入,#49445) |
相关概念
- Paged Attention — KV 缓存块的管理方式
- KV Cache — KV 缓存的基本概念
- Continuous Batching — 连续批处理的详细原理
- Prefix Caching — 共享前缀的 KV 缓存复用