vLLM中continuous batching机制在长上下文高并发场景下的显存分配原理

文章导读
continuous batching 在 vLLM 里的显存分配方式,和传统按请求预留显存的做法完全不同。它把 KV cache 所在的显存区域预先切成固定大小的 block,调度器根据每个序列当前实际占用的 token 数动态分配和回收 block。长上下文会放大单个序列的显存需求,高并发会让总需求在短时间内剧烈波动,因此理解这个机制后,重点就不是“调大某个参数”,而是学会观察显存池是否被耗尽
📋 目录
  1. 连续批处理的显存分配节奏
  2. 长上下文高并发下,显存峰值难算在哪
  3. 需要关注的参数和观察点
  4. 推荐的调整路径
  5. 常见问题
A A

continuous batching 在 vLLM 里的显存分配方式,和传统按请求预留显存的做法完全不同。它把 KV cache 所在的显存区域预先切成固定大小的 block,调度器根据每个序列当前实际占用的 token 数动态分配和回收 block。长上下文会放大单个序列的显存需求,高并发会让总需求在短时间内剧烈波动,因此理解这个机制后,重点就不是“调大某个参数”,而是学会观察显存池是否被耗尽、是否触发 preemption 或 swap。

vLLM 的 continuous batching 核心是“预切块、动态分配、用后释放”。显存不按请求数预留,而按活跃 token 总量动态调配。长上下文放大单序列需求,高并发使总需求波动更剧烈。建议优先控制并发上限、单请求 token 上限和显存利用率,并监控是否触发 preemption 或 swap;没有万能参数,需要结合负载和硬件逐步验证。

连续批处理的显存分配节奏

传统静态 batching 会为每个请求预留一段能容纳最大生成长度的 KV cache,显存峰值容易估算但浪费严重。continuous batching 在每一步选择一批序列执行,只给每个序列分配当前已经生成 token 所需的 block。序列结束(生成 EOS 或到达 max_tokens)后,这些 block 立即释放回池子。因此显存占用是随生成进度动态变化的。

实际分配时,vLLM 会在启动阶段把一部分显存用于模型权重、激活和 CUDA context,剩余部分用于 KV cache block 池。gpu_memory_utilization 控制的是可用显存比例,真正的 KV cache 池大小还要排除其他开销。block 大小(vLLM 中常见参数名是 block-size 或 token-block-size)决定分配粒度。

长上下文高并发下,显存峰值难算在哪

长上下文意味着每个请求的 KV cache 可能从几千 token 到几十万 token,不同请求的 block 数量差异非常大。高并发下,这些请求的生成进度各不相同,显存需求在时间轴上并不平滑:一批长上下文请求同时到达时,KV cache 池可能在几步内耗尽;如果请求很快结束,又会快速释放。因此“平均每个请求占多少显存”这个估算方式在长上下文高并发下基本失效。

vLLM中continuous batching机制在长上下文高并发场景下的显存分配原理

更隐蔽的问题是 preemption。当 KV cache 池不够时,vLLM 会把某些序列的 block 换出到 CPU,或直接丢弃并等后续重算。长上下文场景下,换出后的恢复需要重新读取大量 block,哪怕只有一个请求被频繁抢占,也可能导致整体吞吐明显下降。如果日志中出现大量 swap 或 preempt 事件,就需要考虑调整限制了。

需要关注的参数和观察点

不同版本的 vLLM 参数名可能略有差异,建议先用 vllm serve `--help` 确认。以下参数在长上下文高并发场景中最值得先调:

  • `--max-num-seqs`:限制同时批处理的序列数量。长上下文场景下建议调低,因为每个序列在生成后期占用的 block 数很多。
  • `--max-num-batched-tokens`:每次 forward 能够处理的 token 总数上限。这个值直接影响单次迭代的计算压力和 KV cache 峰值占用。
  • `--gpu-memory-utilization`:控制可用于 KV cache 池的比例。调高可能让池变大,但也可能把模型和激活所需的显存空间挤掉,造成 OOM;调低会更容易触发 preemption。
  • `--max-model-len`:限制支持的单序列最大长度。过高的值会为每个序列预留更大的可能 block 范围,过低的设置可能导致长请求被截断。
场景特征建议初始调整方向观察指标
长上下文多、并发较低降低 max-num-seqs,适当调高 gpu-memory-utilizationKV cache 使用率、是否 OOM
高并发、上下文普遍中等控制 max-num-seqs,逐步提高 max-num-batched-tokenspreemption 事件、平均吞吐
上下文长度差异很大保持较小的 block size,留意碎片情况单步调度延迟、显存分配耗时

启动时可以先用一个保守的配置开始测试:

vllm serve /path/to/model \
  `--max-model-len` 32768 \
  `--max-num-seqs` 16 \
  `--max-num-batched-tokens` 8192 \
  `--gpu-memory-utilization` 0.90

注意:这里的数值只是起点,不代表通用最优值。改参数时一次只改一个变量,否则无法判断是哪个改动造成了影响。

vLLM中continuous batching机制在长上下文高并发场景下的显存分配原理

推荐的调整路径

  1. 先用固定的长上下文请求集(例如 20 个长度为 20000 token 的输入)做一次小规模压测,记录 KV cache 使用峰值和是否出现 preemption。
  2. 如果 KV cache 使用率稳定在 90% 以上且没有 preemption,可以尝试逐步提高 max-num-seqs,直到出现 preemption 或换页,再回退一点。
  3. 如果频繁 preemption,优先降低 max-num-seqs,或者调高 gpu-memory-utilization;如果调高后 OOM,则说明总显存已经接近上限,需要降低单请求长度或改用更大显存的设备。
  4. 最后再考虑调整 block size。block 太小会放大管理开销,太大则可能造成尾部浪费。默认值通常不需要改,除非在日志中观察到大量分配碎片。

验证时不要只看显存总量。最有效的信号是 vLLM 日志中的 preemption 和 swap 事件。如果这类事件频繁出现,说明当前并发和上下文长度已经超出了 KV cache 池的承受能力,继续调高并发只会让吞吐进一步恶化。

常见问题

为什么增加了 gpu-memory-utilization 反而 OOM?

因为模型权重和激活也在这块显存里。调高比例虽然给了 KV cache 更多空间,但可能挤压了其他运行需求的实际空间。需要结合模型大小和实际 batch 中的激活峰值来判断,不能只看 KV cache 使用率。

preemption 和 swap 是同一件事吗?

不完全相同。swap 是一种 preemption 处理方式:把 block 换到 CPU 内存,之后需要再换回 GPU。还有另一种方式是直接丢弃被抢占序列的 KV cache,后续重算。长上下文场景下,swap 恢复慢,重算则浪费计算资源,两者都会拖慢整体响应。