Skip to content

SGLang 源码结构与 API 详细导读

本文配合 SGLang 项目调研 阅读,重点回答:各目录做什么,一个请求如何经过整个系统,KV Cache 保存在哪里,以及常用接口如何使用。

源码基线固定为 v0.5.2,不表示它是最新版本。本文已对照该版本源码核对文件和关键函数;文中模型推理示例未实际运行,不能当作部署验证或性能结论。核对日期:2026-09-17。

为控制阅读范围,主线采用普通文本生成、单机、非投机解码的场景。多 Tokenizer、流水线并行、PD 分离和其他硬件后端会引入额外分支。

1. 整体结构:接口、调度、执行与内核

客户端 / Python 应用
  ├─ HTTP 原生接口 /generate
  ├─ HTTP 兼容接口 /v1/chat/completions
  └─ Python Engine.generate
                 │
                 ▼
        TokenizerManager
        参数规范化、分词、请求状态
                 │ ZMQ IPC
                 ▼
            Scheduler
        排队、批次、缓存与资源预算
                 │
                 ▼
          TpModelWorker
                 │
                 ▼
           ModelRunner
      模型前向、执行模式、图执行
                 │
                 ▼
      模型层 → Attention 后端 → 计算内核
                 │
           logits → 采样 token
                 ▼
     Scheduler 处理完成状态与输出
                 │ ZMQ IPC
                 ▼
       DetokenizerManager
                 │ ZMQ IPC
                 ▼
       TokenizerManager → 客户端

这是一张职责与数据流图,不是类继承树。普通 HTTP 配置下,HTTP 服务与 TokenizerManager 在主进程,Scheduler 与 DetokenizerManager 通过子进程运行;多卡时还会有不同 rank 的执行进程。跨 ZMQ 的箭头不是普通 Python 函数调用。服务启动说明进程创建实现

2. 仓库顶层目录

路径 主要职责 什么时候看
python/sglang/ Python 包入口、运行时与工具 主线
python/sglang/srt/ SGLang Runtime,核心推理服务代码 研究调度和执行
python/sglang/lang/ 前端语言与程序执行抽象 研究生成程序的表达方式
sgl-kernel/ 配套算子包、C++/设备代码及 Python 包装 优化或移植计算内核
sgl-router/ 路由服务相关代码 研究多个服务实例之间的请求分配
benchmark/ 不同任务、模型的评测与性能实验 学习实验设计
test/ 服务和功能测试 查看接口边界及使用方法
examples/ 应用示例 快速建立使用场景
docs/ 文档源码 查版本对应的操作说明
docker/scripts/ 镜像与辅助脚本 部署和开发环境
3rdparty/ 第三方集成相关内容 按具体后端阅读

这些路径对应固定 tag;不要把最新版仓库的目录直接套到旧版本上。仓库目录

python/sglang/ 下几个重要入口

文件 / 目录 作用
launch_server.py 命令行启动 HTTP 推理服务
__init__.py 对外暴露 Engine、前端语言等入口
srt/server_args.py 服务配置、CLI 参数解析与参数关系检查
bench_serving.py 在线服务压测入口
bench_offline_throughput.py 离线吞吐测试入口
bench_one_batch.py 单批次执行测量入口
check_env.py 环境信息检查工具

服务压测和单批次测试测量的范围不同;后者的结果不能直接替代含排队和网络的请求延迟。Python 包目录

3. srt/ 各模块做什么

模块 职责 关键阅读入口
entrypoints/ HTTP、Python Engine 和协议适配 http_server.pyengine.py
entrypoints/openai/ 兼容接口的数据模型、聊天模板和响应转换 protocol.pyserving_base.pyserving_chat.py
managers/ 请求管理、调度、批次状态与结果处理 tokenizer_manager.pyscheduler.py
mem_cache/ 前缀缓存、请求映射、KV 内存池与分配器 radix_cache.pymemory_pool.pyallocator.py
model_executor/ 执行批次、模型前向和图执行 model_runner.pyforward_batch_info.py
models/ 模型结构的运行时实现 例如 llama.py
model_loader/ 创建模型、读取和加载权重 按所选加载器追踪
layers/ Attention、线性层、量化、MoE 等计算层 radix_attention.pyattention/
sampling/ 采样参数及相关信息管理 sampling_params.py
constrained/ 结构化输出的语法约束后端 从 grammar 相关调用进入
distributed/ 并行组与通信支持 多卡运行时阅读
speculative/ 投机解码相关执行路径 启用 draft/验证机制时
disaggregation/ Prefill/Decode 分离相关逻辑 独立部署 P/D 阶段时
lora/ LoRA 加载与运行支持 多适配器服务时
multimodal/ 多模态处理相关模块 图像、音频等输入时
metrics/ 运行指标相关设施 观测与压测
eplb/weight_sync/ 专家负载均衡、权重同步相关逻辑 MoE 或动态权重场景

初学时先看 entrypoints → managers → model_executor,再分别进入缓存和计算层。直接从某个 CUDA kernel 开始,往往看不到它服务于哪一类批次。srt 源码

4. 服务启动链路

python -m sglang.launch_server
 → prepare_server_args(sys.argv[1:])    srt/server_args.py
 → launch_server(server_args)          srt/entrypoints/http_server.py
 → _launch_subprocesses(...)           srt/entrypoints/engine.py
    ├─ 创建 Scheduler 相关进程
    ├─ 创建 DetokenizerManager 进程
    └─ 初始化 TokenizerManager 等对象
 → 设置 HTTP 全局状态、服务生命周期与预热
 → 接收请求

ServerArgs 是服务侧配置,涉及模型路径、硬件、内存预算、并行方式和调度选项;它与单个请求里的 SamplingParams 属于不同层次。不能把 temperature 当成设备配置,也不能把服务启动时的并行参数当成每个请求可随意修改的值。

调试启动失败时,先判断发生在参数校验、模型加载、并行初始化还是图捕获阶段;HTTP 端口可以连通,也不一定说明模型已经完成预热。launch_server.pyengine.py

5. 一个请求怎样进入调度器

5.1 原生 /generate

http_server.py 将 JSON 解析为 GenerateReqInput,再调用 TokenizerManager.generate_request。非流式请求等待结果;流式请求将异步输出包装成 SSE。

POST /generate
 → http_server.generate_request
 → TokenizerManager.generate_request
    → normalize_batch_and_arguments
    → _tokenize_one_request             单请求主线
    → _send_one_request
       → send_to_scheduler.send_pyobj
    → _wait_one_response                异步等待返回

TokenizerManager 不只是一个分词函数。它还通过 rid_to_state 维护请求 ID 到等待状态的映射,接收结果后唤醒等待者。generate_request 是异步生成器,不能按照“普通函数立即返回字符串”理解。HTTP 路由TokenizerManager

5.2 聊天接口多了一层转换

POST /v1/chat/completions
 → openai_v1_chat_completions
 → OpenAIServingChat.handle_request     继承公共处理逻辑
 → _validate_request
 → _convert_to_internal_request
    → 处理 messages、聊天模板和生成参数
    → 构造 GenerateReqInput
 → 流式或非流式处理
 → TokenizerManager.generate_request

因此,向 /generate 发送原始文本,与向聊天接口发送 messages,不保证得到相同的模型输入 token。模板、角色边界、特殊 token 都可能不同。排查“同一个问题输出却不同”时,应先对齐实际输入,而不只比较用户看到的文字。serving_base.pyserving_chat.py

5.3 关键数据结构

对象 所处阶段 重点字段或内容
GenerateReqInput 外部请求 text/input_idssampling_paramsstreamrid
TokenizedGenerateReqInput 分词后跨进程消息 token IDs、请求 ID、规范化的采样参数
Req 调度器中单个请求 输入输出 IDs、缓存前缀索引、完成状态
ScheduleBatch 调度批次 请求集合、运行模式、内存映射和采样信息
ModelWorkerBatch 交给 worker 的批次描述 模型执行所需的数据与元信息
ForwardBatch 执行层批次 输入 tensor、位置、模式、attention 后端与缓存位置
BatchTokenIDOut 调度器输出 多请求 token、完成原因和统计
BatchStrOut 反分词输出 文本结果及对应请求信息

这些对象不是同一个对象的不同名称。Req 关注请求生命周期,ForwardBatch 关注一次模型执行;同一请求会参与多轮 forward。io_struct.pyschedule_batch.pyforward_batch_info.py

6. Scheduler:决定下一批跑什么

6.1 先理解普通循环

event_loop_normal 的骨架是:

接收请求 recv_requests
 → 处理输入 process_input_requests
 → 选择批次 get_next_batch_to_run
 → 执行批次 run_batch
 → 处理结果 process_batch_result
 → 下一轮

重点状态包括等待进入执行的 waiting_queue、持续 decode 的 running_batch、上一轮批次 last_batch,以及分块 prefill 尚未完成的 chunked_req

get_next_batch_to_run 会处理上一轮 prefill 与运行批次的衔接,尝试通过 get_new_batch_prefill 建立新 prefill 批次;不能建立时,再更新已有 decode 批次。实际还要满足内存、token 预算、语法准备等条件。scheduler.py

6.2 三个容易混淆的概念

概念 解决的问题 不应误解为
连续批处理 请求完成后移出、新请求在后续调度中加入 一个固定 batch 一直等到所有请求结束
Chunked Prefill 将长输入的处理分成受预算约束的块 将长文分成互不相关的多个问题
Overlap 调度 重叠部分 CPU 处理与设备计算 模型 token 的依赖关系被取消

SchedulePolicy.calc_priority 处理等待队列优先级,PrefillAdder 在预算约束下判断是否接纳请求。因此“排在前面”和“这一轮一定能跑”并不是一回事。schedule_policy.py

6.3 Overlap 循环为什么更难追

event_loop_overlap 中能看到 result_queuelast_batch 和事件同步:当前批次已经发起时,还可能在处理上一批次的结果。第一次读代码建议先按普通循环理解因果关系,再回到 overlap 分支看异步依赖。

默认配置不一定进入普通循环。需要逐步调试时,可以在专用实验实例中使用 --disable-overlap-schedule;这会改变执行行为和性能,不能把调试配置测得的性能当作默认服务表现。两种事件循环

7. KV Cache:树、映射、内存池与分配器

7.1 四种结构各管什么

请求的 token 序列
      │ 按前缀匹配
      ▼
RadixCache:token 前缀 → 已存在的 KV 槽位索引
      │
      ▼
ReqToTokenPool:请求槽位 + 序列位置 → KV 槽位索引
      │
      ▼
KV Pool:各层真正的 K/V 或模型特定缓存 tensor
      ▲
Allocator:分配/释放可用 KV 槽位或页
组件 核心职责
RadixCache 管理可共享前缀及其引用关系
ReqToTokenPool 保存请求位置到 KV 存储位置的映射
MHATokenToKVPool 保存模型计算产生的缓存 tensor
TokenToKVPoolAllocatorPagedTokenToKVPoolAllocator 管理空闲位置和分配回收

TreeNode.value 在普通 RadixCache 路径中承载的是 KV 位置索引,不是完整 K/V tensor。不同 attention 架构的存储形状也不同,MHA、MLA、滑动窗口不能统一假定成同一份布局。radix_cache.pymemory_pool.pyallocator.py

7.2 共享前缀的例子

假设已经缓存 token 序列 [11, 22, 33],对应 KV 槽位 [100, 101, 102]。新的输入为 [11, 22, 33, 44, 55]

token:     11   22   33 | 44   55
KV 槽位:  100  101  102 | 新分配位置
           可复用前缀  | 尚需执行的后缀

Req.init_next_round_input 会通过缓存匹配得到 prefix_indices,据此计算 extend_input_lenScheduleBatch.prepare_for_extend 将前缀位置与新分配位置组织成模型执行需要的数据。

这是无分页边界等额外约束的教学例子。真实实现还会考虑页对齐、输出 logits 所需的 token、分块和具体缓存模式;即使提示词完全重复,也不能直接推导出一次 forward 都不需要执行。请求与 Extend 准备

下面的小例子只解释 token 与槽位的区别,不是 SGLang 缓存实现:

# CONCEPT_ONLY: 不依赖模型,演示前缀与位置映射。
cached_tokens = [11, 22, 33]
cached_slots = [100, 101, 102]
new_tokens = [11, 22, 33, 44, 55]
matched = 0
for old, new in zip(cached_tokens, new_tokens):
    if old != new:
        break
    matched += 1
reused_slots = cached_slots[:matched]
tokens_to_compute = new_tokens[matched:]
assert reused_slots == [100, 101, 102]
assert tokens_to_compute == [44, 55]

7.3 重点缓存 API

内部方法 职责
match_prefix 查找匹配前缀,返回设备位置及匹配节点等信息
insert 插入前缀与存储位置,必要时拆分节点
cache_finished_req 请求结束后登记可缓存部分并处理资源
cache_unfinished_req 保存尚未结束请求的可缓存部分
inc_lock_ref / dec_lock_ref 沿节点路径调整引用保护
evict 选择可驱逐节点,并通过 allocator 释放位置

这些是运行时内部 API,业务请求通常不直接调用。lock_ref 保护仍在使用的缓存不被淘汰,不是数据库锁;match_prefix 也可能调整树结构,不能简单视为绝不修改状态的字典查询。RadixCache 实现

7.4 一个基础容量估算

对普通 MHA/GQA,若每层保存 K 和 V,未分片时可粗估:

KV 字节数 ≈ 2 × 层数 × 缓存 token 数 × KV head 数 × head_dim × 每元素字节数

例如设 32 层、8 个 KV heads、head_dim=128、FP16,一个 token 约占 2×32×8×128×2 = 131072 字节,即 128 KiB;8192 个缓存 token 约 1 GiB。这是基于假设的理论计算,不含模型权重、激活、页碎片和图执行缓冲,也不适用于所有 MLA、量化缓存或并行布局。

8. 从批次到模型和 Attention 内核

非投机解码的主线:

Scheduler.run_batch
 → ScheduleBatch.get_model_worker_batch
 → TpModelWorker.forward_batch_generation
 → ForwardBatch.init_new
 → ModelRunner.forward
 → ModelRunner._forward_raw
    ├─ 满足条件:graph_runner.replay
    └─ 常规路径:forward_extend / forward_decode / 其他模式
 → 具体模型 forward
 → logits
 → ModelRunner.sample
 → next_token_ids

Overlap worker、流水线并行等会在这条线上增加包装或通信。forward 的输出与“最终可返回给用户的文字”之间,还隔着采样、结束条件处理和反分词。tp_worker.pymodel_runner.py

8.1 ForwardMode 为什么重要

ForwardBatch 中的执行模式用于区分 Extend、Decode 等情况。同一个模型层在不同模式下,查询长度、缓存访问与后端元信息都可能不同。源码里的 Extend 常用于处理新增的输入部分,不应机械理解为“每次都从头处理整个 prompt”。forward_batch_info.py

8.2 RadixAttention 类不等于 RadixCache

models/llama.py 为阅读例子,模型 attention 层使用 layers/radix_attention.py 中的 RadixAttention;后者将计算交给 forward_batch.attn_backend.forward

  • RadixCache 管理哪些前缀状态可以复用。
  • RadixAttention 是模型层调用 attention 的抽象入口。
  • layers/attention/ 中的后端组织具体执行方式及相关元数据。
  • sgl-kernel/ 和其他依赖库提供部分底层计算实现。

因此不应在 radix_cache.py 中寻找所有 attention 矩阵乘法,也不应假定所有内核都由 sgl-kernel 实现。llama.pyradix_attention.pyAttention 后端目录

8.3 CUDA Graph 分支

ModelRunner._forward_raw 会检查当前模式、graph runner 与批次是否满足图执行条件,再决定是否 replay。开启图功能并不意味着所有输入都走图;调试常规 forward 时如果断点没有命中,应先确认是否进入 replay 分支。

研究 CUDA Graph 本身时,再进入 model_executor/cuda_graph_runner.py。临时用 --disable-cuda-graph 简化调试会改变内存和性能行为。图执行实现

9. 输出如何返回客户端

模型产生 next_token_ids
 → Scheduler.process_batch_result
 → process_batch_result_prefill / process_batch_result_decode
 → 更新 Req.output_ids,判断结束条件和缓存状态
 → 发出 BatchTokenIDOut
 → DetokenizerManager.event_loop
 → handle_batch_token_id_out
 → 返回 BatchStrOut
 → TokenizerManager.handle_loop
 → 按 rid 更新等待中的 ReqState
 → HTTP 或 Engine 输出

SchedulerOutputProcessorMixin 将结果处理从主调度器文件中拆出。查“请求什么时候结束”“何时回收缓存”“为什么不立即输出”时,要同时看这个 mixin,不能只搜索 scheduler.py结果处理DetokenizerManager

流式事件不应假定严格对应一个 token:反分词存在文本边界处理,返回格式也因原生接口和兼容接口而异。原生 /generate 输出中的 text 与聊天流式接口的 delta 不应使用同一套拼接假设;应按目标接口处理。Tokenizer 返回处理聊天流式响应

10. 常用接口与参数

10.1 外部接口

接口 作用 注意点
POST /generate 原生文本/token 生成 参数主要对应 GenerateReqInput
POST /v1/chat/completions messages 格式的聊天生成 会经过聊天模板及协议适配
POST /v1/completions 文本补全兼容接口 与聊天 messages 格式不同
POST /v1/embeddings 向量输出 需要适合的模型与服务配置
GET /get_model_info 查询服务模型信息 便于核对启动实例
GET /health 健康检查 此版本涉及推理活性检查,不只是静态返回
Engine.generate Python 同步生成 单条、批量和流式返回形式不同
Engine.async_generate Python 异步入口 根据异步返回形式使用
Engine.shutdown 关闭 Engine 及相关资源 使用 finally 确保释放

这些接口不是完全等价的别名,也不是兼容协议所有功能的保证。http_server.pyEngine

10.2 单请求生成参数

字段 用途
max_new_tokens 原生采样参数中的生成 token 上限
temperature 采样温度;该版本对接近零的值做贪心相关处理
top_ptop_k 限制采样候选范围
stop 停止字符串等规则的入口之一
json_schema 原生采样参数中的 JSON Schema 字符串
stream GenerateReqInput 层面的流式开关,不放在 sampling_params 内
return_logprob 请求返回 logprob 信息,也属于请求级字段

原生接口通常使用 sampling_params.max_new_tokens;聊天兼容接口示例使用顶层 max_tokens。不要把两套 JSON 字段原样混用。约束后端和模型仍需支持所选结构化输出功能。SamplingParams请求定义

10.3 启动配置与源码位置

启动参数 主要控制对象
--model-path 模型权重与配置来源
--tp-size 张量并行规模
--mem-fraction-static 静态内存预算相关配置,并非只设置单请求 KV 大小
--max-running-requests 最大运行请求数约束
--chunked-prefill-size 分块 prefill 的 token 预算相关配置
--schedule-policy 等待队列调度策略
--attention-backend attention 后端选择,受硬件与模型约束
--disable-radix-cache 关闭 Radix 前缀缓存路径,用于特定配置或对照实验
--disable-overlap-schedule 关闭 overlap 调度
--disable-cuda-graph 关闭 CUDA Graph 路径

不要同时修改所有参数再解释性能变化。建议每次只调整一个维度,记录完整启动命令。server_args.py

11. 示例:从请求观察源码行为

以下模型相关命令与 Python 示例未执行推理验证,仅按 v0.5.2 接口核对并检查语法。运行需要单独安装匹配的 SGLang、设备运行时、模型依赖和权重,不应将其加入本站 MkDocs 环境。

11.1 启动与原生请求

python3 -m sglang.launch_server \
  --model-path Qwen/Qwen2.5-0.5B-Instruct \
  --host 127.0.0.1 \
  --port 30000
curl --fail-with-body http://127.0.0.1:30000/generate \
  -H 'Content-Type: application/json' \
  -d '{
    "text": "Explain the purpose of a KV cache in one sentence.",
    "sampling_params": {"temperature": 0, "max_new_tokens": 64},
    "stream": false
  }'

这个例子用于追踪原生入口,不自动套用聊天模板。依次定位 GenerateReqInput → TokenizerManager.generate_request → Scheduler.handle_generate_request,观察文本如何变成 token IDs 和 Req

11.2 聊天与流式响应

curl --no-buffer --fail-with-body http://127.0.0.1:30000/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "Qwen/Qwen2.5-0.5B-Instruct",
    "messages": [{"role": "user", "content": "用三句话解释 KV Cache。"}],
    "temperature": 0,
    "max_tokens": 128,
    "stream": true
  }'

观察 serving_chat.py 如何处理 messages 和流式输出。SSE 不是一个完整 JSON 文档,客户端应按事件解析,并处理结束标记、错误与断连。

11.3 Python Engine:不经过 HTTP

# MODEL_REQUIRED: 需要已安装的 SGLang 和兼容推理环境。
import sglang as sgl


def main():
    engine = sgl.Engine(model_path="Qwen/Qwen2.5-0.5B-Instruct")
    try:
        outputs = engine.generate(
            ["The capital of France is", "The capital of Japan is"],
            sampling_params={"temperature": 0, "max_new_tokens": 16},
        )
        for output in outputs:
            print(output["text"])
            print(output.get("meta_info", {}))
    finally:
        engine.shutdown()


if __name__ == "__main__":
    main()

Engine.generate 将 prompt 转成 GenerateReqInput,仍复用 TokenizerManager 和运行时。绕过 HTTP 不代表绕过调度,也不代表模型执行变成单进程。脚本保留 main guard,便于子进程启动。Engine.generate

11.4 观察共享前缀

下面客户端仅依赖 Python 标准库,但必须先有运行中的模型服务。它顺序发出两次请求,打印该版本原生响应的缓存统计。

# SERVER_REQUIRED: 需要先启动 127.0.0.1:30000 服务。
import json
from urllib.request import Request, urlopen

prefix = "Reference: A KV cache stores attention keys and values. " * 64
for suffix in ["Question A: explain its purpose.", "Question B: explain its cost."]:
    payload = {
        "text": prefix + suffix,
        "sampling_params": {"temperature": 0, "max_new_tokens": 32},
        "stream": False,
    }
    request = Request(
        "http://127.0.0.1:30000/generate",
        data=json.dumps(payload).encode("utf-8"),
        headers={"Content-Type": "application/json"},
    )
    with urlopen(request, timeout=120) as response:
        result = json.load(response)
    meta = result.get("meta_info", {})
    print("prompt_tokens:", meta.get("prompt_tokens"))
    print("cached_tokens:", meta.get("cached_tokens"))
    print(result.get("text", ""))

这里不对命中数量写固定断言:缓存可能受分词边界、页对齐、驱逐及实例配置影响。要做性能实验,还需在独立实验实例中控制冷/热缓存与其他请求。cached_tokens 的响应组装可在 TokenizerManager._handle_batch_output 中找到。

11.5 结构化输出:约束如何进入调度

# SERVER_REQUIRED: 需要支持该约束配置的运行中服务。
import json
from urllib.request import Request, urlopen

schema = {
    "type": "object",
    "properties": {"summary": {"type": "string"}},
    "required": ["summary"],
    "additionalProperties": False,
}
payload = {
    "text": "Summarize what a KV cache does. Return a JSON object with summary.",
    "sampling_params": {
        "temperature": 0,
        "max_new_tokens": 128,
        "json_schema": json.dumps(schema),
    },
    "stream": False,
}
request = Request(
    "http://127.0.0.1:30000/generate",
    data=json.dumps(payload).encode("utf-8"),
    headers={"Content-Type": "application/json"},
)
with urlopen(request, timeout=120) as response:
    output = json.load(response)
print(output)

原生 json_schema 在该版本使用字符串。阅读时从 SamplingParams 跟到 Scheduler 的 grammar 队列和 constrained/,理解语法准备与采样约束怎样衔接。语法约束不保证答案事实正确;如果生成因长度限制终止,也要检查完成原因再解析文本。sampling_params.py约束模块

12. 调试问题与源码入口

现象 优先排查 对应源码
请求格式错误 原生与聊天字段是否混用 io_struct.pyopenai/protocol.py
聊天输出异常 模板、特殊 token、停止规则 serving_chat.pysampling_params.py
首 token 很慢 排队、prefill 长度、冷缓存 scheduler.pyschedule_policy.py
有相同文本但缓存没命中 实际 token 前缀、缓存关闭或驱逐 schedule_batch.pyradix_cache.py
显存不足 权重、KV、临时张量、图缓冲分别占多少 model_runner.pymemory_pool.pycuda_graph_runner.py
Decode 吞吐低 批大小、内存访问、attention/通信后端 tp_worker.pylayers/attention/distributed/
请求迟迟不结束 停止条件、长度上限、结果处理 Reqscheduler_output_processor_mixin.py
已有 token 却没有文本 反分词边界、流式处理、请求映射 detokenizer_manager.pytokenizer_manager.py
常规 forward 断点不命中 图 replay、overlap worker、投机分支 model_runner.pyscheduler.py

这些是排查起点,不是对现象的唯一归因。建议日志始终携带请求 ID,并记录批次模式、输入长度、前缀命中长度和完成原因;不要仅用单个 GPU 利用率数值判断瓶颈。

13. 推荐阅读顺序与后续专题

  1. launch_server.py → http_server.py → engine.py:弄清进程如何创建。
  2. io_struct.py → tokenizer_manager.py:追一个原生请求。
  3. scheduler.py → schedule_policy.py → schedule_batch.py:追一轮普通调度。
  4. radix_cache.py → memory_pool.py → allocator.py:区分前缀索引与实际存储。
  5. tp_worker.py → model_runner.py → models/llama.py:追一次模型执行。
  6. radix_attention.py → layers/attention/:进入具体后端。
  7. scheduler_output_processor_mixin.py → detokenizer_manager.py:闭合返回链路。
  8. 再扩展到 overlap、CUDA Graph、PD 分离、投机解码、多卡通信和内核优化。

建议后续分别记录“调度器一轮执行”“RadixCache 命中与回收”“一个 attention 后端”“一次 TTFT/吞吐实验”,每篇固定模型、版本、硬件与启动参数。目前本文完成源码静态核对和示例说明,尚未完成模型运行及性能验证。