ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

vLLM 大模型推理部署与显存调优全攻略

vLLM 大模型推理部署与显存调优全攻略 最近不少朋友在群里问同一个问题手头有张消费级显卡想本地部署大模型做推理到底用什么工具最省心十有八九我都会先推荐 vLLM。原因很简单它是目前吞吐性能天花板级别的推理框架配合 PagedAttention 显存管理机制能把一张 24GB 显卡的潜力榨到接近极限而且提供 OpenAI 兼容的 API后端换模型对业务代码几乎无感。这篇文章我会从安装、启动到显存调优完整走一遍笔记里踩过的坑也会全部摊开讲适合刚接触大模型推理、被 OOM 和进程秒退折磨过的朋友直接抄作业。先说清楚 vLLM 不是什么新奇玩意儿它就是个大模型推理加速框架核心做两件事一是高吞吐并发推理二是极致的显存利用。同样是跑一个 7B 模型用原生 Transformers 代码逐条推理可能又慢又容易爆显存换到 vLLM 之后吞吐能翻几倍显存占用还能降一大截。这篇文章主要从拿到一台有 NVIDIA 显卡的 Linux 机器开始讲怎么装、怎么把服务拉起来、以及跑起来之后显存参数怎么调全流程都会给到可直接复现的命令和配置。1. 先搞懂 vLLM 到底解决什么问题很多新手一开始就卡在“不知道该不该上 vLLM”。有人觉得 Ollama 够用了有人觉得直接用 HuggingFace 的 transformers 最简单到底有没有必要多学一个框架我的建议是如果你的目标只是本地随便玩一玩Ollama 确实省事如果想让模型服务化、支撑并发请求、做性能优化那 vLLM 基本是绕不开的选项。1.1 从“逐条推理”到“服务化推理”的跨越用 transformers 推理的典型方式是把模型加载到内存里对每条输入前向传播一次。这种方式在小批量调试时没毛病问题在于它没有把 GPU 的并行能力吃满而且每条请求都要重新管理 KV Cache显存碎片化严重。vLLM 引入了一套类似操作系统虚拟内存的显存管理思路把 KV Cache 切成固定大小的块按需分配不再担心“显存明明很多但分配不出一块连续空间”的尴尬。服务化场景里还有一个痛点多个用户同时发请求怎么办。Ollama 这类工具也可以并发但它内部调度策略相对简单VLLM 则专门优化了 continuous batching即请求动态拼批不需要等一个 batch 全部结束再开下一个 batch只要第一个请求的生成结束立即释放资源容纳新请求吞吐量就是这么拉起来的。我自己实测下来同样卡在 24GB 显存上部署 7B 模型vLLM 的单卡吞吐能到每秒几百 token 级别相比逐条推理快出一个量级。1.2 为什么轮不到我去手写 KV Cache 管理可能有人问KV Cache 是什么为什么不直接让 PyTorch 管显存KV Cache 是生成式模型在推理过程中缓存的中间结果每生成一个新 token 都要用到之前所有 token 的 Key 和 Value 向量。它的体积随序列长度线性增长在长上下文场景下甚至比模型权重本身还占显存。vLLM 的 PagedAttention 机制把 KV Cache 分割成物理块虚拟块和物理块之间建立映射只有真正用到的块才占据显存查询时通过 block table 定位。这个思路和操作系统的分页存储如出一辙说穿了就是“用地址转换换内存利用率”。正因如此vLLM 可以把 KV Cache 利用率从传统方案的百分之四五十拉到百分之九十以上。理解这一点后再去调显存参数一通百通。1.3 和 Ollama、LM Studio、SGLang 怎么选每个推理框架都有自己的定位。Ollama 的优势是傻瓜式安装一条命令拉模型LM Studio 适合桌面端图形界面操作Windows 上体验友好SGLang 是和 vLLM 定位接近的另一套高性能框架结构化输出和复杂调度上有独到之处。选择没有绝对对错只看场景。框架安装难度并发性能量化支持典型场景Ollama极简中等GGUF 为主个人本机快速体验LM Studio极简一般GGUF 为主Windows 桌面端摸模型vLLM中等极高AWQ/GPTQ/FP8服务化部署、生产环境SGLang中等偏难极高AWQ/GPTQ/FP8复杂推理、结构化输出我的观点很直接想深入做服务化部署首选 vLLM。它有 OpenAI 兼容接口社区活跃踩坑资料一搜一大把。SGLang 也值得关注但 vLLM 的生态成熟度目前更稳一些被各大云厂商、模型企业与开源项目集成的程度也更深。后面文章所有操作都以 vLLM 为主线。2. 安装之前先把显存这笔账算明白动手装 vLLM 之前先花两分钟算一下手里的显存到底够不够用。很多人装完框架、下载好模型一启动就 OOM症结往往不是框架装错了而是压根没算过“模型权重 KV Cache 激活值 CUDA context”这四块的显存开销。2.1 模型权重占多少显存一行公式模型权重显存 参数量 × 每个参数字节数。fp16 精度下每个参数占 2 字节所以一个 7B 模型大约需要 $7 \times 10^9 \times 2 14\text{GB}$ 左右14B 模型则需要约 28GB32B 模型直接奔着 64GB 去。如果你的卡显存比权重还小第一选择不是硬调参数而是先做量化。把模型从 fp16 量化到 int8显存直接减半量化到 int4大约能降到原值的四分之一到三分之一。这就是为什么我建议部署前先去查一下模型的量化版本。vLLM 原生支持 AWQ、GPTQ、FP8 等静态量化格式加载时加参数--quantization awq即可。千万别指望一个 fp16 的 70B 模型能在 24GB 卡上靠调参跑起来那不是调参是魔法。2.2 KV Cache推理时的显存大户KV Cache 到底多大取决于序列长度、批大小和层数。粗略估算公式是$KV_{\text{bytes}} 2 (\text{K 和 V}) \times \text{层数} \times \text{隐藏维度} \times \text{序列长度} \times \text{批大小} \times \text{字节数}$。拿 7B 模型举例假设 32 层、隐藏维度 4096、单条 4096 token 的请求、fp16 精度一次性预分配最大长度对应的缓存光这一个请求的 KV Cache 就可能吃掉 2GB 以上显存。如果同时来 16 个请求这一项就直奔 32GB 而去。vLLM 之所以能在同样显存下扛住更多并发就是因为它不会一次性把最大长度的缓存全部占住而是按实际生成的 token 数量动态分配。2.3 把显存占用的四项开销列出来模型权重固定开销量化可减小。激活值activation前向计算过程中的中间张量批量越大占用越高但相对权重和 KV Cache 通常小一些。KV Cache随并发和序列长度变化是调优最灵活的部分。CUDA context / 运行时通常 500MB 到 1.5GB别把这部分忘了。很多人设gpu-memory-utilization0.95却仍然 OOM就是因为忘记给 CUDA context 留空间或者同时加载了多个模型副本。我的建议是非极端场景先设 0.85 到 0.9稳定后再尝试往上加别一上来就追求极限。3. 环境准备与 vLLM 安装实操vLLM 的安装坑主要集中在 CUDA 版本、Python 版本和依赖包冲突上。下面这节是按“最小折腾成本”路线整理的步骤照着做基本一次过。3.1 环境检查清单先检查显卡驱动和 CUDA 环境。执行nvidia-smi看右上角“CUDA Version”。这里有个十年老坑值得强调nvidia-smi显示的 CUDA Version 是驱动支持的最高CUDA 版本不代表你当前实际安装的 CUDA Toolkit 版本。vLLM 主要通过 PyTorch 的 CUDA 支持来工作所以真正要关心的是 PyTorch 匹配的是哪个 CUDA 版本。一个比较省心的方案直接用官方 PyTorch 镜像或者安装匹配的torchtorchvisiontorchaudio。比如 vLLM 0.6.x 时代常用 cu118 或者 cu121新版本对 cu124/cu126 的支持也越来越好。安装前在虚拟环境里先跑一句python -c import torch; print(torch.__version__, torch.version.cuda)确认 PyTorch 能看到 GPU再做下一步。注意强烈建议全程使用虚拟环境conda 或 venv不要直接往系统 Python 里扔依赖包。vLLM 的依赖链很长和系统其他 Python 包打架的概率实在太高了。3.2 三种安装方式对比vLLM 有三条安装路径pip 安装预编译 wheel、Docker 镜像、源码编译。对绝大多数人来说pip 和 Docker 就够了。pip 安装python -m venv vllm-env source vllm-env/bin/activate pip install --upgrade pip pip install vllm装完跑一句验证python -m vllm --version如果之前装过旧版本建议先pip uninstall vllm -y再重新安装避免残留的老版本文件干扰。Docker 安装docker pull vllm/vllm-openai:latest docker run --runtime nvidia --gpus all \ -v ~/.cache/huggingface:/root/.cache/huggingface \ -p 8000:8000 \ --ipchost \ vllm/vllm-openai:latest \ --model deepseek-ai/DeepSeek-R1-Distill-Qwen-7BDocker 方案最大的优势是环境隔离宿主机 Python 版本、CUDA 库混乱都不影响容器内部运行。官方镜像里还会装好常用的量化推理依赖省去大量排查版本的时间。注意Docker 跑 vLLM 必须加--ipchost否则多进程张量并行共享内存会报错。这是我在实际部署中遇到过的典型问题--ipchost不加大概率出现 shared memory 相关的奇怪崩溃。源码编译除非要改框架源码或官方 wheel 不支持你的 GPU 架构否则不建议自己从源码编。编一次至少半小时起步中途还可能遇到 GCC 编译错误、NCCL 版本问题性价比太低。3.3 Windows 用户怎么搞vLLM 官方对 Windows 的支持在很长一段时间里都不是一等公民虽然社区有 Windows 版分支但兼容性参差不齐。如果手头只有 Windows 机器我的建议优先级是装 WSL2在 Ubuntu 子系统里跑性能损耗很小。用 Docker Desktop 切到 Linux 容器跑 vLLM 镜像。Windows 社区版分支能跑通但先做好填坑准备。不要一上来就追求原生 Windows 部署那是把简单问题复杂化。我见过太多人折腾 Windows 原生版本好几天装不上换成 WSL2 半小时搞定。4. 启动模型从最小命令到完整服务安装完成后进入今天最有成就感的环节把模型拉起来。这里先用一个常见的中小模型做演示同时给出几种不同的启动姿势。4.1 最小启动命令启动 OpenAI 兼容 API 服务vllm serve deepseek-ai/DeepSeek-R1-Distill-Qwen-7B \ --gpu-memory-utilization 0.85 \ --max-model-len 4096 \ --port 8000vllm serve是 vLLM 新版本提供的命令入口等价于旧版的python -m vllm.entrypoints.openai.api_server。如果你已经用huggingface-cli把模型下载到本地目录也可以直接传本地路径vllm serve /data/models/DeepSeek-R1-Distill-Qwen-7B \ --gpu-memory-utilization 0.85启动日志中重点留意几行输出模型加载完成、GPU 显存分配情况、KV Cache 大小、最大并发批大小。当看到类似Starting vLLM API server或Uvicorn running on http://0.0.0.0:8000的字样服务已经就绪。4.2 用 curl 和 Python 客户端验证服务服务起来后直接用 curl 打一发请求验证curl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: deepseek-ai/DeepSeek-R1-Distill-Qwen-7B, messages: [{role: user, content: 你好介绍一下你自己}], max_tokens: 256, temperature: 0.7 }Python 端用 OpenAI SDK 来写更接近真实业务from openai import OpenAI client OpenAI( base_urlhttp://localhost:8000/v1, api_keyEMPTY, ) resp client.chat.completions.create( modeldeepseek-ai/DeepSeek-R1-Distill-Qwen-7B, messages[{role: user, content: 用一句话解释 KV Cache}], max_tokens256, temperature0.6, ) print(resp.choices[0].message.content)这里注意 vLLM 服务端默认不校验 api_key随便给个非空字符串就行。OpenAI SDK 要求必须传 api_key所以用EMPTY占位即可。4.3 多卡部署与张量并行手头有多张卡的话vLLM 天然支持张量并行。比如两张 4090 部署 14B 模型vllm serve /data/models/DeepSeek-R1-Distill-Qwen-14B \ --tensor-parallel-size 2 \ --gpu-memory-utilization 0.90张量并行的原理是把每一层矩阵运算切分到多张卡上协同计算能突破单卡显存上限但也会引入卡间通信开销。同一机箱里用 NVLink 或 PCIe 直连效果最好跨机器部署则需要配置分布式通信环境复杂度高不少新手阶段先不建议折腾。判断是否需要多卡模型权重 预期 KV Cache 超过单卡显存或者希望同时服务更多并发再启用张量并行。5. 显存调优从 OOM 到稳定运行的完整路径我见过太多人卡在这一步。模型能加载但跑几个请求就 OOM或者吞吐低得离谱。这部分把 vLLM 的核心调优参数逐个拆开讲并给出一套完整的“从崩溃到稳定”的调整路径。5.1 核心显存参数逐个拆解--gpu-memory-utilization控制 vLLM 最多使用多少比例的 GPU 显存。默认 0.9理论上越高越好但必须给 CUDA context、激活值等留余量。我的建议从 0.85 开始测如果显存还有富余再往上加。--max-model-len最大序列长度直接影响 KV Cache 上限。设得过大缓存预分配上限就高可容纳的并发数量就少设得过小长文本请求会被截断。按实际场景来普通闲聊 2048 或 4096 足够代码生成或长文档总结可以调到 8192 甚至更高。--max-num-seqs最大并发序列数。这个参数决定了同时有多少请求在 GPU 上做推理。增大并发能提高吞吐但每个序列都会占用 KV Cache 和激活值并非越大越好。实际效果需要在吞吐和延迟之间做平衡。--max-num-batched-tokens每次前向传播最多处理的 token 数。它和--max-num-seqs共同决定了一个 step 里能塞多少内容。调大这个值可以让动态批处理更充分但也会推高显存峰值。如果启动时报显存不足第一步就是把这里的数值调小。--enable-chunked-prefill开启后会把长提示词的 prefill 阶段拆成小块与 decode 阶段交错执行避免一个超长请求把显存和算力全部占满让后面排队的短请求也能获得响应。并发场景强烈建议开启副作用是极端长提示词的延迟会有轻微劣化但整体吞吐明显受益。--enforce-eager关闭 CUDA graph 加速降低显存占用但推理变慢。只在显存实在不够时作为救命选项生产环境一般不用。--cpu-offload-gb把一部分参数或 KV Cache 挪到 CPU 内存牺牲性能换显存。实测下来这是应急方案性能下降非常明显适合公司测试环境或想在一张卡上跑稍微大一点的模型做功能验证。5.2 一个 14B 模型的调优案例全流程假设手上有一张 24GB 显存的 4090想跑 DeepSeek-R1-Distill-Qwen-14B 服务。14B 的 fp16 权重约 28GB单卡明显装不下直接用原始精度跑是不现实的。给出两条路线路线一使用量化版本推荐。拉取 AWQ 量化版模型例如deepseek-ai/DeepSeek-R1-Distill-Qwen-14B-AWQvllm serve deepseek-ai/DeepSeek-R1-Distill-Qwen-14B-AWQ \ --quantization awq \ --gpu-memory-utilization 0.9 \ --max-model-len 4096 \ --max-num-seqs 8 \ --enable-chunked-prefill路线二fp16 模型 CPU offload 硬跑。这个只做功能验证vllm serve /data/models/DeepSeek-R1-Distill-Qwen-14B \ --cpu-offload-gb 16 \ --gpu-memory-utilization 0.6 \ --max-model-len 2048路线二虽然能启动但每个 token 生成都可能触发 CPU 和 GPU 之间的数据传输推理速度可能慢到不可用因此只建议临时验证接口联通性和业务逻辑真正的生产负载还是走量化路线或多卡方案。5.3 并发参数怎么配才不浪费显存配置并发参数时可以先用一个小公式估算KV Cache 上限 ≈ 总可用显存 - 权重占用 - 激活值缓冲 - CUDA context。在--gpu-memory-utilization 0.9下24GB 卡大约留给模型和上下文之外的余量在 4GB 左右。然后根据每条请求的 KV Cache 大小反推最大并发数。一个小建议先用--max-num-seqs 4起步观察 GPU 利用率和显存占用再往上加。如果nvidia-smi显示显存占用率已经 95% 以上但 GPU 计算利用率还不到 80%说明瓶颈可能在请求排队而不是显存可以考虑调低--max-model-len给并发腾空间。这种“先稳后快、逐步加压”的思路比一上来就填一堆大数字要可靠得多。实操心得启动日志里有一行类似Maximum concurrency for 4096 tokens per request: X的信息vLLM 会自动评估当前显存分配下的并发上限这比你自己反复试错要准确。善用启动日志很多调优参数的合理性一眼就能看出来。6. 常见问题与排查技巧实录把这段时间积累的问题整理成一份速查表按照“现象 → 原因 → 解决”的思路写后面台上台下都能直接用。6.1 进程秒退报 CUDA out of memory最经典的问题。一般有两层原因一是 vLLM 预分配的显存超过了显卡可用量二是gpu-memory-utilization设太高一点余地都没给 CUDA context 留。解决办法分三步第一步把gpu-memory-utilization降到 0.70 到 0.80验证是否还崩第二步把max-num-seqs调小比如改成 2 或 4降低并发第三步开启--enforce-eager这一步会关掉 CUDA graph 预编译显存占用会明显下降但同时会牺牲部分性能。如果这三步全部做完还是 OOM基本可以断定模型原生精度超出硬件能力建议换量化版或减小模型规模。6.2 请求排队严重吞吐上不去日志里大量请求处于 waitingGPU 利用率却不高这个现象在长提示词场景下非常典型。一个长 prefill 请求会把 GPU 线程占死后面的短请求全部排队。优先打开--enable-chunked-prefill让长请求的 prefill 分片执行。再检查是不是--max-num-batched-tokens设得太小这会导致每次前向传播塞的 token 太少计算资源跑不满。还需要确认并发上限没有被--max-num-seqs卡死结合启动日志里的最大并发建议值调整。6.3 推理延迟突刺时快时慢排除网络因素后多半是 CPU 预处理或 tokenizer 成了瓶颈。vLLM 的 tokenizer 和 LLM 推理虽然分离但请求量上来之后 tokenizer 进程仍然可能拖后腿。新版本解决办法是开--tokenizer-pool-size比如设成 4 到 8让多个 tokenizer 进程并行处理。另外检查磁盘 IO如果模型文件放在机械硬盘上加载权重时会非常慢部署时尽量把模型放到 SSD 上甚至直接用内存盘映射。6.4 兼容性相关的坑vLLM 对版本的敏感度非常高。transformers和tokenizers版本不对可能直接加载失败模型原本是用 transformers 4.45 保存的但 vLLM 依赖里锁了 4.44把依赖升级到最新的vllm版本是最快的解法。另一个常见情况是同一个模型别人能跑通你却报某个自定义 op 加载失败优先检查 CUDA 架构是否匹配。python -c from torch.cuda import get_device_capability; print(get_device_capability())能看到显卡的算力版本老卡如 GTX 10 系列算力 6.1很多 vLLM 的新算子已经不做适配了。6.5 一张速查表收尾症状首要排查项常用解法启动即 OOMgpu-memory-utilization 过高降到 0.75 起测并发起来后 OOMmax-num-seqs 过大减半重试长提示词卡住后续请求prefill 阻塞开启 chunked-prefill吞吐低于预期max-num-batched-tokens 太小按显存余量逐步加大推理时快时慢tokenizer 瓶颈调大 tokenizer-pool-size量化模型加载报错未指定 quantization显式加 --quantization awq/gptq多卡部署直接崩缺少共享内存支持Docker 加 --ipchost从一次具体部署里得到的三个体会回到开头那个问题vLLM 值不值得花一晚上装起来。我的答案非常明确——值得。我在部署 DeepSeek 蒸馏模型时先用原生 transformers 跑通业务逻辑再用 vLLM 做服务化前后对比吞吐提升接近一个数量级这是实打实感受到的差距。安装和启动环节只要按上面步骤来半天就能走通显存调优这一部分则会伴随整个后续使用过程每一次新模型、新硬件、新并发场景都会碰到新的调参空间。最后再分享一点个人经验正式压测之前先记录下nvidia-smi的显存基线和 vLLM 启动日志里的 KV Cache 分配信息遇到异常时这些数字就是最可靠的定位依据。我自己每次换模型都会做一个固定动作——先把--gpu-memory-utilization拉低跑通再逐步调高记录每个档位的显存占用和 P99 延迟。别嫌麻烦这个基线就是你之后优化一切的上限参考。把这套流程固定下来从 0 到 1 部署 vLLM 就不再是碰运气的事了。
返回列表