CPU offload fatal: valid unaligned SWA external load exceeds aligned pending-block bound

此报错发生在 vLLM 启用 CPU KV offload 且使用滑动窗口注意力(Sliding Window Attention, SWA)时,由于调度器中 GPU 块边界计算错误,导致合法未对齐的滑动窗口负载被误判为超过限制,引发进程 abort。优先排查 offloading schedule

快速结论:此报错发生在 vLLM 启用 CPU KV offload 且使用滑动窗口注意力(Sliding Window Attention, SWA)时,由于调度器中 GPU 块边界计算错误,导致合法未对齐的滑动窗口负载被误判为超过限制,引发进程 abort。优先排查 offloading scheduler 中 max_pending_gpu_blocks 的计算逻辑是否按 cdiv(sliding_window + gpu_block_size - 1, gpu_block_size) 更新。

适用环境:vLLM(当前主线版本,对应 PR #48959)、CPU KV offload 功能开启、使用滑动窗口注意力的模型(如 DeepSeek V4)、Python、CUDA 及依赖环境同 vLLM 要求;已在 TP=2 的 DeepSeek V4 生产环境复现。

最快修复方案:暂无官方确认的一键修复版本,但已有验证的补丁。若使用 vLLM 当前主线代码,可自行应用补丁(需适配 #48150 之后的命名变化),将 OffloadingConnectorScheduler 中滑动窗口组的 pending-GPU-block 上限修改为 cdiv(sliding_window + gpu_block_size - 1, gpu_block_size),同时保持 Mamba 组的原有单状态限制。也可等待上游 merge 后的正式发布。

注意事项:补丁需根据当前主线版本调整变量名(如 sliding_window_size_in_blocks 已被重命名为 sliding_window_size_in_chunks);Mamba 组上限不应被增量放宽,否则会破坏内存保护;该补丁仅修复 scheduler 断言,不涉及 EngineCore 退出后容器 exit code 为 0 的生命周期问题(该问题需另案处理)。

问题场景

用户在 vLLM 服务中启用 CPU KV offload(CPU KV 卸载),并运行使用滑动窗口注意力(Sliding Window Attention)的模型(如 DeepSeek V4),在串行工具调用流量下触发 EngineCore 进程 fatal abort,终端输出类似 AssertionError: num_pending_gpu_blocks <= sliding_window_size_in_blocks * block_size_factor 的断言失败。复现两例独立 TP=2 生产环境。

报错原文

vllm/distributed/kv_transfer/kv_connector/v1/offloading/scheduler.py
AssertionError:
    num_pending_gpu_blocks
    <= sliding_window_size_in_blocks * block_size_factor

原因分析

根本原因是 OffloadingConnectorScheduler.update_state_after_alloc() 中用于限制 pending GPU 块数量的边界计算过于宽松/严格(此处为“过于严格”)。该断言使用 sliding_window_size_in_blocks * block_size_factor(等价于 sliding_window_size_in_chunks * blocks_per_chunk)作为上限,但这一计算假设滑动窗口起始地址总是按 offloaded block 对齐。实际上,由于 token 缓存中的 num_skipped_tokens 是以 GPU block 粒度取整(而非 chunk 粒度),导致滑动窗口的起始位置可能是未对齐的,从而使合法负载跨越的物理 GPU 块数量最多可达到:

cdiv(sliding_window_tokens + gpu_block_size - 1, gpu_block_size)

例如 sliding_window=4096 tokens、gpu_block_size=32 tokens 时,最大跨度为 cdiv(4096+32-1, 32)=129,而原断言允许的 sliding_window_size_in_blocks * block_size_factor = ceil(4096/256)*8 = 128,导致合法情况被错误断言。

环境排查

  • 确认 vLLM 版本:是否为 #48150(重命名)之后的当前主线?
  • 确认是否启用了 CPU KV offload(--kv-transfer-config 等参数)
  • 确认模型使用滑动窗口注意力(如 DeepSeek V4 的 SlidingWindowSpec
  • 记录触发时的实际 token 数、GPU block size、offloaded block size(chunk size)
  • 检查 max_pending_gpu_blocks 的计算方式(在 scheduler 初始化或配置阶段)

解决步骤

  1. 定位到 OffloadingConnectorScheduler 类中关于滑动窗口组的上限计算逻辑(当前主线可能在 _init_cache_groups 或类似方法中)。
  2. 将滑动窗口组的上限改为准确的最大物理 GPU 块数 cdiv(sliding_window + gpu_block_size - 1, gpu_block_size)。注意在 #48150 重命名后,变量名应为 tokens_per_blockblocks_per_chunk,滑动窗口 token 数来自 kv_cache_spec.sliding_window
  3. 保留 Mamba 组的原有限制(blocks_per_chunk),不因滑动窗口组的改动而错误放宽。
  4. 若使用原 patch(ebf67492f8),需将其移植到当前主线,重命名变量并适配新结构。
  5. 重新编译/重启 vLLM 服务,确保新逻辑生效。

验证方法

使用触发该断言的实际 token 分布(例如 num_cached_tokens=98012sliding_window=4096gpu_block_size=32)运行 CPU offload 的推理请求,不再触发 AssertionError。也可运行 Issue 中提供的回归测试(2 个专注测试 + 完整 offloading_connector 测试套件),确认全部通过。

参考来源

vllm-project/vllm #48959

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 15234

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注