vLLM已经逐渐成为大模型推理部署的主流引擎之一。它的核心优势不只是做了算子优化,更在于引入了PagedAttention这种更符合显存管理习惯的注意力机制。理解这套机制,并且正确配置相关参数,往往比盲目升级显卡更能带来吞吐量提升。下面先从PagedAttention要解决的底层问题说起。

PagedAttention到底解决了什么问题
在Transformer模型的自回归解码过程中,每一步生成都需要读取前面所有token的Key和Value状态,也就是常说的KV缓存。传统推理框架通常会为每个请求预先分配一块连续的显存来存放KV缓存,分配大小按请求可能达到的最大序列长度计算。例如一个请求最大长度是4096个token,那么从一开始就按照4096个token的规模预留显存。这种策略看似简单,却会造成严重的显存浪费。很多请求实际生成长度远小于最大长度,预分配的空间用不完;同时不同请求之间KV缓存不能共享,显存碎片会随着并发请求增加而迅速扩大。
PagedAttention借鉴了操作系统的分页内存管理思想。它不再要求KV缓存连续存放,而是把KV缓存切分成固定大小的块。每个块可以独立分配和释放,同一个请求的KV缓存只需要记录块的序号即可。这样请求实际用到多少块就分配多少块,没用的部分不会占显存。更重要的是,不同请求之间如果前缀相同,比如都带有相同的系统提示词,就可以直接共享同一批物理块,进一步节省显存。对在线服务来说,这意味着同样的显卡可以同时容纳更多请求,批处理效率更高。
从实现角度看,PagedAttention在计算注意力时,会通过块表把逻辑位置映射到物理块上,再交给高效的注意力算子。这个过程对上层用户是透明的,但块大小、块表管理和并行策略会直接影响实际性能。因此,理解并调整相关配置项,是vLLM部署中绕不开的一步。
关键配置项逐项拆解
在vLLM中,和PagedAttention直接相关的参数有多个。首先是block_size,它控制每个KV缓存块容纳多少个token。默认值通常为16,这个值需要在显存利用率和调度开销之间找平衡。块越小,显存分配越精细,碎片越少,但块表会变大,调度和注意力计算的元数据开销也会增加。块越大,管理开销降低,但容易出现内部碎片,尤其是在请求长度分布不均匀时。大部分场景下保持16是合理的,只有在序列长度普遍很长且长度差异很小时,可以尝试调整到32。
接着是max_num_batched_tokens,它决定一个推理批次中最多可以处理多少个token。这个参数并不像名字看起来那样只影响批大小,它同时影响吞吐和延迟。值越大,单次迭代能处理的token越多,计算效率越高,但显存占用会上升。值太小则会导致大量请求排队。另一个重要参数是max_num_seqs,用来限制同时参与调度的序列数量。这两个参数经常一起调整,目的是在显存允许的前提下,让调度器有足够多的序列可调度,同时避免单轮迭代超出显存上限。
显存相关的配置中,gpu_memory_utilization用来设置vLLM可以使用的GPU显存比例。默认值一般在0.9左右,实际部署时如果显卡还承载其他任务,可以适当降低。这个参数直接影响KV缓存可用空间,进而决定能够同时容纳的请求数。另一个是swap_space,它表示当显存不足时,可以将部分KV缓存换出到CPU内存的空间大小。PagedAttention的分页机制天然支持这种换入换出,但换出会带来额外延迟,通常只建议在请求长度波动较大的场景下使用。
enable_prefix_caching是与PagedAttention关系密切的进阶配置。开启后,当多个请求共享相同前缀时,调度器会复用已经计算好的KV缓存块。对于客服系统、RAG应用或带有固定系统提示词的服务,这个配置可以显著降低重复计算,缩短首token延迟。
典型场景下的配置模板
离线批量推理通常追求总体吞吐量,对单个请求的延迟容忍度较高。此时可以把max_num_batched_tokens和max_num_seqs设置得高一些,让GPU尽量吃满。以下是一个Python接口的配置示例:
from vllm import LLM, SamplingParams
llm = LLM(
model="meta-llama/Llama-2-7b-chat-hf",
tokenizer="meta-llama/Llama-2-7b-chat-hf",
tensor_parallel_size=1,
dtype="auto",
block_size=16,
gpu_memory_utilization=0.92,
max_num_batched_tokens=8192,
max_num_seqs=128,
max_model_len=4096,
swap_space=4,
enable_prefix_caching=False,
)
prompts = [
"请解释大模型推理中KV缓存的作用。",
"写一段Python代码实现快速排序。",
]
sampling_params = SamplingParams(temperature=0.7, top_p=0.9, max_tokens=256)
outputs = llm.generate(prompts, sampling_params)
for output in outputs:
print(output.outputs[0].text)
在线服务场景则更关注首token延迟和请求排队的稳定性。如果max_num_seqs设置过高,虽然并发能力提升,但每个迭代的计算量也会变大,排队请求的首token延迟可能变高。通常建议先根据显存测算出能够容纳的序列数,再结合压力测试逐步调整。使用命令行启动服务时可以这样配置:
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-2-7b-chat-hf \
--tensor-parallel-size 1 \
--block-size 16 \
--gpu-memory-utilization 0.88 \
--max-num-batched-tokens 4096 \
--max-num-seqs 64 \
--max-model-len 4096 \
--swap-space 8 \
--enable-prefix-caching
如果服务面向的是RAG问答或带有固定系统提示词的应用,建议开启enable_prefix_caching。同一批请求的前缀块可以被复用,减少重复的注意力计算。需要留意的点是,开启前缀缓存后,调度器会在请求完成后保留一定量的KV缓存块,显存回收不如关闭时及时。因此在显存紧张的环境下,可以先用较小比例测试,确认不会触发频繁换出再正式使用。
调优排查与进阶优化
调整PagedAttention相关参数后,需要通过监控数据判断效果。重点观察两个指标:吞吐量和首token延迟。吞吐量一般用每秒处理的token数衡量,首token延迟则反映从请求到达到开始产生输出之间的等待时间。如果吞吐量偏低,可以尝试增加max_num_batched_tokens和max_num_seqs,前提是显存没有耗尽。如果首token延迟偏高,应降低这两个值,避免单次迭代过度堆积导致排队请求等待过久。
显存不足是最常见的部署问题。vLLM报错中经常出现与KV缓存分配失败相关的信息,这时可以适当降低gpu_memory_utilization,或者增大swap_space。不过换出到CPU内存会引入明显的延迟,尤其是长序列场景下换入换出的开销会非常大。更根本的做法是控制max_model_len,不要把它设置得远超实际业务所需,因为KV缓存空间直接受到最大长度影响。
另一个容易忽略的点是block_size与enable_prefix_caching的配合。前缀缓存只有在请求前缀长度覆盖整数个块时才能高效复用,如果业务中的系统提示词长度不是块大小的整数倍,会有少量块无法完全复用。实际使用中,可以统计常见前缀长度,必要时把块大小调整为更接近这些长度约数,从而提升共享效率。
对于多卡部署,tensor_parallel_size也需要纳入整体配置考虑。PagedAttention的块管理在多卡并行时仍然有效,但KV缓存会被切分到不同GPU上,单卡可容纳的块数会相应减少。配置显存比例时,要考虑到并行切分的额外内存开销,避免简单按单卡比例估算。
最后,调试过程中可以观察vLLM日志中的调度信息,了解实际批大小和缓存使用情况。逐步改参数、压测对比,比一次性修改大量配置更可靠。PagedAttention的优势需要在合适的参数组合下才能充分发挥,而这正是vLLM部署优化中最有实际收益的部分。
vLLMPagedAttention大模型推理部署修改时间:2026-10-01 02:01:26