Chapter 01

为什么需要 vLLM:HF Transformers 的瓶颈

"同一张 A100,HF 每秒只能跑 8 条请求,vLLM 能跑 160 条。" 背后是两件事:KV cache 不再浪费显存,批处理不再等最慢那个——本章先把痛点摊开。

推理这件事,难在哪

LLM 推理看起来简单:输入 prompt,生成 token,拼成回答。但真要把它做成生产服务,会踩到三个硬坑——

显存瓶颈
一个 7B 模型 FP16 就要 14GB,KV cache 还要留一大半。序列越长、batch 越大,越容易 OOM。
长尾拉跨吞吐
一个 batch 里 99% 的请求只要 50 token,但 1% 要 2000 token。GPU 得陪着那个最慢的跑完,前面的早就空转了。
GPU 利用率低
nvidia-smi 看 GPU 利用率 30%-40% 是常态。不是模型跑得慢,是时间都花在等输入、等输出、padding 对齐上。

痛点 1:KV cache 是显存的头号杀手

Transformer 解码时每生成一个 token,都要把前面所有 token 的 Key / Value 存下来作为上下文。这份缓存叫 KV cache

一条 Llama-2-13B 序列的 KV cache 大小公式:
  2 (K+V) × 层数 × 头数 × 头维度 × 序列长度 × 精度

  = 2 × 40 × 40 × 128 × 2048 × 2 byte
  ≈ 1.6 GB / 条请求

一张 A100-80GB 装进 13B 模型本体后大约剩 54GB,按传统做法要给每条请求预留 max_seq_len 的连续 KV 显存——能同时跑的请求数很快撞上上限。

更糟的是浪费
预留按最大长度算,实际序列短得多。测试显示传统推理里 60%-80% 的 KV 显存是空的——被 padding 和预留占着,却没装任何 token。

痛点 2:静态 batch 等最慢的那个

HuggingFace 默认的 model.generate(batch) 要求所有序列同进同出:

请求 A:prompt=20  → 生成 50 token   (40ms 完成)
请求 B:prompt=30  → 生成 200 token  (160ms)
请求 C:prompt=10  → 生成 2000 token (1.6s)
─────────────────────────────────────────────
整个 batch 结束时间 = max(40, 160, 1600) = 1.6s
A 的用户等了 1.6s ——本来只要 40ms

静态 batch 的 GPU 利用率像心电图:开头所有请求一起涌,中段只剩长尾请求,GPU 算力在空转。

痛点 3:吞吐与延迟的矛盾

想提升吞吐就加大 batch size,想降低延迟就减小 batch size——传统架构只能二选一:

策略吞吐 QPSp95 延迟问题
batch=1(流式)100.8sGPU 利用率极低,成本高
batch=32(静态)803.2s尾部请求等得难受
batch=1281108s+长请求拖垮整个 batch

vLLM 是怎么打破僵局的

vLLM 2023 年在 UC Berkeley 由 Kwon 等人提出(SOSP'23 论文 "Efficient Memory Management for Large Language Model Serving with PagedAttention"),核心就两招:

  1. PagedAttention:KV cache 不再预留连续显存,切成固定大小的 block,像操作系统管内存一样按需分配、复用、共享。显存利用率从 40% 拉到 96%。
  2. Continuous Batching:每一步 decode 都可以插入新请求、踢掉完成的请求,GPU 没有空隙。不用再等最慢的那个。
效果有多夸张?
同一张 A100-40GB 跑 Llama-13B:HuggingFace 吞吐约 8 req/s,vLLM 约 160 req/s,20× 提升。相同 SLA 下,云成本直接降到 1/10-1/20。

vLLM 在生态里的位置

方案定位适合场景
HF Transformers研究 / 单次调用实验、少量推理
Text Generation Inference (TGI)HF 官方推理服务HF 生态深度集成,企业订阅
vLLM开源吞吐王自建推理服务、成本敏感
TensorRT-LLMNVIDIA 深度优化延迟极致 + 肯花时间编 engine
llama.cpp / Ollama本地 / 边缘Mac / CPU / 消费级 GPU
SGLang新秀,RadixAttention多轮对话、前缀共享场景极强

选型一句话:自建 LLM 服务、要吞吐、开源,默认上 vLLM;极致低延迟且团队有 CUDA 背景再考虑 TensorRT-LLM。

vLLM 能做什么

vLLM 不是银弹

有些场景它不擅长,提前知道省得踩坑:

场景建议
单条请求极致低延迟TensorRT-LLM 或 Llama.cpp,vLLM 调度层开销可感
CPU / Mac 本地部署Ollama / llama.cpp,vLLM 0.6 才刚加 CPU 后端
训练 / fine-tune用 unsloth / axolotl / transformers,vLLM 只做推理
超长上下文前缀共享SGLang 的 RadixAttention 更省,vLLM 前缀缓存也有但粒度粗

一个 30 秒 demo 感受下

# 启服务
docker run --gpus all -p 8000:8000 \
  vllm/vllm-openai:latest \
  --model meta-llama/Llama-3-8B-Instruct \
  --gpu-memory-utilization 0.9
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="not-used")

resp = client.chat.completions.create(
    model="meta-llama/Llama-3-8B-Instruct",
    messages=[{"role": "user", "content": "用一句话介绍 vLLM"}],
)
print(resp.choices[0].message.content)

跟 OpenAI API 完全同款——你的业务代码一行不改就能切到自建模型。

本章小结