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

102 lines
3.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
```rust
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
```rust
pub struct Scheduler {
waiting: VecDeque<Sequence>, // 等待队列
running: Vec<Sequence>, // 正在执行
max_batch_size: usize, // 最大并发数
block_manager: BlockManager, // KV cache 资源管理
}
```
### 调度循环
```rust
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
- [x] 单请求 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 上,不会积压。