2、vLLM基础架构:核心组件、PagedAttention原理、KV Cache管理与连续批处理机制

vLLM 之所以能在众多 LLM 推理框架中脱颖而出,核心在于其针对大模型推理的显存瓶颈与调度效率进行了系统性的架构重构。本章将深入拆解 vLLM 的四大支柱:核心组件、PagedAttention 原理、KV Cache 管理以及连续批处理机制。理解这些底层设计,是后续进行性能基准测试与调优的前提——只有知道系统如何分配资源、如何调度请求,才能准确解读吞吐量、TTFT(首 Token 延迟)等指标背后的真实含义。

vLLM 推理引擎核心架构总览 并发请求 (多用户/多轮) 调度器 (Scheduler) 连续批处理决策 KV Cache 管理器 物理块分配/释放 Copy-on-Write GPU 显存块池 Block 0,1,2,...,N 块大小: 16 tokens 块表 (Block Table) 逻辑块 → 物理块映射 PagedAttention 分块计算注意力 连续批处理 (Continuous Batching) 时间线 迭代1 Req A,B,C 迭代2 Req B,C,D 迭代3 Req C,E,F 迭代4 Req E,G A完成 D加入 B完成 F加入 传统静态批处理 等待最慢请求完成 GPU 利用率低 核心设计目标:通过分块显存管理消除碎片化,通过细粒度调度最大化 GPU 利用率 PagedAttention + KV Cache 管理器 + 连续批处理 = 高吞吐、低延迟

2.1 核心组件:vLLM 的模块化架构

vLLM 的架构设计遵循了经典的分层解耦原则,主要包含四个核心组件:请求调度器(Scheduler)、KV Cache 管理器(BlockManager)、模型执行器(Model Executor)以及推理引擎(LLM Engine)。这些组件各司其职,又通过高效的内部协议紧密协作。

推理引擎是面向用户和上层应用的门面,负责接收请求、管理并发、组织推理流程。调度器则运行在引擎内部,负责决定每个时间片内哪些请求应该被送入 GPU 计算。KV Cache 管理器是 vLLM 最具创新性的部分,它负责将连续的 KV Cache 逻辑空间映射到非连续的物理显存块上,并跟踪每个块的引用计数。模型执行器则封装了具体的模型前向计算逻辑,包括注意力机制的调用、激活函数的计算等。

组件名称核心职责关键数据结构性能影响
LLM Engine请求生命周期管理、流式输出请求队列、事件循环影响端到端延迟
Scheduler决定每轮迭代的请求集合等待队列、运行队列决定吞吐量上限
BlockManager物理块分配、释放、Copy-on-Write块表、空闲块列表、引用计数决定显存利用率
Model Executor执行 Transformer 层前向计算权重张量、KV 缓存张量决定计算效率

理解这些组件的边界与交互,是后续调优的基础。例如,当遇到显存不足(OOM)时,问题可能出在 BlockManager 的块分配策略上,也可能出在调度器允许的并发请求数过高上,需要分组件排查。

2.2 PagedAttention 原理:从操作系统到显存管理

PagedAttention 是 vLLM 的核心创新,其灵感直接来源于操作系统中的虚拟内存分页机制。在传统的 LLM 推理中,KV Cache 需要预先分配一块连续的显存空间,且大小需按最大序列长度预留。这导致两个严重问题:内部碎片(预留空间大于实际使用)和外部碎片(不同请求的缓存空间无法复用)。

PagedAttention 将 KV Cache 划分为固定大小的物理块(Block),每个块默认存储 16 个 token 的 KV 向量。每个请求维护一个逻辑块表,记录其 KV Cache 在物理显存中的实际位置。这样,即使请求的序列长度不断变化,也只需按需分配新的物理块,并通过块表进行间接寻址。

关键机制:注意力计算时,PagedAttention 不是一次性计算整个序列的注意力分数,而是分块读取物理块中的 K/V 向量,与当前的 Query 向量进行局部注意力计算,然后累加结果。这种分块计算方式虽然增加了少量指针跳转开销,但换来了接近零的显存浪费。

从性能角度看,PagedAttention 带来的收益是巨大的。以 13B 参数模型为例,在传统框架中,处理 2048 个 token 的序列可能需要预留约 2GB 的连续显存;而使用 PagedAttention 后,实际占用可能只有 1.2GB,且这些显存可以分散在显存的任意位置。这意味着在相同的 GPU 上,vLLM 可以并发处理更多的请求,或者支持更长的上下文长度。

2.3 KV Cache 管理:块分配、引用计数与 Copy-on-Write

KV Cache 管理器的核心任务是将逻辑上的 KV 缓存高效地映射到物理显存。其工作流程包含三个关键环节:分配(Allocation)、去重(Deduplication)和释放(Deallocation)。

在分配阶段,当调度器决定将一个请求加入运行队列时,管理器会根据该请求的当前序列长度,计算需要多少个物理块,并从空闲块列表中分配。这里有一个重要的优化:按需分配。vLLM 不会为请求预分配最大可能长度的缓存,而是随着生成过程的推进,每新增 16 个 token 才分配一个新块。

去重机制主要针对共享前缀场景。例如,在多数对话系统中,系统提示词(System Prompt)是固定的。多个并发请求可能共享相同的前缀。vLLM 通过引用计数机制,让多个请求的块表指向同一组物理块,直到某个请求需要写入不同的内容时,才触发Copy-on-Write 操作,复制该块并修改指针。

调优观察点:在监控指标中,关注 num_blocks_shared(共享块数)和 num_blocks_copied(复制块数)。如果共享块占比高,说明系统提示词复用效果好;如果复制操作频繁,则可能意味着前缀差异过大,需要检查提示词设计。

释放阶段相对简单:当请求完成生成,其所有物理块的引用计数减一,当计数归零时,块被归还到空闲列表。这种精细化的生命周期管理,使得显存利用率可以达到 90% 以上,而传统框架通常只有 60%-70%。

2.4 连续批处理机制:动态调度与 GPU 利用率提升

传统的静态批处理(Static Batching)要求一个批次内的所有请求必须全部完成生成后,才能释放 GPU 资源并开始下一批次。这导致 GPU 在批次尾部出现严重的计算空闲——因为大部分请求已经完成,只有少数长序列请求仍在运行。

vLLM 实现的连续批处理(Continuous Batching),也称为迭代级调度(Iteration-level Scheduling),将调度粒度从“请求级”细化到“迭代级”。在每一轮模型前向传播(即生成一个 token)之后,调度器都会重新评估运行队列:已经完成的请求立即退出,新的请求可以随时加入,无需等待当前批次整体结束。

  1. 请求到达:新请求进入等待队列,调度器检查是否有足够的物理块。
  2. 迭代调度:每轮迭代开始前,调度器从等待队列中选取请求加入当前批次,直到 GPU 显存或计算资源达到饱和。
  3. 动态退出:当某个请求生成结束(遇到 EOS 或达到 max_tokens),其占用的物理块被释放,调度器立即从等待队列中拉入新请求。
  4. 优先级策略:默认采用 FCFS(先来先服务),但可以配置优先级队列,保证延迟敏感型请求优先。

这种机制带来的直接收益是 GPU 利用率的大幅提升。在真实负载下,连续批处理可以将 GPU 利用率从静态批处理的 50%-70% 提升到 85%-95%。同时,由于请求可以更早地开始处理,TTFT(首 Token 延迟)也得到显著改善。

风险提示:连续批处理并非没有代价。频繁的请求加入和退出会导致 GPU 内核启动开销增加,且不同请求的序列长度差异过大时,可能造成计算流水线气泡。在实际调优中,需要平衡 max_num_seqs(最大并发序列数)和 max_num_batched_tokens(单批次最大 token 数)这两个参数。

2.5 实战案例:从显存碎片化到吞吐量翻倍

以下是一个典型的调优案例,展示了理解 vLLM 基础架构如何直接转化为性能收益。

背景与约束:某企业部署了一个 7B 参数的 Chat 模型,使用单张 A10G(24GB 显存)。业务场景为内部知识库问答,平均请求长度为 800 tokens,最大长度 2048 tokens。初始部署使用传统静态批处理框架,并发请求数为 8。

初始现象:GPU 利用率平均仅为 45%,且频繁出现显存溢出(OOM)错误。通过 nvidia-smi 观察,显存占用达到 23.5GB,但实际有效计算负载很低。

分析过程:使用 vLLM 的 --verbose 日志模式启动,观察到以下关键指标:

GPU KV cache size: 12.8 GB
Number of blocks: 2048
Block size: 16 tokens
Max concurrency: 8 requests
Observed: 40% of blocks allocated, but 60% of requests waiting for memory

分析发现,传统框架为每个请求预分配了 2048 tokens 的连续缓存空间,导致 60% 的显存被浪费在未使用的预留空间上。同时,由于显存碎片化,即使总空闲显存足够,也无法为新的请求分配连续的缓存块。

操作步骤:

  1. 将推理框架切换为 vLLM,启用 PagedAttention。
  2. 设置 --max-model-len 为 2048,--gpu-memory-utilization 为 0.90。
  3. 调整 --max-num-seqs 从默认值提升至 32,以充分利用释放出的显存空间。
  4. 启用 --enable-prefix-caching,利用系统提示词共享前缀。

结果对比:

指标传统静态批处理vLLM 连续批处理提升幅度
GPU 利用率45%92%+104%
吞吐量 (tokens/s)1,2003,850+220%
TTFT 平均延迟850 ms320 ms-62%
OOM 次数/小时3-5 次0 次消除

复盘经验:本次调优的核心收益并非来自计算优化,而是来自显存管理策略的革新。PagedAttention 消除了内部碎片,连续批处理消除了调度等待。这提醒我们,在 GPU 推理场景中,显存效率往往比计算效率更先成为瓶颈。后续在更大规模模型(如 70B)上,这一优势会更加明显,因为 KV Cache 的绝对大小会随模型参数量线性增长。

同时,也要注意连续批处理带来的新问题:当并发请求数过高时,单个请求的生成速度会变慢(因为 GPU 算力被更多请求分摊)。因此,在生产环境中,需要根据业务对延迟的敏感度,合理设置 max_num_seqs 的上限,必要时结合 --max-paddings 参数进行微调。