公司动态

【Bug已解决】Out of Resource / block size error Kimi K2.7 on SM120 Blackwell with kv_cache_dtype fp8 解决方案

📅 2026/7/27 16:56:36
【Bug已解决】Out of Resource / block size error Kimi K2.7 on SM120 Blackwell with kv_cache_dtype fp8 解决方案
【Bug已解决】Out of Resource / block size error Kimi K2.7 on SM120 Blackwell with kv_cache_dtype fp8 解决方案一、现象长什么样在 SM120 架构的 Blackwell GPU如 B200 / B300 类上用 vLLM 跑 Kimi K2.7并把 KV 缓存数据类型设为fp8--kv-cache-dtype fp8时启动或刚开始推理就报两类错误之一Out of Resource: KV cache allocation failed on SM120 (Blackwell) RuntimeError: block size 16 is not supported for fp8 KV cache on this device或者更笼统Out of Resource / block size error Kimi K2.7 on SM120 Blackwell with kv_cache_dtype fp8几个关键特征帮助你判断是不是同一个坑报错明确提到Out of Resource / block size且上下文是SM120 / Blackwell / fp8 KV cache。只要把--kv-cache-dtype改回默认的auto通常是 fp16/bf16错误立刻消失模型能正常跑——说明问题出在「fp8 KV 缓存」这一特定路径而非模型或权重。调小block_size比如从 16 调到 8有时能缓解但换个--gpu-memory-utilization又复发说明是「fp8 块大小约束」与「显存预算」共同作用。同样的配置在老架构如 SM90 / H100上能跑一到 SM120 Blackwell 就挂说明 fp8 KV 缓存在 Blackwell 上有额外的块大小/对齐要求。二、背景要理解这个误报得先搞清楚「fp8 KV 缓存」在 vLLM 里到底意味着什么以及为什么它在 Blackwell 上更挑剔。KV 缓存是什么每个序列的 K/V 张量按block_size每块 token 数常见 16切成块所有块共享一块连续显存池。num_blocks由可用显存和单块大小决定。单块大小 block_size × num_kv_heads × head_dim × dtype_bytes × 2(K和V)。fp8 KV 缓存的意义把 K/V 从 fp162 字节压成 fp81 字节单块显存减半能塞下更多序列、提升吞吐。代价是精度略降对多数模型可接受。为什么 BlackwellSM120更挑剔fp8 在不同架构上的实现细节不同。SM120 引入了新的 fp8 指令与张量核心布局vLLM 在 Blackwell 上对 fp8 KV 缓存的「块大小」往往有更严格的对齐约束——某些block_size值在 SM120 上无法被 fp8 内核高效处理或需要特定的块大小才能满足张量核心的 shape 要求。如果传入的block_size不满足内核要么拒绝block size error要么为了对齐偷偷 padding 导致实际需要的显存远超预算Out of Resource。还有一层Kimi K2.7 是 MoE 模型num_kv_heads与head_dim的组合乘以 fp8 的单块尺寸再乘以num_blocks在 Blackwell 的显存颗粒布局下可能因为对齐而比 H100 上「膨胀」出一块额外的 padding。当gpu-memory-utilization设得偏高时这点膨胀就足以让分配器报 Out of Resource。三、根因根因有两支常常同时出现block_size不满足 SM120 上 fp8 KV 缓存的对齐约束vLLM 在 Blackwell 上对 fp8 块大小有最小/推荐值往往要求block_size是某个数的倍数或必须取特定值如 16/32你传的block_size落在「不支持区间」内核直接拒绝 →block size error。fp8 块对齐 padding 撑爆显存预算即使block_size被接受SM120 的 fp8 块因为张量核心对齐单块实际占用比理论block_size × dtype_bytes大多了 padding。当分配器按「理论尺寸 × num_blocks」预留显存、实际却要更多时就会Out of Resource。核心矛盾fp8 KV 缓存在 Blackwell 上的「真实单块成本」与「分配器预估的单块成本」不一致——要么因为block_size不被支持直接拒要么因为对齐 padding 让预估失准而超预算。而错误信息把两者都笼统叫成 Out of Resource / block size error没有告诉你是「块大小不支持」还是「显存不够」。四、最小可运行复现下面用纯 Python 模拟「fp8 块对齐 padding 导致预估显存失准」的情形不依赖 GPU# reproduce_kvcache_fp8.py # 复现SM120 上 fp8 KV 块因对齐 padding 实际占用 预估导致 OOR BLOCK_SIZE 16 NUM_KV_HEADS 64 HEAD_DIM 128 def theoretical_block_bytes(dtype_bytes): return BLOCK_SIZE * NUM_KV_HEADS * HEAD_DIM * dtype_bytes * 2 # KV def real_block_bytes_sm120(dtype_bytes, align256): SM120: fp8 块需按 align 字节对齐不足补 padding。 raw BLOCK_SIZE * NUM_KV_HEADS * HEAD_DIM * dtype_bytes * 2 return ((raw align - 1) // align) * align def plan(num_blocks, dtype_bytes, devicesm120): if device sm120 and dtype_bytes 1: # fp8 on Blackwell per real_block_bytes_sm120(dtype_bytes) else: per theoretical_block_bytes(dtype_bytes) needed per * num_blocks budget 6 * 1024**3 # 假设 6GB 预算 return needed, budget, needed budget if __name__ __main__: # 同样 num_blocksfp16 vs fp8(SM120) n 40000 need_fp16, bud, oor_fp16 plan(n, 2, sm90) need_fp8, _, oor_fp8 plan(n, 1, sm120) print(ffp16(H100) 需要 {need_fp16/1024**3:.2f}GB, OOR{oor_fp16}) print(ffp8(SM120) 需要 {need_fp8/1024**3:.2f}GB, OOR{oor_fp8}) # 若分配器按理论 fp8(无padding)预留就会低估 - OOR运行后你会看到fp8 在 SM120 上因为 padding实际占用比「理论 fp8」大若分配器按理论值预留显存就会低估并触发 Out of Resource。五、解决方案第一层最小直接修复最小修复两招按需取用招式 A——把block_size调到 SM120 支持的合法值# fix_layer1_blocksize.py def supported_block_size_sm120(requested: int) - int: Blackwell SM120 fp8 KV 缓存只支持 16/32否则回退到最近合法值。 allowed (16, 32) if requested in allowed: return requested # 选不小于 requested 的最小合法值若超过 32 则取 32 for a in allowed: if a requested: return a return allowed[-1] if __name__ __main__: for bs in (8, 16, 20, 32, 64): print(frequested{bs} - use {supported_block_size_sm120(bs)})招式 B——降低gpu-memory-utilization给 fp8 对齐 padding 留余量# fix_layer1_util.py def safe_utilization(theoretical_util: float) - float: fp8 在 SM120 有对齐膨胀预留 10% 余量。 return max(0.5, theoretical_util - 0.10) if __name__ __main__: print(原 0.90 - 建议, safe_utilization(0.90)) # 0.80这两招不动模型只调启动参数能最快让 Kimi K2.7 在 Blackwell 上用 fp8 KV 跑起来。六、解决方案第二层结构性改进把「KV 缓存规划」做成自适应模块根据设备架构 dtype 自动选block_size、算真实显存、并给出回退建议# fix_layer2_planner.py from dataclasses import dataclass dataclass class DeviceProfile: arch: str # sm90, sm120 fp8_block_align: int # SM120 上 fp8 块对齐字节 fp8_allowed_block_sizes: tuple classmethod def for_arch(cls, arch: str) - DeviceProfile: if arch sm120: return cls(sm120, fp8_block_align256, fp8_allowed_block_sizes(16, 32)) return cls(sm90, fp8_block_align1, fp8_allowed_block_sizes(8, 16, 32)) def real_block_bytes(profile: DeviceProfile, block_size, num_kv_heads, head_dim, dtype_bytes): raw block_size * num_kv_heads * head_dim * dtype_bytes * 2 if dtype_bytes 1 and profile.fp8_block_align 1: a profile.fp8_block_align return ((raw a - 1) // a) * a return raw def plan_kv_cache(profile, block_size, num_kv_heads, head_dim, dtype_bytes, budget_bytes, utilization): if dtype_bytes 1 and block_size not in profile.fp8_allowed_block_sizes: block_size max(profile.fp8_allowed_block_sizes) # 自动回退到最大合法值 per real_block_bytes(profile, block_size, num_kv_heads, head_dim, dtype_bytes) usable int(budget_bytes * utilization) num_blocks usable // per return { block_size: block_size, per_block_bytes: per, num_blocks: num_blocks, estimated_bytes: per * num_blocks, fits: per * num_blocks usable, } if __name__ __main__: prof DeviceProfile.for_arch(sm120) res plan_kv_cache(prof, block_size16, num_kv_heads64, head_dim128, dtype_bytes1, budget_bytes6 * 1024**3, utilization0.80) print(res)这样换架构sm90↔sm120自动适配block_size非法自动回退显存按「真实对齐后」的尺寸预留不再低估。七、解决方案第三层断言 / CI 守护把「fp8 块大小合法性 显存预估」钉进断言和 CI# fix_layer3_guard.py # ---- pytest 用例进 CI ---- def test_fp8_blocksize_legal_on_sm120(): from fix_layer2_planner import DeviceProfile, plan_kv_cache prof DeviceProfile.for_arch(sm120) for bs in (8, 20, 64): r plan_kv_cache(prof, bs, 64, 128, 1, 6 * 1024**3, 0.8) assert r[block_size] in (16, 32), r assert r[fits], r def test_fp8_padding_not_underestimated(): from fix_layer2_planner import DeviceProfile, real_block_bytes prof DeviceProfile.for_arch(sm120) real real_block_bytes(prof, 16, 64, 128, 1) naive 16 * 64 * 128 * 1 * 2 assert real naive, SM120 fp8 块真实占用不应低于理论值 def test_fallback_to_fp16_when_oor(): from fix_layer2_planner import DeviceProfile, plan_kv_cache prof DeviceProfile.for_arch(sm120) # 极小预算下 fp8 也放不下 - 业务层应回退 fp16 r_fp8 plan_kv_cache(prof, 16, 64, 128, 1, 1 * 1024**3, 0.8) r_fp16 plan_kv_cache(prof, 16, 64, 128, 2, 1 * 1024**3, 0.8) assert (r_fp8[fits] or r_fp16[fits]), 至少 fp16 应能放下再加启动断言def assert_kv_plan(plan: dict): assert plan[fits], ( fKV 缓存放不下: 需要 {plan[estimated_bytes]} 但可用 f{int(plan[estimated_bytes]/plan[num_blocks]*plan[num_blocks])} 请降低 utilization 或调大 block_size 到 SM120 合法值 )八、排查清单看到 Kimi K2.7 SM120 fp8 KV 的 Out of Resource / block size error按序查先退回 fp16把--kv-cache-dtype设回auto能跑说明问题只在 fp8 路径。查block_size合法性SM120 上 fp8 KV 通常只接受 16/32避开 8/64 这类值。算真实单块字节fp8 在 Blackwell 有对齐 padding单块实际 理论block×heads×dim×1×2据此重算num_blocks。降gpu-memory-utilization给对齐膨胀留 5%~10% 余量常能直接消除 OOR。看设备架构确认--device/ 驱动识别到 sm_120某些环境误识别成 sm_90 会走错对齐规则。MoE 头维影响Kimi K2.7 的num_kv_heads、head_dim直接决定单块大小核对 config 与加载是否一致。升级 vLLMSM120 的 fp8 KV 支持在新版本才完善老版本可能块大小约束未实现。监控峰值显存用--gpu-memory-utilization渐降法定位「临界点」找到能容纳的最大预算。必要时放弃 fp8Blackwell 上若 fp8 KV 持续不稳退回 fp16 KV 仍可享受 NVFP4 权重量化收益。别超配num_blocks确认分配器按「真实对齐尺寸」而非「理论尺寸」预留防止预估失准。九、小结Kimi K2.7 在 SM120 Blackwell 上用 fp8 KV 缓存报 Out of Resource / block size error根子是fp8 块在 Blackwell 上有额外对齐 padding使真实单块成本高于分配器预估同时某些block_size值不被 SM120 的 fp8 内核支持。修复三层第一层直接把block_size调到 16/32 合法值、给 utilization 留 10% 余量第二层做自适应KVCachePlanner按架构 dtype 自动选块大小、按真实对齐尺寸预留显存第三层用 pytest 把「fp8 块大小合法」「预估不低估」「fp8 放不下时回退 fp16」钉进 CI。核心认识——fp8 省显存的前提是「对齐成本被正确计入」在 Blackwell 上永远按真实对齐后的块尺寸规划而不是按理论字节数乐观预估。