4、vLLM核心概念:PagedAttention、Continuous Batching、KV Cache、吞吐量与延迟指标
vLLM 之所以能在众多推理框架中脱颖而出,核心在于它对传统 LLM 推理引擎的三大痛点——显存碎片化、请求排队低效、KV Cache 管理粗放——进行了系统性的架构重构。理解 PagedAttention、Continuous Batching 与 KV Cache 这三者的协同关系,是掌握 vLLM 性能调优的前提;而吞吐量与延迟指标则是衡量这些优化是否有效的最终标尺。本章将深入拆解这些核心概念,并给出可操作的监控与调优方法。
4.1 PagedAttention:从虚拟内存到显存管理的跨越
核心原理
PagedAttention 是 vLLM 的基石,其灵感来源于操作系统中的虚拟内存分页机制。在传统推理框架中,KV Cache 需要一块连续的内存空间,且大小需在请求开始时按最大序列长度预留。这导致两个严重问题:一是内部碎片(预留空间未用满),二是外部碎片(不同请求的缓存空间无法共享)。PagedAttention 将 KV Cache 划分为固定大小的物理块(通常每个块可存储 16 或 32 个 token 的 KV 向量),每个请求的逻辑 KV Cache 通过一个“块表”映射到非连续的物理块上。这种设计使得显存利用率接近 100%,且仅在需要时分配新块。
判断方法与操作流程
在实际部署中,判断 PagedAttention 是否生效,最直接的方法是观察 vLLM 启动日志中的 GPU KV cache size 参数。若该值大于 0,说明已启用。调优时需关注两个关键参数:
--block-size:默认 16。较小的块减少内部碎片,但增加块表开销;较大的块反之。对于长序列生成(如代码生成),建议 32。--max-num-seqs:限制并发序列数,防止块池被单个请求耗尽。
# 启动时指定块大小和并发数
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.1-8B-Instruct \
--block-size 32 \
--max-num-seqs 256
案例拆解:显存碎片化导致的 OOM
某团队使用 HuggingFace Transformers 部署 70B 模型时,即使 batch size 设为 1,在长对话场景下仍频繁 OOM。分析发现,Transformers 为每个请求预分配了最大长度(2048 tokens)的连续 KV Cache,而实际平均只用到 800 tokens,显存浪费率超过 60%。迁移到 vLLM 后,PagedAttention 按需分配物理块,同样显存下并发能力提升了 4.2 倍,且未再出现 OOM。复盘经验:对于对话式 AI 或长文档处理场景,PagedAttention 的收益远大于短文本分类任务。
block_manager 的 CPU 内存占用。
4.2 Continuous Batching:迭代级调度的威力
核心原理
传统的 Static Batching 必须等待一个 batch 中所有序列生成完毕,才能释放显存并接收新请求。这导致 GPU 在 batch 尾部出现大量空闲(气泡)。Continuous Batching(连续批处理)将调度粒度从“序列级”细化为“迭代级”(即每个 decode 步骤)。vLLM 的调度器在每个 forward pass 前检查所有序列的状态:若某序列已生成结束标记(EOS)或达到最大长度,立即将其移出 batch,并插入排队中的新请求。这种机制让 GPU 始终处于高饱和计算状态。
操作流程与参数配置
- 启用开关:vLLM 默认启用 Continuous Batching,无需额外参数。
- 控制抢占策略:当显存不足时,调度器会抢占(preempt)部分序列。vLLM 支持两种模式:
recompute(重新计算被抢占序列的 KV)和swap(将 KV 换出到 CPU)。 - 调整调度窗口:通过
--max-num-seqs限制单次迭代的最大序列数,避免过度抢占。
实战场景:混合负载下的吞吐量优化
某 SaaS 平台同时处理短问答(平均 50 tokens)和长文总结(平均 1500 tokens)。使用 Static Batching 时,长文请求会阻塞短请求达数秒。切换 vLLM 后,Continuous Batching 让短请求在长文请求的 decode 间隙被插入执行。实测结果:P95 延迟从 4.2s 降至 1.1s,整体吞吐量提升 3.8 倍。关键调优点是将 --max-num-seqs 设为 128,并启用 --enable-prefix-caching 以加速重复前缀(如系统提示词)。
--use-v2-block-manager,其更精细的块管理能进一步减少抢占频率。
4.3 KV Cache 深度管理:显存利用率的隐形战场
核心原理与量化对比
KV Cache 的大小由模型维度(层数、头数、头维度)和序列长度决定。以 Llama-3.1-8B 为例,每个 token 的 KV 占用约 0.5MB(FP16)。若并发 100 个序列,平均长度 1024,则需约 50GB 显存——远超模型权重本身。vLLM 通过三种手段优化:前缀缓存(相同前缀共享 KV)、写时复制(多个采样序列共享初始 KV)、量化(FP16→INT8)。
| 优化手段 | 原理 | 适用场景 | 显存节省 |
|---|---|---|---|
| 前缀缓存 | 相同 prompt 前缀的 KV 复用 | 多轮对话、RAG 固定指令 | 30%-70% |
| 写时复制 | beam search 等采样分支共享 KV | beam search 解码 | 分支数倍数级 |
| KV 量化 | 将 KV 从 FP16 降为 INT8/FP8 | 显存极度紧张 | 约 50% |
监控与调优方法
使用 nvidia-smi 观察显存占用时,需区分“模型权重”和“KV Cache”。vLLM 提供了更精确的指标:gpu_cache_usage_perc(通过 /metrics 端点暴露)。当该值持续高于 90% 时,说明 KV Cache 接近饱和,应降低并发或启用量化。
# 通过 Prometheus 查询 KV Cache 使用率
curl http://localhost:8000/metrics | grep vllm:gpu_cache_usage_perc
案例复盘:前缀缓存带来的成本下降
某客服机器人每天处理 200 万次请求,其中 80% 的请求共享相同的系统提示词(约 500 tokens)。启用 --enable-prefix-caching 后,KV Cache 命中率提升至 65%,单次请求的 TTFT 从 380ms 降至 120ms。同时,由于显存压力减小,可以将 GPU 实例从 4×A100 缩减至 2×A100,年度成本降低约 40%。复盘要点:前缀缓存的效果高度依赖业务负载的重复度,建议先分析 prompt 前缀的分布再决定是否启用。
4.4 吞吐量与延迟指标:性能的度量衡
核心指标定义
衡量推理性能不能只看单一数字,需区分吞吐量和延迟,并进一步拆解延迟的构成:
- 吞吐量(Throughput):单位时间生成的 token 数(tokens/s)或处理的请求数(req/s)。关注 GPU 的饱和利用。
- TTFT(Time To First Token):从请求发出到收到第一个 token 的时间。影响用户感知的“响应速度”。
- TPOT(Time Per Output Token):生成每个输出 token 的平均时间。影响生成流畅度。
- 端到端延迟:TTFT + TPOT × 输出长度。这是用户实际等待的总时长。
测量方法与工具
vLLM 内置了 --metrics 端点,可输出上述所有指标。推荐使用 vllm bench 工具进行压测:
# 压测命令示例
python benchmarks/benchmark_serving.py \
--backend vllm \
--model meta-llama/Llama-3.1-8B-Instruct \
--dataset ShareGPT \
--num-prompts 500 \
--max-concurrency 50
压测时需关注 吞吐量-延迟曲线:随着并发数增加,吞吐量先上升后趋于平稳,而 TPOT 会逐渐恶化。最佳工作点通常在吞吐量达到峰值 80% 的位置。
案例拆解:定位性能瓶颈
某团队压测发现吞吐量仅为理论值的 40%。通过指标拆解:
- TTFT 正常(50ms),排除调度问题。
- TPOT 异常高(120ms),而 GPU 利用率仅 60%。
- 进一步观察发现
gpu_cache_usage_perc仅为 30%,但 CPU 内存占用极高。 - 定位到问题:
--max-num-seqs设置过大(1024),导致 CPU 侧的数据预处理和 tokenizer 成为瓶颈。 - 将参数调至 256 后,TPOT 降至 45ms,吞吐量提升 2.1 倍。
gpu_cache_usage_perc、avg_tpot 和 p99_tpot 三个指标。