![【Bug已解决】[Bug]: Qwen3-Coder-Next-FP8 start error wifh tp=8 on H100 解决方案](http://pic.xiahunao.cn/yaotu/【Bug已解决】[Bug]: Qwen3-Coder-Next-FP8 start error wifh tp=8 on H100 解决方案)
【Bug已解决】[Bug]: Qwen3-Coder-Next-FP8 start error wifh tp8 on H100 解决方案一、现象长什么样在 H100 上加载Qwen3-Coder-Next 的 FP88 位浮点量化版本并设张量并行tp8启动时进程在启动阶段报错退出。典型日志RuntimeError: FP8 tensor parallel init failed with tp8 ValueError: fp8 scaling factors shape mismatch under tp8或者更笼统保留原始标题里的wifh拼写[Bug]: Qwen3-Coder-Next-FP8 start error wifh tp8 on H100几个特征帮你判断是不是同一个坑报错关联FP8 量化 tp8 H100且发生在启动/权重加载阶段不是 forward 中途。FP8 小 tp如 tp1/2可能正常tp8 才崩——说明是「FP8 的缩放因子scaling factors在 tp8 切分下形状对不齐」。同样的模型用 bf16非 FP8tp8 正常——说明问题在 FP8 的量化元数据而非 tp 机制本身。H100 是支持 FP8 的Hopper 架构有 fp8 张量核心所以不是「硬件不支持 fp8」而是「fp8 权重在 tp8 下的加载/切分」有问题。日志常见scaling、scale、shape mismatch、per-tensor/per-block等 FP8 相关字眼。二、背景FP8 量化除了量化权重本身还需要**缩放因子scaling factors**来描述「每个量化块如何反量化」。这些 scale 的形状和量化方式强相关per-tensor scale整个张量一个 scale最简单tp 切分无压力。per-block / per-group scale把权重按块如 128 个元素一块各给一个 scale形状是[num_blocks, ...]。FP8 常用这种以提高精度。当tp8把每层权重按隐藏维切到 8 张卡时FP8 权重的 scale 也要跟着切分若 scale 是per-tensor全局一个tp 切分时每块拿到「相同的全局 scale」或按比例缩放处理简单。若 scale 是per-block每块一个切分隐藏维时每块的内部 block 边界必须和 tp 切分边界对齐——否则某张卡拿到的「半个 block」无法用整 block 的 scale 反量化scale 形状对不齐 → 启动报错。Qwen3-Coder-Next-FP8 的问题往往在于scale 布局与 tp8 不对齐per-block scale 的 block 大小如 128在 hidden_dim 被 8 整除后每张卡内的 block 数若非整数scale 形状错。FP8 内核要求特定 tp 约束某些 FP8 GEMM 内核要求 tp_size 与 scale 的 block 维有对齐关系如 tp_size 必须整除 block 数tp8 下不满足。量化元数据被错误切分加载器在 tp 切分时把 scale 张量按隐藏维直接切片但 per-block scale 需要按「block 边界」切直接切片导致形状错。H100 fp8 内核限制H100 的 fp8 张量核心对输入形状有对齐要求如 16 的倍数tp8 下某卡的局部形状不满足。混合量化不一致不同层用了不同 fp8 scale 模式tp8 切分时某层对齐、某层不对齐启动失败。核心FP8 的 scale 元数据在 tp8 切分时其 block/对齐约束与 tp 切分边界不匹配导致 scale 形状对不齐、或内核对齐不满足启动失败。三、根因根因一句话Qwen3-Coder-Next-FP8 在 H100 上以tp8启动时FP8 权重的缩放因子per-block/per-tensor scale在按隐藏维切分到 8 张卡时其 block 边界/对齐约束与 tp 切分边界不匹配如 per-block scale 的 block 大小在 8 卡切分后非整数、或 fp8 内核要求 tp 对齐 block 数导致 scale 形状对不齐或内核对齐不满足加载/初始化失败。具体成因per-block scale 与 tp 不对齐block 大小在 hidden/8 后非整数 → scale 形状错。fp8 内核 tp 约束某些 fp8 GEMM 要求 tp_size 整除 block 数tp8 不满足。scale 被错误切片加载器按隐藏维直接切 scale未按 block 边界切 → 形状错。H100 fp8 对齐要求fp8 张量核心要求输入形状对齐如 16 倍数tp8 局部形状不满足。混合量化不一致不同层 scale 模式不同tp8 切分部分层对齐失败。缺少启动校验加载前没校验「fp8 scale 在 tp8 下是否可切分」。核心矛盾FP8 scale 的「block/对齐布局」与 tp 的「隐藏维均切」假设不一致tp8 放大了这种不一致每张卡局部形状更小、更易不对齐启动即崩。四、最小可运行复现下面用纯 Python 模拟「per-block scale 在 tp8 切分后 block 数非整数 → 形状错」# reproduce_fp8_tp.py # 复现per-block scale 的 block 数在 tp8 切分后非整数 - 形状错 def blocks_per_shard(hidden, block, tp): per hidden // tp # 每块 block 大小, 每卡应有整数个 block return per / block if __name__ __main__: # hidden8192, block128, tp8 - 每卡 1024, 1024/1288 块(整数 OK) print(tp8 对齐:, blocks_per_shard(8192, 128, 8)) # hidden8000, block128, tp8 - 每卡 1000, 1000/128 非整数 - 形状错 print(tp8 不对齐:, blocks_per_shard(8000, 128, 8)) # 7.8125运行python reproduce_fp8_tp.py会看到当 hidden 不能被block*tp整除时每卡 block 数非整数——FP8 scale 形状会错正是 tp8 启动失败的成因。五、解决方案第一层最小直接修复最小修复启动 FP8tp8 前校验「hidden_dim 能被block_size * tp_size整除」即每卡 block 数为整数不满足时要么调 tp如 tp4使对齐要么对 scale 做「按 block 边界切分」而非按隐藏维直接切片。# fix_layer1_fp8.py def fp8_tp_ok(hidden, block, tp): per hidden // tp return per % block 0 def choose_tp(hidden, block, requested_tp): if fp8_tp_ok(hidden, block, requested_tp): return requested_tp, OK # 向下找能对齐的 tp for tp in (requested_tp - 1, 4, 2, 1): if tp 1 and fp8_tp_ok(hidden, block, tp): return tp, ftp{requested_tp} 不对齐, 降到 tp{tp} 使 block 对齐 return 1, 回退到 tp1 if __name__ __main__: print(choose_tp(8000, 128, 8)) # (4, ...) 对齐这一层把「tp8 直接崩」变成「先校验 block 对齐不对齐则降到能对齐的 tp」让 FP8 模型仍能多卡跑。六、解决方案第二层结构性改进把「FP8 权重在 TP 下的 scale 切分」做成模块校验对齐、按 block 边界正确切分 scale并自动选合规 tp# fix_layer2_fp8tp.py from dataclasses import dataclass dataclass class Fp8TpPlan: hidden: int block: int def blocks_per_shard(self, tp: int) - float: return (self.hidden // tp) / self.block def validate(self, tp: int) - list: errs [] if self.hidden % tp ! 0: errs.append(fhidden {self.hidden} 不能被 tp{tp} 整除) if (self.hidden // tp) % self.block ! 0: errs.append( f每卡 block 数 {self.blocks_per_shard(tp)} 非整数, fper-block scale 无法对齐切分) return errs def pick_tp(self, requested: int) - dict: for tp in (requested, requested - 1, 4, 2, 1): if tp 1 and not self.validate(tp): return {tp: tp, ok: True, note: f选用 tp{tp}} return {tp: 1, ok: False, note: 无对齐 tp, 需调 block 或 hidden} if __name__ __main__: p Fp8TpPlan(hidden8000, block128) print(p.validate(8)) # 非整数 block 报错 print(p.pick_tp(8)) # 降到 tp4这样换模型不同 hidden/block时Fp8TpPlan自动校验对齐并选合规 tpFP8 scale 切分不再形状错。七、解决方案第三层断言 / CI 守护把「FP8 scale 在 TP 下的对齐」钉进断言和 CI# fix_layer3_guard.py # ---- pytest 用例进 CI ---- def test_block_aligned_ok(): from fix_layer2_fp8tp import Fp8TpPlan p Fp8TpPlan(8192, 128) assert p.validate(8) [] def test_block_misaligned_caught(): from fix_layer2_fp8tp import Fp8TpPlan p Fp8TpPlan(8000, 128) assert any(block in e for e in p.validate(8)) def test_pick_tp_falls_back(): from fix_layer2_fp8tp import Fp8TpPlan p Fp8TpPlan(8000, 128) r p.pick_tp(8) assert r[tp] 8 and r[ok] def test_pick_tp_aligned_kept(): from fix_layer2_fp8tp import Fp8TpPlan p Fp8TpPlan(8192, 128) r p.pick_tp(8) assert r[tp] 8再加启动断言def assert_fp8_tp_ok(plan: Fp8TpPlan, requested_tp): from fix_layer2_fp8tp import Fp8TpPlan errs plan.validate(requested_tp) assert not errs, FP8 TP 不对齐:\n \n.join(errs)八、排查清单Qwen3-Coder-Next-FP8 tp8 on H100 启动报错按序查先确认是 FP8 scale 问题FP8小 tp 正常、tp8 崩且 bf16tp8 正常 → 是 fp8 元数据。查每卡 block 数是否整数(hidden // tp) % block_size 0tp8 下更易非整数。降 tp 试tp8 不对齐就试 tp4/tp2能启动说明是 block 对齐问题。按 block 边界切 scale加载器切分 scale 应按 block 边界而非按隐藏维直接切片。查 fp8 内核约束某些 fp8 GEMM 要求 tp 整除 block 数tp8 可能不满足。查 H100 对齐要求fp8 张量核心要求输入形状对齐如 16 倍数tp8 局部形状核对。查混合量化不同层 scale 模式不同tp8 切分部分层对齐失败统一 scale 模式。升 vLLM新版本对 FP8TP 的 scale 切分支持更完善。启动前校验用Fp8TpPlan.validate校验对齐不对齐清晰报错。最后才动内核优先在 scale 切分/并行规划层修不要为绕开去改 fp8 内核。九、小结Qwen3-Coder-Next-FP8 在 H100 上tp8启动报错根子是FP8 权重的缩放因子per-block/per-tensor scale在按隐藏维切到 8 张卡时其 block 边界/对齐约束与 tp 切分边界不匹配如 per-block scale 的 block 数在 8 卡切分后非整数或 fp8 内核要求 tp 对齐 block 数导致 scale 形状对不齐或内核对齐不满足。注意 H100 本身支持 fp8问题在「量化元数据的 tp 切分」而非硬件。修复三层第一层启动前校验hidden 能被 block*tp 整除不对齐则降到能对齐的 tp第二层抽Fp8TpPlan校验对齐、按 block 边界切分 scale、自动选合规 tp第三层用 pytest 把「block 对齐通过」「不对齐捕获」「tp 回退」「对齐保留」钉进 CI启动前断言。核心认识——FP8 的 scale 布局block/对齐与 tp 的「隐藏维均切」假设必须一致tp 越大每张卡局部形状越小、越易不对齐启动前务必校验「每卡 block 数为整数」否则就把启动崩在 FP8 scale 切分上。