vLLM系统拆解-12-源码地图:每个核心能力落在哪一层
vLLM系统拆解-12-源码地图:每个核心能力落在哪一层
这篇文章不要求逐行读源码,但需要建立一张「层次地图」:vLLM 的每个核心能力大致落在哪类模块,面试官追问实现位置时能接住,能把「设计目标→模块分工→运行链路」讲成完整逻辑。
理解 vLLM 源码结构,最重要的视角不是「核心代码在哪个文件」,而是「这个问题属于哪一层」。
vLLM 的七层结构
用一句话概括:上层负责接请求和组装结果,中层负责调度与 KV Cache 管理,下层负责 GPU 执行与采样。
flowchart TD
A["入口层\nPython API / CLI / HTTP Server"] --> B
B["Engine 层\nLLMEngine / AsyncLLMEngine\n把请求转为内部 request,维护生命周期"] --> C
C["Engine Core\nbusy loop:持续调度循环"] --> D & E
D["Scheduler 层\n决定谁进 batch、分多少 token budget"] --> E
E["KV Cache 层\n分配/回收/复用 block,支持 prefix caching"]
C --> F
F["Executor 层\n多进程 worker 管理与任务派发"] --> G
G["GPU Worker 层\n代表单张 GPU 的设备代理"] --> H
H["GPU Model Runner 层\n组织一次 forward、触发采样"]
H --> I["Sampler / Output 层\n从 logits 取 token,解码,流式返回"]
I -.-> A
这张图之后在脑子里要随时能浮现。遇到任何问题,先定位它在哪一层,再解释设计目的。
三条主线:任何追问都能找到落点
请求主线(一个用户请求怎么从 API 走到 GPU 再回来):入口在哪 → engine 怎么接 → scheduler 怎么安排 → worker 怎么执行 → sampler 怎么出 token → 结果怎么流回去。
显存主线(KV Cache 怎么分配、增长、复用、释放):为什么要 block/page 化 → prefix cache 怎么命中 → 为什么能减少碎片 → preemption/eviction 在哪发生。
性能主线(vLLM 为什么快):continuous batching 在哪层体现 → chunked prefill 为什么能实现 → CPU/GPU 各自的瓶颈在哪里 → spec decode/CUDA graph/量化分别属于哪层优化。
把这三条主线抓住,大多数追问不会乱。
六个核心模块
vllm/v1/engine/core.py:控制中枢
Engine core 是 vLLM 的心脏。官方架构文档说明,engine core process 负责:运行 scheduler、管理 KV Cache、协调 GPU workers、在 busy loop 中持续调度请求。
为什么需要持续循环?因为大模型在线推理不是「调用一次函数然后结束」:
- 新请求随时到来
- 老请求可能继续 decode
- 有的请求刚做完 prefill,有的请求已结束需要释放 block
- 有的请求能命中 prefix cache,有的因资源不足需要被抢占或延后
这些事情不是某个单一 forward 能解决的。必须有一个持续存在的控制循环,不断做资源再分配。
面试表达:「vLLM 的核心不是单次 forward,而是 engine core 的 iteration-level scheduling。它通过持续循环管理请求状态、KV Cache 和 worker dispatch,把「很多异步请求共用一套 GPU 资源」的问题组织起来。」
vllm/v1/core/sched/scheduler.py:调度策略大脑
Scheduler 不负责算模型,它负责决定谁有资格在这一轮被算。每轮 iteration,scheduler 回答:
- 哪些请求进入 batch?
- 哪些做 decode,哪些做 prefill?
- 是否需要把长 prefill chunk 化?
- token budget 用完了吗?
- 某个请求能否继续增长 KV Cache?
- 资源不够时,延后、抢占,还是驱逐某些缓存?
很多推理系统的真正差异不在「模型会不会跑」,而在于面对大量不同长度、不同阶段、不同优先级的请求时,调度策略如何平衡吞吐和时延。Scheduler 就是这个策略的载体。
面试表达:「scheduler 的目的,是把「请求级别的不确定性」变成「每轮 iteration 可执行的确定批次」,从而让 GPU 始终有活干,同时尽量控制 TTFT、ITL 和显存占用。」
vllm/v1/engine/llm_engine.py:引擎封装层
这个文件不是模型代码,而是引擎级控制平面接口。职责包括:
- 接收
add_request/generate等上层调用 - 把输入整理为内部 request 结构
- 调用 engine core
- 管理输出处理器
- 提供 reset prefix cache、sleep、profile 等引擎级操作
- 在 data parallel 场景中与其他引擎实例协调
如果没有这一层:外层服务代码会直接依赖 scheduler/worker 细节;在线服务、离线推理、异步接口都会重复拼装底层组件;引擎控制操作无处安放。这层本质上是抽象隔离——把复杂的核心系统封装成稳定、清晰、可调用的 engine 抽象。
vllm/v1/executor/multiproc_executor.py:worker 进程组织者
调度和执行最好解耦。Engine core 决定「做什么」,但不应该自己承担 worker 进程生命周期、IPC、故障隔离这些任务。Executor 负责:
- 启动 worker 进程
- 管理 worker 生命周期
- engine core 和 worker 之间的任务传递
- 隔离控制平面和执行平面
为什么多进程?GPU 服务需要每张 GPU 有相对独立的执行实体、清晰的资源边界、更稳定的故障隔离,避免把过多执行细节塞进单个主进程。这也是 V1 明确走多进程架构的重要原因。
vllm/v1/worker/gpu_worker.py:单 GPU 设备代理
Worker 是「代表一张 GPU 的服务代理人」,不只是简单调用模型:
- 加载模型权重
- 探测可用显存,预留 KV Cache 空间
- 提供执行接口给上层
- 管理与该 GPU 绑定的状态
- 承担 cache / profile / LoRA / 多模态相关操作
为什么不让 engine core 直接碰 GPU?那样会导致控制逻辑和设备逻辑强耦合、多 GPU 场景混乱、难以扩展到 TP/PP/DP/多机场景。Worker 这一层的存在,是把设备相关执行从全局调度策略里拆出来。
vllm/v1/worker/gpu_model_runner.py:贴近 forward 的执行核心
Worker 之下还有 model runner。Worker 偏「GPU 进程级容器」,model runner 偏「这一轮怎么真正组织 forward 和 sample」。
Model runner 通常负责:
- 准备模型执行所需输入张量
- 调用模型 forward
- 更新 device 上的执行状态
- 获取输出,触发采样
- 与 CUDA graph capture、编译优化等机制衔接
为什么 worker 下面还要拆 model runner?Worker 还承担很多进程/设备管理责任,如果把 forward 细节全塞进 worker,单个类职责过重。拆开后可维护性、可测试性都更好,新模型/新执行路径接入也更干净。
KV Cache、PagedAttention、Prefix Caching 的跨层分布
这三个能力经常被当成「某个文件里的实现」,但实际上都是横跨多层的系统能力。
KV Cache:控制面 + 执行面
1 | 控制面(scheduler / engine core) 执行面(worker / model runner) |
vLLM 的 KV Cache 是由调度层的生命周期管理和执行层的 block 化访问共同完成的,不是单点实现。
PagedAttention:不只是 kernel
PagedAttention 包括两个层面:
- 数据结构与内存管理思想:KV Cache 被切成 blocks/pages,不要求物理连续
- 执行层的访问方式:attention kernel 能按逻辑顺序读取这些物理上不连续的 blocks
所以它横跨上层 KV block 管理思想,以及下层 attention/kernel/runner 执行实现。
「PagedAttention 不只是一个 kernel 名字,它背后是整套 paged KV Cache 设计。kernel 只是让这种 block 化内存布局在执行时仍然高效可读。」
Prefix Caching:调度层 + KV Cache 层的跨请求复用
Prefix caching 要解决的问题不是「一个请求内部怎么缓存」,而是:两个请求如果前缀相同,能否复用前面已计算过的 full blocks;如何识别命中;如何维持引用关系;在资源不足时如何驱逐。
源码映射上,它落在:prefix caching 设计文档 + scheduler/cache manager 相关逻辑 + engine 层的 reset_prefix_cache 等控制接口。
Prefix caching 是系统能力,不是某个小工具函数。
配置模块:控制系统行为权衡的外部旋钮
配置不是「参数表」,而是控制系统如何在吞吐、时延、显存、并行性之间做权衡的外部 knobs。
| 配置类 | 影响参数 | 最终流向 |
|---|---|---|
| Scheduler config | max_num_batched_tokens、max_num_seqs、chunked prefill 行为 |
Scheduler 层 |
| Worker / Memory config | gpu_memory_utilization、KV cache 内存上限 |
GPU Worker 层 |
| Parallel config | tensor_parallel_size、pipeline_parallel_size、data_parallel_size |
Executor / Worker 层 |
如何通过配置定位性能问题:先找参数挂在哪个 config 类上,再看它最终流向 scheduler、worker 还是model runner,因为不同目标参数最终影响的是不同层。
遇到新功能,如何推断它落在哪层
| 功能特征 | 优先看 |
|---|---|
| 改变「请求怎么排队、怎么进 batch」 | engine core、scheduler、config |
| 改变「显存怎么分配、缓存怎么复用」 | cache manager / scheduler / worker、prefix cache 相关设计 |
| 改变「模型怎么跑、张量怎么组织」 | gpu worker、gpu model runner、model/kernel 层 |
| 改变「外部如何调用系统」 | CLI、serve、engine 封装、API server 层 |
举几个具体例子:
- speculative decoding:改变 batch 形态和执行路径 → scheduler(speculation 深度/预算分配)+ worker(draft path + verify path)
- LoRA 管理:改变「模型怎么跑」→ gpu worker + gpu model runner 的 LoRA adapter 管理
- structured output:影响 sampling 约束 → sampler / output processor
- 多模态输入:改变「模型怎么跑」+ 可能影响「请求怎么排队」→ model runner + engine 封装
这个推断方法比记具体路径更有用,因为路径可能随版本变化,但分层逻辑是稳定的。
为什么要分这么多层
vLLM 不是简单几千行写一起,因为它面对的问题是:大量请求并发到来、每个请求处于不同阶段、显存有限、GPU 不应空转、延迟不能太差、还要支持不同模型/并行方式/硬件后端。
分层设计的四个原则:
| 分离关注点 | 控制侧 | 执行侧 |
|---|---|---|
| 控制 vs 执行 | engine core + scheduler:谁先跑、跑多少、资源够不够 | worker + model runner:这一轮 forward 怎么完成 |
| 设备管理 vs 模型执行 | gpu worker:设备进程容器 | gpu model runner:单轮模型执行逻辑 |
| 外部接口 vs 引擎内部 | LLM / serve / OpenAI protocol | core + scheduler + executor + worker |
| 单请求逻辑 vs 系统级资源管理 | sampling params、输出处理 | KV Cache、batch token budget、抢占、prefix reuse |
从根上说,vLLM 的源码结构体现的是:它不是一个模型脚本,而是一个推理操作系统。
速查:6 个核心模块一句话总结
| 模块路径 | 一句话职责 | 面试关键词 |
|---|---|---|
vllm/v1/engine/core.py |
控制中枢,持续调度循环 | busy loop、iteration scheduling |
vllm/v1/core/sched/scheduler.py |
调度策略大脑,决定谁进 batch | token budget、preemption、chunked prefill |
vllm/v1/engine/llm_engine.py |
上层调用看到的引擎封装 | 抽象隔离、request lifecycle |
vllm/v1/executor/multiproc_executor.py |
worker 进程组织者 | 控制平面/执行平面解耦 |
vllm/v1/worker/gpu_worker.py |
单 GPU 设备代理 | 显存探测、KV cache 预留 |
vllm/v1/worker/gpu_model_runner.py |
贴近 forward/sampling 的执行核心 | 输入准备、CUDA graph、采样 |
几个常见追问的参考答法
「vLLM 的核心代码主要在哪?」 核心主干在 engine core、scheduler、executor、gpu worker 和 gpu model runner 这几层。engine core 管控制循环,scheduler 决定 iteration 级调度,worker/model runner 负责设备执行,PagedAttention 落在 KV Cache 与 attention 执行的结合处。外层 API 和 LLMEngine 更像是封装和门面。
「continuous batching 相关实现,先看哪里?」 先看 engine core 和 scheduler。Continuous batching 本质不是某个 kernel,而是请求在 iteration 级别持续进入和退出 batch 的调度行为。worker 只是执行结果,策略发生在调度层。
「prefix cache 实现先看哪里?」 先看 prefix caching 设计文档,然后看 scheduler/cache manager 相关逻辑。它属于跨请求 KV block 复用,不是单次 forward 里的临时缓存。
「线上 OOM,先看哪一层?」 先分清是权重、KV Cache 还是 batch 膨胀导致的。分别对应:权重→模型本身 + 并行策略;KV Cache→worker 的可用显存探测和 cache 预分配;batch 膨胀→scheduler 的 batch/token budget 配置。OOM 常常不是「模型本身大」,而是调度层把并发推高了。
