怎么读这一卷
主线位置:三条主线的总览。本页不走进代码路径,只交代版本、源码链接、「默认 / 可选」的判断方法和数字的四种来源,再把「一个请求的一生」的每一段对到讲它的页。
版本:vLLM v0.32.0、SGLang v0.5.22
先说结论:
- 每个源码链接都指向 tag,行号从 tag 取。本地克隆和 tag 一致,工作区干净。
- 页面说某个功能「默认」,意思是不加任何参数和环境变量、在所写的平台上就生效;「可选」要显式打开;「条件默认」会写出全部条件。依据是源码里的默认值,不是上游文档的描述。
- 数字分四种:源码事实、估算、实测、待实测(C3-n)。估算和实测分开写,待实测的格子不填估计值。
flowchart TB
claim["页面上的一句话"] --> kind{"说的是什么"}
kind -->|"行为、默认值"| src["源码事实<br/>附 tag 行号"]
kind -->|"数字"| num{"数字从哪来"}
num -->|"公式 + 公开配置"| est["估算<br/>明确标出"]
num -->|"跑过"| meas["实测<br/>写机器、版本、命令"]
num -->|"要 GPU"| pend["待实测(C3-n)"]
src --> dflt{"默认还是可选"}
dflt -->|"不加参数就生效"| d1["默认"]
dflt -->|"要开关"| d2["可选"]
dflt -->|"默认开,但有回退"| d3["条件默认<br/>列出条件"]
锁定版本
| 项目 | tag | commit | commit 日期 |
|---|---|---|---|
| vLLM | v0.32.0 | 5df63a7203cdb9328d824a27b682e77c7b9928be | 2026-10-17 |
| SGLang | v0.5.22 | baa9befa381511cd4d56f83980951e5a45fffc6d | 2026-10-13 |
| vllm-project/router | v0.1.16 | db02f358da401b60d4ae14eb286163763a0705cc | 2026-10-09 |
| mistral.rs | v0.9.5 | 465191d0b0a7e2432d327b0de06204ab3f94d3a2 | 2026-10-19 |
vLLM 和 SGLang 是主角,各页都对着它们写。router 只在路由一节出现;mistral.rs 只作对照,看「Rust 跑前向」是什么样子。TGI 已于 2026-03 归档,不读源码。
读者手上的版本不同时,先对比本页的 tag,再看链接指向的代码。
源码链接怎么读
链接的形式是:
https://github.com/vllm-project/vllm/blob/v0.32.0/vllm/envs.py#L1475
https://github.com/sgl-project/sglang/blob/v0.5.22/python/sglang/srt/environ.py#L1902
- 路径和行号都以 tag 为准,不以 main 分支为准。main 上同一行号多半已经是别的代码。
- 代码片段只摘关键几行,删掉的部分用
# ...(Python)或// ...(Rust)标出,片段上方给文件路径和链接。 - 每个链接都用脚本对过 tag 上的文件:路径存在、行号不越界,并抽看了指向的行。
每页的结构
> 主线位置:这一页在「一个请求的一生」上的哪一段,这一段在两个引擎里是 Python 还是 Rust。> 版本:本页对照的 tag。- 先给结论,再走源码。
- 至少一张图:进程结构图、时序图或流程图。
## 小结。
「默认 / 可选」怎么判断
这一卷说「默认」「可选」,只看源码里的默认值。分四层看。
环境变量
vLLM 把环境变量集中在 vllm/envs.py:文件前部有一段带默认值的类型声明,后部的 environment_variables 字典里是真正读取环境变量的 lambda。以后者为准。
"VLLM_USE_RUST_FRONTEND": lambda: bool(
int(os.getenv("VLLM_USE_RUST_FRONTEND", "0"))
),
默认是 "0",所以 Rust 前端默认关。注意它先过 int():按这行代码,VLLM_USE_RUST_FRONTEND=true 会在 int("true") 处抛 ValueError,要写 1。前部的声明 envs.py#L170 写的也是 False,两处一致。
SGLang 把环境变量集中在 python/sglang/srt/environ.py 的一个类里,每个字段是一个带类型的描述符:
python/sglang/srt/environ.py#L1902
SGLANG_RUST_SERVER = EnvBool(False)
EnvBool 的解析接受 true、1、yes、y 及对应的假值,其他值报错(environ.py#L143)。所以 SGLang 的 Rust 服务也是默认关。
命令行参数和配置类
vLLM 的参数默认值写在 vllm/config/ 下的配置类里,vllm/engine/arg_utils.py 引用它们。比如显存比例:
gpu_memory_utilization: float = Field(default=0.92, gt=0, le=1)
SGLang 的参数在 python/sglang/srt/arg_groups/fields/ 下按组声明,形如 名字: A[类型, 说明] = 默认值。比如 mem_fraction_static 的默认值是 None(schedule.py#L27),实际比例在启动时按机器算出来,07 部分再讲。
默认值是 None,启动时再决定
不少参数的默认值是 None,意思是「按其他配置推出来」。这时不能只看声明,要找推导它的代码。
- vLLM 的异步调度
async_scheduling默认None。vllm/config/vllm.py#L1585 起逐条排除不兼容的情况(池化模型、部分投机解码方法、执行器不支持等),都不命中时在 #L1644 设为True。所以单卡生成模型默认开异步调度。 - vLLM 的执行器后端
distributed_executor_backend默认None,单卡(world_size == 1)时设为"uni"(vllm/config/parallel.py#L1079),模型跑在 EngineCore 进程里,不另起 worker 进程。 - SGLang 的字段可以带
resolvable=True(arg_utils.py#L78),表示启动时的解析步骤可以改写它。比如disable_overlap_schedule默认False且可解析(schedule.py#L187),所以重叠调度默认开,但某些配置下会被关掉。
默认开,运行时回退
最容易写错的一层。SGLang 的前缀树核心默认选 Rust:
python/sglang/srt/environ.py#L688
SGLANG_UNIFIED_RADIX_TREE_CORE_BACKEND = EnvStr("rust")
但创建时会检查一串条件,命中任何一条就回退到 Python 实现:
python/sglang/srt/mem_cache/unified_cache/tree_core_registry.py#L44
def _rust_fallback_reason(params: CacheInitParams) -> Optional[str]:
if params.enable_session_radix_cache:
return "session-aware caching requires the Python TreeCore"
# ...
if sys.platform != "linux":
return "the Rust TreeCore supports Linux only"
# ...
所以这一卷写它是「条件默认」:Linux 上、PyTorch 版本在支持范围内、设备是 CPU / CUDA、没有开会话缓存等条件都满足时才是 Rust;在 macOS 上永远是 Python。全部条件在 06 部分逐条列出。
判断规则
| 写法 | 含义 | 例子 |
|---|---|---|
| 默认 | 不加参数、不设环境变量,在所写的平台上生效 | vLLM 单卡用 uni 执行器;SGLang 重叠调度 |
| 可选 | 要显式打开 | VLLM_USE_RUST_FRONTEND=1、SGLANG_RUST_SERVER=1 |
| 条件默认 | 默认值指向它,但运行时检查不过就回退;页面列出全部条件 | SGLang 的 Rust 前缀树核心 |
上游文档、README 和博客里的「默认」只当线索。和 tag 上的代码不一致时,以代码为准,并在页面上写出差别。
四种数字
- 源码事实:默认值、通道类型、进程数。附 tag 上的行号。
- 估算:按公式和模型公开配置算出来的数,比如 为什么服务大模型难 里 Llama-3.1-8B 的 KV cache 每 token 128 KiB。页面写明「估算」、写出公式和输入,算它的脚本只用 Python 标准库。估算回答「量级是多少、瓶颈在哪」,不回答「实际跑多快」。
- 实测:写明机器、版本、命令和原始输出。本卷在 CPU 上能做的实测只涉及前端(对假引擎)、路由器和文本栈的微基准、前缀树核心的单项操作和机制模拟,不涉及模型前向。
- 待实测(C3-n):要 NVIDIA GPU 才能得到的数。本机没有 GPU,这些实验集中登记在 GPU 待测清单里,租到机器后一次跑完再回填。页面上写「待实测(C3-n)」,不写估计值、不引用别人的跑分。
| 编号 | 内容 |
|---|---|
| C3-1 | 显存预算:两个引擎上显存比例取几档时的 KV 块数、最大并发 |
| C3-2 | 吞吐与延迟曲线:并发 1–256,TTFT、TPOT、总吞吐 |
| C3-3 | 前缀复用:命中率和 TTFT |
| C3-4 | 连续批处理与分块预填充:长 prompt 混进短请求时短请求的 TPOT |
| C3-5 | 投机解码与量化:开关前后的吞吐与输出一致性 |
| C3-6 | 路由层:缓存感知路由在多副本前的效果 |
| C3-7 | (多卡时)张量并行、预填充与解码分离 |
默认按单张 24 GB 的卡、一个 8B 模型设计这些实验。
主线落在哪一页
一个请求的一生:
| 段 | vLLM 默认 | SGLang 默认 | 换成 Rust 的开关 | 讲它的页 |
|---|---|---|---|---|
| HTTP 进来 | Python(FastAPI) | Python(FastAPI) | VLLM_USE_RUST_FRONTEND=1 / SGLANG_RUST_SERVER=1 | 01 vLLM、01 SGLang、两种 Rust 前端 |
| 模板与分词 | Python 调 HF tokenizers | Python 调 HF tokenizers | 同上 | 同上 |
| 调度 | Python | Python | 无 | 03 调度 |
| KV 块 / 前缀命中 | Python | Python 编排 + Rust 树核心(条件默认) | SGLang 的树核心默认就是 Rust | RadixAttention、树核心 |
| 前向、采样 | Python 调 CUDA kernel | Python 调 CUDA kernel | 无 | 04、05 |
| 反分词、流式返回 | Python | Python(独立进程) | 同第一行 | 01 vLLM、01 SGLang |
HF tokenizers 本身是 Rust 写的库,经 PyO3 被 Python 调用。这一卷说某段「是 Python」,指的是两个项目自己的代码;第三方库的内部实现另算。
一张 24 GB 的卡上放一个 8B 模型:公式在为什么服务大模型难,块怎么分在 02,参数和预算在 07,数字等 C3-1。
在本站中:08。
阅读路线
- 只想知道请求怎么走:01 两页,再看 06 的两种前端。
- 要调显存和并发:为什么服务大模型难 → 02 → 03 → 07。
- 关心 Rust 能接手哪一段:本页的表 → 01 → 06。
小结
- 四个 tag:vLLM
v0.32.0、SGLangv0.5.22、routerv0.1.16、mistral.rsv0.9.5;链接和行号都从 tag 取。 - 「默认」以源码默认值为准:vLLM 看
envs.py的 lambda 和vllm/config/的字段,SGLang 看environ.py和arg_groups/fields/。默认值是None的要找推导代码;默认开但会回退的写成「条件默认」并列出条件。 VLLM_USE_RUST_FRONTEND要写1,写true会在int()处报错;SGLANG_RUST_SERVER接受1或true。- 数字分源码事实、估算、实测、待实测四种;要 GPU 的写「待实测(C3-n)」,不填估计值。