vLLM在生产环境高并发推理时CUDA out of memory的排查与batch策略调整

文章导读
vLLM在生产环境高并发推理时出现CUDA OOM,先不要急着调小batch,因为OOM可能来自KV cache预分配、长prompt瞬时峰值或显存碎片,不同原因对应的调整参数完全不同。建议先定位OOM出现的阶段,再决定改哪一类batch策略。
📋 目录
  1. 先定位OOM来自哪个阶段
  2. 调整vLLM的batch与显存参数
  3. 在上游控制并发与排队
  4. 验证调整是否有效
A A

vLLM在生产环境高并发推理时出现CUDA OOM,先不要急着调小batch,因为OOM可能来自KV cache预分配、长prompt瞬时峰值或显存碎片,不同原因对应的调整参数完全不同。建议先定位OOM出现的阶段,再决定改哪一类batch策略。

CUDA OOM排查重点在“瞬时还是稳态”:瞬时OOM多半由并发突发或长prompt触发,优先限制max_num_seqs和请求排队;稳态OOM则要调低gpu_memory_utilization或启用chunked prefill。调整后须用同等峰值流量验证吞吐和延迟,不能只看显存曲线。

先定位OOM来自哪个阶段

观察vLLM日志和OOM发生时机。如果请求一进来、还在prefill阶段就OOM,很可能是一次性处理了超长prompt或大量prompt;如果是在decode阶段逐渐涨满,多半是KV cache累计超过上限。用nvidia-smi `--query-gpu`=timestamp,memory.used,utilization.gpu `--format`=csv -l 1定时记录显存曲线,能区分是瞬时尖峰还是持续走高。也可以看vLLM启动时打印的KV cache分配量,比如Maximum concurrency for ...,这能直接告诉你当前显存能撑住多大的batch。

如果进程启动阶段就OOM,通常不是batch问题,而是`--gpu-memory-utilization`设置过高,给CUDA context和模型权重留下的空间不足;这种情况先调低gpu_memory_utilization,再处理batch。

调整vLLM的batch与显存参数

vLLM的batch行为主要受`--max-num-seqs``--max-model-len``--enable-chunked-prefill`影响。一个可用的启动参数示例(数值需按实际显存调整):

python -m vllm.entrypoints.openai.api_server \
    `--model` /path/to/model \
    `--max-num-seqs` 64 \
    `--max-model-len` 8192 \
    `--gpu-memory-utilization` 0.85 \
    `--enable-chunked-prefill`

`--max-num-seqs`是并发序列上限,也是动态KV cache的上限之一。把它从默认值调低,是压住OOM最直接的手段;但调太低会导致高并发时大量请求排队,响应时间变长。`--max-model-len`限制单条序列最大长度,如果业务里偶尔有超长文本,可以按实际分布设置,而不是直接给满支持长度。`--enable-chunked-prefill`会把长prefill拆成小段,让显存使用更平滑,但会增加调度开销,需要结合请求长度分布决定是否开启。

vLLM在生产环境高并发推理时CUDA out of memory的排查与batch策略调整

使用`--tensor-parallel`时,KV cache按GPU数均分,OOM发生在单卡上就要按单卡显存校核这些参数,不要只看整体容量。

在上游控制并发与排队

即使vLLM内部会排队,过大的瞬时并发也会让排队请求积压,进而延长请求持有时间,让显存压力更久。建议在API入口处增加并发限制,让进入vLLM推理引擎的请求速率更平稳。比如接入层用信号量限制同时进行的推理请求数:

import asyncio
sem = asyncio.Semaphore(64)

async def call_vllm(payload):
    async with sem:
        async with httpx.AsyncClient() as client:
            return await client.post("http://127.0.0.1:8000/v1/completions", json=payload)

这里的并发数要低于vLLM的`--max-num-seqs`,留出余量,避免请求在vLLM队列中堆积。也可以单独设置排队超时,超过就直接返回错误,避免用户反复重试放大压力。

验证调整是否有效

  • 启动后观察日志中KV cache分配量和模型加载是否正常,确认没有启动期OOM。
  • 用与生产峰值接近的流量回放或压测,看OOM是否再现;记录显存曲线、吞吐和延迟。
  • 如果OOM只在长时间运行后出现,重启后恢复,优先怀疑显存碎片;可尝试设置PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True后再观察。
  • 调整batch后若延迟下降但吞吐明显偏低,说明并发限制过严,需要按业务SLA重新调节max-num-seqs和上游并发。

不要单独看显存占用率,还要看稳定性:如果启动时显存占用很低,但请求高峰期瞬间打满,说明batch峰值管理仍需加强;如果启动后占用一直很高且无OOM,说明现有参数刚好卡在边界,需保留一定余量应对波动。