vLLM 大模型部署指南:原理、显存与选型
阅读顺序就是学习顺序:是什么 → 为什么快 → 怎么装 → 显存去哪了(KV Cache)→ 怎么选模型 → 怎么选精度 → 怎么部署 → 和 SGLang 怎么选。 本文示例环境为阿里云 PAI-DSW 实例:单卡 AMD Instinct MI300(gfx942),192GB 显存,ROCm 生态,预装 vLLM 0.28.0;示例模型为实测部署验证过的 Qwen/Qwen3.8-27B-FP8。文中所有实测数据均来自该环境。
目录
- vLLM 是什么
- 核心原理:vLLM 为什么快
- 安装 vLLM
- 读懂 KV Cache:显存到底去哪了
- 模型选择:我的显卡能跑多大的模型
- 量化精度怎么选
- 部署步骤(通用流程)
- vLLM vs SGLang:怎么选
- 常见问题
1. vLLM 是什么
一个已经训练好的大模型(比如 Qwen),本质上只是硬盘上的一堆权重文件(几十 GB 的 .safetensors 文件)。它自己不会"运行"——就像一个 .exe 需要操作系统才能跑起来一样。
推理引擎(inference engine)就是模型的"操作系统",它负责:
- 把权重加载进显存;
- 接收用户的请求("帮我翻译这段话");
- 调度 GPU 一个 token 一个 token 地生成回答;
- 通过 HTTP 接口把结果返回给调用方。
vLLM 就是目前最流行的开源推理引擎(UC Berkeley 团队发起,Apache 2.0 协议),核心卖点如下:
| 特点 | 白话解释 |
|---|---|
| PagedAttention | 显存利用率极高,同样显存能同时服务更多用户(第 2 节细讲) |
| 吞吐量高 | 一张卡每秒能"吐"出的总文字量远超裸的 PyTorch/HuggingFace |
| OpenAI 兼容 API | 服务起来之后,任何会调 OpenAI 接口的应用(包括各类 Agent 框架)都能直接接上用 |
| 新模型支持快 | Qwen/DeepSeek/Llama 等新模型发布后通常很快就能跑,NVIDIA(CUDA)和 AMD(ROCm)双生态都支持 |
| 量化支持全 | FP8 / AWQ / GPTQ 等量化模型开箱即用 |
一句话总结:vLLM 把"模型文件"变成"可供程序调用的 API 服务"。
2. 核心原理:vLLM 为什么快
2.1 先理解生成过程
大模型生成文字是自回归的:一次只生成一个 token(词元),生成后拼回输入再生成下一个,循环往复。每生成一个 token,都要对"整段已生成内容"做注意力计算。为了避免每次都从头算,引擎会把每层注意力里的 K(Key)、V(Value)向量缓存下来——这就是 KV Cache(第 4 节细讲)。KV Cache 是显存的最大变数,理解了它,就理解了大模型部署的一半。
2.2 PagedAttention:像操作系统管内存一样管显存
早期的做法:为每个请求按最大可能长度预留一整块连续显存来放 KV Cache。请求最多可能生成 2048 个 token,就预留 2048 的空间,哪怕它最后只生成了 20 个字。实测浪费率高达 60%~80%。
vLLM 的 PagedAttention 借鉴了操作系统虚拟内存分页的思想:把 KV Cache 切成固定大小的小块(block,即"页"),按需分配、用多少占多少,请求之间还可以共享相同内容的页。显存浪费率降到 4% 以下——同样的显存能同时装下多得多的请求,吞吐量成倍提升。这是 vLLM 的成名技术(论文:Efficient Memory Management for Large Language Model Serving with PagedAttention)。
2.3 Continuous batching:让 GPU 一直吃满
传统"静态批处理"的做法:攒一批请求一起算,必须等这一批里最慢的那个生成完,才能开始下一批——短请求被长请求拖死,GPU 大量空转。
vLLM 的调度器在每一个生成步都动态调整批次:有请求生成完了立刻退出,新来的请求立刻插入。GPU 几乎没有空闲,这就是 continuous batching(连续批处理)。
2.4 其他优化(了解即可)
- Prefix caching(前缀缓存):多个请求若有相同开头(比如同一个很长的系统提示词、同一篇文档的多轮问答),公共部分的 KV Cache 直接复用,跳过重复计算,首字延迟大幅下降。
- CUDA Graph / 内核编译:把每步生成的 GPU 指令录制成图回放、预先编译内核,消掉调度开销。ROCm 环境首次启动会有约 2 分钟的内核编译,之后走缓存。
- 量化内核:为 FP8 等量化模型准备了融合内核(实测日志中可见
Enabled custom fusions: norm_quant, act_quant),量化模型的速度损失非常小。
3. 安装 vLLM
方式 A:pip 安装(最常见的 Linux 环境)
pip install vllm # NVIDIA(CUDA)显卡:官方默认 wheel说明:
- vLLM 的发行包自带匹配版本的 PyTorch 和运行库,一般不需要自己装 CUDA 工具包,但要求显卡驱动版本够新(
nvidia-smi右上角的 CUDA Version); - AMD(ROCm)显卡不能直接
pip install vllm——那会装成 CUDA 版。需要用 ROCm 专用安装源(见 vLLM 官方文档 "AMD GPU" 章节),或者直接使用预装好的 ROCm 版镜像。
方式 B:Docker(环境隔离,推荐生产)
docker run --gpus all \
-v ~/.cache/huggingface:/root/.cache/huggingface \
-p 8000:8000 \
vllm/vllm-openai:latest \
--model Qwen/Qwen3.8-27B-FP8方式 C:云上预装环境(PAI-DSW 实例)
云厂商的 DSW 类实例通常已经预装好完整环境,无需安装,直接验证:
python3 -c "import torch; print(torch.__version__, torch.version.hip, torch.cuda.is_available())"
# 实测输出:2.10.0rc11bbd260 7.2.53211 True ← ROCm 构建,HIP 7.2,链路可用
python3 -c "import vllm; print(vllm.__version__)"
# 实测输出:0.28.0 ← ROCm 版 vLLM,开箱即用[!warning] AMD 环境的两条铁律
- AMD 卡(ROCm/HIP)没有
nvidia-smi,用rocm-smi;- 不要在 ROCm 环境的机器上
pip install vllm/pip install torch——会装成 CUDA 版,覆盖掉原有 ROCm 环境。
4. 读懂 KV Cache:显存到底去哪了
4.1 显存的三笔开销
GPU 显存跑一个大模型服务时,被分成三块:
总显存 = 模型权重 + KV Cache + 激活值/框架开销
(固定) (随上下文×并发增长)(较小,相对固定)- 模型权重:模型多大就占多少,是固定的(30.9GB 的 FP8 模型实际占 ~29.4GiB);
- KV Cache:随"上下文长度 × 并发请求数"增长,是弹性部分,也是部署调参的核心;
- 激活值/框架开销:推理时的临时计算空间。多模态模型还会为视觉编码器预留(本例实测预留了 16384 token 的 encoder cache)。
4.2 KV Cache 占多大:一个公式
每 token 的 KV 显存 ≈ 2 × 层数 × KV头数 × 每头维度 × 每元素字节数不用手算,vLLM 启动日志会直接告诉我们结果。以 Qwen3.8-27B-FP8 的实测为例:
Available KV cache memory: 131.7 GiB
GPU KV cache size: 2,073,046 tokens反推:131.7 GiB ÷ 2,073,046 tokens ≈ 68 KB/token(该模型混合注意力架构 + GQA 的实际值)。由此可以估算:
- 一条 128K(131072 token)的满长对话 ≈ 131072 × 68KB ≈ 8.7 GB
- 一条 8K 上下文的普通对话 ≈ 0.5 GB
[!tip] 小知识 现代模型普遍采用 GQA(分组查询注意力)——多个 Query 头共享一组 KV 头,正是为了把 KV Cache 压小。Qwen3 全系都用 GQA,这也是它们对显存友好的重要原因。
4.3 由此得出的调参逻辑
vLLM 启动时会做一次"显存预算":先按 --gpu-memory-utilization(默认 0.9,即最多用 90% 显存)划出总预算,扣除权重和开销后,剩下的全部预分配给 KV Cache。实测拆解:
总预算 205.8GB × 0.90 ≈ 185GB
− 模型权重 29.4 GiB(Qwen3.8-27B-FP8)
− 激活/多模态预留
= KV Cache 131.7 GiB(约 207 万 token)于是有两条铁律:
--max-model-len(单条请求最大上下文) 越大,单条请求占的 KV 越多,能同时服务的请求数就越少——实测 131072 长度下最大并发 15.83x(即 128K 满长请求最多 ~16 条);- 并发数 × 平均上下文长度不能超过 KV 总量,超了请求就会排队。
这就是为什么"长上下文 / 高并发 / 大模型"三者不能同时全要——在固定显存上,它们互相挤压。好消息是 192GB 的卡让这个矛盾大幅缓解:KV 总量约 207 万 token,是 24GB 小卡(约 2.5 万 token)的 80 倍。
4.4 省 KV 的两个开关(了解)
--enable-prefix-caching:相同前缀复用 KV(新版 vLLM 默认开启),多轮对话和固定系统提示词场景收益巨大;--kv-cache-dtype fp8:把 KV 本身也量化成 FP8,再省一半空间——MI300X 支持此特性;KV 已经足够大时可以不开,追求极限上下文/并发时再打开。
5. 模型选择:我的显卡能跑多大的模型
5.1 第一步:估算权重大小
权重大小(字节) ≈ 参数量 × 每参数字节数| 精度 | 每参数字节数 | 27B | 122B(MoE) | 397B(MoE) |
|---|---|---|---|---|
| BF16(原版) | 2 | ~54 GB | ~244 GB | ~800 GB |
| FP8 | ~1.1 | ~31 GB | ~127 GB | ~400 GB |
| INT4(GPTQ/AWQ) | ~1.0(实测口径) | ~30 GB | ~79 GB | ~400 GB |
[!important] 以实际文件大小为准 官方量化仓库的实际体积通常远超"参数量×0.5字节"的理论值(scale/嵌入层/多模态模块都是全精度存的),下面 5.3 的实测数据会反复证明这一点。
5.2 第二步:给 KV Cache 留地方
权重装得下 ≠ 能用,还必须留出 KV Cache。192GB 单卡实测口径下的选型表:
| 模型 | 实测权重体积 | 192GB 卡上的体验 |
|---|---|---|
| ≤9B(BF16) | ~19 GB | ✅ 非常轻松,KV 可开百万级 token |
| Qwen3.8-27B(FP8) | 30.9 GB | ✅ 本文示例部署:KV 131.7GiB(207 万 token),128K×16 路并发 |
| Qwen3.8-27B(BF16) | 55.6 GB | ✅ 不量化保质量,KV 仍剩 ~135GB |
| Qwen3.5-35B-A3B(BF16,MoE) | 71.9 GB | ✅ 轻松,MoE 解码快 |
| Qwen3.5-122B-A10B-GPTQ-Int4 | 78.9 GB | ✅ 放得下,剩 ~90GB 给 KV |
| Qwen3.5-122B-A10B-FP8 | 127.2 GB | ✅ 放得下,剩 ~50GB 给 KV |
| Qwen3.8-Flash-Next-FP8 | 185.5 GB | ⚠️ 权重占满预算,KV 几乎为零,不可用 |
| Qwen3.8-Flash-Next(BF16) | 360 GB | ❌ 超出 |
| Qwen3.8-2.4T-A95B(任意精度) | ≥2.4 TB | ❌ 需要多卡服务器 |
5.3 重要教训:以实际文件大小为准
通过 ModelScope API 实测过官方仓库的权重体积,几个容易被名字"骗"到的例子:
| 模型 | 实测权重体积 | 教训 |
|---|---|---|
| Qwen3.5-27B-GPTQ-Int4 | 30.2 GB | "27B 的 Int4"并不是想象中的 ~14GB——官方量化仓含 scale/全精度模块,约是理论值(13.5GB)的 2.2 倍 |
| Qwen3.5-35B-A3B-GPTQ-Int4 | 34.4 GB | 同上,约理论值的 2 倍 |
| Qwen3.8-Flash-Next-FP8 | 185.5 GB | "192GB 的卡跑 FP8 大模型"差之毫厘:权重刚好占满,KV 归零,等于不可用 |
结论:名字带"27B/35B"不代表量化后体积就是"参数量×量化字节",一切以实际文件大小为准。同样的 192GB 卡,"装得下"和"装得下还有余量给 KV"是两回事。
5.4 查任何模型实际大小的方法
ModelScope 网页:打开模型主页 →「模型文件」标签页,看 .safetensors 加起来多大。
命令行(可复用):
curl -s "https://modelscope.cn/api/v1/models/Qwen/Qwen3.8-27B-FP8/repo/files?Recursive=true" \
| python3 -c 'import sys,json; d=json.load(sys.stdin); print(round(sum(x["Size"] for x in d["Data"]["Files"] if x["Name"].endswith(".safetensors"))/1e9,1),"GB")'5.5 192GB 单卡的选型结论
[!tip] 192GB 单卡的选型结论 首选:
Qwen/Qwen3.8-27B-FP8(30.9GB,实测部署验证)——新一代 27B、FP8 在 MI300X 上原生加速,剩余约 150GB 显存全部给了 KV Cache(207 万 token、128K×16 路并发)。 其他可选:Qwen3.8-27B BF16(55.6GB,零量化)、Qwen3.5-35B-A3B BF16(71.9GB,MoE 快)、Qwen3.5-122B-A10B-FP8(127.2GB,这张卡的能力上限)。 不可行:Flash-Next(360GB / 185.5GB)与 2.4T-A95B——需要多卡大规格实例。
6. 量化精度怎么选
6.1 一张表看懂主流精度
| 精度 | 每参数字节 | 质量损失 | 速度 | 硬件要求 | 典型用途 |
|---|---|---|---|---|---|
| BF16/FP16(原版) | 2 | 基准 | 基准 | 无特殊要求 | 显存充裕、追求最高质量 |
| FP8(W8A8) | ~1.1 | 极小 | 更快 | MI300X(gfx942)原生支持;NVIDIA 需 sm≥8.9(Hopper/Ada) | 192GB 卡部署 27B+ 的首选 |
| INT8(W8A8) | ~1.1 | 小 | 快 | 多数卡可用 | 显存减半诉求 |
| INT4(AWQ/GPTQ) | ~1.0(实测口径) | 小(14B+ 级别日常几乎无感) | 接近原版 | 无特殊要求;但 Marlin 等高效内核是 CUDA 专属,ROCm 上走不同路径 | NVIDIA 小显存卡(如 24GB 跑 14B)的主流方案 |
6.2 经验法则
- AMD(MI300X)优先 FP8:硬件原生支持、vLLM 支持最成熟(实测融合内核已生效),FP8 版 27B 只有 30.9GB,质量损失几乎无感;
- 显存足够时 BF16 也可以:192GB 跑 27B BF16(55.6GB)毫无压力,质量最稳;
- AMD 卡上慎用 AWQ/GPTQ:它们的高效内核(Marlin 等)是 CUDA 专属,ROCm 走的是不同路径,能用但优先级排 FP8 之后;
- 小模型(≤7B)尽量 BF16:小模型参数冗余少,量化后质量下降相对更明显。
6.3 决策速查
模型有官方 FP8 版吗?
├─ 有(如 Qwen3.8-27B-FP8)→ 直接用,MI300X 原生加速 ✅
├─ 没有,但显存装得下 BF16 → 用 BF16(192GB 卡大多数 ≤70B 模型都装得下)
└─ 只发布了 GPTQ/AWQ Int4 → 可以用,但启动前先小规模验证(ROCm 内核路径不同)7. 部署步骤(通用流程)
7.1 最小可用命令
vllm serve Qwen/Qwen3.8-27B-FP8 --port 8000就这一条:vLLM 会自动从 HuggingFace(设置 VLLM_USE_MODELSCOPE=1 则从 ModelScope)下载模型、加载权重,然后起一个 OpenAI 兼容的 API 服务。生产使用再按需加参数。
7.2 常用参数表(含实测取值)
vllm serve <模型路径或ID> \
--host 0.0.0.0 \ # 监听地址,默认仅本机
--port 8000 \ # 服务端口
--served-model-name qwen3.8-27b \ # API 里的 model 名,客户端按这个名字调用
--max-model-len 131072 \ # 单请求最大上下文;不能超过模型原生上限
--gpu-memory-utilization 0.90 \ # 最多用多少比例的显存(默认 0.9)
--max-num-seqs 64 \ # 最多同时处理的请求数
--tensor-parallel-size 1 \ # 用几张卡切分模型(单卡=1)
--api-key sk-xxx \ # 可选:加个简单鉴权,防裸奔
--trust-remote-code # 个别模型需要(Qwen 旧版需要,新版通常不用)[!note] 两个注意点 参数细节随版本可能有变化,拿不准时
vllm serve --help是最权威的参考。 关于--max-model-len的上限:模型原生上下文不一定写在 config.json 顶层——本例 Qwen3.8-27B 的 262144 就嵌套在text_config里,读配置时要注意。
7.3 验证服务
启动日志出现 API server: HTTP server started 即成功(实测全程约 4 分钟,含首次内核编译)。另开一个终端:
# 看模型列表
curl http://127.0.0.1:8000/v1/models
# 实测返回:{"object":"list","data":[{"id":"qwen3.8-27b",...,"max_model_len":131072,...}]}
# 对话一次
curl http://127.0.0.1:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3.8-27b",
"messages": [{"role": "user", "content": "用一句话介绍你自己"}]
}'
# 实测回答:"I am Qwen, a large language model independently developed by
# Alibaba Group's Tongyi Lab, ready to assist you with a wide range of tasks."Python 调用(任何支持 OpenAI SDK 的应用都长这样):
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8000/v1", api_key="empty")
resp = client.chat.completions.create(
model="qwen3.8-27b",
messages=[{"role": "user", "content": "写一句关于秋天的诗"}],
)
print(resp.choices[0].message.content)7.4 调优速查
| 现象 | 处理 |
|---|---|
| 启动 OOM / KV cache 太小 | 调小 --max-model-len,或调低 --gpu-memory-utilization 留出系统余量 |
max_model_len cannot be larger than ... | 超过模型原生上下文了,调小 --max-model-len(本例原生 262144) |
| 并发上不去 | KV 总量不够:换更小的量化模型,或降 --max-model-len |
| 想要更快首字 | 确认 prefix caching 开启;系统提示词尽量统一放前面 |
| 生成质量异常 | 检查量化版本来源是否官方;试试显式 --dtype bfloat16(BF16 模型) |
| 看显存/GPU 占用 | AMD 卡用 rocm-smi(不是 nvidia-smi) |
8. vLLM vs SGLang:怎么选
8.1 SGLang 是谁
SGLang 是目前另一个主流开源推理引擎,同样提供 OpenAI 兼容 API,同样支持 Qwen/DeepSeek 等主流模型,与 vLLM 功能重叠约 80%。区别在核心技术和工程侧重:
| 维度 | vLLM | SGLang |
|---|---|---|
| 核心技术 | PagedAttention(分页 KV)+ continuous batching | RadixAttention(前缀树管理 KV,自动深度复用)+ continuous batching |
| 多轮对话 / Agent(同前缀重复请求多) | 好(prefix caching) | 通常更强(前缀树复用更彻底) |
| 结构化输出(强制 JSON / 语法约束) | 支持(guided decoding) | 强项(xgrammar 集成,开销低) |
| 极限吞吐 | 高 | 高,部分负载略胜 |
| 硬件生态 | CUDA + ROCm(AMD)双支持,预装镜像常见 | 也支持 ROCm/MI300X,但通常需自行配置 |
| 社区/生态/教程 | 最大最全,新模型 day-0 支持广 | 活跃,模型支持也快 |
| 上手难度 | 参数体系成熟,踩坑资料多 | 类似,参数命名略有差异 |
8.2 选型建议
- 默认选 vLLM:生态最大、教程最多、出问题最好搜到答案;DSW 类实例通常已预装 ROCm 版 vLLM,直接用,零成本;
- 出现以下场景,值得装个 SGLang 对比实测:
- Agent 应用:多轮请求共享很长的相同前缀(同一个 system prompt + 工具说明);
- 大量强制 JSON/格式输出;
- 对吞吐极限有明确要求(SGLang 在 AMD 上也可用,但环境要自己配,有踩坑成本);
- 团队/项目已经绑定了某家的参数和脚本,就别折腾迁移,收益通常不值得。
一句话:先用 vLLM 跑起来,遇到瓶颈再换 SGLang 做同负载压测,用数据说话。
9. 常见问题
Q1:启动时提示 out of memory(显存不够)? 显存超了。依次尝试:换更小的量化模型 → 调小 --max-model-len → 调低 --gpu-memory-utilization(如 0.85)→ 加 --enforce-eager(省显存,速度略降)。注意别有其他进程占着显存(rocm-smi 检查)。
Q2:模型下载慢/失败? 国内环境设 export VLLM_USE_MODELSCOPE=1 走 ModelScope 源;或用 modelscope download 命令先下到本地目录,再 vllm serve 本地路径。下载支持断点续传,中断后重跑同一命令即可(实测 ModelScope 内网约 170MB/s,31GB 约 3 分钟)。
Q3:服务起来后显存占满了是坏事吗? 不是。vLLM 启动时就把 KV Cache 一次性预分配好了,占满是预期行为,不是泄漏。
Q4:怎么看当前 KV Cache 还剩多少 / 并发还能加吗? 启动日志里 GPU KV cache size: xxx tokens 是总量、Maximum concurrency for xxx tokens per request: N 直接给出并发倍数(实测:207 万 token、128K×15.83 倍);请求日志里有排队(waiting)信息,排队长说明该降上下文或换小模型了。
Q5:model 参数该填什么? 填 --served-model-name 指定的名字(本例 qwen3.8-27b);没设置的话默认是模型路径本身,用 /v1/models 接口能看到准确值。
Q6:怎么确认量化模型用的是官方版? 认准模型发布组织(如 Qwen/ 官方命名空间)+ 名字里的 FP8/GPTQ-Int4/AWQ 后缀;社区个人发布的量化仓库质量参差,用前要自己验证效果。
Q7:怎么确认这台机器是什么卡、什么架构? NVIDIA 卡用 nvidia-smi;AMD 卡用 rocm-smi(状态)和 rocminfo | grep gfx(架构代号,gfx942 = MI300 系列)。架构决定了能用什么量化(MI300X 支持 FP8)和该装哪个版本的 vLLM。
Q8:AMD 卡和 NVIDIA 卡部署有什么区别? 推理命令完全一样(vLLM 屏蔽了差异),区别在底层:AMD 走 ROCm/HIP(rocm-smi、torch.version.hip),NVIDIA 走 CUDA(nvidia-smi、torch.version.cuda);AMD 上 FP8 可用(MI300 系列)但 AWQ/GPTQ 内核受限;首次启动的内核编译 AMD 更明显(实测 124 秒,走缓存后消失)。
[!info] 相关文章 Qwen3.8-27B-FP8 部署实录:AMD MI300X 上跑通 vLLM —— 本文所述流程在真实服务器上的完整实操记录,每条命令可直接复制执行。