SGLang 深度优化:Radix 缓存与复杂任务的极致吞吐实践
摘要
本文从 vLLM 与 SGLang 的定位差异出发,先说明 PD 分离架构为什么是复杂 LLM 服务的底座,再拆解 SGLang 语言与运行时优化,包括 RadixAttention 前缀树缓存、缓存感知调度、压缩有限状态机约束解码和 API 推测执行。最后在 RTX 4090 上用 Qwen3-0.6B 完成五个业务场景实验,并同机对比 vLLM 与 SGLang 的延迟和吞吐差异,给出选型边界。
SGLang 深度优化:Radix 缓存与复杂任务的极致吞吐实践
写在前面
vLLM 解决了模型推理本身的速度问题:PagedAttention 消除 KV Cache 碎片,Continuous Batching 消除 GPU 空闲,推测解码加速 Decode 阶段,PD 分离消除 Prefill 与 Decode 的资源争抢。但业务侧的大语言模型应用已经不只是单次问答,多轮对话、批量评分、结构化 JSON 输出、多次模型调用穿插控制流,这类复杂程序的主要开销在于重复计算相同前缀的 KV Cache,以及格式约束导致的无效生成。SGLang 的定位就是解决业务逻辑的吞吐问题,用前端语言简化编程,用后端运行时自动复用缓存并加速约束解码。
本文主线:先对比 vLLM 与 SGLang 的定位,说明 PD 分离架构为什么是复杂服务的底座;再拆解 SGLang 语言的编程方式与运行时四项优化,即 RadixAttention、缓存感知调度、压缩有限状态机(FSM,Finite State Machine,有限状态机)和 API 推测执行;随后给出环境搭建与五个代表性场景;最后用 Qwen3-0.6B 在 RTX 4090 上完成性能验证,并同机对比 vLLM 与 SGLang,量化 RadixAttention 的价值边界。
项目概览
项目 | 内容 |
|---|---|
推理引擎 | SGLang,对比 vLLM |
模型 | Qwen3-0.6B(FP16) |
硬件 | RTX 4090(24 GB 显存) |
服务端口 | vLLM 8000,SGLang 8001,同机部署 |
核心机制 | RadixAttention、缓存感知调度、压缩 FSM、API 推测执行 |
验证场景 | 基础对话、流式输出、多轮对话、批量任务、JSON 约束输出 |
关键结果 | 单请求延迟快 5.0 倍,共享前缀快 2.8 倍,8 并发吞吐高 3.5 倍 |
结果边界 | 对比受 vLLM 的 |
背景、目标与约束
从 vLLM 到 SGLang:优化层次的迁移
先把 vLLM 的优化体系按层次拆开,每一层解决一个独立问题:
技术 | 优化层面 | 解决的问题 |
|---|---|---|
PagedAttention | 内存层 | KV Cache 碎片与浪费,按块分配、按需扩展 |
Continuous Batching | 调度层 | 短请求完成后空等长请求,GPU 空闲 |
推测解码(EAGLE/MEDUSA) | 算法层 | Decode 阶段算力空闲,减少生成步数 |
PD 分离 | 架构层 | Prefill 与 Decode 互相干扰,资源无法特化 |
这些技术叠加使用时遵循固定逻辑:PagedAttention 是基础设施,贯穿 Prefill 与 Decode 两端;Continuous Batching 在两类节点内部各自运行;推测解码只部署在 Decode 集群;PD 分离是最外层的部署策略,决定前三者在哪里运行。
vLLM 解决的是如何让模型跑得快,SGLang 解决的是如何让业务逻辑跑得快。两者的关系可以类比为:vLLM 像通用数据库 MySQL,SGLang 像带脚本能力的 Redis 加 Lua。简单问答用 vLLM 足够;多轮对话、批量任务、严格 JSON 输出这类场景,SGLang 在缓存复用和约束解码上的收益更明显。
Prefill 与 Decode:两种截然不同的计算特性
LLM 推理分为两个阶段。Prefill(预填充)一次性读入整个 Prompt,计算所有位置的 KV Cache(Key-Value Cache,键值缓存),特征是计算密集型,大量矩阵乘法,GPU 算力是瓶颈,关键指标是 TTFT(Time To First Token,首 Token 延迟)。Decode(解码)基于已生成内容逐个预测下一个 token,特征是内存带宽受限,每一步只算 1 个 token,瓶颈在读取 KV Cache,GPU 计算单元大量空闲,关键指标是 TPOT(Time Per Output Token,每个输出 Token 的耗时)。
两个阶段混在同一 GPU 上交替执行会互相伤害:执行计算密集的 Prefill 时阻塞延迟敏感的 Decode,TPOT 飙升;执行访存密集的 Decode 时计算单元空闲,浪费了 Prefill 需要的算力。PD 分离(Prefill-Decode Decoupling,预填充与解码解耦)把两阶段放到不同 GPU:Prefill 节点作为生产者计算 KV Cache,通过 Connector 异步传输给 Decode 节点,Decode 节点作为消费者边接收边生成。

图 1 是 PD 分离的工作流程示意图:用户请求先进入 Prefill 节点完成 KV Cache 计算,经高速互联传输到 Decode 节点,由 Decode 节点完成自回归生成并返回结果。图中两个节点独立部署,对应正文的核心结论,即两阶段物理隔离后可以分别选择最合适的硬件和 batch 策略。
PD 分离的核心优势有四条:消除干扰,Prefill 不再阻塞 Decode,TPOT 稳定;资源特化,Prefill 用高算力 GPU,Decode 用大显存 GPU;独立扩展,按负载动态调整两类实例比例;Batching 差异化,Prefill 用小 batch,因为其本身已经算力打满,增大 batch 只会增加排队延迟,Decode 用大 batch,把空闲算力利用起来,从内存受限转向算力受限。
vLLM 通过 KV Transfer Connector 实现 PD 分离,Prefill 节点配置 kv_role: kv_producer,Decode 节点配置 kv_role: kv_consumer,传输支持 NVLink、InfiniBand 等高速互联。业界其他实现包括 Mooncake(Kimi 的以 KV Cache 为中心的分离式架构)、NVIDIA Dynamo(声明式 PD 部署)以及 SGLang、llm-d 等。
整体架构
SGLang 由前端语言和后端运行时两部分组成。前端是把 Python 作为宿主语言的领域特定语言,提供生成控制原语(gen、select)和并行控制原语(fork、join);后端是 SGLang Runtime(SRT,SGLang 运行时),负责执行这些原语并应用运行时优化。

图 2 是本文的整体架构图:用户请求经 SGLang 语言程序进入解释器或编译器,由 SRT 运行时执行;运行时内部包含 RadixAttention 前缀复用、缓存感知调度、压缩 FSM 约束解码和 API 推测执行四类优化;本地模型与商业 API 模型接入同一套程序语义,输出结果后通过 cache_hit_rate、TTFT、TPOT 等指标反馈调优。这张图对应全文主线,即 SGLang 的价值来自语言层与运行时层的协同设计。
执行模式有两种。解释器模式把提示视为异步流,程序可以在不等待生成完成的情况下继续执行;编译器模式把程序编译成计算图,由图执行器运行,为编译优化留出空间。SGLang 与 LMQL、Guidance 同属低级系统,可以直接操作提示和原语而不改变提示本身,区别在于 SGLang 更专注运行时效率,并配有独立的高性能运行时。
技术选型:SGLang vs vLLM
维度 | vLLM | SGLang | 选型建议 |
|---|---|---|---|
定位 | 通用推理引擎,类似 MySQL | 复杂 LLM 程序运行时,类似 Redis 加 Lua | 简单对话用 vLLM;Agent、多轮、结构化输出用 SGLang |
缓存策略 | PagedAttention 块级复用 | RadixAttention 前缀树复用 | 多轮对话场景 SGLang 缓存命中率更高 |
编程方式 | OpenAI API 调用 | Python 原生嵌入 | 需要分支、循环、并行控制流时选 SGLang |
JSON 生成 | 靠 Prompt 加后校验 | 原生约束解码,正则强制 | 必须输出标准 JSON 的接口用 SGLang 更稳定 |
兼容性 | OpenAI API 兼容 | OpenAI API 兼容加 SGLang 原生语法 | 迁移成本低,可逐步切换 |
选型的本质是看业务场景:单次问答的缓存价值不大,vLLM 生态更成熟;多轮对话、批量任务、严格格式输出、并行子任务组合时,SGLang 的缓存与约束能力直接转化为成本和延迟收益。
核心实现
SGLang 语言:用 Python 写 LLM 程序
SGLang 提供一组原语控制语言模型的提示状态:function 定义函数,system、user、assistant 表示对话角色,image 处理图像输入,select 从选项中选择最高概率项,fork 创建并行分支,gen 生成文本,run 执行函数。这些原语与 Python 语法完全兼容,可以直接使用 if、for、return 写控制流。
资料中的多维论文评分程序是理解 SGLang 语言的最佳案例,它使用分支-求解-合并技术,把一个大任务拆成三个维度并行评估再合并。根据资料对代码的逐行说明,核心流程可以整理为如下伪代码:

图 3 是资料中该程序的原始代码示例,红色标注了 SGLang 提供的原语。对比手写 Prompt,这段程序的关键收益有三个:一是 if s["related"] == "不相关": return 让模型判断结果直接决定分支走向;二是 fork 表面上只是 for 循环,运行时却让三个维度并行生成,并通过 RadixAttention 复用共同上下文;三是 regex=SCHEMA 在解码层面强制 JSON 格式,比提示词约束可靠得多。
RadixAttention:前缀树级的 KV Cache 复用
自回归解码中每一步输出都依赖前一步,同一系统提示、同一段对话历史会在多次调用中被反复处理。RadixAttention 用基数树(Radix Tree)管理 token 序列与 KV Cache 张量的映射,允许快速的前缀搜索、重用、插入和逐出。
基数树与传统前缀树的差别在于边的标记方式:传统 Trie 的每条边只标一个字符,查找“北京市朝阳区”要经过六个节点;基数树的边可以标记一段连续序列,例如直接把“北京市”作为一条边,下面再分叉“朝阳区”和“海淀区”,查找效率大幅提升。KV Cache 张量以非连续、分页的布局存储,每页对应一个 token,索引关系由树结构维护。
配合 LRU(Least Recently Used,最近最少使用)逐出策略:显存不足时优先逐出最久未使用的叶子节点,公共祖先保留,因为其他请求仍可能复用。这相当于先抽掉最具体的档案,保留“北京市朝阳区”这个公共文件夹。

图 4 是 AI 客服中心场景下 RadixAttention 在 9 个时间点的动态演变:绿色节点表示新建的缓存,蓝色节点表示被复用的缓存,红色节点表示被 LRU 驱逐的缓存。核心观察有三点:同一客户第二轮对话直接复用第一轮前缀;新客户加入时节点拆分,多个客户共享系统提示;显存不足时驱逐最久未使用的叶子,保留公共祖先。这张图对应一个结论,即 RadixAttention 的收益来自前缀共享和分层逐出。
根据 LMSYS 的研究,在 A10G GPU 上使用 Llama-7B 与 Mixtral-8x7B 模型测试,与 Guidance 和 vLLM 等现有系统相比,采用 RadixAttention 的 SGLang 实现了最高 5 倍的吞吐量提升;论文端到端测试中,Llama-7B 上吞吐量最高提升 6.4 倍,延迟最高降低 3.7 倍。
缓存感知调度
缓存命中率定义为缓存命中的 token 数除以提示 token 总数。当等待队列中有大量请求时,执行顺序显著影响命中率:如果频繁在不同请求之间切换,刚加载的缓存马上被换出,产生缓存抖动。SGLang 的缓存感知调度按匹配前缀长度对请求排序,优先处理前缀匹配更长的请求,而不是先来先服务(FCFS,First Come First Served)。类比机场安检,让去同一目的地的乘客连续过安检,设备状态不用反复切换。
调度的核心步骤为:取出等待队列请求,用基数树匹配每个请求输入 token 的前缀,按匹配长度排序,结合可逐出缓存与可用内存选择下一批请求,执行后更新基数树引用计数,并把完成的请求插入树中供后续复用。
约束解码:压缩有限状态机
LLM 应用经常要求输出符合 JSON 等特定格式。传统做法把正则表达式转换成 FSM,解码时维护当前状态、检索允许的 token、把非法 token 的概率置零,逐个 token 解码。问题在于常量序列(例如 {"summary": )本来只有一个有效后续,逐 token 解码仍然要多次前向传播,FSM 与模型运行器之间缺乏集成,无法合并解码步骤。
SGLang 的压缩 FSM 把 FSM 中相邻的单次转换边压缩成一条边,识别出可以一次解码多个 token 的片段,在单次前向传播中同时解码,并且适用于所有正则表达式。

图 5 对比了正常 FSM 与压缩 FSM 的解码过程:常规方式下常量序列跨多个解码阶段,压缩后相邻转换边合并,多个 token 在一次前向中完成。这张图对应约束解码章节的核心结论,即压缩边减少了前向传播次数,长序列和复杂约束场景收益最大。
约束解码与 Prompt 工程的对比:
对比维度 | Prompt 工程(传统方式) | 约束解码(SGLang) |
|---|---|---|
格式正确率 | 约 90%-95%,总有意外 | 100%,物理上不可能输出非法格式 |
错误处理 | 需要 try-except 加重试逻辑 | 不需要,一次生成就是合法的 |
生产稳定性 | 可能因格式错误报警 | 输出结构恒定 |
API 推测执行
商业模型 API(如 GPT、Claude)只能黑盒访问,每次 gen 对应一次 API 调用,既耗时又计费。API 推测执行在第一次调用时忽略停止条件,多生成一些 token 并缓存,后续原语直接从缓存中匹配复用,减少 API 调用次数。适用条件很明确:程序结构规律(如固定提取 name、job、age 三个字段),能接受 10%-20% 的 token 浪费换取延迟和成本下降;程序有复杂分支或使用本地开源模型时不应使用,本地模型直接用 RadixAttention 更划算。
环境与部署
环境准备与安装
SGLang 与 vLLM 存在依赖冲突(sgl-kernel、triton 等),必须使用独立虚拟环境,并且选择 CUDA 12.4 镜像。CUDA 11.8 环境下 FlashInfer 和 sgl-kernel 都会报错,不要试图就地修复,直接换镜像最快。
安装顺序很重要,资料中的顺序为:升级 pip;无依赖模式安装 sgl-kernel(pip install sgl-kernel --force-reinstall --no-deps);安装 sglang[all] 并指定 FlashInfer 的 cu124/torch2.5 版本 wheel;最后固定安装 transformers==4.57.1,因为 transformers 5.x 会报错。
启动服务与推荐参数
启动 Qwen3-0.6B 服务前先设置 CUDA 库路径解决 libnvrtc.so.12 错误,然后启动服务:
RTX 4090 上运行 Qwen3-8B 的推荐配置给出了更完整的参数组合:
常见启动参数及其作用:
参数 | 说明 |
|---|---|
| API 服务端口号 |
| 允许外部访问 |
| GPU 显存静态占用比例,小模型低一些,大模型可到 0.85 |
| 张量并行,多 GPU 时使用 |
| 最大上下文长度,越短越省显存 |
| 分块预填充大小,降低首 Token 延迟 |
| 最大并发请求数 |
| 开启 Prometheus 格式监控指标 |
| 强制指定对话模板,报错时手动指定 |
启动成功后会输出 Running on http://0.0.0.0:8001,可用 curl http://localhost:8001/health 做健康检查,用 curl http://localhost:8001/v1/models 查看模型信息。
常见问题排查
问题排查集中在四类:CUDA 版本不对,用 nvcc --version 和 nvidia-smi 检查,11.8 直接换 12.4 镜像;PyTorch 的 CUDA 版本与系统不匹配,手动安装 cu124 的 PyTorch wheel,对应 torch==2.5.1、torchvision==0.20.1、torchaudio==2.5.1;依赖冲突,删除旧环境重新创建纯净环境;显存不足,降低 --mem-fraction-static 到 0.5-0.6,减小 --context-length,换更小模型,或使用 --quantization awq 加载量化模型。
代表性场景
场景一:基础对话
基础对话与 OpenAI API 完全一致,已有代码只需改 base_url 即可迁移:
场景二:流式输出
流式输出设置 stream=True,逐块接收并打印,适合聊天机器人和实时翻译:
场景三:多轮对话
多轮对话是 SGLang 的优势场景,RadixAttention 自动缓存之前轮次的 KV Cache:
第一轮对话时系统 Prompt 与用户消息的 KV Cache 被计算并存入基数树,第二轮不变的前缀直接命中缓存,只需计算新增的用户消息;轮次越多缓存复用越多,资料中给出的结论是首 Token 延迟可降低 50% 以上。
场景四:批量任务处理
批量任务共享同一个 Prompt 模板,是 RadixAttention 收益最大的场景。以文章批量评分为例,系统提示词是共享前缀,只计算一次:
资料给出的量化对比:用 vLLM 处理 100 篇文章,系统 Prompt(约 100 tokens)被重复计算 100 次;SGLang 通过 RadixAttention 只计算一次,相当于省了约 9900 tokens 的 Prefill 计算量,文章越多节省越大。
场景五:JSON 约束输出
对接下游系统时,用 SGLang 原生 API 的 regex 参数做约束解码,输出一定是合法 JSON:
输出结果为:
性能验证与监控
性能验证脚本包含三组测试:冷启动(第一次请求,无缓存)、缓存命中(重复相同 Prompt 十次)、共享前缀(20 个请求共享同一前缀,10 并发)。结果如下:
测试 | 结果 |
|---|---|
冷启动测试 | 平均延迟 298 ms |
缓存命中测试 | 平均 208 ms,最小 206 ms,最大 213 ms |
共享前缀测试 | 20 个请求总耗时 381 ms,平均每请求约 19 ms |
缓存命中率是 SGLang 的核心监控指标,启动时加 --enable-metrics 后通过 Prometheus 格式暴露:
不同场景的缓存命中率参考值:多轮对话通常 60%-80%,批量任务(共享模板)通常 80%-95%,完全随机的单轮问答接近 0%。命中率越低,SGLang 相对 vLLM 的优势越不明显。
对比结果与评估
实验设置
对比实验在 RTX 4090(24 GB)上运行,模型为 Qwen3-0.6B,vLLM 与 SGLang 同机部署,分别占用 8000 和 8001 端口。为保证互不抢占资源,两个引擎各分配 40% 显存:vLLM 使用 --gpu-memory-utilization 0.4,SGLang 使用 --mem-fraction-static 0.4。
先单独测试 vLLM 基线,覆盖冷启动、缓存命中、共享前缀、多轮对话、并发吞吐五个维度:
测试项 | 结果 | 说明 |
|---|---|---|
冷启动延迟 | 1482 ms | 首次请求,无 KV Cache |
缓存命中延迟 | 1061 ms(10 次平均) | 加速比 1.40 倍,Prefix Caching 生效 |
共享前缀(20 请求) | 8722 ms | 平均每请求 436 ms |
多轮对话 | 2183 / 1895 / 1907 ms | 后续轮次有缓存加速但幅度有限 |
并发吞吐(8 并发) | QPS 6.41,吞吐 641.3 tok/s | 20 个请求,总耗时 3.12 s |
同一台机器上使用相同测试逻辑测试 SGLang:
测试项 | 结果 | 说明 |
|---|---|---|
冷启动延迟 | 315 ms | 首次请求即很快 |
缓存命中延迟 | 222 ms(10 次平均) | 最低 206 ms,RadixAttention 高效复用 |
共享前缀(20 请求) | 432 ms | 平均每请求仅 21.6 ms |
同脚本直接对比
最后用同一个脚本同时连接两个引擎,保证请求、参数和测试条件完全一致:
指标 | vLLM | SGLang | 对比 |
|---|---|---|---|
单请求延迟(ms) | 1051.8 | 209.7 | SGLang 快 5.0 倍 |
共享前缀耗时(ms,20 请求并发) | 1071.2 | 378.9 | SGLang 快 2.8 倍 |
QPS(8 并发) | 6.47 | 22.43 | SGLang 高 3.5 倍 |
吞吐(tok/s) | 647.2 | 2242.5 | SGLang 高 3.5 倍 |
差异来源需要拆开看。单请求延迟差距较大的一部分原因是配置不对称:vLLM 使用 --enforce-eager 禁用了 torch.compile,且 Qwen3 默认开启 thinking 模式,vLLM 端生成 token 数远多于未触发 thinking 的 SGLang 端,延迟差异被放大。共享前缀场景的差距则来自缓存机制本身:SGLang 的 RadixAttention 通过基数树自动识别 20 个请求的共同前缀,KV Cache 只计算一次;vLLM 的 Prefix Caching 是块级匹配,粒度不如基数树灵活。并发吞吐的差距叠加了缓存感知调度,SGLang 优先处理命中率高的请求,减少重复计算。
公平性说明:两个引擎使用相同模型、相同硬件、相同 40% 显存占比;但 vLLM 使用 --enforce-eager,SGLang 使用默认配置,且 Qwen3 在 vLLM 中默认触发 thinking 模式,生成更多 token。实际生产的差距会随场景和配置变化,本次对比重在验证 RadixAttention 的价值趋势,不把具体倍数当作绝对结论。
问题、限制与改进
第一,对比实验存在配置不对称。--enforce-eager 和 thinking 模式差异会放大单请求延迟差距,要得到严格公平的基准,需要统一关闭 thinking、统一 eager 与编译模式,再分别测量。
第二,SGLang 的优势集中在缓存可复用的场景。完全随机的单轮问答缓存命中率接近 0%,此时 RadixAttention 没有发挥空间,SGLang 相比 vLLM 的优势不明显,简单问答选择 vLLM 更合适。
第三,API 推测执行依赖预测准确性。程序分支复杂或下一步难以预测时,多生成的 token 无法被复用,反而增加调用成本;商业 API 场景下官方 Prompt Caching 是更成熟的选择。
第四,环境依赖严苛。SGLang 要求 CUDA 12.4、特定版本的 sgl-kernel、FlashInfer 与 transformers==4.57.1,与 vLLM 环境不能共存,多引擎部署需要独立环境和显存预算分配。
工程启发
选型先看业务场景。vLLM 适合简单单轮问答、需要 EAGLE/MEDUSA 推测解码、与现有 OpenAI API 代码无缝迁移、依赖社区生态的场景;SGLang 适合多轮对话(客服、面试)、批量任务共享前缀、结构化输出(JSON 约束解码)、复杂 LLM 程序(fork/join 并行)的场景。
监控层面,把 cache_hit_rate 作为 SGLang 服务的首要指标,它直接反映 RadixAttention 的复用程度;配合 num_running_requests、num_waiting_requests、TTFT、TPOT 判断瓶颈在缓存、调度还是解码。多轮和批量场景若命中率低,优先检查提示结构是否稳定,避免每次请求都改变前缀。
调度层面,缓存感知调度给了一个通用启发:同类请求聚合处理,优先复用已有计算状态,而不是严格按到达顺序执行。这个思想同样适用于 RAG 查询、批处理任务和缓存系统设计。
小结
SGLang 的优化体系可以归纳为三层:架构层用 PD 分离把 Prefill 与 Decode 解耦,让两类节点各自特化;缓存层用 RadixAttention 和缓存感知调度,在前缀树粒度复用 KV Cache;解码层用压缩 FSM 保证结构化输出一次生成合法。实测数据表明,在多轮、批量、结构化输出这类业务逻辑复杂的场景中,SGLang 的延迟和吞吐优势显著,但优势边界与业务结构强相关,选型应基于场景判断,而不是引擎能力对比的单一数字。
