vLLM vs SGLang:两个推理引擎的架构哲学
vLLM vs SGLang:两个推理引擎的架构哲学
前情说明:《现代 LLM 推理引擎剖析》讲了 vLLM 的完整实现。这一篇讲同一个赛道另一个明星选手 SGLang——它的技术路线跟 vLLM 有本质差异,理解这两个引擎的哲学分歧,能让你看清整个 LLM Serving 领域的取舍地图。 素材主要来自两处:vLLM Blog 《Anatomy of vLLM》(2025-09-05)和 LMSYS Blog《Fast and Expressive LLM Inference with RadixAttention》(2024-01-17)+《SGLang v0.4》(2024-12-04)。
起手:两个引擎的定位差异
如果 2024 年初你要选一个 LLM 推理引擎:
- vLLM(UC Berkeley,2023 年 6 月发布)——核心贡献是 PagedAttention,用操作系统级的分页思想管理 KV cache
- SGLang(同 UC Berkeley 团队,2024 年 1 月发布)——核心贡献是 RadixAttention + 前端 DSL,用编译器思想优化整个 LLM 程序
有意思的是——两个项目是同一个学校甚至有部分重叠人员(Ion Stoica 是两边共同的 mentor)。为什么会出两个不同的引擎?因为它们要解决的问题不一样:
| 维度 | vLLM 想解决 | SGLang 想解决 |
|---|---|---|
| 核心痛点 | 单一 request 的 KV 内存碎片 | 多 request 之间的重复计算 |
| 抽象层 | request 内的 token 序列 | request 之间的公共前缀 |
| 前端 | 无(就是个 serving 库) | 有 DSL(可以写 LLM 程序) |
| 哲学 | 内存管理器(OS 视角) | 编译器 + 缓存(编译器视角) |
一句话概括:vLLM 是"给 LLM 做一个操作系统",SGLang 是"给 LLM 做一个编译器 + 缓存"。
一、PagedAttention vs RadixAttention:两种数据结构的哲学
1.1 PagedAttention:把 KV cache 当虚拟内存
vLLM 的 PagedAttention 借鉴 OS 虚拟内存的思想:
- 把 KV cache 切成固定大小 block(默认 16 token)
- 每个 request 有自己的 block table(token → block 映射)
- 全局有个 free block pool(空闲块的双向链表)
Request 1: token_ids = [t0, t1, ..., t31] (32 tokens)
Block Table: [block_5, block_12] (2 blocks × 16 tokens)
Free Block Pool (在物理 HBM 上的具体分布):
[block_0, block_3, block_7, block_9, ...]
关键洞察:block 之间不需要物理连续——就像 OS 里一个进程的内存页可以分散在任何物理帧里。
这解决了什么?内存碎片。传统实现(比如 HuggingFace 早期)会给每个 request 预留 max_seq_len 的连续空间——实际用不了那么多就浪费。PagedAttention 让内存利用率从 60% 提到 95%+。
1.2 RadixAttention:把 KV cache 当共享前缀树
SGLang 的思考完全不同:同一个 batch 里的多个 request,很可能有共同的前缀——
场景一:System prompt 共享——1000 个 chat 请求都以同样的 system prompt 开头 场景二:Few-shot examples——所有查询都带同样的 5 个示例 场景三:Tree-of-Thought——同一个问题采样 10 条思路,前缀相同 场景四:多轮对话——第 5 轮请求的前缀是前 4 轮的完整历史
如果每个 request 都各自算一遍前缀的 KV,巨额浪费。
SGLang 的方案是 RadixAttention:
- 用 radix tree(压缩前缀树)存所有 request 的 KV cache 索引
- 树的每条边可以是一段 token 序列(不像标准 trie 一条边一个字符)
- 全局共享——一个 request 存进去的 KV,别的 request 立刻能复用
- 用 LRU 策略淘汰不常用的分支
来看关键的数据结构层面对比:
PagedAttention:
request_1 → [block_5, block_12, block_3] ┐
request_2 → [block_7, block_9, block_15] ├─ 各自独立
request_3 → [block_1, block_4, block_8] ┘
RadixAttention:
Root
├── "You are a helpful assistant" (共享 system prompt)
│ ├── "User: hello. Assistant: hi" (chat session 1)
│ │ └── "User: how are you"
│ └── "User: what is AI" (chat session 2)
│ └── "Assistant: AI is..."
└── "Solve this math problem" (共享 few-shot prefix)
├── "Q: 2+2? A: 4"
└── "Q: 3+5? A: 8"
每一个共享路径 = 一次 KV cache 复用。RadixAttention 是自动发现共享的——用户不需要显式配置什么是 system prompt、什么是 few-shot。
1.3 一图对比:两种数据结构解决什么问题
| 问题 | PagedAttention 解 | RadixAttention 解 |
|---|---|---|
| KV 内存碎片 | ✅ 分页 | ❌ 不直接解决 |
| 单请求内存利用率 | ✅ 高 | 中等 |
| 多请求共享前缀 | ⚠️ 需要 prefix caching 补丁 | ✅ 原生支持 |
| System prompt 复用 | ✅(通过 prefix caching) | ✅ 原生 |
| 多轮对话历史复用 | ✅(v0.5+) | ✅ 原生 |
| 树状生成(Tree-of-Thought / Beam Search) | ⚠️ 需要特殊支持 | ✅ 天然契合 |
| Few-shot batch 学习 | ⚠️ 需要手动配置 | ✅ 自动 |
vLLM 后来也在 PagedAttention 基础上加了 prefix caching(就是给 block hash 建 hash map),功能覆盖也差不多了。但架构哲学的原始差异依然存在——vLLM 是"给 request 分页 + 加个 prefix hash 表",SGLang 是"用树天然表达共享关系"。
二、调度哲学:Continuous Batching vs Zero-Overhead Scheduler
2.1 vLLM V1 的做法
vLLM V1 的 scheduler 做的事情(参考前一篇文章):
- 每一步
step():schedule → forward → postprocess - Scheduler 同步阻塞——决定完这一步跑什么,才能发 forward
- CPU 和 GPU 之间顺序执行
这是最直觉的模式,但有个隐性开销——CPU 上 scheduling 逻辑跑的时候,GPU 是空转的。
2.2 SGLang v0.4 的 Zero-Overhead Scheduler
SGLang v0.4 引入了一个特别聪明的优化——Zero-overhead batch scheduler:
我们把 CPU scheduling 和 GPU 计算重叠起来。Scheduler 提前一批调度,把下一批的所有 metadata 都准备好。这样 GPU 永远不停。
原理示意:
时间轴:
CPU: [调度批 1] [调度批 2] [调度批 3] [调度批 4] [调度批 5]
GPU: [跑批 1 ] [跑批 2 ] [跑批 3 ] [跑批 4 ] [跑批 5]
↑ GPU 从批 2 开始永远不停
灵感来自论文 NanoFlow。实现细节:
- 提前一步计算调度
- 通过 future tokens 解决依赖问题
- 精心编排 CUDA event 和 stream 同步
收益:SGLang v0.4 用 Nsight profiler 验证了 5 个连续 decode 批的 GPU 没有一次空闲。相比 v0.3 提速 1.1x,相比其他 SOTA 提速 1.3x。
为什么 vLLM V1 当时没做? 因为 vLLM 的抽象层复杂,同步模型简单可靠。SGLang 从设计之初就走"极致榨干 GPU"路线,愿意为一点点性能吃复杂度。
2.3 调度理念的深层差异
如果把两个引擎的 scheduler 放一起对比:
| 层面 | vLLM V1 | SGLang v0.4 |
|---|---|---|
| 同步模型 | 单批同步执行 | 一批调度、一批 GPU 计算重叠 |
| 调度开销 | 明显(一半时间可能耗在 CPU) | 接近零 |
| 实现复杂度 | 中等 | 高(future tokens、CUDA event) |
| 稳定性 | 高 | 中(需要精细同步) |
| 收益 | 一批批稳定跑 | GPU 永远不空闲 |
架构哲学:vLLM 走"OS 稳态"路线,SGLang 走"编译器极致优化"路线。
三、分布式负载均衡:DP Coordinator vs Cache-Aware Router
场景:你部署了 8 个 DP 副本,请求进来时打给哪一个?
3.1 vLLM 的策略:负载均衡得分
vLLM 用的是简单的 加权评分:
score = len(waiting) * 4 + len(running)
# 选 score 最低的副本
意思是尽量让队列最空的副本接下一个请求。这是纯"看忙不忙"的策略。
3.2 SGLang 的策略:Cache-Aware Router
SGLang v0.4 引入了 Cache-Aware Load Balancer:
- Router 维护一个近似的全局 radix tree——记录每个 worker 上缓存了哪些前缀
- 请求进来时,先查这个近似树,选前缀命中率最高的 worker
- 用惰性更新,几乎零同步开销
对比:
| Round-robin (naïve) | Cache-Aware Router | |
|---|---|---|
| Throughput | 82,665 tok/s | 158,596 tok/s |
| Cache hit rate | 20% | 75% |
1.9x throughput, 3.8x cache hit rate——差距惊人。
为什么效果这么大? 因为在共享前缀多的场景里(多轮对话、agent workload),把请求路由到"已经有缓存"的副本,能省下整段 prefill——收益是 prefix 长度的线性倍数。
Router 本身是用 Rust 实现的独立进程,跟 SGLang worker 通信,比 Python 版快 2x。可以横向扩展到多节点。
3.3 哲学差异
vLLM 假设 request 是均匀无状态的,所以负载均衡看"哪个副本最闲"就行。
SGLang 假设 request 是"程序",有共享结构——所以要看"哪个副本已经有相关缓存"。
这个差异体现了两个引擎的世界观:
- vLLM:LLM = 无状态的 token generator
- SGLang:LLM = 有状态的、有共享结构的程序
四、DP Attention For DeepSeek:MoE 的特殊优化
SGLang v0.4 还有个专门针对 MLA (Multi-head Latent Attention) 的优化——DP Attention。
背景:DeepSeek 模型用 MLA,只有一个 KV head。如果按标准 TP=8 切分:
- 8 个 GPU 都保留完整的 KV cache → 8x 冗余 → 显存浪费严重 → batch size 上不去
SGLang 的做法:attention 层走 DP,MoE 层走 TP/EP:
Attention 层 (DP=8):
每个 GPU 处理自己的 batch,各自的 KV cache 独立
→ KV cache 显存需求 / 8
↓ All-Gather
MoE 层 (TP/EP):
合并所有 batch,跑专家计算
↓ Split back
后续 layer...
收益:SGLang v0.4 在 8×H100 上跑 DeepSeek,decoding throughput 提升 1.9x——因为省下的显存全部转成更大 batch。
这个优化在 vLLM 里最近也在跟进,但 SGLang 早了半年。再次印证 SGLang "为特定模型架构做定制优化" 的路线——它更愿意为 SOTA 模型做深度手术。
五、前端 DSL:SGLang 独有的杀手锏
到目前为止讨论的都是 backend。SGLang 有个 vLLM 完全没有的东西——前端 DSL。
一个例子(多维度作文评审):
@sgl.function
def multi_dim_judge(s, essay):
forks = s.fork(4) # 并行创建 4 个副本
forks[0] += "Judge grammar: " + essay
forks[0] += sgl.gen("grammar_score", choices=["1", "2", "3", "4", "5"])
forks[1] += "Judge creativity: " + essay
forks[1] += sgl.gen("creativity_score", choices=["1", "2", "3", "4", "5"])
# ...
s += "Merge: " + forks[0]["grammar_score"] + " " + forks[1]["creativity_score"]
s += sgl.gen("final_grade")
这是在 Python 里嵌入的 DSL——可以:
fork并行分支gen非阻塞异步生成choices约束采样空间- 自动做共享前缀优化(同一 essay 只算一次 KV)
执行模式两种:
- Interpreter 模式:像 Python 一样解释执行
- Compiler 模式:先跟踪成 dataflow graph,再由 graph executor 跑——可以做代码移动、指令选择、auto-tuning
为什么这个前端重要? 因为很多 LLM 应用本质上是"程序"——Agent flow、Tree-of-Thought、self-consistency、DSPy pipeline——这些用普通 API 表达起来啰嗦、性能差,用 SGLang DSL 能:
- 自动利用 RadixAttention 共享前缀
- 并发多个 gen 调用(不用手写 asyncio)
- 编译期优化调度顺序
这才是 SGLang 名字的由来——Structured Generation Language。RadixAttention 只是 runtime 支撑,DSL 才是它跟 vLLM 真正拉开差距的地方。
但这也是双刃剑:SGLang 前端有学习成本,OpenAI-compatible API 生态没那么友好。vLLM 走的是"OpenAI API 完全兼容"路线,Drop-in 替换成本极低。
六、当代场景对比:什么时候用谁
到 2026 年,两个引擎都发展得很成熟,vLLM 补齐了 prefix caching,SGLang 补齐了 disaggregated P/D 和 speculative decoding。功能层面差异在缩小。真正的选型逻辑长这样:
6.1 选 vLLM 的场景
- 通用推理:无特定模型架构偏好,Llama / Qwen / Mistral 等主流模型
- OpenAI-compatible API 兼容性:需要 drop-in 替换现有 OpenAI 后端
- 生产稳定性优先:需要成熟的 CI/benchmark 体系(vLLM 有专门的产品质量团队和 nightly benchmark)
- 异构硬件:需要跑在 AMD / Ascend / ARM CPU 等非 NVIDIA 硬件(vLLM 生态更广)
- 多样化 workload:既有 chat 又有 batch 生成、code、multimodal 都要
6.2 选 SGLang 的场景
- DeepSeek / MoE 模型:DP Attention 优化是 SGLang 独门武器(vLLM 后来跟上但 SGLang 早半年)
- 高共享前缀 workload:agentic workload、tree-of-thought、self-consistency、DSPy pipeline
- Structured Generation 是刚需:JSON schema decoding、复杂 grammar 约束(SGLang 有 compressed FSM 和 xgrammar 深度集成)
- 需要 DSL 表达复杂 LLM 程序:想在服务器端写 fork/branch/gen 而不是靠 client 自己 orchestrate
- 需要 Rust 级性能的 router:cache-aware routing 生产上跑 8+ 副本时收益极大
6.3 一个可能被忽视的事实
很多生产团队最后是"两个都装"——用 SGLang 跑 DeepSeek 主力,vLLM 跑其他模型。因为两个引擎的核心贡献是互补而不是替代:
- 一个 request 内的内存管理:vLLM 的 PagedAttention 是标杆
- 多个 request 间的缓存复用:SGLang 的 RadixAttention 是标杆
这两个东西在设计层面本来就该并存。事实也是——vLLM 从 v0.5 开始加了 prefix caching(基本上就是 hash-based 版本的 RadixAttention),SGLang 也在用 paged 布局管理 KV。两者在互相收敛。
七、承前启后:更大的图景
站远一点看,vLLM 和 SGLang 的分歧反映了 LLM 推理领域的一个根本张力:
LLM 是"独立请求的黑盒 API" 还是 "结构化的程序"?
- 前者视角(vLLM):LLM 是无状态的 token generator,我们优化的是 stateless 请求的吞吐
- 后者视角(SGLang):LLM 是有共享结构的程序,我们优化的是程序间的结构复用
2026 年,随着 agentic workload 崛起(fin 讲 CPU 完全体那篇的核心论点之一),"程序视角"越来越占上风:
- Agent 是长时间运行的 stateful 进程
- 每个任务的 sandbox 需要复制大量共享上下文
- Tree-of-Thought、self-consistency 天然是并行分支
- Multi-turn chat 就是共享前缀的最典型 case
RadixAttention 这个设计在 2024 年看起来是"针对特定 workload 的优化",但到 2026 年回看,它其实提前 2 年预判了 agent 时代的到来。这也是为什么 SGLang 在 DeepSeek 上飞、在 agent 上飞——它的核心数据结构就是为这类 workload 生的。
而 vLLM 的胜利在于普世性——它的 PagedAttention 是 KV 内存管理的 canonical 方案,几乎所有后来的引擎都在借鉴。到 v1 版本,vLLM 已经把 prefix caching / disaggregated P/D / speculative decoding 都集成进来,成了事实上的参考实现。
两个引擎,两条路线,共同定义了 2024-2026 的 LLM Serving 状态艺术。
附:素材来源
主线
- vLLM Blog · Inside vLLM: Anatomy of a High-Throughput LLM Inference System(2025-09-05)
- LMSYS Blog · Fast and Expressive LLM Inference with RadixAttention and SGLang(2024-01-17)
- LMSYS Blog · SGLang v0.4: Zero-Overhead Batch Scheduler, Cache-Aware Load Balancer, Faster Structured Outputs(2024-12-04)
论文
- SGLang paper · Efficiently Programming Large Language Models using SGLang
- PagedAttention paper · SOSP 2023
姊妹篇
整理于 2026-07-31。原文技术细节归属 vLLM 和 SGLang/LMSYS 团队。中文表达可能有解读偏差,请以原文为准。

Written by
Zoe
AI Infra Engineer · LLM Serving · GPU/RDMA · 造工具的偏执狂