@amitiitbhu: SGLang 是如何工作的?在此阅读:https://outcomeschool.com/blog/how-does-sglang-work…

X AI KOLs Timeline 工具

摘要

一篇教育性博客文章,解释了 SGLang 的工作原理,包括其运行时、前端语言、RadixAttention 机制以及与 vLLM 的比较。

SGLang 是如何工作的? 在此阅读:https://outcomeschool.com/blog/how-does-sglang-work…
查看原文
查看缓存全文

缓存时间: 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 服务框架。两者都旨在实现高吞吐量和低延迟,但采取了不同的方法。

方面vLLMSGLang
核心优化PagedAttention(KV 缓存管理)RadixAttention(前缀共享)
批处理连续批处理连续批处理
共享前缀通过自动前缀缓存(APC)支持通过 RadixAttention 原生支持
结构化输出通过外部库,非核心与 outlines 深度集成,核心功能
前端语言无专用前端,使用标准 Python拥有专用前端语言描述程序
控制解码有限强大,支持正则、JSON、CFG
编程能力低(专注于推理)高(前端允许高级程序编排)
社区成熟度非常成熟,广泛采用快速增长,备受关注

在共享长前缀的场景下,SGLang 通常优于 vLLM,因为它专为 RadixAttention 设计。vLLM 的 PagedAttention 在内存管理和分页方面表现出色,但前缀复用是后来才添加的功能。

总结:如果工作负载涉及大量共享文本(如系统提示、文档检索),SGLang 是更好的选择。如果工作负载多样化且前缀复用较少,vLLM 仍然是一个非常强大的竞争者。

到这里,就是 SGLang 的整体概述。希望这能帮助你理解它的工作原理。

如果你需要更详细的信息,可以查阅 SGLang 官方文档和仓库。

下次再见,Happy coding!

相似文章