用vLLM部署大模型遇到CUDA out of memory基本是每个入坑的人都绕不过去的一道坎。我印象很深第一次在A100上跑一个7B模型启动参数照着网上的示例抄结果请求一进来直接OOM我当时第一反应是“这卡是不是假的”。后来认真看了显存日志、翻了一遍vLLM的KV Cache分配机制才算彻底搞明白这个框架的“性格”它跟普通PyTorch程序不一样不是用到多少显存才占多少而是启动时就把显存预算几乎全包圆了。如果你恰好也在这个问题上耗过半天或者正在怀疑“为什么同样的模型别人能跑我不能”这篇文章应该能直接帮到你。这篇文章会从vLLM的显存管理原理、OOM报错怎么定位、参数怎么调、多卡与多模型部署怎么做几个角度把问题讲透并附上可以直接抄的配置方案。适合刚用vLLM上线推理服务、以及准备做单机多卡部署但还没完全理清资源规划的开发者。1. 先搞清楚vLLM为什么一上来就“吃满”显存1.1 它的默认行为KV Cache 预分配vLLM和普通推理脚本最大的区别在于它默认会把显卡显存的90%都“圈”进自己的池子然后在这个池子里做精细管理。这90%里不仅包括模型权重还包括给KV Cache预留的大头空间。为什么要这么做因为vLLM的核心卖点是PagedAttention它借鉴了操作系统虚拟内存的分页思想把KV Cache切成固定大小的物理块按需分配、按页管理。这样多个请求并发时显存不用为每一个请求单独预留完整空间而是动态共享大幅提高吞吐。但代价就是框架启动时会预先申请一大块显存作为可用池如果模型权重较大、并发参数又拉得比较高预留的这部分就可能直接撑爆物理显存报出torch.OutOfMemoryError。很多人在这一步就开始慌以为是模型太大其实大多数情况只是“预算超支”。1.2 显存预算到底花在哪我们把一次vLLM推理过程中显存的去向拆开看基本是这几块模型权重加载进来的参数FP16格式下每10亿参数约占2GB。KV Cache推理过程中保存的历史token注意力缓存跟层数、KV heads数量、序列长度、并发数直接相关。激活值前向计算中产生的中间张量大batch、长序列时会显著增加。CUDA context与PyTorch预留缓存框架自身占用的基础空间一般百MB到几个GB不等。vLLM启动时那行日志会打印类似GPU KV cache size: 55.61 GB这样的信息它指的就是KV Cache部分。你把这个值和模型权重加起来基本就是这张卡被框架“锁定”的总量。要是申请量已经大于显存总量启动阶段就会OOM要是启动阶段正常、请求时OOM往往是运行期KV Cache扩展到了峰值加上其他临时张量一起越过了物理上限。理解了这套预算逻辑后续优化方向就很明确了要么缩模型权重要么缩KV Cache这块大头要么换更大显存或者多卡分摊。2. 看到CUDA OOM先定位是哪种“爆法”2.1 怎么读vLLM和PyTorch的报错信息CUDA OOM的报错信息分两种常见形态处理思路完全不同。第一种是纯vLLM层面的报错常出现在启动或调用接口时文本类似“not enough memory”或“Unable to allocate KV cache”这种基本是启动参数给定的模型权重加KV Cache预算已经超过显存总量需要直接改参数再重启。第二种是PyTorch抛出的torch.OutOfMemoryError: CUDA out of memory. Tried to allocate ...。注意这串文本里有个关键点它后面通常会跟“already allocated”和“reserved in total by PyTorch”。allocated是指当前真正被张量占用的量reserved是PyTorch显存池里预留的量free则是物理剩余但未被缓存池释放的量。vLLM在这种情况下的分配往往是一口气要几GB到几十GB一旦free部分不够就直接崩。如果你把日志打出来看到“Tried to allocate 30.00 GiB”这种说明有一个巨大的连续显存请求无法满足。这种大请求多数出现在prefill阶段处理用户输入长文本时因为要一次性计算整段prompt的KV Cache并写入显存。遇到这个情况降低max-model-len或者限制单请求长度是最直接的解法。2.2 用nvidia-smi看现场我每次排查OOM第一步永远是开另一个终端跑watch -n 0.5 nvidia-smi看显存变化曲线如果启动阶段显存就顶着上限、进程直接被kill说明权重加KV Cache预算超了得调小gpu-memory-utilization。如果启动时显存平稳一打并发请求显存唰一下上去然后崩掉说明KV Cache在运行期扩张太猛需要降低并发数或限制序列长度。如果显存没满但也报OOM大概率是出现了少量“显存碎片”有一块大的连续空间分配不出来。这个时候调小max-num-seqs、或者换成更新版本的vLLM新版对碎片优化更积极比单纯降预算更有效。nvidia-smi只能看总量趋势它不会告诉你是哪块占的但结合请求压测的时机能帮你快速缩小范围。2.3 从启动日志里找KV Cache的实际分配量还有一个很实用的小习惯把vLLM启动时的完整日志保留下来。日志里会打印模型config、Maximum concurrency for ... tokens、以及KV Cache的分配大小。比如我用Qwen2.5-14B在A100上启动日志里直接能看到类似“GPU KV cache size: 45.00 GiB”和“max_num_seqs”的相关计算值。根据这个值你可以倒推当前配置下KV Cache还能支持多大并发、多长序列。比如KV Cache是45GB单序列8K长度大约占用几个GB那么并发数能到多少一目了然。别等到OOM了才回头看日志启动日志本身就是最好的“显存预算表”。3. 调参解决OOM的完整实操3.1 先说结论常用的参数组合表如果你现在急着上线不想深究原理下面这张表可以直接作为调整起点。注意这只是“能跑”的起点不是最优配置实际要按模型规格和业务并发来微调。参数作用推荐值单卡80GB14B模型推荐值单卡80GB70B量化模型--gpu-memory-utilization框架可使用的显存比例0.85 ~ 0.900.88 ~ 0.92--max-model-len最长序列长度4096 ~ 81922048 ~ 4096--max-num-seqs最大并发序列数32 ~ 6416 ~ 32--dtype权重精度bfloat16float16或量化权重--quantization量化方式有需要才引awq / gptq--swap-spaceCPU换页空间GB4 ~ 88 ~ 16--cpu-offload-gb把部分层权重放内存0默认视内存余量调整这张表的核心逻辑是显存总量一定模型权重大小一定剩下的空间就用来养KV Cache。你要么降权重要么降并发要么降长度总要有一边先妥协。3.2 gpu-memory-utilization怎么算、怎么调gpu-memory-utilization是vLLM最有存在感的参数默认值是0.90。它表示vLLM最多可以占用显卡显存的90%。为什么不是100%因为CUDA context本身要占空间激活值和临时buffer也要空间全占满会导致运行时崩溃。合理范围一般在0.80到0.95之间不建议低于0.75否则KV Cache太小并发吞吐损失非常明显。举个例子8GB的消费级显卡跑7B模型如果不量化权重就要占据差不多14GB直接放不下就算用INT4量化权重压到4GB左右留给KV Cache的预算也就1~2GB这种情况下建议把gpu-memory-utilization设在0.90同时把max-model-len压到1024~2048才能勉强稳住。反过来在80GB的A100/H100上跑14B模型FP16权重约28GB设置0.90时有72GB可用KV Cache能拿接近44GB跑8K上下文、64并发都还很宽裕。这里给一个估算公式可用KV Cache ≈ 显存总量 × 0.90 − 模型权重大小 − 预留buffer约2~4GB。如果算出来是负数说明权重太大要不换更大的卡要不量化要不走多卡。3.3 max-model-len和max-num-seqs是OOM重灾区这两个参数直接影响KV Cache的峰值占总空间很多人容易忽视。单序列KV Cache大小可以用这个公式粗算KV Cache(bytes) ≈ 2 × num_layers × num_kv_heads × head_dim × seq_len × dtype_bytes拿Qwen2.5-14B为例config里num_hidden_layers48num_key_value_heads8head_dim128FP16下dtype_bytes2那么在seq_len8192时2 × 48 × 8 × 128 × 8192 × 2 ≈ 1.61 GB看起来一个序列8K才1.6GB不算大。但如果max-num-seqs设为256那就是256个请求同时跑KV Cache峰值直接到400多GB一张80GB的卡根本不可能扛住。所以OOM很多时候不是模型放不下是并发请求把KV Cache撑爆了。实际操作时建议先把max-num-seqs降下来看效果。比如从默认256降到64KV Cache峰值就压掉四分之三。如果业务确实需要高并发优先考虑多卡部署不要硬塞单卡。3.4 量化与dtype换显存的另一条路当模型权重占用太高、把KV Cache挤得没地方时量化是最有效的“拆东墙补西墙”手段。vLLM对AWQ、GPTQ、FP8等量化格式支持比较成熟用INT4量化后7B模型权重大概从14GB降到4GB左右省下来的10GB可以全部投给KV Cache等价于把并发能力翻倍。还有一个容易被忽略的选项是FP8 KV Cache也就是把KV Cache本身压缩成8位浮点显存占用直接减半。vLLM里加一个--kv-cache-dtype fp8就能启用效果比较明显但要留意模型和显卡的兼容性H100、L40S这类卡支持较好。如果什么都不想动只想稳妥那把--dtype从float16换成bfloat16也能稍微缓解一点。大部分新卡对BF16支持更好但显存占用跟FP16基本没差别别指望这一项能救OOM。3.5 swap-space、cpu-offload等兜底手段当所有参数都压到极限还是不够可以试试vLLM的“换页”机制。--swap-space参数指定了多少GB的CPU内存可以拿来作为KV Cache的溢出缓冲。也就是说当显存里的KV Cache满了paged到内存推理依然可以继续只是变慢。这个方案适合显存差一点、但机器内存很充裕的场景。个人经验是swap-space给4GB到16GB可以救急但不要指望它扛高并发一旦大量page faulttoken生成速度会是断崖式下跌。另一个选项是--cpu-offload-gb把模型的部分层权重放到CPU内存前向计算时再搬到GPU。这个方案能跑更大模型但速度损失同样很明显。我一般只在需要验证一个模型是否能配合vLLM跑通时用线上基本不会开。4. 单机多卡、多模型部署的显存规划4.1 tensor-parallel-size的作用机制单卡显存实在不够时正确的方向是上多卡而不是继续压缩参数。vLLM里对应的参数是--tensor-parallel-size它可以按张量并行方式把模型切到多张显卡上。举个例子70B模型FP16权重约140GB单张80GB肯定放不下但只要--tensor-parallel-size 2权重会被切到两张80GB卡上每卡70GB再开--tensor-parallel-size 4每卡35GB这时候KV Cache的空间就很宽裕了。需要注意多卡部署后gpu-memory-utilization建议留一点余量不要设到0.95因为张量并行在每一层前向后向计算之间都要做allreduce通信通信buffer和中间激活都会占显存。我实测下来TP2时设在0.88到0.92之间比较稳妥TP4时甚至可以试试0.93但必须观察启动日志确认没有隐性OOM风险。多卡部署还有一个好处KV Cache也会跟着权重一起切分到多张卡上。也就是说哪怕每张卡上KV Cache预算不变总量是N倍并发能力同步放大。这也是为什么对大模型推理来说“单机多卡部署”几乎成了生产环境标配。4.2 多模型同卡、多实例部署怎么分配有时候一张卡或一台机器要同时服务多个模型最常见的是两个不同尺寸的模型同时在线。这里有几种做法多实例按卡隔离假设有2张80GB卡一个14B模型用卡0另一个7B模型用卡1互不干扰。这是最省心、最推荐的做法。单卡多实例分配只有一张卡但想同时跑两个小模型可以分别启动两个vLLM实例设置不同的CUDA_VISIBLE_DEVICES和gpu-memory-utilization。比如实例A给0.55实例B给0.33加起来必须小于1且要留出少量余量给CUDA context。这种做法有个坑两个实例各自独立申请显存如果A先启动并且申请多了B可能直接起不来。所以分配比例要保守千万别卡死在临界值。单实例多模型新版vLLM支持一次启动服务多个模型底层的显存池由引擎统一调度。这种方式对显存的利用率更高但多模型之间的KV Cache、调度策略会更复杂如果版本较旧或模型差异大建议先跑通再上生产。我在多实例方案上踩过一个坑给实例A设了gpu-memory-utilization0.5实例B设了0.5结果B启动时报OOM因为CUDA context和框架自身占的空间没留够。后来调整为A0.48、B0.40总共只用了0.88左右才稳定运行。这就是典型的“账面预算”和“实际预算”之间的误差多实例部署时一定要留至少10%的缓冲。4.3 纯CPU模式和LM Studio有什么区别很多人看到vLLM的热搜词里有“纯CPU模式”和“LM Studio bionic”会以为它们是可以互相替代的部署方式。实际上两者定位完全不同。vLLM CPU模式是通过--device cpu启动的主要解决的是“没有GPU但还想验证vLLM特性”的场景比如本地测试API接口、CI流水线跑集成测试。性能上和GPU没法比实测Qwen2.5-7B在纯CPU上生成速度可能只有每秒几个token也就是勉强能用的水平。而且CPU模式也有内存压力只不过报错从CUDA OOM变成了普通的内存不足本质逻辑一样。LM Studio则是一个本地GUI推理工具它会自动做CPU与GPU的混合调度显存不够时会把层offload到内存所以用LM Studio跑同样的模型可能会“不崩”但速度同样很慢。vLLM的思路相反它更激进地占用GPU显存来保吞吐一旦预算超了就明确报错而不是悄悄降到内存去慢慢跑。所以“LM Studio能跑但vLLM却OOM”这件事不代表vLLM更差而是它把资源规划的责任交到了部署者手上。你在用vLLM时心里要有个预期它更像一台精心调过转速的发动机参数给对了才能发挥最大功率。5. 高频问题排查速查表故障现象可能原因解决思路启动即报CUDA OOM模型权重 KV Cache预算超过显存总量调小gpu-memory-utilization降低max-model-len或量化权重启动正常首个请求OOMPrefill阶段一次性分配KV Cache过大调低max-model-len限制单请求最大输入长度并发一高就OOMmax-num-seqs过大导致KV Cache峰值超限降低max-num-seqs或换多卡部署显存没满但OOM显存碎片化大块连续空间不足调低max-num-seqs升级vLLM版本或减少实例数多实例同时跑第二个起不来两个实例显存预算相加挤爆给两个实例留出10%以上的缓冲空间70B以上大模型单卡放不下权重太大使用AWQ/GPTQ量化或开启--tensor-parallel-size多卡部署压测时生成速度骤降但没崩触发了CPU swapKV Cache被paged到内存增加swap-space只会更慢建议扩容显存或降并发日志提示CPU OOM而不是GPU OOMCPU模式或offload模式下内存不足调低max-model-len和max-num-seqs或加大系统内存这张表覆盖了我自己遇到过的绝大多数情况。如果你按顺序排查完仍然OOM不妨把vLLM的启动日志和错误堆栈完整贴给模型仓库的Issue区社区回复速度一般还行。记得贴日志时带上vLLM版本号、模型路径、显卡型号和全部启动参数缺少这些信息别人很难帮到你。最后再说一个容易被忽略的细节每次调参后不要只看进程是否起来一定要压一压真实流量。有些配置启动时看着稳一旦来几个长上下文请求就崩这往往是因为KV Cache的峰值预算没算清楚。可以根据实际业务里最长的那条prompt和最大并发量用上面的公式先粗算一遍再决定参数怎么配。我个人的习惯是先在日志或/metrics接口确认KV Cache利用率长期在70%到90%之间才算把显存用到位——低了说明浪费高了说明随时有OOM风险。
阅读完成 · 觉得有帮助?