2026年4月19日

vLLM 核心:PagedAttention 与 Continuous Batching

vLLM 是一个高性能 LLM 推理与服务引擎,由 UC Berkeley 团队开发。其核心创新是 **PagedAttention**,借鉴操作系统虚拟内存分页机制管理 KV Cache。

知识库大模型推理与系统

vLLM 核心:PagedAttention 与 Continuous Batching

1. vLLM 概述

vLLM 是一个高性能 LLM 推理与服务引擎,由 UC Berkeley 团队开发。其核心创新是 PagedAttention,借鉴操作系统虚拟内存分页机制管理 KV Cache。

核心特性

  • PagedAttention:高效 KV Cache 内存管理
  • Continuous Batching:迭代级调度,最大化 GPU 利用率
  • 高效吞吐:相比 naive 实现可达 24× 吞吐提升
  • 兼容性:支持 HuggingFace 模型,OpenAI 兼容 API

QUESTION 面试题:vLLM 的核心创新是什么?解决了什么问题? vLLM 的核心创新是 PagedAttention,它解决了传统推理服务中 KV Cache 显存利用率低的问题(传统方案仅 20-40%)。PagedAttention 借鉴操作系统虚拟内存分页机制,将 KV Cache 划分为固定大小的 Block,按需分配,消除内部碎片和外部碎片,使 KV Cache 利用率提升到 >95%。配合 Continuous Batching 迭代级调度,vLLM 相比传统方案可实现 2-4× 甚至更高的吞吐提升。

2. 传统推理服务的内存问题

静态内存分配

传统方案为每个请求预分配最大长度的连续 KV Cache 内存:

请求 A (实际生成 100 Token,预分配 2048): [■■■■■□□□□□□□□□□□□□□□] → 浪费 95%
请求 B (实际生成 800 Token,预分配 2048): [■■■■■■■■□□□□□□□□□□□□] → 浪费 61%
请求 C (实际生成 2000 Token,预分配 2048): [■■■■■■■■■■■■■■■■■■■■] → 浪费 2%

三大问题

  1. 内部碎片:预分配空间大于实际使用
  2. 外部碎片:请求结束后释放的内存块无法被新请求使用(不连续)
  3. 预留浪费:为最大长度预留大量内存,限制了并发数

结果:传统方案中 KV Cache 显存利用率仅 20-40%

3. PagedAttention 详解

核心思想

借鉴操作系统虚拟内存分页机制:

  • 将 KV Cache 划分为固定大小的 Block(页)
  • 每个 Block 存储固定数量 Token 的 KV 数据
  • 使用 Block Table(页表)映射逻辑 Block 到物理 Block
  • 按需分配 Block,不需要预分配最大长度

架构图

逻辑视图(序列看到的):
Block 0: [K0,V0] [K1,V1] [K2,V2] [K3,V3]
Block 1: [K4,V4] [K5,V5] [K6,V6] [K7,V7]
Block 2: [K8,V8] [K9,V9] [empty] [empty]

物理内存(实际存储):
物理 Block 5:  [K0,V0] [K1,V1] [K2,V2] [K3,V3]    ← 序列 A
物理 Block 12: [K4,V4] [K5,V5] [K6,V6] [K7,V7]    ← 序列 A
物理 Block 3:  [K8,V8] [K9,V9] [empty] [empty]     ← 序列 A
物理 Block 7:  [K0',V0'] [K1',V1'] [K2',V2'] [K3',V3'] ← 序列 B

Block Table(序列 A):
逻辑 Block 0 → 物理 Block 5
逻辑 Block 1 → 物理 Block 12
逻辑 Block 2 → 物理 Block 3

Block 大小选择

  • 通常为 16 或 256 个 Token
  • 太小:Block Table 管理开销大
  • 太大:内部碎片增加
  • vLLM 默认:block_size=16

内存管理优势

  1. 消除外部碎片:Block 可以分散在物理内存任意位置
  2. 减少内部碎片:仅最后一个 Block 可能有浪费(最多 block_size-1 个 Token)
  3. 按需分配:新 Token 生成时才分配新 Block
  4. 即时释放:序列结束后立即释放所有 Block
  5. KV Cache 利用率接近 100%(仅最后一个 Block 有少量浪费)

QUESTION 面试题:PagedAttention 与操作系统分页机制有什么对应关系?

操作系统分页 PagedAttention
虚拟地址空间 逻辑 KV 序列(Token 0, 1, 2, ...)
物理内存页 物理 KV Block(GPU 显存中的实际存储)
页表 (Page Table) Block Table(逻辑→物理映射)
按需分配页面 按 Token 生成进度分配 Block
页面换出 (Swap) KV Block 换出到 CPU 内存
共享内存 (Shared Memory) 前缀共享 (Prefix Caching)

PagedAttention 借鉴了虚拟内存的核心思想:逻辑连续但物理不连续,按需分配,支持共享和换出。

Prefix Caching(前缀共享)

多个请求共享相同前缀时(如相同 System Prompt),可共享 KV Cache Block:

请求 A: [System Prompt] + [User Query A]
请求 B: [System Prompt] + [User Query B]
              ↑ 共享 Block(Copy-on-Write)

这意味着:

  • System Prompt 只需 Prefill 一次,后续请求直接复用
  • 共享的 Block 是只读的,新请求追加新 Block 时不影响共享部分
  • 在 RAG 场景中,相同检索文档的 KV 也可共享

4. Continuous Batching(连续批处理)

传统 Static Batching

时间 →
请求 A: [████████████████████] 完成
请求 B: [████████] 完成 ... 空闲等待 ...
请求 C: [████████████████] 完成 .. 空闲

整个批次必须等最慢的请求完成后才能开始新批次。

Static Batching 的问题:Batch 内所有请求必须等到最长的那个完成后才能开始新 Batch,造成大量 GPU 空闲时间。

Continuous Batching(迭代级调度)

时间 →
Slot 1: [请求 A...A...A...A...A完成][请求 D...D...D完成][请求 F...]
Slot 2: [请求 B...B...B完成][请求 E...E...E...E完成][请求 G...]
Slot 3: [请求 C...C...C...C...C...C...C完成][请求 H...]

每个 Decode Step(迭代)

  1. 检查哪些请求已完成(输出 EOS 或达到最大长度)
  2. 从运行队列中移除已完成的请求,释放其 KV Cache Block
  3. 从等待队列中取出新请求,分配 KV Cache Block
  4. 如果显存不足,可抢占(preempt)低优先级请求(换出到 CPU 或重新计算)

调度策略

# 伪代码:迭代级调度
def schedule_step(running, waiting, block_manager):
    # 1. 移除已完成的请求
    completed = [r for r in running if r.is_finished()]
    for r in completed:
        block_manager.free(r)
        running.remove(r)

    # 2. 尝试从等待队列中接纳新请求
    while waiting:
        r = waiting[0]
        if block_manager.can_allocate(r):
            block_manager.allocate(r)
            running.append(r)
            waiting.pop(0)
        else:
            break  # 显存不足

    # 3. 如果没有空间且没有运行的请求,抢占
    if not running and waiting:
        preempt(running, block_manager)

QUESTION 面试题:Continuous Batching 相比 Static Batching 有什么优势? Static Batching:整批请求一起开始,等最慢的完成后才接受新请求。GPU 空闲时间多,吞吐低。 Continuous Batching:每个 Decode Step 检查完成情况,完成的请求立即释放资源,新请求立即填充进来。

  • 消除了等待最慢请求的空闲时间
  • 每个 GPU Slot 始终保持忙碌
  • 延迟更稳定(新请求无需等整批完成)
  • 吞吐量可提升 2-4×

5. 抢占与换出机制

当 GPU 显存不足以接纳新请求时,vLLM 使用两种策略:

策略 1:Swapping(换出到 CPU)

  • 将某些请求的 KV Cache Block 从 GPU 复制到 CPU 内存
  • 需要时再换回 GPU
  • 适合 CPU 内存充裕的场景

策略 2:Recomputation(重新计算)

  • 直接丢弃被抢占请求的 KV Cache
  • 需要恢复时,使用原始 Prompt 重新计算 Prefill
  • 适合 Prefill 较快的场景
策略 换出成本 恢复成本 适用场景
Swapping GPU→CPU 内存拷贝 CPU→GPU 内存拷贝 CPU 内存充足
Recomputation 无(直接丢弃) 重新 Prefill Prefill 短、Prompt 不长

QUESTION 面试题:vLLM 如何处理显存不足的情况? 当 GPU 显存不足以接纳新请求时,vLLM 采用**抢占(Preemption)**策略:

  1. Swapping:将某些请求的 KV Cache 换出到 CPU 内存,需要时换回。适合 CPU 内存充裕的场景。
  2. Recomputation:直接丢弃被抢占请求的 KV Cache,恢复时用原始 Prompt 重新 Prefill。适合 Prompt 较短的场景。

默认使用 Recomputation 策略(--swap-space 0),因为 Prefill 通常比 PCIe 传输更快。

6. 部署框架对比

主流推理框架对比

特性 vLLM TGI TensorRT-LLM SGLang llama.cpp
开发者 UC Berkeley HuggingFace NVIDIA LMSYS 社区
PagedAttention 原生 类似机制 RadixAttention
Continuous Batching 支持 支持 支持 支持 有限
Prefix Caching 支持 支持 支持 支持(更高效)
投机解码 支持 支持 支持 支持 有限
量化支持 GPTQ/AWQ/FP8 GPTQ/AWQ 全量化格式 GPTQ/AWQ/FP8 GGUF K-Quant
硬件支持 NVIDIA/AMD NVIDIA/AMD NVIDIA NVIDIA/AMD CPU/GPU/Apple
易用性
适用场景 通用在线服务 HF 生态集成 追求极致性能 高吞吐研究 消费级/本地

选型建议

场景 推荐框架 理由
在线推理服务(生产) vLLM / TGI 成熟稳定,吞吐高
追求极致性能 TensorRT-LLM NVIDIA 深度优化
研究实验 SGLang 灵活,RadixAttention 更高效
CPU/Mac 本地部署 llama.cpp GGUF 格式,CPU 优化好
快速原型 vLLM OpenAI 兼容 API,上手快

QUESTION 面试题:vLLM、TGI、TensorRT-LLM 有什么区别?如何选型?

  • vLLM:开源生态活跃,PagedAttention + Continuous Batching,通用性强,适合大多数在线服务场景
  • TGI (Text Generation Inference):HuggingFace 开发,与 HF 模型生态无缝集成,适合 HF 用户
  • TensorRT-LLM:NVIDIA 开发,针对 NVIDIA GPU 深度优化(Kernel Fusion、FP8 等),性能最高但使用门槛也最高
  • SGLang:RadixAttention 前缀缓存更高效,适合研究场景

选型原则:通用选 vLLM,HF 生态选 TGI,极致性能选 TensorRT-LLM,研究选 SGLang。

7. 性能数据

vLLM 论文基准(vs Orca, TGI)

指标 vLLM Orca HF TGI
吞吐量 (tokens/s) 2-4× Orca 基准 0.5-1× Orca
KV Cache 利用率 >95% 20-40% 20-40%
延迟 (P99) 更低 基准 更高

与同类引擎对比(2025)

引擎 PagedAttention Continuous Batching Prefix Caching Speculative Decoding
vLLM 原生 支持 支持 支持
TGI 类似机制 支持 支持 支持
TensorRT-LLM 支持 支持 支持
SGLang RadixAttention 支持 支持(更高效) 支持

8. vLLM 使用示例

启动推理服务

python -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Llama-3-8B \
  --tensor-parallel-size 2 \
  --gpu-memory-utilization 0.9 \
  --max-model-len 8192 \
  --enable-prefix-caching

离线批量推理

from vllm import LLM, SamplingParams

llm = LLM(model="meta-llama/Llama-3-8B",
           tensor_parallel_size=2,
           gpu_memory_utilization=0.9)

params = SamplingParams(
    temperature=0.7,
    top_p=0.9,
    max_tokens=512
)

outputs = llm.generate(prompts, params)

常用启动参数

参数 含义 推荐值
--tensor-parallel-size 张量并行 GPU 数 根据模型大小选择
--gpu-memory-utilization GPU 显存使用比例 0.85-0.95
--max-model-len 最大序列长度 根据业务需求
--enable-prefix-caching 启用前缀缓存 推荐(System Prompt 场景)
--quantization 量化方式 awq / gptq / fp8
--swap-space CPU 换出空间大小(GB) 0(默认 Recomputation)

9. 核心设计总结

组件 功能 优化效果
PagedAttention KV Cache 分页管理 消除碎片,利用率 >95%
Continuous Batching 迭代级调度 最大化 GPU 利用率
Block Manager 跟踪 Block 分配/释放 精确的内存管理
Scheduler 请求级别的抢占/恢复 保证服务质量
Prefix Caching 共享前缀 KV Block 减少重复计算