vLLM基础:架构、推理流程与吞吐量瓶颈分析

2.1 vLLM核心架构:从PagedAttention到分布式引擎

vLLM之所以能在LLM推理服务中脱颖而出,核心在于其PagedAttention机制与连续批处理(Continuous Batching)引擎的协同设计。要理解vLLM的缓存优化,必须先厘清其整体架构脉络。

vLLM的架构可划分为三大层次:

  • 模型执行层:基于PyTorch/XFormers的算子层,负责Transformer的forward计算,但将KV Cache的存储方式从“连续内存”改为“分页内存块”。
  • 调度与缓存层:这是vLLM的核心,包含BlockManager(块管理器)、Scheduler(调度器)和CacheEngine(缓存引擎)。BlockManager以固定大小的block(默认16个token)为单位分配KV Cache;Scheduler决定每轮迭代哪些序列参与计算;CacheEngine负责GPU/CPU缓存块的换入换出。
  • 服务与API层:提供OpenAI兼容的HTTP接口,支持流式输出、多轮对话等,并通过AsyncLLMEngine处理并发请求。
关键认知:vLLM与传统推理框架的本质区别在于——它把KV Cache视为“虚拟内存”,通过块表(Block Table)映射到物理块,从而实现了接近零碎片的内存利用。
vLLM 核心架构与推理数据流 ① API / 服务层 HTTP / gRPC 接口 AsyncLLMEngine Token流式输出管理 多轮对话上下文 ② 调度与缓存层(核心) Scheduler 调度器 • 连续批处理决策 • 序列优先级排序 • 抢占/恢复管理 • 迭代级调度 每token步决策一次 BlockManager 块管理器 • 物理块分配/释放 • Block Table 维护 • 块引用计数 • Copy-on-Write 块大小=16 tokens CacheEngine 缓存引擎 • GPU KV Cache • CPU 交换空间 • 换入/换出策略 • 缓存复用 显存不足时换出 ③ 模型执行层(GPU Kernel) Transformer Decoder Self-Attention Feed-Forward Network LayerNorm / RMSNorm KV Cache 物理块池 B0 B1 B2 B3 B4 B5 块表映射:逻辑块 → 物理块 读写 连续批处理(迭代级) 请求A: [t0] [t1] [t2] ✓完成 请求B: [t0] [t1] [t2] [t3] ... 请求C: [t0] [t1] ... 新请求D随时插入 PagedAttention:将KV Cache分页管理,消除内存碎片,支持高效共享

2.2 推理流程:从请求到生成的完整链路

vLLM的推理流程与传统的“静态批处理”有显著差异。其核心是迭代级调度(Iteration-level Scheduling),即每个forward step都重新决定批次的组成。一个典型请求的完整流程如下:

  1. 请求接入:客户端发送prompt到HTTP服务,AsyncLLMEngine将其封装为序列(Sequence)对象,加入等待队列。
  2. 预填充阶段(Prefill):调度器从等待队列中选取请求,为其分配初始物理块,执行一次完整的forward计算,生成第一个token,并将所有输入的KV Cache写入分配的物理块。
  3. 解码阶段(Decode):进入生成循环。每步只输入上一步生成的token,从KV Cache中读取历史信息,计算当前token的logits并采样。
  4. 块分配与释放:随着生成token数增加,BlockManager动态分配新物理块;当序列结束(遇到EOS或达到max_tokens)时,释放其占用的所有物理块。
  5. 流式返回:每个token生成后立即通过HTTP流式接口返回给客户端,无需等待整个序列完成。
效率关键:Prefill阶段是计算密集型(矩阵乘法),Decode阶段是访存密集型(读取KV Cache)。vLLM通过连续批处理,让多个处于Decode阶段的序列共享一次forward,大幅提升GPU利用率。

2.3 吞吐量瓶颈:为什么朴素实现会“卡住”

在未使用vLLM的朴素推理系统中,吞吐量主要受以下瓶颈制约:

瓶颈类型 根因分析 对吞吐量的影响
内存碎片化 KV Cache按最大序列长度预分配连续显存,不同序列长度差异导致内部碎片和外部碎片 显存利用率仅40%~60%,能并发处理的序列数大幅减少
批处理粒度粗 静态批处理必须等批次内最慢的序列全部生成完才能释放资源 GPU在批处理尾部出现大量空闲,有效算力利用率低
KV Cache冗余存储 多轮对话中,历史对话的KV Cache重复计算;并行采样时相同前缀重复存储 浪费大量显存和计算资源,增加延迟
CPU-GPU同步开销 每个step都需要CPU调度GPU kernel,启动开销大 小batch时kernel启动时间占比超过50%

用一个具体场景来说明:假设GPU显存为40GB,模型为7B参数(权重约14GB),剩余约26GB用于KV Cache。若每个序列最大长度2048,每token的KV Cache占用约0.5MB(以7B模型为例),则每个序列最多需要1GB。朴素实现中,由于预分配连续内存,实际可能只能容纳8~10个并发序列。而vLLM通过分页管理,碎片率降低到接近0,可容纳20~25个序列,吞吐量直接提升2~3倍。

2.4 案例复盘:一个真实的生产环境瓶颈诊断

某团队部署Llama-2-13B模型(单卡A100 80GB),使用朴素FastAPI+Transformers实现推理服务。压测发现:吞吐量仅约8 requests/s,GPU利用率波动剧烈(30%~95%),且延迟P99高达4.2秒。

诊断过程:

  • 通过nvidia-smi观察显存分配,发现显存占用率仅55%,但新请求报CUDA out of memory——典型的碎片化问题。
  • 使用PyTorch Profiler分析,发现GPU kernel启动间隙(gap)占总时间的47%,说明batch内序列完成时间差异大,GPU频繁空闲。
  • 检查多轮对话场景,发现每轮对话都重新计算历史KV Cache,导致有效计算量浪费约60%。

优化方案与效果:

# 迁移到vLLM后的关键配置
from vllm import LLM, SamplingParams

llm = LLM(
    model="meta-llama/Llama-2-13b-chat-hf",
    gpu_memory_utilization=0.90,      # 允许vLLM使用90%显存
    max_num_seqs=64,                  # 最大并发序列数
    max_num_batched_tokens=8192,      # 每批次最大token数
    enable_prefix_caching=True,       # 启用前缀缓存(多轮对话优化)
)

sampling_params = SamplingParams(
    temperature=0.7,
    max_tokens=1024,
)

优化后,吞吐量从8 req/s提升至31 req/s(提升约2.9倍),GPU利用率稳定在85%~95%,P99延迟降至1.8秒。显存碎片率从约45%降至接近0%。

常见误区:不要盲目调大gpu_memory_utilization到0.95以上。需要预留足够的KV Cache交换空间和CUDA context内存,否则会导致GPU显存溢出或频繁换入换出,反而降低吞吐量。建议从0.85开始,逐步调优。

2.5 吞吐量优化核心公式与关键参数

理解吞吐量的决定因素,有助于后续章节深入缓存优化。系统吞吐量(tokens/s)可近似表示为:

吞吐量 ≈ (GPU算力利用率 × 峰值算力) / (每token平均计算量)

其中:
- 每token平均计算量 ≈ 2 × 模型参数量(FLOPs)
- GPU算力利用率 = f(批大小, KV Cache命中率, 内存带宽, kernel效率)

vLLM通过以下手段提升公式中的“GPU算力利用率”:

  • 增大有效批大小:通过分页KV Cache容纳更多并发序列,让每次forward处理更多token。
  • 减少内存访问开销:PagedAttention减少显存碎片,提高Cache命中率。
  • 前缀缓存复用:对相同system prompt或多轮对话前缀,直接复用KV Cache,避免重复计算。
  • 动态batch组合:每步将处于Prefill和Decode阶段的序列混合调度,填满GPU计算间隙。