【硬核】啃透vLLM源码:从PagedAttention到连续批处理,大模型推理加速24倍的秘密
啃透vLLM源码:从PagedAttention到连续批处理,大模型推理加速24倍的秘密
如果说大模型是AI的“大脑”,那推理引擎就是让它“开口说话”的声带。vLLM是如何成为业界公认的推理加速之王的?本文带你手撕源码,揭开PagedAttention与连续批处理的神秘面纱。
引言:一个推理工程师的深夜崩溃
凌晨两点,钉钉群炸了:“线上推理服务超时率飙升到30%!”你打开监控,发现8张A100的GPU利用率只有15%,但延迟却高达5秒。你颤抖着点开nvidia-smi,显存占用95%,但实际处理请求数不到50个。
这,就是没有用vLLM之前的日常。
一、先看疗效:24倍吞吐量是怎么来的?
在正式开撕源码之前,先看一张官方测试表(数据基于Llama-7B,A100 80GB):
| 框架 | 吞吐量 (tokens/s) | 显存利用率 | 最大并发 |
|---|---|---|---|
| HuggingFace Transformers | 112 | 35% | 4 |
| vLLM | 2,688 | 92% | 96 |
24倍。这不是PPT,是真实的生产数据。vLLM的秘密,就藏在下图这个架构里(文字描述):
[请求队列] → [调度器(Scheduler)] → [Worker(GPU执行)]
↑ ↓
[Block管理器] ← [PagedAttention]
现在,我们一层层扒开它的衣服。
二、PagedAttention:向操作系统“偷师”的内存管理
2.1 传统KV Cache的“四合院”式浪费
在原生Transformer推理中,每个请求的KV Cache必须提前分配一整块连续内存,大小等于max_seq_len × num_layers × 2 × head_dim × num_heads。假设max_seq_len=2048,这就像一个住户住进一栋四合院——房子大得离谱,而且一旦住进去,即使你只用了其中两间房,剩下的房间也不能给别人用。
2.2 vLLM的“现代公寓”革命
vLLM把KV Cache切成固定大小的Block(默认16个token),通过逻辑块→物理块的映射,让每个请求只需占用实际需要的块数,且物理块可以任意分散。
源码验证:vllm/block.py中PhysicalTokenBlock和LogicalTokenBlock的实现:
class PhysicalTokenBlock:
"""实际的GPU内存块"""
def __init__(self, device: Device, block_number: int, block_size: int):
self.device = device
self.block_number = block_number
self.block_size = block_size
self.ref_count = 0 # 引用计数,用于共享
self.token_ids = [None] * block_size
class LogicalTokenBlock:
"""逻辑上的连续token序列"""
def __init__(self, block_number: int, block_size: int):
self.block_number = block_number
self.token_ids = [_BLANK_TOKEN_ID] * block_size
self.num_tokens = 0
def append_tokens(self, token_ids: List[int]) -> None:
curr_idx = self.num_tokens
self.token_ids[curr_idx:curr_idx+len(token_ids)] = token_ids
self.num_tokens += len(token_ids)
关键点:PhysicalTokenBlock有ref_count——这意味着多个请求可以共享同一个物理块(比如共享前缀的prompt),这是传统缓存做不到的。
2.3 Block大小的“黄金分割”
Block太小(如4),管理开销大;太大(如256),内部碎片多。vLLM默认16,但可以通过环境变量VLLM_BLOCK_SIZE调整。我曾经在代码里看到过某公司为了节省显存,强行改成32,结果长文本生成时延迟飙升——因为block太大导致不能充分利用,反而浪费。
三、调度器:连续批处理的“指挥家”
传统批处理是等一车人坐满了才发车,发车后即使有人下车,车也不能停下来接新人。vLLM的连续批处理(Continuous Batching)则像地铁:每站都有人上下,车门永远不关。
调度核心在vllm/core/scheduler.py的schedule()方法中:
def schedule(self) -> SchedulerOutputs:
# 1. 从等待队列中挑选可调度的请求
scheduled_seq_groups = []
for seq_group in self.waiting:
# 检查是否有足够的block
if self.block_manager.can_allocate(seq_group):
self.block_manager.allocate(seq_group)
scheduled_seq_groups.append(seq_group)
else:
break # 显存不足,停止调度
# 2. 将正在运行的请求加入本次迭代
for seq_group in self.running:
# 如果请求已经完成,释放其block,并加入finished队列
if seq_group.is_finished():
self.block_manager.free(seq_group)
self.finished.append(seq_group)
else:
scheduled_seq_groups.append(seq_group)
# 3. 生成SchedulerOutputs,交给Worker执行
return SchedulerOutputs(
scheduled_seq_groups=scheduled_seq_groups,
blocks_to_swap_in=...,
blocks_to_swap_out=...,
...
)
注意这里的blocks_to_swap_out——当显存不够时,vLLM会把部分block swap到CPU内存,等需要时再换回来,这就是vLLM支持超长上下文的关键(虽然会慢一点,但不会OOM)。
四、Worker执行:CUDA Graph与Triton的加持
当调度器决定好本次迭代要执行的请求后,Worker会调用execute_model()。其中最关键的是CUDA Graph的运用:
# vllm/worker/model_runner.py
class ModelRunner:
def __init__(self, ...):
self.cuda_graph_memory_pool = None
def capture_model(self, kv_caches):
# 预热
for _ in range(2):
self._run_model(...)
# 捕获
self.graph = torch.cuda.CUDAGraph()
with torch.cuda.graph(self.graph, pool=self.cuda_graph_memory_pool):
self._run_model(...)
CUDA Graph把一系列GPU操作打包成一个“图形”,一次性提交给GPU,避免了每次迭代的kernel launch开销。对于小batch size场景,这个优化能带来30%以上的性能提升。
五、实战:从部署到调优的“血泪史”
5.1 最简单的部署命令
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-2-7b-chat-hf \
--tensor-parallel-size 4 \
--dtype float16 \
--max-model-len 4096 \
--gpu-memory-utilization 0.9
5.2 踩坑1:量化导致精度下降
vLLM原生支持GPTQ和AWQ,但如果你用的是FP16模型,直接加--quantization gptq会报错。正确做法是先用AutoGPTQ量化模型,再加载。
5.3 踩坑2:动态batching与流式输出
很多同学用curl测试时发现vLLM没有流式输出,以为卡住了。其实vLLM默认开启流式,但curl不会实时打印。正确的测试姿势是:
from vllm import LLM, SamplingParams
llm = LLM(model="your-model")
params = SamplingParams(max_tokens=100)
outputs = llm.generate(["Hello"], params, use_tqdm=False)
for output in outputs:
for token in output.outputs[0].text:
print(token, end='', flush=True)
六、总结与展望
vLLM的成功,本质上是系统思维对算法思维的一次降维打击。它没有发明新的模型结构,只是用更好的内存管理和调度,把现有硬件的潜力榨干到极致。
未来,随着PagedAttention v2(支持多轮对话的显存共享)、异步调度等新特性的引入,vLLM的统治地位还会持续很久。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)