4. vLLM高性能推理:PagedAttention机制、连续批处理、张量并行、部署生产级API服务

聊到本地大模型推理,很多人第一反应就是「显存不够用」。确实,我刚开始部署 13B 模型时,一张 24G 的卡根本跑不起来。后来接触了 vLLM,才真正体会到什么叫「榨干每一寸显存」。今天咱们就深入聊聊 vLLM 的四个核心武器。

4.1 PagedAttention:把显存管理玩出花

传统推理框架有个通病——显存碎片化。你想想看,每个请求的 KV Cache 都是连续分配的,但请求长度不一样,有的长有的短。结果就是显存里到处是空洞,利用率可能不到 60%。

vLLM 的 PagedAttention 借鉴了操作系统的分页思想。它把 KV Cache 切成固定大小的「块」(Block),每个块可以独立分配和回收。我去年在部署一个对话机器人时,显存利用率直接从 55% 飙到了 92%,效果立竿见影。

核心原理:

  • 每个 Block 默认 16 个 token 的 KV 值
  • Block 之间通过页表映射,逻辑连续但物理不连续
  • 支持 Copy-on-Write,多个请求共享相同前缀的 Block

举个例子,假设两个用户都在问「如何部署 vLLM」,前几个 token 完全一样。传统做法要存两份,vLLM 只需要存一份,后面不同的部分再单独分配。嗯,这里要注意,共享 Block 是只读的,一旦某个请求要修改,就会触发写时复制。

我的经验:Block 大小不是越大越好。16 个 token 是经验值,太小了页表开销大,太大了碎片化严重。如果你处理的是超长文本(比如 32K 上下文),可以试试 32 或 64。

4.2 连续批处理:别让 GPU 闲着

传统批处理有个尴尬的地方——必须等所有请求都生成完,才能处理下一批。这就导致 GPU 经常在空转。我见过一个场景,批大小设为 8,但其中 3 个请求已经生成了结束符,剩下 5 个还在跑,那 3 个的算力就白白浪费了。

vLLM 的连续批处理(Continuous Batching)解决了这个问题。它会在每个 token 生成后检查:哪些请求结束了?哪些新请求进来了?然后动态调整当前批次。

# 伪代码示意
while True:
    batch = scheduler.schedule()
    outputs = model.generate(batch)
    for req in outputs:
        if req.finished:
            scheduler.remove(req)
            scheduler.add(new_requests)

说白了,就是让 GPU 永远在处理「有效」的 token。我做过对比测试,同样 4 张 A100,连续批处理的吞吐量是静态批处理的 2.3 倍。特别是处理短请求时,效果更明显。

避坑指南:我曾经把 max_num_seqs 设得太大,结果显存爆了。建议从 256 开始调,观察显存占用和延迟,逐步往上加。另外,连续批处理对调度器要求高,vLLM 默认的调度策略已经够用,别轻易改。

4.3 张量并行:多卡协作的艺术

单卡跑不动大模型怎么办?上多卡。但多卡不是简单地把模型拆开就行,通信开销是个大问题。vLLM 的张量并行(Tensor Parallelism)做得相当成熟。

它把每个 Transformer 层的参数切分到多张卡上。比如 4 张卡,每张卡只存 1/4 的权重。计算时,每张卡算自己的部分,然后通过 all-reduce 汇总结果。我习惯用 8 卡跑 70B 模型,每张卡分到的权重不到 9B,显存压力小很多。

并行方式 适用场景 通信开销 我的推荐
张量并行 单机多卡 高(每层都要通信) 4-8 卡时首选
流水线并行 多机多卡 低(层间通信) 跨机器时用
数据并行 多副本推理 无(独立推理) 高并发场景

这里有个细节——张量并行对 GPU 间带宽要求很高。我踩过坑,用 PCIe 4.0 x16 连 8 张卡,通信成了瓶颈。后来换成 NVLink,延迟直接降了 70%。如果你用多卡,强烈建议检查一下卡间互联方式。

4.4 部署生产级 API 服务

模型调好了,最终要上线。vLLM 自带了一个 OpenAI 兼容的 API 服务,部署起来非常方便。

# 启动服务
python -m vllm.entrypoints.openai.api_server \
    --model /path/to/model \
    --tensor-parallel-size 4 \
    --max-num-seqs 512 \
    --port 8000

# 客户端调用
curl http://localhost:8000/v1/chat/completions \
    -H "Content-Type: application/json" \
    -d '{
        "model": "my-model",
        "messages": [{"role": "user", "content": "你好"}],
        "max_tokens": 512
    }'

生产环境有几个要点:

  • 负载均衡:前面加个 Nginx,把请求分发到多个 vLLM 实例
  • 超时处理:设置合理的 timeout,防止某个慢请求拖垮整个服务
  • 监控指标:重点关注 TTFT(首 token 延迟)和 ITL(每 token 延迟)
  • 优雅降级:当并发超过阈值时,返回 429 状态码,而不是直接崩溃

我的习惯:用 Docker 部署 vLLM,配合 Kubernetes 做自动扩缩容。每个 Pod 分配 4 张 GPU,根据请求量动态调整副本数。这样既能应对突发流量,又不会在低峰期浪费资源。

最后,别忘了做压力测试。我一般用 Locust 模拟 1000 个并发用户,持续跑 30 分钟,观察各项指标是否稳定。如果发现显存持续增长,那大概率是内存泄漏,需要检查一下模型配置。

vLLM 高性能推理核心架构 用户请求 连续批处理调度器 PagedAttention 显存分页管理 KV Cache 共享 张量并行 模型参数切分 多卡协同计算 API 服务 OpenAI 兼容接口 负载均衡与监控 推理结果输出 四个核心组件协同工作,实现高性能推理

说实话,vLLM 是我用过最顺手的推理框架。它把显存管理、批处理、并行计算这些底层细节都封装好了,让我们能专注于业务逻辑。你只要把模型扔进去,调几个参数,就能得到一个生产级的推理服务。