vLLM 报显存不足时,先看服务走到了哪一步。权重加载失败、KV Cache 无法容纳配置的上下文,以及服务启动后在高并发下抢占或 OOM,对应的调法不同。先记录第一条异常和它前面约 30 行日志,每次只改一个影响显存的参数。

用日志区分三个阶段
| 现象 | 先查什么 | 优先处理 |
|---|---|---|
| 权重加载期间报 CUDA OOM | 空闲显存、其他进程、实际权重精度、模型加载峰值 | 换空闲卡、减小模型或用后端支持的量化权重 |
| 权重已加载,KV Cache 初始化报错 | 可用缓存块、最大上下文、CUDA Graph 和工作区开销 | 先按业务需要降低 –max-model-len,保留真实显存余量 |
| 空载能启动,并发时延迟飙升或失败 | 请求实际 token 数、运行中和排队请求、抢占、KV Cache 占用、第一条异常 | 限制请求长度或并发,分级压测;再单独调整批处理预算 |
还有一种报错出现在权重加载之前:启动时可用显存低于配置所需的预算。此时先看同卡其他进程,或把 –gpu-memory-utilization 调到这张卡实际能承担的范围。保留调参前后的 nvidia-smi 输出。
本次测试就遇到了这一层检查。RTX 2080 Ti 总显存为 11.0 GiB,当时空闲 9.87 GiB。vLLM 0.32.0 的默认比例为 0.92,需要约 10.12 GiB,服务在加载权重前就退出。显式设置 0.35 后才进入后续的模型与缓存测试。这里的比例只适用于本次小模型实验。
1 2 | nvidia-smi --query-gpu=index,name,memory.total,memory.used,memory.free --format=csv nvidia-smi --query-compute-apps=pid,process_name,used_gpu_memory --format=csv |
权重装不下时,先估权重和加载峰值
以十进制 7B 参数、每参数 2 字节为例,裸 FP16 权重约 14 GB(约 13.0 GiB)。还要加上 CUDA 上下文、运行时张量、工作区和缓存。量化权重也会附带分组尺度等数据,并依赖 GPU 和内核支持。多卡张量并行会分摊部分内存,同时引入通信开销,按当前模型和后端核对分片布局。
如果报错栈落在权重加载,缩短上下文不会缩小那批权重。先用小模型验证服务能启动,再选择合适的权重精度、模型大小或设备。核对真实可用显存,避免把宿主机上看不到的容器进程或其他用户任务漏掉。
权重已装下时,看 KV Cache 能容纳多少 token
对普通 decoder-only Transformer 的全注意力层,一个 token 的 KV Cache 粗算为 2 × 层数 × KV heads × head_dim × 每元素字节数。例如 32 层、8 个 KV heads、head_dim 128、FP16 时约 128 KiB/token,一条 8,192 token 的序列约 1 GiB。估算应计入输入和生成;GQA 用 KV heads,混合注意力、滑动窗口、MLA、分块及多卡布局则以实际实现为准。
–max-model-len 限制单条序列的总长度;–gpu-memory-utilization 是执行器的显存预算比例。降低前者可以让较小的缓存池通过上下文容量检查,但缓存池通常仍按预算分配。使用自动分配时,提高预算比例可增加缓存,也会减少显存余量。若安装版本支持 –kv-cache-memory-bytes,它可直接指定每 GPU 的 KV Cache 字节预算,覆盖自动缓存大小估算;启动时的可用显存检查仍可能使用比例预算。
单卡复现:用同一个模型分离缓存容量与请求行为
实测环境是 WSL2、RTX 2080 Ti(11 GiB)、vLLM 0.32.0、PyTorch 2.13.0+cu130。使用同一版 Qwen2.5-0.5B-Instruct、FP16 和 16 MiB 的显式 KV Cache 预算,两个配置都成功加载权重,日志记录的加载显存都是 0.93 GiB。
- –max-model-len 512:启动成功,日志报告缓存池容量 1,360 token,/v1/completions 返回 HTTP 200。
- –max-model-len 2048:权重加载后在容量检查时报错,日志估算可接受的最大序列长度为 1,344。缓存池总 token 和启动检查估计值是两个日志字段,部署时以该版本校验结果为准。错误将 GiB 四舍五入,两个数字即使显示相同,也应看完整错误和估算长度。

这组测试覆盖了启动预算检查、模型加载、缓存容量校验和一次请求。下面的并发步骤供读者在自己的请求分布上验证。
下面的命令固定公开模型 revision,服务只监听本机。显式设置 16 MiB 缓存,是为了在同一张卡上复现容量边界。对照时先停止第一个进程,只把 –max-model-len 改成 2048,再启动一次。部署时按业务上下文和硬件重新选预算与后端。
1 2 3 4 5 6 7 8 9 | MODEL=Qwen/Qwen2.5-0.5B-Instruct REV=7ae557604adf67be50417f59c2c2f167def9a775 vllm serve "$MODEL" --revision "$REV" --dtype half --enforce-eager \ --host 127.0.0.1 --port 18737 --max-num-seqs 1 \ --enable-chunked-prefill --max-num-batched-tokens 512 \ --block-size 16 --kv-cache-memory-bytes 16777216 \ --attention-backend TRITON_ATTN \ --max-model-len 512 --gpu-memory-utilization 0.35 |
打开另一个终端,发一个固定请求。记录 HTTP 结果、启动日志中的权重显存和缓存容量。测试较长上下文时保留同一模型、精度和后端,以日志是否通过容量校验为判断依据。
1 2 3 | curl -fsS http://127.0.0.1:18737/v1/completions \ -H 'Content-Type: application/json' \ -d '{"model":"Qwen/Qwen2.5-0.5B-Instruct","prompt":"1+1=","max_tokens":4,"temperature":0}' |
只有并发起来才出问题时,固定请求长度做阶梯测试
- 选一组脱敏输入,记录 tokenizer 计数和输出上限。从单个请求开始,固定模型、后端和输入长度。
- 在测试实例逐步增加客户端并发。每一级同时看成功率、P95 首 token 延迟、排队、缓存占用和抢占。服务刚起来就出错,先回到启动日志。
- 用 curl -fsS http://127.0.0.1:18737/metrics 查看当前版本实际暴露的指标。抢占增加同时延迟变差时,先降低并发或缩短请求,再检查 KV Cache 预算。–max-num-seqs 限制调度序列数量,–max-num-batched-tokens 限制每次调度处理的 token 数,分别改变才能知道哪项生效。
- 如果请求在 API 层因总长度被拒绝,先计算模板和生成预算。如果出现真正的 CUDA OOM,保留最早堆栈,核对同卡进程、运行时峰值和失败时的批次大小。
选型阶段可回看SGLang 和 vLLM 的对比。正式压测时固定输入、输出和并发,再比较不同预算下的延迟与吞吐。
参考资料:vLLM 显存配置、vLLM serve 参数、vLLM 监控指标。
汤不热吧