返回归档
🥌LLM 与 Agent

vLLM:PagedAttention 和 Continuous Batching

自回归推理的 KV Cache 不能按最大长度预留连续显存。vLLM 用 PagedAttention 把 KV 切成 block,再用 Continuous Batching 每步重组 batch,吞吐才上得去。

文章目录

自回归生成时,每个请求的 KV Cache 会随输出变长。传统做法是按最大长度预留一块连续显存:预留多了是内部碎片,请求走了留下不规则空洞是外部碎片。显存账面上还有,新请求却塞不进去,GPU 算力跟着闲。利用率掉到 20–40% 并不罕见。

Left: contiguous KV reserve creates internal/external frag. Right: block table maps logical slots to scattered physical blocks

vLLM 要解决的就是这两件事:显存放得碎,batch 等得慢。

PagedAttention

思路从操作系统的虚拟内存来:不要整段连续 KV,切成固定大小的 block(常见是 16 或 32 个 token 的 K/V)。物理上这些 block 可以散落在显存各处。每个请求有一张 block table,逻辑位置映射到物理 block id。注意力用自定义 CUDA kernel 按表间接访问,数值上和连续布局等价。

收益是三件:

  • 碎片几乎消失,显存利用率可以到 90% 以上
  • 相同 prefix、多轮对话可以共享已经算过的 KV block
  • 序列变长时按 block 增减,不必整段搬迁

没有这张表,就做不到下面的动态 batch。

Continuous Batching

静态 batch 要等这一批里最慢的那条走完,短请求在 padding 里空转。vLLM 每生成一个 token 就重组一次 batch:结束的请求立刻摘掉、释放 block;有空位就塞进新请求。GPU 不必为对齐陪跑。

吞吐提升和模型、硬件、负载有关,官方给出的量级是数倍到一个数量级以上。延迟分布也会好一些:快请求不再被慢请求拖到同一道终点线。

API Server、Scheduler、PagedAttention 内核

可以记成:vLLM = PagedAttention 内核 + 每步重组的 Scheduler + OpenAI 兼容的 API Server

起一个本地服务

模型建议走 ModelScope 或自己已经下好的权重目录,不要把 Hub token 写进命令历史。

uv venv llm
source llm/bin/activate
uv pip install vllm

# 权重放到本地目录后再 serve
vllm serve ~/models/qwen2-1.5b-instruct \
  --dtype float16 \
  --port 8000 \
  --served-model-name qwen2-1.5b-instruct
curl http://127.0.0.1:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen2-1.5b-instruct",
    "messages": [{"role": "user", "content": "用一句话介绍你自己"}],
    "max_tokens": 128
  }'

流式把 stream 设成 true,响应是 text/event-stream。生产上还要看 prefix caching、抢占、多卡并行,那是调度参数,不是再装一套运行时。

文档:vLLM