资讯动态

vLLM 快速上手教程:PagedAttention 与吞吐优化实战

发布时间:2026/9/10 19:02:53 来源:尧图企业网站定制
我一直觉得搞大模型部署的人早晚都得跟 vLLM 打交道。不管你是跑开源模型做验证还是要把模型落地成线上服务吞吐上不去、显存爆掉、首字延迟高这些坑我基本都踩过一遍。vLLM 厉害的地方在于它把 PagedAttention、continuous batching、量化推理这些优化全揉进了一个框架里你不用自己从底层去改装好、配好参数就能把 GPU 的潜力榨出来。这篇算是我的 vLLM 教程系列第四篇专门讲怎么快速上手。我不会跟你从分布式推理讲起也不扯太深的源码分析就是给你一条我自己实测下来最顺的路径环境准备、装库、跑一个模型、起一个 OpenAI 兼容的服务、把性能调一调最后列一些新手最容易踩的坑。这部分内容适合刚接触 vLLM 的人也适合已经能跑通但想搞清楚参数背后逻辑的兄弟。读完之后你应该能自己从零部署一个可以对外提供服务的模型接口而不只是照抄命令行。1. 动手之前先把这三件事搞清楚1.1 vLLM 到底帮你解决了什么问题先说个很实在的背景。Transformer 模型推理其实是个“内存墙”问题。模型参数量大KV cache 也大传统的推理框架在处理请求时预先给每一条请求分配一整块连续的显存空间但请求的实际长度是不知道的往往导致预留空间要么不够用、要么大量浪费。并发一高显存直接爆掉。vLLM 的核心思路是 PagedAttention借鉴了操作系统的虚拟内存分页管理。KV cache 不再需要连续存储而是拆成多个 block按需分配。这个设计带来两个直接收益显存利用率大幅提高请求之间还能共享前缀缓存。再加上 continuous batching也就是一个请求跑完生成就立刻“插队”下一个 token 的任务进来GPU 几乎不会闲着。所以如果你要部署大模型并且考虑性能vLLM 基本是绕不过去的基础设施。1.2 硬件和软件环境到底要满足什么我自己做测试常用的配置是一张 24GB 显存的 RTX 3090 或 A10跑 7B 到 14B 的模型如果只是做 API 开发调试8GB 显存也能跑小尺寸模型但生成速度和并发上限就别抱太大期望。vLLM 当前版本对 CUDA 有要求一般 NVIDIA 驱动建议 535 以上CUDA toolkit 可以不用单独装因为 vLLM 的 wheel 包通常会绑定好对应的 CUDA runtime但 PyTorch 版本必须匹配。操作系统上Ubuntu 20.04 / 22.04 最省心Windows 原生支持还是差点意思虽然有 Windows 版本但我更推荐你在 WSL2 里跑或者直接用 Docker。很多人在 Windows 上装 vLLM 遇到 dll 缺失、NCCL 初始化失败之类的问题十有八九是环境隔离没做好。提示如果你用的是 Jetson 这类 ARM 设备安装方式完全不同要去找官方为 JetPack 编译的 wheel直接 pip install vllm 大概率会失败。1.3 快速看看自己的 GPU 能不能跑不需要跑任何脚本直接看两个数就行。一个是显卡显存一个是模型参数量。以 FP16 精度为例一个 7B 模型光权重就要占 14GB 显存再加上 KV cache 和激活值24GB 勉强够用如果你要跑 70B 模型单卡基本无望要么多卡张量并行要么做 AWQ/GPTQ 量化把权重大幅压缩。显存估算有个粗略公式模型权重显存 ≈ 参数量(以B为单位) × 2如果是FP16也就是 1B 参数约等于 2GB。 额外预留 4GB 到 8GB 给 KV cache 和推理开销根据你的并发数和序列长度动态调整。把这步算清楚你就知道自己该选什么部署方案了。2. 环境准备与 vLLM 安装全记录2.1 用 Docker 还是裸机环境我的习惯是本地做实验直接建 Python 虚拟环境项目上线直接上 Docker。两种方式我都列一下。如果你在干净的 Linux 服务器上裸机安装其实不麻烦python -m venv vllm-env source vllm-env/bin/activate pip install --upgrade pip pip install vllm第一次安装会拉不少依赖比如 torch、transformers、xformers 这些时间取决于网络通常 5 到 10 分钟。装完验证一下python -c import vllm; print(vllm.__version__)能打印出版本号说明核心库装好了。如果你是生产环境或者是想省去 CUDA、驱动各种乱七八糟的环境问题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 Qwen/Qwen2.5-7B-Instruct这个镜像把依赖全都打包好了你只要把模型目录挂载进去就行。注意--ipchost这个参数很多人漏掉它结果多进程数据共享的时候出现莫名其妙的内存错误。2.2 从 Modelscope 下载模型的坑国内网络环境拉 Hugging Face 模型经常超时我的建议是直接用 Modelscope。ModelScope 上有大量开源模型的镜像下载速度快得多。vLLM 本身没有直接把 Modelscope 集成进去所以你需要在启动脚本里先手动下载模型然后指定本地路径。这个过程不复杂from modelscope import snapshot_download model_dir snapshot_download(Qwen/Qwen2.5-7B-Instruct) print(model_dir)更彻底的办法是设置环境变量让 vLLM 启动时默认走 Modelscopeexport VLLM_USE_MODELSCOPETrue有了这个环境变量你甚至可以直接把模型的 Modelscope 路径传给--model参数。vLLM 启动时如果发现本地没有缓存就会自动从 Modelscope 拉取。这个方式对国内用户特别友好能省去一堆代理配置的麻烦。注意VLLM_USE_MODELSCOPE 这个环境变量依赖 modelscope 库存在pip install modelscope记得先装上。如果你用的镜像里没内置启动会直接报找不到模型的错。2.3 常见安装报错能避开就避开我遇到过很多次安装阶段的坑列几个高频的Torch 版本不匹配vLLM 对 PyTorch 版本有严格的要求装新不用旧。如果你环境里已经有旧版 torchpip 在解析依赖时可能会保留旧版导致冲突。建议在干净的虚拟环境里重装。CUDA 版本检查不过vLLM 的 wheel 是按 CUDA 12.1 / 12.4 等版本编译的你需要确认 nvidia-smi 显示的驱动支持对应版本。驱动太老的话会提示 libcuda.so 找不到。显存不足不是安装问题首次运行 vLLM 示例时如果 OOM不要怀疑代码去查你的并发数和 max-model-len减下来基本就好了。3. 两条路跑通第一个模型3.1 用 Python API 快速验证推理在写任何服务之前先用 Python API 把模型加载起来确认模型本身没问题。这是排错最快的方式。我通常会在项目根目录放一个quick_test.py内容很精简from vllm import LLM, SamplingParams llm LLM(modelQwen/Qwen2.5-7B-Instruct, gpu_memory_utilization0.9) sampling_params SamplingParams( temperature0.7, top_p0.8, max_tokens256, ) prompt 用一句话解释什么是大语言模型 outputs llm.generate([prompt], sampling_params) for output in outputs: print(output.outputs[0].text)这里有个参数我要单独拿出来说gpu_memory_utilization。它表示 vLLM 最多占用多少比例的显存。默认值是 0.9如果你的卡上还要跑别的进程就得调低一点比如 0.6 或者 0.7。设得太高会直接 OOM设得太低则 KV cache 空间不够并发一上来就报错。建议按自己的实际负载来调没有万能值。跑完这段脚本如果终端里能正常打印出模型回复说明推理流程通了。这时候你还可以顺手看下打印的日志里面会有显存使用、加载时间、吞吐量这些指标可以作为后续调优的基线。3.2 用命令行动态测试推理效果vLLM 也提供了一个 CLI 方式不用写 Python 代码就能直接测试模型。这种方式对快速换不同模型做对比非常方便vllm serve Qwen/Qwen2.5-7B-Instruct --port 8000启动后它会监听 8000 端口。然后你在另一个终端用 curl 发请求curl http://localhost:8000/v1/completions \ -H Content-Type: application/json \ -d { model: Qwen/Qwen2.5-7B-Instruct, prompt: 中国大模型的优势是什么, max_tokens: 200 }正常返回的 JSON 里会包含 choices 字段里面就是模型生成的文本。如果你想测试对话模型就用/v1/chat/completions请求体稍微不一样curl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: Qwen/Qwen2.5-7B-Instruct, messages: [ {role: user, content: 你好介绍一下你自己} ], max_tokens: 200 }这个接口最大的价值是它完全兼容 OpenAI 的 API 格式。也就是说你原来代码里用的是openai这个 Python 库只要把base_url改成http://localhost:8000/v1其他代码几乎不用动。这意味着你之前封装的业务逻辑、prompt 模板、流式请求全部可以直接复用迁移成本非常低。3.3 模型格式选型原生权重、AWQ、GPTQ 还是 FP8跑通基础推理之后你很快就会面对一个新的选择用哪种格式的模型权重。原生 HF 权重FP16/BF16是最通用的直接--model指定路径即可兼容性最好。缺点是占显存高7B 模型至少 14GB卡一紧张就跑不动。AWQ 和 GPTQ 是量化格式4bit 量化后 7B 模型权重只有 4GB 左右vLLM 原生支持加载。启动时加参数vllm serve TheBloke/Qwen2.5-7B-Instruct-AWQ --quantization awq如果 GPTQ 也是一样vllm serve TheBloke/Qwen2.5-7B-Instruct-GPTQ --quantization gptqFP8 格式适合 H100、L40S 这些支持 FP8 计算的卡显存占用和速度和 FP16 相比优势明显但注意部分老卡不支持要提前确认。我的个人建议是线上服务优先考虑量化格式本地调试用原生权重最省心。量化后的质量损失通常很小但显存省出来的空间可以让并发翻倍幅度非常大。4. 把服务跑起来参数这样调才靠谱4.1 启动参数的优先级与选择逻辑vLLM 的参数非常多新手最容易犯的错就是把参数挨个全调一遍结果每个参数都是默认值真正影响性能的没改。我建议你按这个优先级来第一梯队--model、--tensor-parallel-size、--gpu-memory-utilization。这三个直接决定了能不能跑起来。模型不指定肯定不行多卡并行不设就用单卡大模型直接 OOM显存利用率不调就会默认 0.9小显存卡容易满。第二梯队--max-model-len、--max-num-seqs、--enforce-eager。这三个决定了并发能力和显存开销。max_model_len默认值往往很大比如 32768如果你的应用只需要 4096建议手动手动设小因为 KV cache 会按这个长度预分配。这是很多人遇到“明明模型不大但显存一直很紧”的核心原因。第三梯队--quantization、--dtype、--trust-remote-code。这些是跟模型格式、精度相关的开关需要根据你下载的权重来定不能随意乱设。我贴一个实际生产环境常用的启动命令供你参考vllm serve Qwen/Qwen2.5-7B-Instruct \ --tensor-parallel-size 1 \ --gpu-memory-utilization 0.92 \ --max-model-len 8192 \ --max-num-seqs 64 \ --port 8000 \ --host 0.0.0.0这个配置在单张 24GB 显存的卡上能跑到一个不错的吞吐水平。max-num-seqs表示最多同时处理的序列数设太大不会自动压垮显存因为 vLLM 会按显存动态调度但它会影响排队策略设得太小可能导致 GPU 空闲。4.2 并发数、batch size 与显存的关系很多人不理解 vLLM 为什么吞吐高就是因为它会把并发请求动态拼成一个大 batch 一起算。你每次请求进来vLLM 不会像传统方案那样排着队一个个处理而是同时处理很多请求在 GPU 上并行计算单位时间的 token 产出自然就上去了。但这里有个平衡batch 越大每个 sequence 的 KV cache 占用总和越大。当显存接近上限时vLLM 会主动拒绝新的请求返回 429。所以你在压测的时候如果看到一堆 429不要去调超时时间而是去看显存余量把max-num-seqs调小或者减少max-model-len。另一个经验如果追求并发能力优先调gpu-memory-utilization给 KV cache 留越多空间能容纳的并发序列就越多。但千万别设成 1.0因为 PyTorch 和 CUDA context 本身也要占显存设成 0.95 都可能直接 CUDA OOM。我用 0.92 是比较稳的。4.3 借助缓存机制提升命中率响应速度翻倍vLLM 的另一个隐藏优势是 prefix caching也就是自动前缀缓存。它会把已计算过的 token 的 KV cache 保存下来当新的请求和之前请求有相同前缀时直接复用跳过重复计算。这个机制对多轮对话特别有用。因为多轮对话里每次你都要把历史消息拼到前面前缀基本不变vLLM 会自动命中缓存首 token 延迟会大幅下降。想开启这个能力启动时加上--enable-prefix-caching有了这个开关你可以在日志里看到Prefix cache hit rate这个指标命中率越高说明越多的重复计算被跳过了。如果你在做 RAG 类的应用把固定的 system prompt 做得足够长且不频繁变动这个缓存命中率会非常漂亮。相反如果你每个请求都把历史聊天记录全部换掉那缓存基本没意义。注意prefix caching 会占用额外显存来存储历史 block所以要把 KV cache 空间留足。开启了之后如果出现 OOM优先把 max-model-len 降下来。4.4 不做推理性能测压等于白部署部署完了别急着上线先做一轮简单的压测确认瓶颈在哪里。vLLM 官方带了一个 benchmark 脚本非常好用python benchmarks/benchmark_serving.py \ --backend vllm \ --model Qwen/Qwen2.5-7B-Instruct \ --endpoint /v1/completions \ --num-prompts 100 \ --max-concurrency 20 \ --request-rate 10这个脚本会统计吞吐量、延迟分布、TTFTtime to first token首 token 延迟、TPOTtime per output token每输出一个 token 的时间这些核心指标。我一般只看三个数Throughput (output tokens/s)代表总产出速度越高越好。TTFT 中位数低于 500ms 属于正常超过 1s 就要看看是不是前缀缓存没命中。TPOT每个 token 的生成速度约等于你感知到的“打字机”速度越低越流畅。如果你测出来吞吐远低于预期常见原因是max-num-seqs太小导致并发批不起来或者max-model-len太大导致 KV cache 碎片化、可用块变少。把这两个参数摆到一起观察基本能找到问题。5. 常见坑与排查经验5.1 常见报错速查表我把自己实际遇到的问题整理成了一个速查表你碰到类似情况可以直接对照处理。报错信息原因处理方式CUDA out of memory显存不足可能是模型太大或 max-model-len 太大降低 gpu-memory-utilization缩短 max-model-len或换量化模型Error in model execution: RuntimeError: NCCL error多卡通信异常检查显卡之间的 NVLink 或 PCIe 连接设置NCCL_P2P_DISABLE1试试AssertionError: tensors are on cuda and host多进程/多线程数据拷贝异常启动容器时加上--ipchost或者减少 num-workersThe models max seq len is larger than the maximum number of tokens输入长度超过模型限制裁剪文本或增加 max-model-lenValueError: Unknown quantization method: gptq量化参数不匹配确认模型仓库里的量化方式传对应的--quantization值ModuleNotFoundError: No module named vllm._CvLLM 安装不完整重装对应版本的 wheel确认和 torch 版本匹配bitsandbytes 相关报错某些量化方式依赖此库加载失败vLLM 官方对 bitsandbytes 支持有限建议直接用 AWQ 或 GPTQopenai error: model not found请求中的 model 名称和启动参数中的不一致请求体里的 model 字段必须和--model保持一致5.2 显存规划与 OOM 的排查思路遇到 OOM第一件事不是改代码而是搞清楚显存去了哪里。我提供一个很实用的排查链条。先看模型权重占了多少。如果是 FP16 的 7B那大概 14GB如果是 13B那接近 26GB单卡 24GB 基本没戏。再看 KV cache。vLLM 启动时会打印 KV cache 的 block 数量和每个 block 的大小这些信息在日志里都能找到。最后看 CUDA context 和运行时占用这部分通常 1 到 2GB。如果已经 OOM优先调整顺序是降低max-num-seqs→ 降低max-model-len→ 降低gpu-memory-utilization→ 换量化模型。不要一上来就换小模型那样影响精度。我见过最多的情况是模型只有 7B显存也够但max-model-len默认 32768导致 KV cache 预分配了十几 GB其他请求全部被拒。把max-model-len调成 4096 或 8192问题直接消失。5.3 关于 Windows 和 Modelscope 的现实建议很多读者会私信问 Windows 能不能跑。我的答复是能跑但不推荐。vLLM 在 Windows 上依赖 CUDA 的必要库和编译工具链即使能用性能也不如 Linux。如果你只有 Windows 环境优先考虑 WSL2或者用云 GPU 实例跑比自己折腾省太多时间。关于 Modelscope 我再强调一句虽然VLLM_USE_MODELSCOPETrue很方便但同一时间只建议配一个模型源。如果你本地已经下载过 Hugging Face 的缓存又打开了 ModelscopevLLM 会优先去 Modelscope 的缓存目录找反而会触发重复下载。建议二选一路径清晰才不会乱。5.4 序列长度和 padding 的一些细节有些模型需要 padding比如 embedding 模型。如果你用 vLLM 跑完 BGE-M3 这类 embedding 模型的 API记得在请求里传truncate_prompt_tokens参数。这个参数的作用是超长时自动截断否则会报输入长度溢出的错。跟 vLLM 0.28 相关的版本里BGE-M3 的加载参数有过一些调整遇到报错时多看一眼官方 release note 比乱猜更高效。另外如果你为了优化显存自己把输入统一 padding 到固定长度这个操作在 vLLM 里其实是多余的。vLLM 内部有自己的 batching 策略外部 padding 只会浪费算力和带宽。直接把原始长度发过去就好。结语个人体会vLLM 这个框架说实话我已经离不开它了。每次拿到一张新显卡第一件事永远是装 vLLM跑一遍 benchmark看看这张卡在当前模型下能压出多少吞吐。它能火起来不是没有原因的——你不需要是内核开发者也不需要精通 CUDA只要把几个关键参数理解透彻就能把一个开源模型调教成有实用价值的高并发服务。在这篇文章里我尽量把最容易挡住新手的东西都讲清楚了。从环境准备、安装、跑通推理到最后调参和压测你照着走一遍每个环节中出现的问题都能从上面的速查表里找到答案。当然vLLM 的迭代速度非常快版本之间参数和功能差异不小你安装新版本时最好瞄一眼官方的 changelog。我的建议是不要盲目追新选一个自己验证过稳定的版本把它的参数吃透比什么都强。最后分享一个小技巧如果你是多卡环境先从单卡跑通再上多卡不要一开始就上 tensor-parallel。多卡的通信开销和显存分配策略跟单卡完全不一样先走通单卡链路多卡只是加一个参数的事。踩过几次坑之后你会发现vLLM 上手其实就这么简单。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价