Disaggregation 队列、bootstrap 与 transfer 阶段#
这章解决什么问题#
前面的调度与内存章节大多默认请求在单条本地 runtime 主线里排队、等待、forward、完成。但一旦进入 prefill/decode disaggregation,队列形状会明显变化:不再只是一个 waiting_queue,而会出现 bootstrap queue、prealloc queue、transfer queue,甚至 retracted queue。没有这一章,调度与内存章节仍然会偏单机稳态,而覆盖不到分离式运行时的真正队列结构。
为什么这层值得单独成章#
从 scheduler.py 和 req_time_stats.py 看,disaggregation 不是纯传输细节,而是会直接改变:
- 请求进入哪个队列
- 哪些阶段被记录进时间线
- queue time 如何拆解
- transfer / bootstrap 是否成为新的瓶颈
这意味着它理应属于调度与内存主线,而不是只留在 disaggregation 实现目录里。
一张图:disaggregation 模式下,请求不再只经过一个 waiting queue#
这张图解决的理解障碍是:很多读者脑中默认只有 waiting_queue -> forward,但 disaggregation 会把中段拆成更多阶段。
flowchart LR
Req["incoming req"] --> P0["prefill bootstrap queue"]
P0 --> P1["wait queue"]
P1 --> PF["prefill forward"]
PF --> T["transfer queue / KV transfer"]
T --> D0["decode prealloc queue"]
D0 --> D1["decode transfer queue"]
D1 --> DW["decode waiting"]
DW --> DF["decode forward"]图比纯文字多解释的一点是:disaggregation 不是只在 forward 之后多一个传输动作,而是把整个排队结构改写成多段阶段链。
scheduler.init_disaggregation() 真正改变了什么#
scheduler.py 会根据 disaggregation_mode 初始化不同结构:
- decode 模式会建立
DecodeTransferQueue、DecodePreallocQueue - prefill 模式会建立
PrefillBootstrapQueue
这说明 disaggregation 不是某个 runtime flag 轻微影响行为,而是从 scheduler 初始化时就改变了队列拓扑。
_add_request_to_queue(...) 为什么是阅读入口#
这段代码非常能说明问题:
DisaggregationMode.NULL:进普通waiting_queueDisaggregationMode.PREFILL:进disagg_prefill_bootstrap_queueDisaggregationMode.DECODE:进disagg_decode_prealloc_queue
并且在进入不同队列时,还会更新不同的 ReqTimeStats 阶段:
set_prefill_bootstrap_queue_entry_time()set_decode_prealloc_queue_entry_time()set_retract_time()等
这说明 queue 类型和时间线语义是同步变化的,不是两个独立系统。
ReqTimeStats 为什么在这里特别重要#
req_time_stats.py 对 disaggregation 路径做了比普通请求更细的拆分:
prefill_bootstrapprefill_transfer_kv_cachedecode_preallocdecode_transferdecode_transferred
这说明系统不是只想“支持 transfer”,而是想让 transfer 成为可被独立观察、可被独立归因的阶段。
也正因为这样,disaggregation 章节和前面写过的 RequestStage/ReqTimeStats 可以形成非常强的回扣。
prefill 路径与 decode 路径为什么不对称#
ReqTimeStats 文件里已经直接写出两条典型路径:
- Prefill:
bootstrap_queue -> wait_queue -> forward -> transfer_queue -> completion - Decode:
prealloc_queue -> transfer_queue -> wait_queue -> forward -> completion
这说明两条路径并不是镜像关系。prefill 更像“先建立 bootstrap,再去等和跑”;decode 更像“先预分配,再等待 transfer,再进入正常 decode waiting/forward”。
这点很值得写进书里,因为它能防止读者把 disaggregation 简化成“prefill 和 decode 只是拆成两个节点”。
transfer metrics 为什么属于调度证据而不只是网络指标#
ReqTimeStats.compute_and_observe_kv_transfer_metrics(...) 会计算:
- transfer latency
- transfer total MB
- transfer speed GB/s
这说明 KV transfer 在这里不是隐藏在底层 transport 里的黑箱,而是被提升成调度可见的瓶颈证据。
对维护者来说,这意味着:
- 如果请求慢,不一定是 scheduler 排序问题
- 也可能是 bootstrap / transfer 阶段本身在拖后腿
scheduler.is_fully_idle() 为什么还要管 disaggregation 队列#
源码里明确写了,waiting_queue 之外,bootstrap / prealloc / transfer 队列也会影响“系统是否 truly idle”的判断。因为这些队列里的请求虽然还没进入最终 forward,但它们同样代表系统仍在承担活跃工作。
这是一种非常现实的设计:调度系统不会只盯着 running batch,而会把“尚未完成 transfer/bootstrapping 的请求”也视作活跃负担。
这一层最容易出现的误判#
1. 把 disaggregation 问题当成单纯网络问题#
实际上它先表现为队列结构和时间线结构变化。
2. 以为只有 waiting_queue 影响调度#
disaggregation 场景下有多条正式队列。
3. 把 transfer 时间看成外部附加信息#
系统已经把它纳入 request stage 和 metrics 主线。
如果你怀疑问题出在 disaggregation 队列,先怎么查#
建议按这个顺序:
- 先确认当前
disaggregation_mode是NULL、PREFILL还是DECODE。 - 看请求进入的是哪一条队列,而不是只盯
waiting_queue。 - 看
ReqTimeStats当前卡在哪个 stage:bootstrap、transfer 还是真正 forward。 - 再看 transfer metrics、bootstrap room 或 offload/transfer 状态。
- 最后才深入具体 transport backend 或 staging handler。
小结#
这一章真正想补齐的,是调度与内存主线里对分离式运行时的覆盖:
- disaggregation 会改变队列拓扑。
- 队列拓扑变化会同步改变 request stage 时间线。
- 只有把 bootstrap / prealloc / transfer 阶段当成正式调度阶段看,才能真正理解这类运行时的行为。
叶王 © 2013-2026 版权所有。如果本文档对你有所帮助,可以请作者喝饮料。