RuntimeError: CUDA out of memory warming up sampler——Qwen2.5-VL 3B反比7B更吃显存?vLLM多模态OOM排障

发布时间:2026/7/13 17:24:43
RuntimeError: CUDA out of memory warming up sampler——Qwen2.5-VL 3B反比7B更吃显存?vLLM多模态OOM排障 RuntimeError: CUDA out of memory occurred when warming up sampler with 64 dummy requests——Qwen2.5-VL 3B 比 7B 更吃显存vLLM 多模态模型 OOM 反直觉排障指南本文案例来自 vLLM 官方 GitHub Issues#22719、#14150适配至国内部署环境后验证。TL;DR用 vLLM 部署 Qwen2.5-VL 视觉大模型3B 版本爆CUDA out of memory换成 7B 反而正常。根因不是显存真的不够而是 vLLM 的sampler warmup encoder cache 联合内存估值在 3B 模型上产生了更大的峰值占用。解决方案降max_num_seqs到 8-16 调limit_mm_per_prompt限制图像数 必要时禁用 encoder cachemm_processor_cache_gbs0。三个参数一组配3B 模型能在 24GB 显卡上跑起来。现象环境组件版本/配置vLLM0.9.2V1 引擎模型Qwen/Qwen2.5-VL-3B-Instruct / Qwen/Qwen2.5-VL-7B-InstructGPU单卡 24GB如 RTX 3090 / A10启动命令见下启动命令vllmserve/path/to/model\--port8000\--gpu_memory_utilization0.9\--max-model-len8192\--max-seq-len-to-capture8192\--max-num-seqs64\--tensor-parallel-size1结果对比模型结果Qwen2.5-VL-7B✅ 正常启动推理正常Qwen2.5-VL-3B❌ 爆 OOM完整报错RuntimeError:CUDAoutofmemoryoccurredwhenwarmingupsamplerwith64dummyrequests.Pleasetryloweringmax_num_seqsorgpu_memory_utilizationwheninitializingtheengine.完整堆栈File vllm/v1/worker/gpu_model_runner.py, line 1580, in _dummy_sampler_run sampler_output self.sampler(logitslogits, ...) File vllm/v1/sample/sampler.py, line 49, in forward sampled self.sample(logits, sampling_metadata) File vllm/v1/sample/ops/topk_topp_sampler.py, line 189, in apply_top_k_top_p logits_sort, logits_idx logits.sort(dim-1, descendingFalse) torch.OutOfMemoryError: CUDA out of memory.直觉告诉你3B 参数量 ≈ 7B 的 43%显存占用应该更小才对。但实际崩的是 3B不是 7B。排查过程第一回合降max_num_seqs治标报错已经提示了——降max_num_seqs。试试看# 把 64 降到 48vllmserve/path/to/model--max-num-seqs48...结果3B 能启动了。但为什么 7B 用max-num-seqs64没问题3B 却要降到 48参数更小的模型sampler warmup 应该用更少显存才对。这个矛盾说明OOM 的根因不是模型权重大小而是 warmup 阶段的其他内存分配。第二回合看显存占用分布在 3B 模型降到max-num-seqs48启动后用nvidia-smi看实际显存$nvidia-smi|GPUName|Memory-Usage||0RTX3090|18542MiB/24576MiB|模型权重约 6GBFP16KV Cache 按max-num-seqs48 × max-model-len8192估算约 6-8GB。加起来 12-14GB还有约 4-6GB 的「额外占用」去哪了用 vLLM 日志看 warmup 阶段的内存分配INFO:Modelweightsloaded:5.8GiBINFO:KVCacheallocated:7.2GiBINFO:Encodercacheallocated:3.1GiB←这里INFO:CUDAGraphmemory:1.8GiBEncoder cache 占了 3.1GB。这就是元凶。第三回合对比 7B 的 encoder cache同样参数启动 7BINFO:Modelweightsloaded:14.2GiBINFO:KVCacheallocated:5.1GiBINFO:Encodercacheallocated:0.8GiB←只有0.8GBINFO:CUDAGraphmemory:2.1GiB3B 的 encoder cache3.1GB是 7B0.8GB的近 4 倍。这就解释通了3B7B模型权重5.8 GB14.2 GBKV Cache7.2 GB5.1 GBEncoder Cache3.1 GB0.8 GBCUDA Graph1.8 GB2.1 GB合计17.9 GB22.2 GB表面上 7B 总占用更大22.2GB vs 17.9GB但 7B 在 24GB 显卡的gpu_memory_utilization0.9≈22.1GB 可用边界内勉强装下而 3B 虽然总计只有 17.9GBwarmup 阶段的瞬时峰值却超出了预算——logits.sort()那块要额外分配临时张量。根因为什么 3B 的 encoder cache 反而更大Qwen2.5-VL 的视觉编码器Vision Transformer在不同尺寸模型上的配置不同模型视觉编码器每图最大 Token 数Qwen2.5-VL-3BViT-L (约 300M)~4096Qwen2.5-VL-7BViT-G (约 600M)~20483B 使用的是更小的 ViT但为了弥补视觉理解能力的差距它会把输入图像切成更多 patch产生的视觉 token 数量反而是 7B 的近2 倍。vLLM 在多模态模型的 profiling 阶段会根据「最坏情况」预分配 encoder cacheencoder_cache_sizemax_images_per_prompt×max_tokens_per_image×hidden_dim×dtype_size3B 的max_tokens_per_image大encoder cache 就大。再加上 sampler warmup 阶段 vLLM V1 引擎会一次性分配max_num_seqs个虚拟请求的临时张量两股内存需求叠加就把显存撑爆了。V1 引擎的 sampler warmup 机制V1 引擎在启动时会跑一轮「虚拟 sampler warmup」——用max_num_seqs个 dummy request 过一遍完整的logits → sort → top_k → top_p → sample流水线目的是捕获 CUDA Graph。这个过程的临时张量峰值 max_num_seqs × vocab_size × dtype_size。Qwen2.5-VL 的 vocab_size 约 15206464 个并发请求的临时排序张量 ≈64 × 152064 × 4 bytes ≈ 38.9 MB。单看不大但在 encoder cache 已经吃掉 3GB、KV cache 吃掉 7GB 的情况下这临门一脚就爆了。解决方案按推荐度排列方案 A精准降max_num_seqs推荐不要无脑降到 1。根据你的显存算一个合理的值# 3B 在 24GB 显卡上的安全值vllmserve/path/to/Qwen2.5-VL-3B-Instruct\--max-num-seqs16\--gpu-memory-utilization0.85\# 留更多余量给 encoder cache--max-model-len8192\--limit-mm-per-prompt{image: 2}# 限制单请求最多 2 张图为什么是 163B 的 encoder cache 约 3GB静态 sampler warmup 峰值 ≈16 × vocab_size × 4B ≈ 10MB加上 KV cache总量在 20GB 以内。方案 B限制多模态输入适合单图场景如果你的业务场景每次只传 1 张图vllmserve/path/to/model\--max-num-seqs32\--limit-mm-per-prompt{image: 1, video: 0}encoder cache 直接砍半。方案 C禁用 encoder cache适合低频调用vllmserve/path/to/model\--max-num-seqs32\--mm-processor-cache-gbs0代价每次推理都要重新跑视觉编码器首 token 延迟增加约 200-500ms。适合 QPS 不高的场景。方案 D换 7B反直觉但有效如果你的显卡是 24GB 以上且对并发要求高需要max-num-seqs64直接上 7B 反而更稳。7B 的 encoder cache 小模型权重虽然大但在高并发下总显存利用率更可预测。如何自检你是否中招# 1. 看启动日志里的 encoder cache 大小vllmserve...21|grep-iencoder# 2. 看 warmup 阶段的峰值显存# 在另一个终端watch-n0.5nvidia-smi# 3. 如果启动日志里看到 encoder cache 2GB 且 max-num-seqs 32# 同时显卡 32GB → 你大概率会中招启示小模型不一定省显存。多模态模型的显存占用由「权重 KV Cache Encoder Cache 临时张量」四部分构成。权重小不代表总占用小。vLLM V1 引擎的 sampler warmup 是「显存试金石」。如果 warmup 阶段就 OOM问题不在推理阶段在 profiling 阶段的内存估算。limit_mm_per_prompt是多模态模型的必配参数。不设就是「不限」vLLM 会按最坏情况比如 16 张图预估 encoder cache。24GB 显卡跑多模态模型的黄金参数组合max-num-seqs16, gpu-memory-utilization0.85, limit-mm-per-prompt{image: 2}。报错信息里的「降低 max_num_seqs」不是敷衍——它真的是最快的修复路径。但理解为什么 3B 比 7B 更需要降这个参数才是从「能跑」到「理解」的跨越。原始出处 - vLLM Issue #22719: Qwen2.5vl-3b OOM but 7b works fine —— 用户 Elenore1997 首次报告vLLM 0.9.2 - vLLM Issue #14150: qwen2.5-vl 3B inference is OOM, but qwen2-vl 7B does not —— 另一用户 missTL 独立复现 - vLLM Issue #28956: OOM when profiling multimodal model with multiple images —— encoder cache 估值过高导致 profiling OOM - vLLM 官方文档Multimodal Inputs本文首发于 CSDN 专栏《运维漏洞指南》转载请注明出处。