vLLM 启动时出现下面这类报错,通常不是模型权重下载坏了:
|
|
它表示:模型权重、运行时开销占掉显存后,剩下的空间不足以为你设定的最大上下文长度准备 KV Cache。最直接的处理顺序是:先降低 --max-model-len,再检查并发,最后才考虑提高显存利用率、量化 KV Cache 或扩容。
不要一上来就把 --gpu-memory-utilization 拉到 1。那样可能让 vLLM 通过初始化,却在真实请求、CUDA graph 或其他进程抢显存时更容易 OOM。
先理解:KV Cache 为什么会占满显存
模型权重决定“模型能不能加载”,KV Cache 决定“模型能同时记住多少 token、服务多少请求”。每生成或读取一个 token,注意力层都需要保存对应的 Key/Value 状态。
因此 KV Cache 占用会随下面几项增加:
max_model_len:允许的最大上下文越长,缓存需求越大。- 并发请求数:每个并发会占用自己的上下文空间。
- 模型结构:层数、KV head 数、hidden size 和数据类型都会影响缓存大小。
- 缓存精度:默认通常跟随模型数据类型;FP8 等低精度缓存可节省空间,但有兼容性和质量边界。
最容易忽略的是并发。即使每个请求只给 8K 上下文,多个请求同时运行时,KV Cache 的总需求仍会快速增长。
先从日志判断是哪一种问题
启动日志通常会给出三个关键信息:
- 模型声明或你配置的最大序列长度。
- KV Cache 需要多少 GiB。
- 当前可用于 KV Cache 的实际空间,以及 vLLM 估算出的可行最大长度。
如果日志说“最大长度 32768 需要 10GiB KV Cache,但当前只有 4GiB”,不要先纠结模型是否支持 32K。模型支持不代表你的这张卡能在当前配置下服务 32K 请求。
先记下日志提供的估算最大长度,把第一次测试设置在它的 60% 到 80% 左右,再逐步提高。
第一优先级:降低 --max-model-len
这是成功率最高、影响最可预测的调整。比如模型原生支持 32K,你的实际业务只处理普通对话、短代码和小文档,可以先从 8K 开始:
|
|
如果仍然不足,再试:
|
|
不要把模型卡标称的 128K、256K 或更长上下文直接写进服务参数。对于单卡部署,真实可用长度取决于权重、显存、并发与缓存精度。
一个实用的起点:
| 显存与用途 | 先试的上下文长度 |
|---|---|
| 12GB–16GB 单卡、8B 级模型 | 4096 或 8192 |
| 24GB 单卡、7B–14B 模型 | 8192 或 16384 |
| 24GB 单卡、30B 级量化模型 | 先从 4096 开始 |
| 长文档/RAG/多用户服务 | 根据日志估算与压测决定,不要照表硬套 |
这些数值是排障起点,不是显存承诺。不同模型的 KV Cache 大小可能相差很大。
第二优先级:限制并发和批处理
服务能启动,不代表高并发时一定稳定。先把并发控制在小值:
|
|
--max-num-seqs 限制一次迭代可处理的序列数。数值越大,吞吐可能更高,但 KV Cache 和调度压力也更大。
如果请求里经常带长 prompt,还要关注 --max-num-batched-tokens。它决定一次调度最多处理多少 token;过高可能让 prefill 阶段瞬间占用更多资源。排障时先保持保守,确认稳定后再逐步放大。
建议按这个顺序压测:
- 单请求、4K 上下文,确认服务稳定。
- 单请求、8K 上下文,观察首 token 延迟与显存。
- 两个并发请求,确认不会 OOM。
- 再逐步增加
max_num_seqs或批处理 token 数。
不要同时调整上下文、并发、batch 和模型量化,否则很难定位哪一个参数造成爆显存。
第三优先级:合理设置 --gpu-memory-utilization
--gpu-memory-utilization 指定当前 vLLM 实例可使用的 GPU 显存比例,取值在 0 到 1 之间。vLLM 会根据这部分空间规划权重、运行时和 KV Cache。
例如:
|
|
当报错明确说“可用 KV Cache 太少”且 GPU 上没有其他进程时,可以小步提高,例如从 0.90 试到 0.92 或 0.94。每次只加一点,并用真实请求测试。
以下情况不建议硬拉高:
- 同一张卡上还跑着桌面程序、另一个推理服务或训练任务。
- 启动后可用显存会波动。
- 使用 CUDA graph、视觉模型或高并发 prefill 时容易出现峰值。
- 你已经遇到运行过程中的 CUDA OOM,而不只是初始化检查失败。
如果多实例共享同一张 GPU,应为每个实例留出明确预算,而不是都设置成 0.9。
FP8 KV Cache:省显存前先确认模型和版本
vLLM 支持通过 --kv-cache-dtype 改变缓存数据类型。CUDA 11.8+ 环境可使用 FP8 相关选项,例如:
|
|
FP8 KV Cache 可以显著减少缓存占用,但不要把它当成完全无代价的开关:
- 先确认当前 vLLM、CUDA 与硬件支持该数据类型。
- 模型检查点是否提供合适的 KV scale,会影响结果;缺失时需要谨慎评估。
- 长上下文、复杂推理、工具调用和结构化输出都应与默认缓存精度做对照测试。
- 如果只是为了让一个不合适的大模型塞进显卡,先降低上下文或换更合适模型通常更稳。
所以更推荐的顺序是:先用默认精度把 4K/8K 跑稳,再测试 FP8 是否真的能让业务获得所需的上下文或并发。
手动指定 KV Cache 大小时要知道优先级
较新的 vLLM 提供 kv_cache_memory_bytes,可按每张 GPU 的字节数精确指定 KV Cache 大小。它适合多服务共卡、需要固定显存预算的场景。
要注意:显式设置缓存字节数时,会覆盖通过 gpu_memory_utilization 自动推算缓存大小的行为。不要同时把两者当作叠加增益;先决定是采用“比例预算”,还是采用“固定缓存预算”。
固定预算适合运维约束明确的服务器,单机排障则通常先用 --gpu-memory-utilization 和 --max-model-len 更直观。
CPU offload 能解决什么,不能解决什么
新版 vLLM 支持把一部分 KV Cache offload 到 CPU,或结合 LMCache 等后端处理缓存层。这可以在 GPU 缓存不够时扩展容量,但代价是 PCIe/内存传输和延迟。
它更适合:
- 偶尔出现超长上下文请求;
- 业务能接受更高首 token 延迟;
- 有充足系统内存,并已做真实压测;
- 想为长 prompt 做降级路径,而不是追求最高吞吐。
它不适合拿来掩盖“模型权重本来就放不下”的问题,也不能替代对并发和上下文上限的管理。若所有请求都频繁换出换入,吞吐往往会明显下降。
一个可复制的排障模板
先从保守配置启动:
|
|
确认能稳定处理单请求后,按下面顺序调整:
|
|
每轮记录四项数据:可用 KV Cache token 数、首 token 延迟、生成 tokens/s、峰值显存。这样才能判断某项优化是在提升吞吐,还是只是把 OOM 推迟到更高并发时发生。
常见误区
误区一:模型支持长上下文,所以服务必须开满
模型能力上限和你的显卡可服务上限是两回事。服务端的 max_model_len 应按硬件和业务上限设置。
误区二:把 gpu_memory_utilization 设成 1 就能解决
这会减少安全余量,不能减少 KV Cache 本身的需求。运行时峰值反而更容易触发 CUDA OOM。
误区三:只降低权重量化,不管并发
权重量化能腾出显存,但并发与上下文仍会让 KV Cache 迅速膨胀。服务配置必须同时限制长度和请求数量。
误区四:CPU offload 一定比拒绝长请求好
如果延迟敏感,频繁 offload 可能比明确把长请求路由到更大显存节点更差。先定义业务的延迟和成本边界。
总结
vLLM KV Cache 内存不够,最可靠的处理优先级是:
|
|
先让一个短上下文、低并发配置稳定工作,再按业务数据逐步扩大。只要把模型权重、KV Cache、并发和真实上下文长度分开看,大多数“内存不够”问题都能更快定位。
参考: