4. vLLM整体架构:请求处理流程、核心组件

在了解了vLLM的定位与核心优势之后,我们需要深入其内部,理解一个推理请求从进入系统到最终返回文本,究竟经历了怎样的旅程。vLLM并非一个简单的“模型加载+前向计算”的黑盒,而是一个由多个职责明确的组件构成的分布式系统。本章将拆解其整体架构,重点剖析LLMEngine、Scheduler与Worker这三个核心角色,并梳理它们之间的协作关系。

4.1 核心组件与职责划分

vLLM的架构设计遵循了典型的“中心化调度、分布式执行”模式。我们可以将整个系统想象成一家高效运转的餐厅:LLMEngine是餐厅经理,负责接收订单、协调全局;Scheduler是后厨调度员,决定哪一桌的菜先做、如何拼桌;而Worker则是灶台上的厨师,真正动手烹饪(执行张量计算)。

下表清晰地列出了各核心组件的职责与关键接口:

组件 核心职责 关键对象/接口
LLMEngine 顶层抽象,接收请求、维护KV Cache管理器、驱动执行循环、管理输入输出 add_request(), step(), abort_request()
Scheduler 决定序列的调度顺序,管理序列状态(等待、运行、交换),执行抢占与交换策略 schedule(), SequenceGroup, SchedulerOutput
Worker 实际执行模型推理的进程,管理GPU显存、加载模型权重、执行注意力计算 execute_model(), ModelRunner, CacheEngine
BlockManager 管理KV Cache的物理块分配与释放,实现PagedAttention的显存管理 allocate(), free(), PhysicalTokenBlock
ModelRunner 将调度器输出的批次转换为张量输入,调用模型前向计算 prepare_input_tensors(), execute_model()

在单机场景下,LLMEngine直接持有Scheduler和Worker的引用;在多机分布式场景下,LLMEngine通过Ray或自定义通信层与远程Worker交互。但无论何种部署形态,请求处理的主循环逻辑始终由LLMEngine驱动。

理解要点:LLMEngine是“大脑”,Scheduler是“决策者”,Worker是“执行者”。不要将三者混为一谈——Scheduler不执行任何张量计算,Worker也不决定先处理哪个请求。

4.2 请求处理主循环:从文本到Token再到文本

一个典型的请求处理流程可以拆解为以下五个阶段。我们以一次用户输入“请介绍一下Transformer”为例:

  1. 请求注册:客户端调用engine.add_request(request_id, prompt, sampling_params)。LLMEngine将文本提示转换为Token ID序列,并创建Sequence对象,放入等待队列。
  2. 调度决策:LLMEngine调用scheduler.schedule()。Scheduler根据当前KV Cache剩余空间、序列优先级、抢占策略,决定本次迭代执行哪些SequenceGroup,并为新序列分配逻辑块。
  3. 数据准备:Scheduler输出SchedulerOutput,包含需要执行的序列列表及其块映射。ModelRunner将这些信息转换为GPU张量(如input_tokens, slot_mapping)。
  4. 模型执行:Worker调用execute_model(),执行一次前向传播,得到每个序列的logits。随后通过采样(如top-p, temperature)生成新的Token。
  5. 状态更新:LLMEngine将新Token追加到对应Sequence中,更新KV Cache的块引用计数。若序列未结束(未生成EOS),则继续放入运行队列等待下一轮;若已结束,则释放其占用的物理块,并将完整输出返回给客户端。

上述步骤在engine.step()中被循环调用,直到所有请求处理完毕。值得注意的是,vLLM采用“迭代级调度”(iteration-level scheduling),即每次step()只生成一个Token,而非等待整个序列生成完毕。这使得不同长度的序列可以在同一批次中动态加入或退出,极大提升了GPU利用率。

# 伪代码:LLMEngine主循环
while self.has_unfinished_requests():
    scheduler_output = self.scheduler.schedule()  # 决策
    output = self.worker.execute_model(scheduler_output)  # 执行
    self._update_sequences(output)  # 更新状态
关键设计:vLLM的“连续批处理”(continuous batching)正是依赖上述迭代级调度实现。传统批处理需等待最慢的序列完成才返回,而vLLM允许序列级抢占与插入,显著降低首Token延迟。

4.3 调度器核心逻辑:抢占与交换

Scheduler是vLLM性能优化的关键。它需要回答两个问题:“下一步执行谁?”和“显存不够时怎么办?”

对于第一个问题,Scheduler维护了三个队列:等待队列(waiting)、运行队列(running)和交换队列(swapped)。默认采用FCFS(先来先服务)策略,但支持优先级调整。每次调度时,Scheduler会尝试将等待队列中的序列加入运行队列,直到KV Cache无法容纳更多序列。

对于第二个问题,vLLM引入了抢占(preemption)机制。当新请求到达且显存不足时,Scheduler会选择运行队列中优先级最低的序列进行抢占。抢占分为两种:

  • 交换(swap):将该序列的KV Cache从GPU显存复制到CPU内存(或NVMe),释放GPU空间给新请求。当后续显存充足时再交换回来。
  • 重计算(recompute):直接丢弃该序列的KV Cache,后续重新从初始Token开始计算。这节省了显存带宽,但增加了计算开销。

默认策略是先交换,后重计算。交换适合KV Cache较大且CPU内存充足的场景;重计算适合计算资源充裕但显存极小的场景。Scheduler通过SequenceGroup的state字段跟踪每个序列的当前状态,确保状态转换的原子性。

常见误区:初学者常误以为“抢占”等同于“终止请求”。实际上,被抢占的序列并未丢失,其状态被完整保存(交换)或可重建(重计算)。vLLM保证最终仍会完成所有已接受的请求,只是执行顺序可能被打乱。

4.4 Worker与模型执行细节

Worker是真正与GPU交互的组件。在单卡场景下,Worker内部包含一个ModelRunner和一个CacheEngine。ModelRunner负责将Scheduler输出的逻辑批次转换为物理张量,而CacheEngine管理GPU上的KV Cache张量。

以PagedAttention为例,ModelRunner需要为每个Token生成slot_mapping张量,该张量指示每个Token的KV Cache在物理块中的具体位置。这一映射关系由BlockManager维护。执行前向计算时,注意力机制通过gather操作从分散的物理块中收集KV对,计算完成后无需移动数据,仅更新块引用计数。

# Worker执行模型的核心调用链
class Worker:
    def execute_model(self, scheduler_output):
        input_tensors = self.model_runner.prepare_input_tensors(scheduler_output)
        hidden_states = self.model_runner.execute_model(input_tensors)
        return self.model_runner.sample(hidden_states)

在多GPU场景下,vLLM支持张量并行(tensor parallel)和流水线并行(pipeline parallel)。每个Worker负责模型的一部分(如部分注意力头或部分层),通过AllReduce等集合通信同步梯度或中间结果。此时,Scheduler的调度决策需要广播到所有Worker,确保它们执行相同的批次。

4.5 案例复盘:一次显存不足引发的调度决策

假设GPU总显存为16GB,KV Cache可用空间为8GB。当前运行队列中有3个序列(A、B、C),分别占用2GB、3GB、2GB Cache。此时新请求D到达,预估需要3GB Cache。Scheduler的决策过程如下:

  1. 检查剩余空间:8 - (2+3+2) = 1GB,不足以容纳D。
  2. 选择抢占目标:按优先级排序,假设C优先级最低。
  3. 执行交换:将C的2GB Cache写入CPU内存,释放GPU空间。此时可用空间变为3GB,恰好满足D的需求。
  4. 将D加入运行队列,C移入交换队列。
  5. 下一轮调度时,若A或B完成释放空间,则优先将C交换回GPU继续执行。

这一过程体现了vLLM“以空间换时间”的核心思想:通过精细的显存管理,最大化GPU利用率,同时保证请求的最终完成。

复盘要点:调度决策的优劣直接影响系统吞吐量。若交换过于频繁,CPU-GPU传输将成为瓶颈;若重计算过多,则浪费计算资源。实际生产中,建议通过--swap-space参数控制CPU交换空间大小,并监控cpu_cache_usage和gpu_cache_usage指标来调优。

通过上述分析可以看出,vLLM的架构设计将复杂的显存管理与调度策略从模型计算中解耦,使得开发者可以专注于模型本身,而系统自动优化资源利用。理解LLMEngine、Scheduler与Worker的协作关系,是深入使用vLLM高级特性(如分布式部署、自定义调度策略)的基础。