@amitiitbhu: SGLang 是如何工作的?在此阅读:https://outcomeschool.com/blog/how-does-sglang-work…
摘要
一篇教育性博客文章,解释了 SGLang 的工作原理,包括其运行时、前端语言、RadixAttention 机制以及与 vLLM 的比较。
查看缓存全文
缓存时间: 2026/06/30 07:38
How does SGLang work?
Read here: https://outcomeschool.com/blog/how-does-sglang-work…
SGLang 是如何工作的?
来源: https://outcomeschool.com/blog/how-does-sglang-work SGLang 是如何工作的?
在本篇博客中,我们将学习 SGLang 的工作原理,同时了解它解决了什么问题,如何让大语言模型的推理更快,以及那些巧妙的思路让它变得如此特别。
我们将涵盖以下内容:
- 什么是 SGLang
- 回顾 LLM 如何生成文本
- SGLang 解决的问题
- RadixAttention:SGLang 的核心
- RadixAttention 如何复用已完成的 KV Cache
- SGLang 的前端语言
- 运行时与前端如何协作
- SGLang 中的连续批处理
- 结构化输出与加速解码
- 简单的端到端全景图
- SGLang 的更强大功能
- SGLang 与 vLLM 的对比
我是Amit Shekhar,Outcome School (https://outcomeschool.com/) 创始人。我曾教导和指导过许多开发者,帮助他们获得了高薪技术工作,帮助多家科技公司解决独特问题,并创建了多个被顶级公司使用的开源库。我热衷于通过开源、博客和视频分享知识。
我在 Outcome School 教授AI 和机器学习 (https://outcomeschool.com/program/ai-and-machine-learning) 课程。
现在开始吧。
什么是 SGLang
SGLang 是一个用于大语言模型和多模态模型的高性能推理服务框架。
简单来说,它是一个工具,可以把一个大语言模型部署到服务中,同时为许多用户尽可能快速地提供答案。
SGLang 这个名字由两部分组成:
SGLang = SG(Structured Generation,结构化生成)+ Lang(Language,语言)。
因此,这个名字本身告诉了我们两件事:它帮助我们以受控的方式生成结构化输出,同时提供了一种小语言来编写指令。
别担心,后面我们会详细学习每个部分。现在只需记住:SGLang 有两个主要组件来实现这些功能。一个是运行时(runtime),即实际快速运行模型并负责结构化输出的引擎;另一个是前端语言(frontend language),即我们刚才提到的小语言,一种友好地告诉模型该做什么的方式。
在理解 SGLang 为何快速之前,我们首先要理解 LLM 是如何生成文本的。明白了这一点,SGLang 的神奇之处就会非常清晰。
快速回顾:LLM 如何生成文本
一个 LLM 一次只写一个词。准确地说,是一次写一个token。
token 是一个小的文本片段。它可以是一个完整的单词、一个单词的一部分,甚至是一个字符。为了便于理解,我们可以粗略地把一个 token 看作一个单词。
因此,模型读取我们的输入,然后生成一个 token。接着模型读取我们的输入加上这个新 token,再生成下一个 token。它会一直重复这个过程,直到答案完成。
假设我们问模型:“The sky is”(天空是)。
首先,模型读取 “The sky is” 并生成 “blue”(蓝色的)。
然后它读取 “The sky is blue” 并生成 “today”(今天)。
这里我们可以注意到一件重要的事:为了生成每个新 token,模型会回顾之前所有的内容。
然而,一次又一次地回顾之前所有 token 会非常慢。因此,模型会存储一些前序 token 的有用数值并复用它们。这些存储的数值被称为KV cache。
KV cache 是模型对已经处理过的 token 的记忆。它代表 Key-Value 缓存。我们不需要深究其中的数学原理,只需要理解这个简单的概念:
KV cache 是模型为已经见过的 token 保存的已完成工作,这样它就不必为每个新 token 重新做那些工作。
这个KV cache (https://outcomeschool.com/blog/kv-cache-in-llms) 非常重要。它是理解为什么 SGLang 如此快速的关键。请记住这个概念。
SGLang 解决的问题
现在,让我们了解真正的问题。
在实际应用中,一个 LLM 不仅仅服务一个人,而是同时服务成千上万的人。而且同一个模型会被反复问到很多相似的问题。
假设我们构建一个聊天机器人。每一次对话都以相同的指令开始,这称为系统提示(system prompt)。
系统提示 是在用户说话之前我们给模型的一组固定指令。例如:“You are a helpful assistant.”(你是一个乐于助人的助手)。
因此,每个用户的请求都以相同的指令开始。模型不得不为每个用户反复处理同样的文本。这是浪费工作。
再举一个例子:假设我们构建一个agent (https://outcomeschool.com/blog/ai-agent) 来回答关于某个文档的问题。文档对每个问题都是相同的。但模型会对每个问题从头读取整个文档。
这就像有人问了一个关于一本书的小问题,你就得每次都从第一页开始读整本书。非常慢,而且浪费精力。
因此,问题很明确:模型重复做了很多昂贵的相同工作。我们需要一种方法,只做一次共享的工作,然后复用。
于是,SGLang 登场了。
SGLang 围绕一个核心思想构建:如果两个请求共享相同的起始文本,它们就可以共享该文本的已完成工作。这个已完成工作,正如我们学到的,就是 KV cache。
使得这种共享成为可能的技术称为RadixAttention。这是 SGLang 的核心。
RadixAttention:SGLang 的核心
先来分解这个名字:
RadixAttention = Radix(一种通过共享前缀组织文本的方式) + Attention (https://outcomeschool.com/blog/self-attention-in-transformers)(LLM 用来回顾之前 token 的核心操作)。
要理解 Radix,我们必须先理解什么是前缀(prefix)。
前缀 是指两段文本共同的起始部分。
例如,看 “The sky is blue” 和 “The sky is clear”。它们共享前缀 “The sky is”。之后,一个说 “blue”,另一个说 “clear”。
现在,SGLang 维护了一个巧妙的结构,称为radix tree(基数树),用于存储多个请求的 KV cache。
radix tree 是一种树状结构,通过共享前缀将文本分组。我们可以把它想象成句子家族树。以相同方式开头的句子共享同一个分支。只有在它们变得不同的地方才会分叉。
用一个具体的例子来说明:
假设三个用户发送了以下三个请求:
- “You are a helpful assistant. What is the capital of France?”
- “You are a helpful assistant. What is the capital of Japan?”
- “You are a helpful assistant. Tell me a joke.”
可以看到,三个请求共享相同的开头:“You are a helpful assistant.”。
如果没有 SGLang,模型会分别三次处理 “You are a helpful assistant.”。
有了 SGLang,radix tree 把共享部分只存储一次。它的 KV cache 只计算一次。然后每个请求从那里继续。而只要某些请求仍然共享更多文本,共享文本又会变成另一个分支,也只存储一次。
下面的 radix tree 展示了这一点:
"You are a helpful assistant." (共享前缀,KV cache 只存储一次) | +-------------+--------------+ | | v v "What is the capital of" "Tell me a joke." (两个请求共享,KV cache (唯一尾部) 只存储一次) | +---+---+ | | v v "France?" "Japan?" (唯一) (唯一)
这里可以看到,共同的开头 “You are a helpful assistant.” 位于顶部,作为一个共享分支。它的已完成工作只计算一次。下面,两个询问首都的请求共享更多文本:“What is the capital of”,因此那部分成为另一个共享分支,其已完成工作也只存储一次。只有在最底部,“France?” 和 “Japan?” 才分成各自的唯一部分。而 “Tell me a joke.” 请求只共享顶部分支,然后走自己的路。任何共享分支都不会被重新计算。
这就是 RadixAttention 节省大量工作的方法。共享前缀只处理一次,其已完成工作被其他共享它的请求复用,只要它还在内存中。
问题解决了。
RadixAttention 如何复用已完成的 KV Cache
现在,我们来理解 SGLang 执行的步骤。这将让一切变得清晰。
步骤 1: 一个新请求到达。例如:“You are a helpful assistant. What is the capital of Italy?”。
步骤 2: SGLang 查看它的 radix tree。它检查这个请求的开头有多大部分已经存在于树中。
步骤 3: 它发现 “You are a helpful assistant. What is the capital of” 已经在树中,因为之前关于法国和日本的请求添加了它。因此,它直接复用那整个部分的已保存 KV cache,而不重新计算。
步骤 4: 只有新部分 “Italy?” 被从头处理。新的已完成工作随后作为树中的一个新分支添加。
步骤 5: 模型按照我们之前学到的方式,一个 token 一个 token 地生成答案。
因此,不同请求共享的文本越多,SGLang 节省的工作就越多。这种共享是自动的,我们不需要做任何特殊操作来启用它。
还有一点需要理解:存储 KV cache 的内存是有限的,它不能永远保存所有内容。
因此,当内存满时,SGLang 必须决定保留什么、丢弃什么。它使用一个简单而聪明的规则:首先移除最近最少使用的分支。这意味着那些最长时间未被使用的分支会被优先移除。
这是一个巧妙的选择。像系统提示这样常用的共享前缀会一直留在内存中,因为它们被频繁使用。而很少用到的、一次性部分会在需要空间时被移除。
这就是 SGLang 如何保留最有用的已完成工作,并尽可能地复用它们。
我们有一篇详细的博客讲解了prompt caching 的工作原理 (https://outcomeschool.com/blog/how-does-prompt-caching-work),深入说明了这一点。
SGLang 的前端语言
到目前为止,我们学习了引擎部分。现在来学习友好的部分:前端语言。
前端语言 是一种简单的方式,让我们在 Python 代码中给模型编写指令。
为什么需要它?因为真实任务很少是单一问题,往往有很多步骤。
例如,我们希望模型做以下事情:
- 读取一段文字
- 总结它
- 然后将总结翻译成法语
这是三个相互依赖的步骤。手工编写所有这些并同时保持快速是很困难的。
因此,前端语言让我们能清晰地描述这些步骤。我们可以编写一系列模型调用,混入自己的文本,并将模型输出捕获到变量中。
下面是一个小例子:
`` import sglang as sgl
@sgl.function def summarize_and_translate(s, paragraph): s += “Summarize this paragraph:\n” + paragraph + “\n” s += “Summary: “ + sgl.gen(“summary”, max_tokens=64) s += “Now translate the summary to French.\n” s += “French: “ + sgl.gen(“french”, max_tokens=64) ``
这里,我们定义了一个函数,按顺序进行两次模型调用。
- 变量
s保存不断增长的对话;我们不断向它添加文本。 sgl.gen("summary", ...)要求模型生成摘要,并将结果保存在名称summary下。- 之后,我们要求模型翻译,并将结果保存在名称
french下。
我们以清晰的步骤描述了整个流程,而 SGLang 在后台高效地运行它。
这就是前端语言的美妙之处:我们简单地表达意图,而运行时负责速度和共享。
运行时与前端如何协作
现在,把我们学到的两个部分连接起来。
前端语言是我们描述任务的方式,运行时是使用 RadixAttention 快速运行任务的引擎。
这里有一个很好的点:前端理解我们程序的结构。它知道哪些部分是固定的、共享的,哪些部分是新的。
因此,当许多请求运行同一个程序时,它们自然共享相同的前缀。运行时看到这一点,并通过 radix tree 复用 KV cache。
简单来说,前端和运行时是相互配合设计的。前端暴露共享结构,运行时利用它来提高速度。
这种协作是 SGLang 既易用又快速的核心原因。
给你一个小提醒
无论你在哪个技术领域工作,都应该熟悉这些主题:
- LLM
- RAG
- MCP
- Agent
- Fine-tuning
- Quantization
我们把它们都整合在一个视频中:
AI Engineering Explained: LLM, RAG, MCP, Agent, Fine-Tuning, and Quantization (https://www.youtube.com/watch?v=lnfWvX66FUk)
别停下阅读——先收藏它,等有时间再看。未来的你会感谢自己。
现在,回到主题。
SGLang 中的连续批处理
现在,我们来学习另一个帮助 SGLang 保持快速的概念。这个想法并非 SGLang 独有,许多现代推理服务系统也在使用它:称为连续批处理 (https://outcomeschool.com/blog/continuous-batching-in-llms)。
首先,什么是批(batch)?一个批是模型同时一起处理的一组请求。将多个请求一起处理比一个个处理高效得多。
但简单批处理有一个问题:不同的请求完成时间不同。一个用户可能想要短答案,另一个可能想要长答案。
在旧式批处理中,我们要等整个组都完成再开始新的组。这意味着快的请求要等着慢的请求,浪费了时间。
因此,连续批处理 登场了。
连续批处理 意味着一旦组内的一个请求完成,一个新的等待请求立刻取而代之。我们不需要等待整个组完成。
假设我们有四个请求的批次。请求二提前完成。SGLang 不会留下空插槽,而是立即用下一个等待请求填满该插槽。
看看区别:
`` 旧式批处理(插槽空置,时间浪费):
Slot 1: [ R1 running ………… ] Slot 2: [ R2 done ] [ empty, waiting for whole group ] Slot 3: [ R3 running ………… ] Slot 4: [ R4 running ………… ]
连续批处理(空插槽立即被填充):
Slot 1: [ R1 running ………… ] Slot 2: [ R2 done ] [ R5 starts immediately …….. ] Slot 3: [ R3 running ………… ] Slot 4: [ R4 running ………… ] ``
可以看到,在旧式批处理中,已完成请求的插槽一直空着直到整个组完成。而在连续批处理中,一个新的等待请求 R5 立即跳入该插槽。没有插槽闲置。
因此,模型始终处于忙碌状态,没有插槽空着。这使硬件满负荷工作,并在相同时间内服务更多用户。
这就是连续批处理如何最大限度地发挥机器性能。
有一点需要记住:连续批处理是一种通用技术,因此它不是让 SGLang 特别的部分。特别之处在于 SGLang 将连续批处理与 RadixAttention 结合在一起。RadixAttention 通过 radix tree 复用共享工作,而连续批处理让机器保持忙碌。这种组合使得当许多请求共享一个长公共开头时,SGLang 大放异彩。
要了解服务引擎如何保持快速——KV Cache、连续批处理、前缀缓存——请查看我们的博客:How Does Prompt Caching Work (https://outcomeschool.com/blog/how-does-prompt-caching-work) 和 Continuous Batching in LLMs (https://outcomeschool.com/blog/continuous-batching-in-llms)。
结构化输出与加速解码
现在,我们来谈谈结构化输出(structured output)。这也是 SG(Structured Generation)部分的来源。
在生成文本时,模型返回一个字符串。但为了处理这个字符串,我们通常需要解析它,提取有用的部分。比如从模型回答中提取 JSON、代码块或特定格式的数据。
标准模型可能输出类似 “Sure, here is the info: …” 的内容,后面跟着非结构化的文字。除非我们非常小心地设计提示,否则很难可靠地从中提取出干净的结构化数据。
SGLang 对此有不同的处理方式。它提供了控制解码过程的工具。
“控制解码” 意味着我们可以限制模型只允许输出符合某种语法或模式的 token。
例如,我们可以要求模型只输出 JSON。在生成过程中,运行时知道 JSON 的语法,因此每次只允许输出对于有效 JSON 来说合法的 token。它不会产生无效的 token。
这避免了事后解析和重试,解码速度更快,并且保证输出格式正确。
SGLang 内部集成了 outlines 库进行结构化生成。它还提供了压缩状态机等优化,使得这种受控解码非常高效。
标准 LLM 输出往往包含额外文字,需要事后解析;而 SGLang 的结构化输出直接从生成中约束,产生干净的可消费输出,无需解析。
两者的对比可以这样看:
`` 标准 LLM 输出(需要解析): “The capital of France is Paris.” (其中包含多余文字)
SGLang 结构化输出(直接可用): {“capital”: “Paris”} (仅 JSON,无需解析) ``
简单的端到端全景图
我们看一张简单的图,将所有内容整合起来:
`` User 1: “System: You are helpful. What is the capital of France?” User 2: “System: You are helpful. What is the capital of Japan?” User 3: “System: You are helpful. Tell me a joke.”
+---------------------+
| SGLang Frontend |
| (programmer writes |
| @sgl.function) |
+----------+----------+
|
v
+---------------------+
| SGLang Runtime |
| (RadixAttention + |
| Continuous Batching|
| + Control Decoding)|
+----------+----------+
|
v
+---------------------+
| Radix Tree |
| (shared prefix KV) |
+---------------------+
|
v
+---------------------+
| LLM Model |
| (GPU) |
+---------------------+
|
v
Outputs: "Paris", "Tokyo", "Why did the chicken..."
``
这里,前端处理程序定义,运行时管理共享和批处理,radix tree 提供 KV 缓存复用,GPU 实际运行模型,最终输出结果流回各自的用户。
SGLang 的更强大功能
除了我们已经涵盖的内容,SGLang 还有更多强大功能,使其成为综合性服务引擎:
- API 兼容性:提供与 OpenAI API 兼容的端点,便于替换。
- 多模态支持:支持图像+文本输入,与 LLaVA 等模型配合使用。
- 约束解码:通过正则表达式、JSON Schema 或上下文无关文法约束输出。
- 分块预填充:将长提示分块处理,避免内存溢出。
- 专家并行:用于混合专家模型(MoE)。
- GPU 间通信优化:减少多 GPU 推理的通信开销。
- 量化支持:原生支持 FP8、INT4、INT8 等量化。
- 推理 Profiling:内置工具用于分析和调试。
这些功能使其成为生产环境中部署 LLM 的强大选择。
SGLang 与 vLLM 的对比
vLLM 是另一个流行的 LLM 服务框架。两者都旨在实现高吞吐量和低延迟,但采取了不同的方法。
| 方面 | vLLM | SGLang |
|---|---|---|
| 核心优化 | PagedAttention(KV 缓存管理) | RadixAttention(前缀共享) |
| 批处理 | 连续批处理 | 连续批处理 |
| 共享前缀 | 通过自动前缀缓存(APC)支持 | 通过 RadixAttention 原生支持 |
| 结构化输出 | 通过外部库,非核心 | 与 outlines 深度集成,核心功能 |
| 前端语言 | 无专用前端,使用标准 Python | 拥有专用前端语言描述程序 |
| 控制解码 | 有限 | 强大,支持正则、JSON、CFG |
| 编程能力 | 低(专注于推理) | 高(前端允许高级程序编排) |
| 社区成熟度 | 非常成熟,广泛采用 | 快速增长,备受关注 |
在共享长前缀的场景下,SGLang 通常优于 vLLM,因为它专为 RadixAttention 设计。vLLM 的 PagedAttention 在内存管理和分页方面表现出色,但前缀复用是后来才添加的功能。
总结:如果工作负载涉及大量共享文本(如系统提示、文档检索),SGLang 是更好的选择。如果工作负载多样化且前缀复用较少,vLLM 仍然是一个非常强大的竞争者。
到这里,就是 SGLang 的整体概述。希望这能帮助你理解它的工作原理。
如果你需要更详细的信息,可以查阅 SGLang 官方文档和仓库。
下次再见,Happy coding!
相似文章
@Mayhem4Markets: https://x.com/Mayhem4Markets/status/2069090022117019928
两大主流LLM服务框架SGLang和vLLM的详细技术对比,涵盖KV缓存管理(RadixAttention vs PagedAttention)的架构差异、吞吐量、延迟以及自托管环境的部署考量。
@amitiitbhu:新文章:vLLM 是如何工作的?请在此阅读:https://outcomeschool.com/blog/how-does-vllm-work…
一篇详细的博客文章,解释了 vLLM 的工作原理,包括 PagedAttention、KV 缓存管理和连续批处理,以实现高效的 LLM 服务。
@0xSero:关于 LLM 推理与部署,看这一篇就够了。你听说过:- vLLM - SGLang - llama.cpp - …
vLLM、SGLang、llama.cpp 与 ExLlamaV3 等主流开源推理引擎概览,助你轻松托管并运行大模型。
@athleticKoder:一篇关于LLM推理原理的1600字笔记,涵盖:1. 注意力机制——token交互的唯一场所 2. KV缓存——为何...
一篇详细阐述LLM推理关键概念的推文:注意力机制、KV缓存、分块预填充以及批处理技术,包括vLLM和SGLang中使用的连续批处理。
@grapeot: LLM 推理系统到底是怎么跑的?SGLang Omni 团队最近公开了一篇很少见的文章——把一个顶级推理系统团队的完整决策链路摊在明面上。我顺着原文梳理了一篇科普,从自回归decode、KV cache、continuous batchi…
本文基于SGLang Omni团队的内部决策文章,从自回归解码、KV缓存、连续批处理等基础概念出发,深入浅出地介绍了LLM推理系统的运作原理。