Files
xserv/docs/12-continuous-batching.md
Gahow Wang 7d05ececa0 docs: split Phase 12 and Phase 13 into separate design documents
- docs/12-continuous-batching.md: scheduler, sequence management,
  batching strategy (currently single-request, expandable)
- docs/13-http-api.md: HTTP server, OpenAI-compatible API,
  axum architecture, SSE streaming (TODO)

Phase 12 = WHAT to compute (scheduling decisions)
Phase 13 = HOW to expose it (HTTP protocol layer)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 13:15:27 +08:00

3.4 KiB
Raw Blame History

Phase 12: Continuous Batching + Request Scheduler — Design Document

Goal

实现 iteration-level 请求调度器,支持多请求并发执行和动态 batch 管理。这是 LLM serving 系统的核心调度逻辑。

核心概念

Static Batching vs Continuous Batching

Static朴素:

Batch 1: [req1, req2, req3] → 等所有完成才开始下一批
问题: req1 10 token 就完了req3 要 200 token → req1 的 slot 空转

Continuous本阶段目标:

Iteration 1: [req1, req2, req3]  → req1 完成! slot 释放
Iteration 2: [req2, req3, req4]  → req4 立即填入
每一个 iteration一次 forward pass重新决定哪些请求参与

核心组件

Sequence

pub struct Sequence {
    pub id: SeqId,
    pub prompt_tokens: Vec<u32>,
    pub generated_tokens: Vec<u32>,
    pub status: SequenceStatus,
    pub sampling_params: SamplingParams,
    pub kv_cache_handle: KVCacheHandle,  // 该 seq 的 KV cache 资源
    pub arrival_time: Instant,
    pub output_sender: tokio::sync::mpsc::Sender<GenerateEvent>,
}

pub enum SequenceStatus {
    Waiting,       // 等待调度
    Prefilling,    // 正在 prefill
    Decoding,      // 正在逐 token decode
    Finished,      // 完成 (EOS / max_len)
}

Scheduler

pub struct Scheduler {
    waiting: VecDeque<Sequence>,      // 等待队列
    running: Vec<Sequence>,           // 正在执行
    max_batch_size: usize,            // 最大并发数
    block_manager: BlockManager,      // KV cache 资源管理
}

调度循环

loop {
    // 1. 回收已完成的 sequence释放 KV cache
    // 2. 从 waiting 中 admit 新请求(如果有空位+显存)
    // 3. 对 running 中的所有 seq 做一步 forward
    //    - 新加入的做 prefill
    //    - 已在运行的做 decode
    // 4. 对每个 seq 的 logits 做 sampling
    // 5. 发送新 token / 完成信号
}

当前状态 (Phase 12 初版)

当前实现是 单请求顺序执行max_batch_size=1是 continuous batching 的退化形式:

  • 一次只处理一个请求
  • 完成后才接受下一个
  • 无 preemption、无 batching

这是合理的起步——先跑通单请求 E2E后续扩展为真正的并发 batching。

后续扩展 (Phase 15+)

  1. 多请求 batch forward: 将多个 seq 的 token 拼接为一个 batch 输入
  2. Prefill-Decode 分离: prefill (compute-bound) 和 decode (memory-bound) 分开调度
  3. Preemption: 显存不足时暂停低优先级 seq
  4. 动态 batch size: 根据 KV cache 使用量调整

Test Plan

  • 单请求 E2E: 提交请求 → 收到 token 流 → 完成信号
  • (后续) 多请求并发: 提交多个请求,验证都能正确完成
  • (后续) 短请求完成后新请求立即加入

Takeaways

  1. 单请求是 continuous batching 的特殊情况 (batch_size=1):当前实现的 engine 循环已经是正确的调度结构——receive request → prefill → decode loop → done → next request。扩展为多请求只需在 decode loop 中处理多个 sequence。

  2. Engine 在独立 OS thread 上跑是正确的设计GPU 操作是同步阻塞的cudaDeviceSynchronize如果放在 tokio runtime 中会 block 整个 async runtime。独立线程 + channel 通信是标准模式。

  3. std::sync::mpsc::SyncSender(capacity=1) 实现了天然的背压:当 engine 忙时,新请求会 block 在 channel send 上,不会积压。