SGLang · Prefill / Decode Disaggregation
PD 分离请求生命周期
单请求 100k token、bs1、DeepSeek-V4.1-Flash、B300 3P1D 现网配置。
图中粗线为当前场景的关键路径,灰色为可并行或被掩盖的部分;队列以
有界
隐式
标注在对应步骤上。
01 · 请求时序
Client → Gateway → P / D,SVG 时序图
架构与队列 02队列在系统中的物理位置 · 状态来自实测
实测卡过(积压 / 成为瓶颈)
实测容量触顶(拒绝 / 抢占)
实测未见积压
未启用 / 无量化数据
琥珀色外圈 = 当前场景最可能触及
02.1 队列卡点 按路径位置排列,点密度控制信息量
队列卡点
颜色 = 实测状态;圆点 = 压力等级(3 最高)
队列实测记录 来自巡检 / 压测 / trace 复盘
| 队列 | 状态 | 观测证据 | 时间 |
| Q1 | 容量触顶 | H20 4P3D 峰窗重放:窗口自然并发 ~1360 vs cap 256,synth429 87.4%;g9i 侧 p-queue-reqs-cap=5 / hold 10s | 2026-10-21 |
| Q2 | 实测卡过 | t15 多轮 cache-hit 回放 TTFT p50 8.3s,P/D 各 100% 单核,栈在 apply_jinja_template → tokenizer.encode;--tokenizer-worker-num 8 后 3.9s(-53%) | 2026-09-21 |
| Q5 | 实测卡过 | 突发窗口 P #queue-req 52–93;H20 4P3D TTFT p50 89.9s 几乎全是排队;L3 wait_complete 期间请求停在此队列 | 09-30 / 10-21 |
| Q6 | 未见积压 | #inflight-req 6–29,随 P 计算同步起伏;传输完成后即出队,未独立成为瓶颈 | 2026-09-30 |
| Q7 | 未见积压 | 车道排队 p50 1.2ms / p99 20ms,实际传输 p50 3.6ms,忙碌 ≤5%。但单 D 实例下 hash 退化,90 分钟内仅 2/16 车道有 span | 2026-09-30 |
| Q8 | 容量触顶 | max_running=256 把 decode 批量钳在 ~190(TPOT 假饱和);10-18 扩到 300;KV 残血时启动即 leaves no room 崩溃循环 | 10-11 / 10-18 |
| Q9 | 堆高非积压 | 峰值 49 条,同期 prealloc=0、传输 p50 3.6ms、排队 p50 1.2ms;确认是「P 还没算完」的镜像 | 2026-09-30 |
| Q11 | 容量触顶 | 3 台 D 合计仅 192 个 decode 槽,成为峰窗硬瓶颈;出现 retraction | 2026-10-21 |
| Q3 | 无数据 | IPC 静默,未单独量化;scheduler 长 step 时会滞后 | — |
| Q4 | 无数据 | 未观测到握手队列深度(10-13 p2 楔死是 forward 卡死,不是 Q4 积压) | — |
| Q10 | 未启用 | V4.1 hisparse 与 decode radix 互斥,当前 D 无 hicache | — |
| Q12 | 无数据 | 单进程 detok,无独立指标;高输出速率下是否滞后未做实验 | — |
| Q13 | 无数据 | 未做慢客户端反压实验 | — |
关键路径耗时 03对数刻度,右侧为线性占比
队列清单 04Q1–Q13
| # | 位置 | 队列 | 容量 / 上限 | 指标 | 对应时序 | 100k 单请求占用 | 100k 下的表现 |
理论瓶颈 05100k token,单请求关键路径
| 场景 | 关键路径 | 瓶颈 | 队列 | 数值 | 占比 | 单实例吞吐上限 |
| 冷启动 | ≈ 1.71 s | P prefill 计算 | Q5 | 1.55 s | 91% | 0.62 req/s(2 GPU) |
| L3 命中 | ≈ 0.60 s | L3 → L2 prefetch | Q5 | 0.43 s(8 lane 0.15 s) | 72% | 2.3 req/s;8 lane 6.7 |
| L1/L2 命中 | ≈ 0.17 s | tokenize + 模板 | Q2 | 120 ms(60–200) | 71% | 8.3 req/s per worker |
| L1/L2 + optimistic | ≈ 0.15 s | tokenize + 模板 | Q2 | 120 ms | 78% | 同上 |
| 高并发(命中) | — | KV 传输带宽 | Q7 | 190 MB ÷ 26 GB/s | — | ≈ 137 req/s |
| 高并发(冷启) | — | P 队列 / chunk 预算 | Q4Q5 | 100k > 65536 | — | 由 max_prefill_tokens 与 KV 池决定 |
瓶颈随命中层级左移:冷启动看计算(1.55s,91%)→ L3 命中看 prefetch(0.43s,72%)→
L1/L2 命中看 tokenize(120ms,71%)。传输在任何单请求场景都不是瓶颈;
只有命中吞吐接近 ~137 req/s(单 P 实例)时才会成为上限。
L3 命中不是直达 device:路径是 storage → host 池(L2) → device(L1)。
HiCacheController.prefetch() 的注释即 “storage backend to host memory”,
之后才由 init_load_back() 做 H2D。100k 的 0.43s 中,L3→host 占 ~430ms,
L2→L1 的 DMA 只有 ~2–3ms。因此 L2 必须有足够空间,否则 prefetch 回来的页会在 load_back 前被驱逐。
Jaeger trace 粒度 06request 级 + 阶段级,无 kernel 级
Jaeger 覆盖矩阵
每行阶段 × 每列可观测信号
prefill Req 9f3a1c2d root · role=prefill
├─ tokenize L1 含 chat template / 请求转换
├─ api_server_dispatch L2
├─ prefill_prepare L1
├─ prefill_bootstrap L1 ← Q4
├─ prefill_waiting L1 ← Q5(含 L3 prefetch 等待)
├─ prefill_forward L1
│ └─ chunked_prefill L3 每 chunk 一条
├─ prefill_transfer_kv_cache L1 ← Q6
│ └─ mooncake_send L1
│ └─ mooncake_worker_send L1
│ └─ mooncake_worker_send_session L2 ★ 真实传输
└─ (Sender Req 9f3a1c2d) 独立 trace · role=Sender
decode Req 9f3a1c2d root · role=decode
├─ decode_prepare L1 ← Q8
├─ decode_bootstrap L1 ← Q4 注册
├─ decode_waiting L1
├─ decode_transferred L1 ← Q9
├─ decode_forward L1
├─ decode_loop L3 ★ 每个 token 一条
│ ├─ spec_draft L2 dspark draft
│ └─ spec_verify L2 dspark verify
└─ fake_output L3 首 token 打底
粒度:请求级 + 阶段级,最小到「一个 scheduler step / 一个 chunk」;没有 kernel、没有算子。
要看 kernel 用 nsys / torch profiler,不要指望 Jaeger。
- 量:
decode_loop 每个 decode step 一条。4k 输出无投机时 ≈ 4k span/请求;dspark 下约 1k step ×(loop+draft+verify)≈ 3k span/请求。高 QPS 下是 badger 的主要压力来源。
- 默认级别:
SGLANG_TRACE_LEVEL=3;level 4(run_batch_cpu)默认被丢弃,要看得显式调高。
- 采样:没有采样率参数,
--enable-trace 即全量;SGLANG_TRACE_SAMPLER_E2E 是 sampler 的调试打印,不是采样开关。
- 关联:P 与 D 共用同一
trace_id(网关注入同一 traceparent);但 mooncake 的 Sender Req 是独立 trace(未透传 header),实际跨端 join 用 bootstrap_room。
- 缺口:L3 prefetch 没有 span,只能看
prefill_waiting 或 SGLANG_HICACHE_TIMING=1 日志;tokenize 只进 trace,不进 per_stage_req_latency_seconds 指标。
- 噪声:健康检查请求(pt=1、无 room)也会产生 prefill span,约占 30%,统计时必须过滤。
- 保留:我们线上 Jaeger 用 badger,TTL 6h —— 只能回看最近 6 小时。
数据来源 07
| 参数 | 取值 | 类型 | 来源 |
| V4.1 稀疏 KV 体积 | ≈ 1900 B/token | 源码 + trace 交叉 | 按 kv_source_layer_ids / fp4 indexer 计算,用 160k ≈ 304MB 校验 |
| P 单流 prefill | 62k tok/s @160k | 实测日志 | bench_serving / aiperf 基线 |
| L3 prefetch | 233k / 668k tok/s | 实测日志 | 2026-10-10 mc1 / mc8 测试轮次 |
| KV 传输 | 160k ≈ 15ms | Jaeger | mooncake_worker_send_session 子 span 汇总 |
| decode TPOT | 4.3–6ms | 实测日志 | aiperf 稳态基准 |
| tokenize + 模板 | 60–200ms(取 120) | 估算 | 由 76k ctx 单核打满反推,按文本量外推 |
| 握手 | ≈ 15ms | 估算 | ZMQ 注册 + 全 rank 同步 + 一个 scheduler step |