如果只看 SGLang 的 benchmark,很容易把“快”理解成某个 attention kernel 很猛。其实不是。SGLang 真正厉害的地方是:上层 scheduler 负责把请求组织成对 GPU 最友好的工作负载,下层 kernel/backend 再把这批工作尽量以最少浪费跑完。这页就专门拆这两层。
SGLang 不是“某个算子快”,而是“把在线 serving 的碎片化工作,压成更连续、更可预测、更可复用的 GPU 工作流”。
决定哪些请求进入 batch、prefill 与 decode 怎么穿插、哪些 prefix 能复用、KV cache 何时保留或释放、speculative worker 何时介入。
把已经成型的 batch 真正落到 GPU 上,选择 FlashInfer / Triton / CUDA Graph / piecewise graph / paged KV 等低层执行路径。
上层尽量减少“坏 batch”,下层尽量减少“坏执行”。前者决定工作形状,后者决定执行效率。
从源码结构看,Scheduler 不是单纯的 FIFO 队列,而是一个把请求、缓存、batch、worker、speculative 路径、并行策略绑在一起的“在线推理控制器”。
python/sglang/srt/managers/scheduler.py:270 定义 class Scheduler。它直接依赖 ScheduleBatch、PrefillAdder、SchedulePolicy、RadixCache 和 speculative 相关结构,说明它是整个 serving runtime 的编排中心,而不只是一个“排队器”。
ScheduleBatch 在 schedule_batch.py:1250,负责把一轮要跑的请求整理成 GPU 可消费的 batch;prepare_for_extend() 在 1503,mix_with_running() 在 1831,这两个点说明它既要准备 prefill/extend,又要跟正在运行的 decode batch 进行合流。
schedule_policy.py:96 定义 class SchedulePolicy,calc_priority() 在 117。这层的意义不是抽象优雅,而是把“谁先跑”“怎么兼顾延迟和吞吐”从具体执行里分离出来。
scheduler.py:746-780 一段很关键:根据不同模式实例化 ChunkCache、SWAChunkCache、RadixCacheCpp、HiRadixCache、SWARadixCache。这说明 scheduler 一开始就在思考“prefix 怎么复用”,不是把 cache 当附属品。
scheduler.py:334 会解析 speculative_algorithm;381 和 601 注释明确写了会在 speculative decoding 下额外启动 draft worker。也就是说,推测解码不是外挂,而是 scheduler 内建的分支路径。
最本质的理解:Scheduler 的目标不是公平,而是把请求组织成 GPU 最赚钱的 batch。
Scheduler 直接绑定 RadixCache(scheduler.py:183)和多种 tree cache 初始化逻辑(746-780),说明一个请求值不值得马上跑,取决于它能否复用已有 KV,而不是只看 arrival time。
ScheduleBatch 负责把这轮能一起跑的请求打包,prepare_for_extend() 说明 prefill/extend 是单独准备的,mix_with_running() 则说明 SGLang 会想办法把新增工作和正在 decode 的工作合并,避免 GPU 出现空洞。
scheduler.py:334 解析 speculative algorithm,381/601 启动 draft worker。也就是说 scheduler 不只是发 batch,还要选执行模式。
scheduler.py:3144-3162 这些位置就是显式的释放逻辑。
SGLang 的“kernel”不是单一 .cu 文件,而是一整条执行路径:attention backend 选择、KV 索引规划、workspace 复用、CUDA Graph 捕获与 replay,以及对 speculative/滑窗/cross-attention 的特殊处理。
flashinfer_backend.py:5-6 直接写明:现在有 FlashInfer 和 Triton 两个 backend,FlashInfer 更快,Triton 更容易定制。
51-58 引入 BatchDecodeWithPagedKVCacheWrapper、BatchPrefillWithPagedKVCacheWrapper、fast_decode_plan,说明它不是手写一个 attention kernel 就完事,而是用 paged KV 的批式执行接口组织 decode/prefill。110 定义 global_workspace_buffer,203-219 复用全局 workspace,目的就是减少每轮临时分配。1132-1144 的 global_override_indptr_cpu 是个很工程化的点:为了绕掉一部分 host-to-device copy 开销,直接快路径覆盖 plan 所需的 indptr。decode_wrappers / prefill_wrappers 逻辑说明,kernel 层已经按场景区分了 sliding window、cross attention、speculative 等路径,而不是一个大一统 kernel。triton_backend.py 里把 decode_attention_fwd、extend_attention_fwd、extend_attention_fwd_unified 包进 backend,对自定义逻辑和调试更友好。
kv_indptr、kv_indices、qo_indptr、mask_indptr 等 metadatacuda_graph_runner.py:40 有 graph_capture,454 有 get_batch_sizes_to_capture(),后面还有 replay()。这说明 SGLang 会为常见 batch 形状提前 capture,再在运行时 replay,减少 kernel launch 和 Python/CPU 调度噪音。
model_runner.py:664-665 明确初始化了 piecewise CUDA graph。它的价值在于:在线 serving 的 batch 形状并不总固定,piecewise 图能在“不完全静态”的情况下也吃到 graph 的收益。
model_runner.py:285 定义 ModelRunner,它拿着 req_to_token_pool 和 token_to_kv_pool_allocator(339-340),说明执行层并不只看到 tensor,还直接掌握 request→token 和 token→KV 的映射资源。
这很关键:它让 kernel 执行不是“盲算”,而是和 serving cache 布局强绑定。
Kernel 层的本质不是把 attention 写得更炫,而是把 batch 的 memory layout、KV 访问方式、launch 开销、graph 复用 一起压缩到最省的形态。
| 层 | 它决定什么 | 如果做不好会怎样 | 典型源码锚点 |
|---|---|---|---|
| Scheduler | 请求分组、cache 命中、prefill/decode 穿插、speculative 是否启用 | GPU 吃到碎 batch,prefix 复用低,延迟和吞吐一起烂 | scheduler.py:270, 183, 334, 746-780 |
| ScheduleBatch | 把逻辑请求整理成一轮可执行 batch | batch 形状差,kernel 即使快也救不回来 | schedule_batch.py:1250, 1503, 1831 |
| ModelRunner | 承接 batch,组织 forward、KV pool、graph 路径 | 执行层和 cache 布局脱节,memory traffic 暴涨 | model_runner.py:285, 339-340, 664-665 |
| Kernel Backend | paged KV、attention kernel、workspace、metadata、launch 优化 | 单步算得慢,或每步 overhead 太高 | flashinfer_backend.py:5-6, 110, 203-219, 1132-1144 |
| CUDA Graph | 把高频 batch 形状 capture/replay | CPU launch 开销吃掉吞吐收益 | cuda_graph_runner.py:40, 454, 1250 |
请求进来 → scheduler 判定 cache / priority / phase → ScheduleBatch 组织 batch → ModelRunner 把 batch 映射到 KV pool 与 graph 路径 → backend 用合适 kernel 跑出来。
这里最容易被忽略的一点是:kernel 的好坏,严重依赖 scheduler 先给它一个“好问题”。如果上层让 GPU 每轮都面对随机长度、低命中、低合并度的碎请求,再牛的 kernel 也只能在烂输入上尽量少输。
SGLang 快,不是因为有“一个神级 kernel”;而是因为它有一套 让 kernel 持续工作在甜区 的 scheduler。
很多系统也有 KV cache,但 SGLang 的关键是 scheduler 自己就围绕 tree cache / radix cache 做决策,而不是让 kernel 被动处理已经定型的请求。
prefill 和 decode 的资源画像不同。SGLang 通过 ScheduleBatch 的 prepare / mix 逻辑,把两种 phase 的冲突尽量压低,同时保留合流机会。
FlashInfer / Triton backend 都显式维护 KV 索引、indptr、workspace、wrapper metadata。这不是“代码复杂”,而是在买更低的访存浪费。
在线���务最烦的是每步都要 launch 一堆小 kernel。graph capture/replay 把这部分 CPU 发射成本压掉,尤其在 decode 场景更值钱。
推测解码不是 accept rate 高就行,还需要 scheduler 能组织 draft worker 和验证路径,不然收益会被同步与协调开销吃掉。
FlashInfer 追求极致性能,Triton 保留可定制性,piecewise graph 处理非完全静态形状。它的哲学是:不同硬件和模型,留多条最优路径。
高吞吐 ≈ 好调度 × 高 cache 命中 × 低 launch 开销 × 高效 attention backend × 尽量稳定的 batch 形状
这也是为什么你如果只 benchmark 单个 kernel,通常只能解释 SGLang 为什么“局部快”;但解释不了它为什么在真实 online serving 里整体更猛。
下面这些点都来自我实际抓到的官方文档或源码路径,不是拍脑袋总结。
python/sglang/srt/managers/scheduler.py:270 — class Schedulerpython/sglang/srt/managers/scheduler.py:183 — 引入 RadixCachepython/sglang/srt/managers/scheduler.py:334 — 解析 speculative algorithmpython/sglang/srt/managers/scheduler.py:381, 601 — 启动 speculative draft worker 注释python/sglang/srt/managers/scheduler.py:746-780 — 初始化 Chunk/Radix/SWA/HiRadix 等 tree cachepython/sglang/srt/managers/scheduler.py:3144-3162 — aborted / release KV cache 相关逻辑python/sglang/srt/managers/schedule_batch.py:1250 — class ScheduleBatchpython/sglang/srt/managers/schedule_batch.py:1503 — prepare_for_extend()python/sglang/srt/managers/schedule_batch.py:1831 — mix_with_running()python/sglang/srt/managers/schedule_policy.py:96, 117 — SchedulePolicy 与 calc_priority()python/sglang/srt/model_executor/model_runner.py:285, 339-340, 664-665 — ModelRunner、KV pool、piecewise CUDA graphpython/sglang/srt/model_executor/cuda_graph_runner.py:40, 454, 1250 — graph capture、batch size capture、replaypython/sglang/srt/layers/attention/flashinfer_backend.py:5-6 — FlashInfer 更快,Triton 更易定制python/sglang/srt/layers/attention/flashinfer_backend.py:51-58 — paged KV wrappers / fast_decode_planpython/sglang/srt/layers/attention/flashinfer_backend.py:110, 203-219 — 全局 workspace 复用python/sglang/srt/layers/attention/flashinfer_backend.py:1132-1144 — override indptr 以减少 host-to-device copydocs/developer_guide/benchmark_and_profiling.md — benchmark 分层:server / scheduler / model runner / kerneldocs/developer_guide/bench_serving.md — 在线 serving 的 TTFT、ITL、throughput 观测口径