3. vLLM核心特性:PagedAttention、Continuous Batching、优化目标
3.1 为什么传统推理系统难以支撑大模型高并发?
在深入 vLLM 的核心特性之前,我们先要理解传统推理框架(如简单的 Hugging Face Transformers 的 generate 接口)在高并发场景下遇到的瓶颈。大模型推理的显存占用主要由两部分构成:模型权重和KV Cache(键值缓存)。模型权重是静态的,而 KV Cache 是动态的,它随着生成的 Token 数量线性增长。
传统系统通常采用静态显存预分配策略:为每个请求预先分配一块连续的最大长度显存(例如 max_length=2048)。这会导致两个严重问题:
- 显存碎片化:不同请求的生成长度差异巨大,预分配的长短不一,经过多次调度后,显存中会出现大量无法利用的碎片。
- 显存浪费:即使请求只生成了 50 个 Token,系统也占用了 2048 个 Token 的 KV Cache 空间,利用率极低。
这种“按最大长度预留”的方式,使得系统能同时处理的并发请求数(Throughput)非常有限。vLLM 正是为了解决这一问题而设计的,其核心是 PagedAttention 机制。
3.2 PagedAttention:核心原理与实现
PagedAttention 是 vLLM 的基石,它借鉴了操作系统虚拟内存的分页思想。在传统的 Attention 计算中,KV Cache 被存储为连续的张量。而 PagedAttention 将每个序列的 KV Cache 划分为固定大小的块(Block),每个块可以存储在物理显存的任意位置,通过块表(Block Table)来维护逻辑块到物理块的映射。
具体实现包含以下关键数据结构:
- 逻辑块(Logical Block):每个请求的 KV Cache 在逻辑上被划分为连续的块,编号从 0 开始。
- 物理块(Physical Block):显存中实际分配的块,每个块大小为
block_size(通常为 16 或 32 个 Token)。 - 块表(Block Table):一个映射数组,记录每个逻辑块对应的物理块 ID。
在 Attention 计算时,PagedAttention 不是一次性计算整个序列的 Attention,而是逐块(Block-wise)进行计算。对于每个查询 Token,它通过块表找到对应的物理块,然后在该块内执行标准的 Attention 计算,最后将结果累加。这种方式虽然增加了循环次数,但避免了显存拷贝,且能充分利用 GPU 的并行能力。
# 伪代码:PagedAttention 核心逻辑
def paged_attention(query, block_table, kv_cache, block_size):
scores = []
for logical_block_id in range(len(block_table)):
physical_block_id = block_table[logical_block_id]
key_block = kv_cache[physical_block_id] # 获取物理块
# 计算当前块内的 attention score
block_scores = query @ key_block.T
scores.append(block_scores)
# 拼接所有块的 scores 并 softmax
all_scores = concat(scores)
probs = softmax(all_scores)
# 加权求和 value
output = 0
for logical_block_id in range(len(block_table)):
physical_block_id = block_table[logical_block_id]
value_block = kv_cache[physical_block_id]
output += probs[:, logical_block_id*block_size:(logical_block_id+1)*block_size] @ value_block
return output
3.3 Continuous Batching:动态调度与吞吐优化
传统的 Batching 是静态的:一个 Batch 中的所有请求必须全部完成生成后,才会释放资源并接收新的请求。这会导致 GPU 在 Batch 尾部出现大量空闲等待(因为短请求已经结束,长请求还在生成)。
vLLM 实现了 Continuous Batching(连续批处理),也称为 Iteration-level Scheduling(迭代级调度)。其核心思想是:在每个 Decoding 迭代(Iteration)结束后,立即将已经完成的请求移出 Batch,并加入新的等待请求。
这种机制带来了显著收益:
- GPU 利用率最大化:每个迭代都保持 Batch 满载,不会因为个别长请求而阻塞整个 Batch。
- 降低请求延迟:新请求无需等待当前 Batch 完全结束,只需等待下一个迭代即可开始处理。
- 提高吞吐量:在相同显存下,可以处理更多的并发请求。
- 检查完成:扫描当前 Batch 中所有请求,标记已生成
EOS或达到max_tokens的请求为完成。 - 释放资源:回收已完成请求的 KV Cache 物理块,加入空闲块池。
- 加入新请求:从等待队列中取出请求,为其分配物理块,加入当前 Batch。
- 执行迭代:对更新后的 Batch 执行一次 Decoding。
3.4 优化目标:吞吐量、延迟与显存效率的平衡
vLLM 的设计围绕三个核心优化目标展开,它们相互制约,需要在实际部署中权衡:
| 优化目标 | 核心指标 | vLLM 的应对策略 | 典型场景 |
|---|---|---|---|
| 吞吐量(Throughput) | Tokens/s(每秒生成的 Token 数) | PagedAttention 提高显存利用率 → 更大 Batch;Continuous Batching 保持 GPU 满载 | 离线批量处理、数据标注 |
| 延迟(Latency) | TTFT(首 Token 时间)、TPOT(每 Token 时间) | 动态调度避免排队等待;并行采样(Parallel Sampling)加速单请求 | 在线对话、实时交互 |
| 显存效率 | KV Cache 利用率、最大并发数 | 分页管理消除碎片;按需分配;支持 gpu_memory_utilization 参数调节 |
单卡部署、低成本推理 |
在实际使用中,vLLM 提供了多个参数来帮助用户在这三个目标之间进行调节:
# 通过参数调节优化目标
from vllm import LLM, SamplingParams
# 高吞吐模式:增大 batch,使用更大的 gpu_memory_utilization
llm_throughput = LLM(
model="meta-llama/Llama-3-8B-Instruct",
gpu_memory_utilization=0.95, # 尽量多分配显存给 KV Cache
max_num_batched_tokens=8192, # 增大单次迭代的 token 数
max_num_seqs=256, # 增大并发序列数
)
# 低延迟模式:限制 batch,优先保证响应速度
llm_latency = LLM(
model="meta-llama/Llama-3-8B-Instruct",
gpu_memory_utilization=0.80, # 留出余量
max_num_batched_tokens=2048, # 减小 batch
max_num_seqs=32, # 减少并发
)
3.5 案例与误区复盘
案例:从 8 并发到 80 并发的提升
某团队使用 Hugging Face Transformers 部署 Llama-2-7B 模型,在单张 A100-40GB 上,最大并发数仅为 8(因为每个请求预分配了 2048 Token 的 KV Cache)。切换到 vLLM 后,通过 PagedAttention 和 Continuous Batching,在保持相同延迟(TTFT < 200ms)的前提下,并发数提升至 80,吞吐量从 120 tokens/s 提升至 1100 tokens/s,提升了近 9 倍。
常见误区
实际上,虽然 PagedAttention 引入了块级循环,但 vLLM 通过高度优化的 CUDA Kernel 和块内并行计算,使得计算效率接近甚至超过传统的连续 Attention。在长序列场景下,由于减少了显存拷贝,效率反而更高。
gpu_memory_utilization 到 1.0。如果设置为 1.0,可能会导致没有足够显存加载模型权重或进行 CUDA 上下文初始化。建议保留 5%-10% 的显存余量,设置为 0.90-0.95 较为安全。
max_num_seqs 与显存的匹配关系。即使设置了很大的
max_num_seqs,如果显存不足,vLLM 会自动降低实际并发数。反之,如果设置过小,则无法充分利用 PagedAttention 的优势。建议根据模型大小和显存容量进行估算,并通过 /metrics 端点监控实际的 KV Cache 使用率。
复盘总结
vLLM 的成功在于它将操作系统的成熟思想(分页、虚拟内存)引入到了大模型推理领域,解决了显存管理的根本性痛点。PagedAttention 解决了“空间”问题,Continuous Batching 解决了“时间”问题,两者结合,使得 vLLM 在吞吐量和显存效率上远超传统框架。理解这三个核心特性及其背后的权衡,是正确配置和使用 vLLM 的基础。