调度
主线位置:「一个请求的一生」里分词之后、前向之前的那一段:排队、进批、每一步拿多少 token。两个引擎的调度器都是 Python,跑在引擎核心 / 调度器进程里;SGLang 的调度器在按前缀排序时会调用前缀树,那棵树的核心在 Linux 上默认是 Rust(见 树核心)。
版本:vLLM v0.32.0、SGLang v0.5.22
两个引擎都做连续批处理:每一步重新决定这一批里有谁,请求随到随进、做完就走。区别在「一步」的形状:
- vLLM v1:没有预填充阶段和解码阶段之分。每个请求只有「已算 token 数」和「总 token 数」,调度器在一个 token 预算内让它们往前追。解码(每个请求 1 个 token)和预填充块在同一次前向里。
- SGLang:每一步先试着组一个预填充批,组得成就只跑预填充;组不成才让运行中的请求解码一步。
--enable-mixed-chunk打开后,解码请求才会搭在预填充批里一起跑,默认关。
这个差别决定了长 prompt 混进来时短请求的解码会不会停。本部分三页:
- 连续批处理:vLLM
schedule()先排 running、再排 waiting 的两段循环;token 预算max_num_batched_tokens与并发上限max_num_seqs的默认值(按显存和入口分档,24 GB 卡上的 API 服务是 2048 和 256);SGLang 的预填充批与解码批、PrefillAdder、调度策略(默认fcfs,lpm等按前缀树排序)。 - 分块预填充:长 prompt 切块后每步的预算怎么分;两边的开关与默认值;一个按源码规则写的离散步模拟:vLLM 分块时短请求每步都拿到解码 token,SGLang 默认配置下长 prompt 的每个块都让解码停一步,打开混合块后不停。效果的毫秒数待实测(C3-4)。
- 抢占:KV 块不够时,vLLM v1 从 running 尾部踢请求、清零重算、放回 waiting 队首;SGLang 在解码前检查显存,按「输出最少、输入最长」先 retract,释放的 KV 不进前缀树,放回队尾,并把新 token 估计比调高。两边单机都没有换出到 CPU(SGLang 只在 PD 分离的解码端备份 KV)。
flowchart LR
wait["等待队列"] --> sched{"调度器一步"}
run["运行中"] --> sched
sched -->|"vLLM:一个预算统一分"| mix["解码 + 预填充块 同一次前向"]
sched -->|"SGLang:有预填充就先跑"| pre["预填充批"]
sched -->|"SGLang:没有预填充"| dec["解码批"]
sched -->|"KV 不够"| out["抢占 / retract 回等待队列"]
out --> wait
💬 关联讨论
发起讨论加载中...