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
2
3
4
5
控制面(scheduler / engine core)        执行面(worker / model runner)
哪个请求需要多少 block ←→ 真正的 KV 内存布局
哪些 block 可以复用/回收 ←→ block 的 device 端更新
prefix cache 是否命中 ←→ attention 按逻辑顺序读取物理散落的 blocks
资源不足时驱逐哪些 block ←→ 某些优化 kernel 如何利用 paged layout

vLLM 的 KV Cache 是由调度层的生命周期管理和执行层的 block 化访问共同完成的,不是单点实现。

PagedAttention:不只是 kernel

PagedAttention 包括两个层面:

  1. 数据结构与内存管理思想:KV Cache 被切成 blocks/pages,不要求物理连续
  2. 执行层的访问方式: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_tokensmax_num_seqs、chunked prefill 行为 Scheduler 层
Worker / Memory config gpu_memory_utilization、KV cache 内存上限 GPU Worker 层
Parallel config tensor_parallel_sizepipeline_parallel_sizedata_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 常常不是「模型本身大」,而是调度层把并发推高了。



系列导航