仓库地址:github.com/hhk-png/cyc...
03 · vLLM 架构设计:从 HTTP 请求到 Token
本章从宏观到微观梳理 vLLM 的系统架构:引擎如何组织调度循环,各组件(Scheduler、BlockManager、ModelRunner、Worker)各自负责什么,分布式并行如何切分模型,以及一个请求在整条流水线中的完整生命周期。理解这张地图之后,读代码和调参数都会有抓手。
1. 分层总览
vLLM 大体分为三层:
| 层级 | 关键模块 | 职责 |
|---|---|---|
| Serving 层 | vllm/entrypoints/openai/、vllm/entrypoints/llm.py |
对外暴露 API / 离线接口,做协议解析、请求排队、流式输出 |
| 引擎层 | vllm/engine/(llm_engine.py、async_llm_engine.py、engine_core.py) |
请求入队、调用调度器、执行模型、处理输出 |
| 核心层 | vllm/core/scheduler.py、BlockManager、vllm/worker/model_runner.py、vllm/worker/worker.py |
连续批处理、KV 缓存管理、张量拼接、GPU 执行 |
整条链路的骨架如下(简化版):
scss
HTTP 请求
│ (OpenAI 协议: vllm/entrypoints/openai/api_server.py)
▼
ServingEngine ── 解析 prompt/messages/sampling 参数
│ add_request()
▼
AsyncLLMEngine (前端) ──> EngineCore (后端进程)
│ ├─ Scheduler ------ 选择下一批要执行的 seq
│ ├─ KV Cache Manager ------ 分配/回收 KV block
│ ├─ ModelExecutor ------ 下发到 GPU Worker
│ └─ OutputProcessor ------ 采样后的 token 后处理
▼
GPU Worker(s) (Ray / 多进程 / 单卡)
└─ ModelRunner ------ 拼接 input_ids、padding、跑 CUDA Graph、forward
1.1 vLLM 源码地图
把上面三层展开成一份"源码地图"------每个目录/文件负责什么、实现哪个概念。这是你读真实 vLLM 源码时的导航:
| 路径 | 职责 | 实现的概念 |
|---|---|---|
vllm/engine/llm_engine.py |
LLMEngine:add_request() + 循环 step() 的同步引擎 |
请求入队、"调度-执行-输出"主循环 |
vllm/engine/async_llm_engine.py |
AsyncLLMEngine:把 step() 放进后台 asyncio 任务,支持流式输出 |
在线服务、SSE 流式 |
vllm/engine/engine_core.py |
V1 EngineCore:把调度/执行/KV 管理收进独立后端进程 |
V1 前后端解耦 |
vllm/engine/arg_utils.py |
EngineArgs:CLI 参数 → 各 Config 对象 |
配置解析 |
vllm/core/scheduler.py |
Scheduler.schedule():每步选批、准入/出批/抢占 |
continuous batching、preemption |
vllm/core/block_manager_v1.py |
V0 BlockAllocator/CachedBlockAllocator:块分配、ref count、CoW |
分页 KV 管理 |
vllm/worker/model_runner.py |
拼 input_ids/positions/slot_mapping、padding、CUDA Graph 重放、forward() |
模型执行、slot 映射 |
vllm/worker/worker.py |
每 GPU 分片执行者:init_worker 加载模型、profile_run 摸显存 |
分布式执行单元 |
vllm/model_executor/ |
模型加载、各架构 Model、TP/PP 切分、量化层 | 并行、量化 |
vllm/attention/ |
ops/paged_attn.py(PagedAttention 内核)与 flash/triton 等后端 |
PagedAttention |
vllm/entrypoints/openai/ |
FastAPI server、OpenAI 协议 ↔ 引擎请求转换 | OpenAI 兼容 API |
vllm/spec_decode/ |
投机解码的 draft 与 verify 主循环 | speculative decoding |
vllm/sampler/ |
logits 后处理(top-k/p、惩罚)+ 采样 + 引导解码 | 采样 |
vllm/v1/ |
V1 全套:core/sched/scheduler.py(SchedulerV1)、core/kv_cache_manager.py、engine/、worker/ |
V1 架构(≥1.0 的唯一主线) |
vllm/config/ |
ModelConfig / CacheConfig / SchedulerConfig / ParallelConfig 等 |
配置对象 |
读法建议:先读
vllm/v1/engine/的step()抓到主循环,再顺着v1/core/sched/scheduler.py→v1/worker/model_runner.py→v1/attention/读下去。注意版本坐标 :V0 引擎(llm_engine.py、block_manager_v1.py等)已在 vLLM 1.0(2025 年末)中被彻底移除 ,以下 V0 路径仅为历史对照,≥1.0 版本直接读vllm/v1/。mini-vLLM 的每个模块顶部都标注了它对应的上面这些路径。
2. 同步与异步引擎
LLMEngine(vllm/engine/llm_engine.py):同步引擎。add_request()之后必须手动调用step()来推进一次"调度 + 执行 + 输出"循环。它没有事件循环,适合离线批量推理。AsyncLLMEngine(vllm/engine/async_llm_engine.py):异步引擎,step()被放进后台 asyncio 任务持续运行。它把 LLMEngine 包装起来,用队列与外部隔离,支持流式输出(StreamingOutputProcessor)。在线服务器使用它。
V1 的 EngineCore 拆分
较新版本(V1 架构)进一步把"前端"与"执行后端"解耦:
EngineCore(vllm/engine/engine_core.py):把 Scheduler、ModelExecutor、KV Cache Manager、OutputProcessor 打包在一起,作为一个独立实体运行(默认是独立的EngineCoreProcess子进程)。EngineCoreClient:前端(AsyncLLMEngine)通过进程间通信(默认基于 zmq 的 RPC / 共享内存)向 EngineCore 提交EngineCoreRequest并回收EngineCoreOutput。- 好处:推理主循环与 API 服务解耦,前端崩溃不拖垮推理;GPU 显存分配(KV cache pool)由后端统一管理。
前端 step() 每轮会:发请求 → EngineCoreClient.scheduled_requests() → EngineCore.step() → 取回输出 → 交给 OutputProcessor。
2.1 V1 EngineCore 前后端协议
V1 把引擎拆成"前端(AsyncLLMEngine + API server)"和"后端(EngineCoreProcess)"两个进程,它们之间有一套明确的通信协议:
perl
AsyncLLMEngine(前端进程,Python asyncio)
│ EngineCoreClient
│ ├─ 控制面:zmq RPC socket ── 提交 EngineCoreRequest、回收 EngineCoreOutput
│ └─ 数据面:共享内存 ── 大块张量(logits、tokens)免拷贝
▼
EngineCoreProcess(后端独立进程,默认)
├─ SchedulerV1 ------ 每步选批
├─ KVCacheManager ------ KV block 池(后端统一分配、引用计数)
├─ ModelExecutor ------ 下发到 GPU Worker
└─ OutputProcessor ------ 采样后处理
└─ 独占 GPU:KV cache pool 与模型权重都归后端管理
一次请求 / 输出的完整生命周期:
- 前端把
EngineCoreRequest(request_id、prompt token ids、SamplingParams)序列化后经 zmq RPC 发给后端; - 后端把请求送进
SchedulerV1的等待队列; - 前端每个循环周期通过
EngineCoreClient触发一次后端step()(V1 里是"推送"式:前端add_request后,后端持续 step 直到没有活请求); - 后端
step():调度 → 执行 → 采样 → 后处理,产出EngineCoreOutput(本步的 token 增量、logprobs、finish_reason); - 输出经 zmq 回传(大张量走共享内存,zmq 只传元数据),前端
OutputProcessor组装成RequestOutput推给 asyncio 流。
设计动机(为什么值得拆进程):
- 故障隔离:前端(HTTP 层)崩溃不影响后端推理,反之亦然;
- 显存唯一所有权:KV pool 由后端独占管理,避免多进程重复初始化 / 碎片化;
- 控制面与数据面分离:小消息走 zmq、大张量走共享内存,降低 IPC 开销。
与 mini-vLLM 的对应 :mini 版刻意不拆进程 ------minivllm/async_engine.py 用后台线程跑 LLMEngine.step(),FastAPI 只负责收发,本质上保留了"循环步进 + 异步输出"这两个核心,把 IPC 复杂度省略了。看懂 mini 版再回来看 vllm/v1/engine/engine_core.py,会更容易。
3. 各组件职责
3.1 Scheduler ------ vllm/core/scheduler.py
Scheduler 是"连续批处理"的中枢,核心是 schedule() 方法,每步调用一次,返回 SchedulerOutputs(决定本轮执行哪些 seq、哪些 block 需要 copy-on-write / swap)。
主要逻辑:
- Admission(准入) :从等待队列取请求加入运行批次,受
SchedulerConfig.max_num_seqs、max_num_batched_tokens约束。 - Continuous batching:每一步都可能让请求随时入批/出批(生成完成后让位),不等待整批结束。
- Preemption(抢占) :当新请求需要的 KV block 不够时,按策略(
--preemption-mode,recompute或swap)抢占已运行请求的资源。recompute丢弃其 KV 缓存、之后重算;swap把 block 换出到 CPU 显存。 - 优先级 :
--priority支持按队列优先级调度;默认先到先服务。
3.2 BlockManager ------ KV 缓存管理
KV cache 以固定大小 block(默认 --block-size 16 token)为单位管理,而非按序列长度动态分配:
- V0 :
vllm/core/block_manager_v1.py(BlockAllocator/CachedBlockAllocator)。CachedBlockAllocator支撑 prefix caching,维护 ref count 和 hash table。 - V1 :
vllm/v1/core/kv_cache_manager.py+vllm/v1/core/kv_cache_utils.py(KVCacheBlockPool)。V1 默认开启 prefix caching,block 以引用计数管理,支持跨请求共享公共前缀的物理 block。 - 核心概念:
- Block table(block 表) :每个序列维护"逻辑 token 位置 → 物理 block id"的映射,见
Seq的block_manager字段。 - Ref count:一个物理 block 可能被多个序列共享(前缀共享),每多一个引用 +1,序列退出时 -1,归零才回收。
- Copy-on-write(CoW):当某个序列要写它共享的 block(例如出现分叉)时,才复制出一个私有 block,避免"先拷贝再写"的浪费。
- Block table(block 表) :每个序列维护"逻辑 token 位置 → 物理 block id"的映射,见
3.3 ModelRunner ------ vllm/worker/model_runner.py
ModelRunner 负责把调度结果"翻译"成 GPU 上的张量并执行模型:
- 构造
ModelInputForGPU:把本批所有序列的 token 拼成二维张量(input_ids、positions、slot_mapping),slot_mapping告诉 attention 每个 token 的 KV 应写入哪个 block slot。 - Padding:为了触发 CUDA Graph,输入被 padding 到预设的 batch size。
prepare_input_tensors()/execute_model():前者做张量准备,后者真正调用self.model.forward()并返回 logits。- CUDA Graph 捕获 :在
capture_model阶段用 dummy 输入捕获多个 batch size 的 kernel graph(见第 08 章)。
3.4 Worker 与分布式 Worker
Worker(vllm/worker/worker.py):每个 GPU 分片上的执行者。它持有 ModelRunner、KV cache 分配器(LocalGPUModelParallelState)、并负责init_worker阶段的模型加载与profile_run(摸底显存、计算可分配的 KV block 数)。WorkerWrapperBase(vllm/worker/worker_base.py):Worker 的通用包装,提供execute_method以便通过 RPC 调用 Worker 上的任意方法(如init_device、load_model、execute_model)。RayWorkerWrapper(vllm/worker/worker_base.py):基于 Ray 的包装,通过@ray.remote(num_gpus=1)把 Worker 放到远端 GPU 节点,是 TP/PP 多卡时的默认选择。- Executor (
vllm/executor/):GPUExecutor(单卡直连)、MultiprocExecutor/MPDistributedExecutor(多进程,无需 Ray)、RayExecutor(多机/多卡)。ExecutorBase.execute_model()会 fan-out 到所有 Worker,汇总各分片的 hidden states / logits。 - CPU offload(显存腾挪) :当"显存墙"压顶但不便加卡时,vLLM 支持两条 CPU 卸载路径------
--cpu-offload-gb(把模型权重 驻留 CPU 显存、按需取回 GPU,代价是 PCIe 往返带宽)与--cpu-offload-kvcache(把 KV cache 换到 CPU,是抢占 SWAP 的延伸)。它们都不是默认行为、只缓解"放不下",详见第 08 章 §2 与第 17 章附录 B。
3.5 OutputProcessor ------ 输出后处理
vllm/engine/output_processor/:
SingleStepOutputProcessor:处理常规采样的输出(更新序列、检查终止)。StreamingOutputProcessor:流式场景下,把 token 增量推给等待的 asyncio 消费者。stop_checker.py:依据stop、stop_token_ids、max_tokens等条件判断是否终止生成。- 统一接口在
interfaces.py,EngineCore每步结束后调用process_outputs()。
3.6 Serving 层 ------ OpenAI 兼容 API
vllm/entrypoints/openai/:
api_server.py:FastAPI 应用,vllm serve命令的入口。serving_engine.py:OpenAIServingChat/OpenAIServingCompletion,把 OpenAI 格式请求转为LLMEngine请求。protocol.py:ChatCompletionRequest/CompletionRequest等 Pydantic 模型。tool_parsers/:支持 function calling / 结构化工具输出。
3.7 核心数据结构:Sequence / SequenceGroup / RequestOutput / SamplingParams
vLLM 在 vllm/sequence.py 里定义了贯穿全系统的四类核心对象:
| 数据结构 | 是什么 | 关键字段 / 职责 |
|---|---|---|
Sequence |
一条最小生成单元 | seq_id、prompt_token_ids、output_token_ids、cumulative_logprob、SequenceStatus(WAITING / RUNNING / FINISHED / PREEMPTED)、自己这张序列的 block table |
SequenceGroup |
一个请求 = 一个 group | request_id、prompt、一份共享的 SamplingParams 、1..N 条 Sequence(承载 n / best_of / beam search 多序列);作为整体入队 / 出队 / 抢占 |
RequestOutput |
引擎产出给前端的输出 | request_id + 若干 CompletionOutput(每条序列的 text、token_ids、cumulative_logprob、finish_reason) |
SamplingParams |
采样配置 | temperature、top_k/top_p、max_tokens、stop、n、best_of、use_beam_search、seed 等 |
它们如何流经整个系统:
python
客户端请求
→ ServingEngine 解析 → 生成 SamplingParams
→ LLMEngine.add_request() → 构造 SequenceGroup(含 1..N 条 Sequence)
→ 加入 scheduler 的 waiting 队列
→ Scheduler.schedule() 准入 → SequenceGroup 进入 RUNNING
→ ModelRunner 按每条 Sequence 的 block table 拼 slot_mapping → forward
→ Sampler 依据每条 Sequence 自己的 SamplingParams 采样
→ OutputProcessor 更新 Sequence 状态、追加 token、判断终止
→ 完成 → 组装成 RequestOutput 返回前端 / 客户端
beam search / n 的载体就是 group :n=3 时一个 group 里同时维护 3 条 Sequence,各自独立采样;beam search 时这 3 条是"束",Scheduler 以 group 为单位调度、ModelRunner 一次性执行 group 内所有序列。
与 mini-vLLM 的对应 :minivllm/sequence.py 实现了 Sequence / CompletionOutput / RequestOutput(字段与 vLLM 对齐),minivllm/config.py 实现 SamplingParams。mini 版每个 request 只有一条序列(n=1),没有独立的 SequenceGroup 类------多序列由 scheduler 近似处理,但数据流形状与真实 vLLM 一致(第 06 章会逐行看)。
4. 请求生命周期:schedule → step → output 循环
一次推理的完整流程(LLMEngine 视角):
python
# vllm/engine/llm_engine.py 的 add_request()
engine.add_request(request_id, prompt, sampling_params)
# → 创建 SequenceGroup、Sequence,加入 scheduler 的 waiting 队列
# vllm/engine/llm_engine.py 的 step()(在线时由 async 后台循环反复调用)
while self.has_unfinished_requests():
# 1. schedule:Scheduler 挑出本步执行的 seq group
scheduler_outputs = self.scheduler.schedule()
# 2. execute:executor → worker(s) → model_runner
# - 拼接张量、padding、CUDA graph 重放、模型 forward
# - attention 通过 block table 读写 KV cache
# - 返回 logits 给 sampler
outputs = self.model_executor.execute_model(scheduler_outputs)
# 3. sample:Sampler 依据 logits + sampling params 采样 token
sampled = sampler(outputs, ...) # SamplerOutput: token_ids, logprobs
# 4. process:OutputProcessor 更新序列状态、判断终止
# - 追加新 token,更新 block table
# - 完成序列返回 outputs,释放其 KV block
self.output_processor.process_outputs(...)
# 5. 回到 1,直到所有请求结束
要点:
- "调度-执行-输出"三步循环 每一步都是完整的前向传播,而不是 token-by-token。
- 每步的 batch 是"动态组装的":正在解码的序列 + 新准入的序列一起跑,这就是 continuous batching 的核心。
- 离线
LLM.generate()内部也是这个循环,只是把结果收集起来一次性返回;在线则把每一步的输出增量推给客户端。
4.1 一次请求的生命周期(带时间戳视角)
把上面的循环换成在线场景下、带时间戳的一条时间线,看清每个时刻是哪个组件在干活:
| 时刻 | 组件 | 动作 |
|---|---|---|
t0 |
客户端 | POST /v1/chat/completions |
t1 |
api_server.py |
FastAPI 路由 → OpenAIServingChat 解析 messages → 构造 SamplingParams |
t2 |
AsyncLLMEngine |
add_request() → 创建 SequenceGroup → 入 scheduler waiting 队列(排队) |
t3 |
EngineCore → Scheduler |
schedule() 准入:KV 块够 → group 进 RUNNING,进入本步批次 |
t4 |
ModelRunner |
prefill :拼 input_ids / positions / slot_mapping → forward,写 KV |
t5 |
Sampler → OutputProcessor |
采样出第 1 个 token,追加进 Sequence,判断终止 |
t6 |
前端 | 把第一个 token 增量经 SSE 推给客户端 → TTFT = t6 − t0 |
t7 ... tN−1 |
全链路 | decode 循环 :每步 1 个 token,schedule → execute → sample → process 反复 |
tN |
OutputProcessor |
命中 EOS / max_tokens / stop → 判 finish_reason,释放该 group 的 KV 块 |
tN+1 |
前端 | 最后一个 SSE chunk(含 finish_reason)+ usage → TPOT = (tN − t6) / (token 数 − 1) |
要点:
t2 → t3之间是排队时间:并发高时 waiting 队列变长,TTFT 随之上升(第 21 章附录 D 里排队是 TTFT 超标的第一嫌疑);t4的 prefill 如果很长,会被 chunked prefill 切成多段,穿插在别的请求的 decode 里(第 02 章 §5);t7...tN−1的每个 token 步,批次都在变(别的请求可能加入/离开)------这就是 continuous batching;- 离线
LLM.generate()没有t0/t6/tN+1这些网络往返,其余完全相同。
5. 执行模式对比
| 维度 | 离线(LLM,vllm/entrypoints/llm.py) |
在线(AsyncLLMEngine + API server) |
|---|---|---|
| 入口 | LLM(model=...);llm.generate(prompts) |
vllm serve <model> / --api-key |
| 引擎 | 同步 LLMEngine |
异步引擎 + EngineCoreProcess |
| 输出 | 一次性返回 RequestOutput 列表 |
SSE 流式,stream=True |
| 典型场景 | 离线批处理、评测、后处理 | 生产 API、对话、agent 应用 |
LLM 类内部本质上是 LLMEngine 的薄封装:llm.generate() → 逐个 add_request → 循环 step() 直到完成。
6. 分布式并行
vLLM 通过 ParallelConfig 描述切分方式,Executor 据此启动对应数量的 Worker。
- 张量并行(TP,
--tensor-parallel-size):把单层权重按维度切分到多卡,前向时跨卡 All-Reduce。单卡放不下模型时用。 - 流水线并行(PP,
--pipeline-parallel-size):按层切分,卡之间流水执行,减少单卡显存但增加气泡和通信延迟。 - 专家并行(EP) :MoE 模型下把 expert 分到不同卡,路由时做 All-to-All 通信。
--expert-parallel-size显式指定(部分 MoE 实现里--tensor-parallel-size会同时切分 expert)。 - 数据并行(DP) :完整模型在多个 Worker 上各放一份,批次按序列切分。vLLM 中通常与 PP/EP 组合(
--data-parallel-size在较新版本可选)。 - EP/DP 自动推导 :启动时
ParallelConfig(vllm/config/parallel_config.py/engine_config.py)会根据 TP、PP、MoE 配置自动计算 EP/DP 布局,无需手动指定;启动日志打印tp_size/pp_size/ep_size/dp_size即可确认(详见第 14 章)。
分布式执行的关键路径:
RayExecutor通过RayWorkerWrapper在各节点@ray.remote拉起 Worker;多进程模式(VLLM_WORKER_MULTIPROC_METHOD=spawn)则不依赖 Ray。- 卡间通信原语在
vllm/distributed/(parallel_state.py的ModelParallelGroup、PPGroup;device_communicators/中封装 NCCL / PyNccl)。 - 多机场景:
vllm serve支持--tensor-parallel-size+ Ray cluster,节点间走 NCCL。
6.1 流水线并行(PP)下的执行与调度
PP 把模型按层切成 PP 段,每段一个 Worker(PPGroup 里 pp_rank 标识段号)。它对"调度与执行"的影响值得单独讲清:
- 调度器在每段独立运行 :每个 PP Worker 各有一个调度器实例,各自维护自己的等待/运行队列。它们处理的批次是本段可见的序列,但序列集合相同------因为 PP 不切序列,只切层。
- step 是一次流水线往返 :引擎触发一次
step(),各段的 ModelRunner 分别对"本段那几层"做 forward,段间用PPGroup传 hidden state(all_reduce只发生在 TP 组内,段间是点对点发送)。整段管线走完一个 micro-batch,才算完成一个 step。 - 微批次(micro-batch)切分 :为压低段间气泡,一个 batch 会切成多个 micro-batch 依次灌入管线(V0 由
--pipeline-parallel-size与--num-micro-batches控制,V1 类似)。气泡比例 ≈(PP − 1) / (PP + num_micro_batches − 1)------micro-batch 越多气泡越小,但每个 micro-batch 的调度/前向都要独立跑。 - 与 chunked prefill 的相互作用:chunked prefill 把一个长 prompt 切成小 chunk,chunk 也是按 micro-batch 在管线里走的;PP 的 bubble 会叠加在 chunk 边界上,所以"PP 大 + chunk 小"会让 prefill 的有效吞吐下降------生产上 PP 与 chunked prefill 要一起调(第 14 章 §6 决策树)。
一句话:PP 改变的是 step 的耗时与微批次结构,不改变调度决策本身 (选哪些序列、分多少块、抢谁)------调度决策在每段是一致的。想动手看代码,从
vllm/v1/worker/的PPGroup和execute_model里的pp_rank分支读起。
7. 配置对象
vllm/config/ 下的配置类在 EngineArgs(vllm/engine/arg_utils.py)解析 CLI 参数后构建:
| 配置类 | 文件 | 控制内容 |
|---|---|---|
ModelConfig |
vllm/config/model_config.py |
模型路径、--dtype、--revision、--trust-remote-code、max model len |
CacheConfig |
vllm/config/cache_config.py |
--gpu-memory-utilization、--block-size、--kv-cache-dtype、--swap-space |
SchedulerConfig |
vllm/config/scheduler_config.py |
--max-num-seqs、--max-num-batched-tokens、--preemption-mode |
ParallelConfig |
vllm/config/parallel_config.py(新版本并入 engine_config.py) |
--tensor-parallel-size、--pipeline-parallel-size、--worker-cls、--distributed-executor-backend |
CompilationConfig |
vllm/config/compilation_config.py |
--enforce-eager、--torch.compile 相关、--cuda-graph-max-batch-size |
启动命令示例:
bash
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--gpu-memory-utilization 0.85 \
--max-num-seqs 128 \
--tensor-parallel-size 2 \
--max-model-len 8192
8. 数据流总图:HTTP → Token
css
┌─────────────────────────────── 客户端 ───────────────────────────────┐
│ POST /v1/chat/completions {"messages":[...], "max_tokens": 512} │
└──────────────────────────────────┬──────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────────────┐
│ vllm/entrypoints/openai/api_server.py (FastAPI) │
│ └─ OpenAIServingChat → 构造 LLMEngine 格式的 SamplingParams │
└──────────────────────────────────┬──────────────────────────────────┘
▼ add_request()
┌──────────────────────────────────────────────────────────────────────┐
│ AsyncLLMEngine ──(EngineCoreClient, zmq/shared-mem)──► EngineCore │
│ │ │
│ 循环 per step: ▼ │
│ Scheduler.schedule() ──> SchedulerOutputs │
│ ModelExecutor.execute_model() ──> Worker ──> ModelRunner │
│ input_ids / positions / slot_mapping ──> forward() │
│ Sampler ──> token_ids + logprobs │
│ OutputProcessor ──> 更新序列、判断 stop、释放 KV block │
└──────────────────────────────────┬──────────────────────────────────┘
▼ SSE chunk: data: {"delta":{"content":"..."}}
客户端收到增量 token
9. 小结
- vLLM 的三层分工:Serving 管协议、引擎管调度循环、核心层管 GPU 执行与 KV 缓存。
- 一切围绕 "schedule → execute(model) → sample → process" 每步一循环的连续批处理展开。
- KV 缓存用 block + block table + ref count + CoW 管理,是 PagedAttention 的基础(见第 02 章)。
- V1 引入
EngineCore前后端分离;在线服务默认异步引擎,离线LLM是同步封装。 - 分布式通过 Executor 决定 Worker 布局,TP/PP/EP/DP 由
--tensor-parallel-size等参数驱动,Ray 或多进程承载。
下一步:深入 PagedAttention 内核与性能调优,见《08 · 性能优化》。