ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

CANN ops-transformer matmul_reduce_scatter_v2 的 Batch-Invariance(BI)测试套件全解:三步方法学、三态判定与源码级验证

2026/9/23 2:58:41 拓冰建站 浏览量
CANN ops-transformer matmul_reduce_scatter_v2 的 Batch-Invariance(BI)测试套件全解:三步方法学、三态判定与源码级验证 算子库人工智能深度学习Ascend【免费下载链接】ops-transformer本项目是CANN提供的transformer类大模型算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-transformer点击查看免费下载导读本文以 CANN ops-transformer 仓库中mc2/matmul_reduce_scatter_v2/tests/bi/目录下的 BIBatch-Invariance批不变性测试套件为对象系统讲解 MC2Matmul Communication 融合算子如何验证「输出行只依赖输入行与共享权重、不依赖同 batch 其他行」这一文献级不变量。文章将完整还原测试套件的三步方法学HCCL warm-up → Multi-trial 确定性基线 → Variant A/B 对比、三态判定语义、矩阵参数设计并结合bi_test_driver.py、run_bi_test.sh与 arch35 kernel 源码说明为什么matmul_reduce_scatter_v2是 natively BI-clean 的算子。读完本文你将掌握一套可复用的、能区分「真 BI 违例」与「trial 非确定性噪声」的严格测试方法学并能直接在本仓库硬件环境下运行与解读该测试。1. 为什么需要 BI 测试现有 acc/perf 覆盖的盲区1.1 现有 UT/ST 测试覆盖了什么matmul_reduce_scatter_v2算子的常规 UT/ST 测试主要覆盖两个维度acc与 reference 实现的数值精度对比要求在 tolerance 范围内perf性能阈值验证吞吐 / 延迟。这两个维度保证「算得对、跑得快」但都不覆盖 literature batch-invariance。1.2 literature BI 的定义文献意义上参考 Thinking Machines 的公开技术文章Defeating Nondeterminism in LLM Inference的 batch-invariance 可以写成y[i] f(x[i], W) 输出第 i 行仅依赖于输入第 i 行 共享权重 W 与同一 batch 中其他行的内容无关。也就是说对于一个批量矩阵乘算子任意一行输出不应受到 batch 中其他行数据内容的影响——即使这些行在物理上共享了某些计算路径例如共享的中间 buffer、未初始化的残差、跨行的归约逻辑等。1.3 一个反例acc/perf 全过但仍违反 BI文档明确指出一个关键事实一个 op 可以同时满足 acc 与 perf、甚至满足 cross-rank determinism但仍然违反 literature-BI行间相互影响。仓库中AllGatherMatmulV2在 PR #6373 修复之前即属此类——它能在精度和性能测试中通过但存在行间串扰的 BI 缺陷。这正是 BI 测试套件存在的意义它是独立于精度与性能的第三类正确性维度专门捕捉「行间独立性」被破坏的问题。2. 方法学 Note三步流程缺一不可本文重点这是整个测试套件的核心。文档强调前一版方法学每个 cell 只跑 variant A B 各一次存在隐藏漏洞——无法区分「真 BI bug」和「trial 非确定性」。下述三步流程是修正后的版本也是本测试套件区别于朴素 A/B 对比测试的关键。每个 BI cell 必须依次走完三个 phase。源码实现在 bi_test_driver.py 中以下逐 phase 对照代码讲解。Phase 0HCCL warm-up稳定通信状态动机Worker 启动后HCCL state 在前几次调用中容易处于 cold-start 状态。第一次 op 调用经常出现HcclAllocComResourceByTiling failederror code 561000即便成功HCCL 也可能选择运行非确定性的 ring 拓扑污染后续所有比较。做法测试矩阵正式开始前先做N_WARMUP2次 dummy 调用使用任意合法 input让 HCCL state 稳定。warm-up 阶段产生的 error 不计入测试结果。驱动代码中的实现bi_test_driver.py# Phase 0: HCCL warm-up (critical — first worker cold start often non-deterministic) if rank 0: print( Phase 0: HCCL warm-up..., flushTrue) try: for _ in range(N_WARMUP): _ make_and_call(dev, hcom, ws, 16, 256, 64, 0, 42) except Exception as e: if rank 0: print(f warmup ERROR (continuing): {str(e)[:100]}, flushTrue)注意 warm-up 调用被包在try/except中异常只打印提示、不中断测试——cold-start 失败正是 warm-up 要吸收的噪声。Phase 1Multi-trial determinism baseline确定性基线做法每个 cell 的 variant A 用同一个 input 跑N_TRIALS3次所有 SHA 必须完全一致variant B 同样处理。for _ in range(N_TRIALS): sha_list_a.append(run(seed_a)) assert len(set(sha_list_a)) 1 # variant A 必须 deterministic若sha_list_a不一致 → 标记为NON_DETERMINISTIC该 cell不进入 Phase 2因为此时 A vs B 的比较已无意义。这一步是方法学的灵魂它先证明「同一个输入多次执行是确定的」然后才允许用 A vs B 的差异去归因于输入内容差异。若跳过这一步A/B 不一致既可能是 BI 违例也可能是 trial 本身的非确定性共享 NPU、HCCL state 抖动、硬件噪声等。Phase 2Variant A vs Variant B 的 BI 检查只有 Phase 1 通过才把sha_a与sha_b比较相同 →BI_PASS行间独立不同 →BI_FAIL真 BI 违例。驱动代码中rigorous_bi_cell完整实现了三个 phase 与三态判定bi_test_driver.pydet_a len(set(shas_a)) 1 det_b len(set(shas_b)) 1 if not det_a or not det_b: return (NON_DET, shas_a[0] if shas_a else None, shas_b[0] if shas_b else None, shas_a, shas_b) sha_a, sha_b shas_a[0], shas_b[0] return (BI_PASS if sha_a sha_b else BI_FAIL, sha_a, sha_b, shas_a, shas_b)三态输出总表状态判定条件含义解读BI_PASS确定性基线通过 A Brow 内容跨 batch 独立 ✓BI_FAIL确定性基线通过 A ≠ B真 BI 违例需修复NON_DETERMINISTIC同 input 多次跑 SHA 不一致测试系统问题HCCL state / 共享 NPU / 硬件需排查不能简单判 BI failERRORop 调用抛异常op-plugin shape 拒绝或基础设施故障驱动代码中SKIP状态用于 target row 超出输出 shape 的边界情形见rigorous_bi_cell中对target_row y.shape[0]的处理。关键教训把BI_FAIL与NON_DETERMINISTIC混淆会产生大量假阳性淹没真 bug。这正是 2026-06-06 调研中通过 multi-trial 重测纠正的方法学漏洞——也是本测试套件刻意做成「三步走」的根本原因。3. 算子状态为什么 matmul_reduce_scatter_v2 是 natively BI-clean3.1 源码证据单一计算路径、无路径分裂文档给出的结论是matmul_reduce_scatter_v2是natively BI-clean的算子。其 kernel 层面的证据链如下op_kernel/arch35下的 kernel 使用ExecuteAicMatMulPipeline作为 AIC 阶段统一的 MatMul 计算流水线入口声明与实现见 matmul_a2a_vec_reduce_fp16_bf16.h 与 quant_bmm_a2a_vec_reduce_fp8_hif8.h量化 BMM 场景下计算统一通过单一Mc2QuantBatchMatmulASWKernelisGatherfalsewrapper 完成。例如 quant_bmm_reduce_scatter_fp8_hif8.h 中Mc2MatmulV3::Mc2QuantBatchMatmulASWKernelAType, BType, ScaleType, float, CType, CubeFormat::ND, CubeFormat::ND, CubeFormat::ND, ATrans, BTrans mmv3; ... mmv3.Init(aGM_, bGM_, biasGM_, x2ScaleGM_, x1ScaleGM_, tempGM, workspaceGM_, qBMmtiling, GetTPipePtr(), cfg, isTail, false, preCoreNum_, PeerOnly ? cGM_ : nullptr); for (uint32_t i 0; i tileCnt; i) { mmv3.UpdateSlice(i, isTail); mmv3.Process(isLast (i (tileCnt - 1))); handles_[i shift] hccl_.template ReduceScattertrue(cWork, recvBuffer, recvCount, dataType_, HcclReduceOp::HCCL_REDUCE_SUM, stride, repeat); }即所有 input 行都走同一份 wrapper 的同一套 matmul ReduceScatter 流程不存在 LOCAL / GATHER 两路径分裂。从实现结构看这种「单路径、无分支处理行内容」的设计天然不会出现「某些行走了 A 路径、另一些行走了 B 路径」导致的行间差异。本测试套件在严格方法学下对该算子确认零失败BI_PASS24 BI_FAIL0作为 positive baseline 验证测试方法学本身是可工作的——即一个好的算子在该套件下必须全绿测试才能被信任去抓坏算子。3.2 灵敏度验证方法学真的抓得到 bug作为对照AllGatherMatmulV2pre-PR-#6373 版本在同一测试套件下会触发BI_FAIL。这一正一反两个案例共同证明了方法学的灵敏度positive baseline 不误报negative case 不漏报。4. 测试矩阵参数设计4.1 参数总表文档给出的矩阵参数如下维度取值WorldSize{2, 4, 8}M_total{8, 16, 32}必须能被 WS 整除K{256, 512, 1024}多次跨 cubebaseK128 边界N{64, 128}含baseN64 边界量化 scale(x1ID,x2VAR) — K-axis varying已知 BI 触发条件目标 row 位置0N_WARMUP2N_TRIALS3约 24 cells × WS{2,4,8} 72 cells每个 cell 6 次 op 调用3 trials × 2 variantsCI wall-clock 估算小于 15 分钟。4.2 驱动代码中的矩阵定义矩阵规模在驱动main()中硬编码bi_test_driver.pyM_TOTALS [8, 16, 32] KS [256, 512, 1024] NS [64, 128] TARGET_ROW 0 # x1_scale varying coverage: cell index 满足 % 4 0 时启用 x1_scale 沿 MK 轴 varying X1_VARYING_MOD 4遍历逻辑中if M_total % ws ! 0: continue保证只跑 M_total 能被 WorldSize 整除的 cell符合算子约束中「m 须为卡数 rank_size 的整数倍」的要求参见 算子文档 约束说明。4.3 数据构造K-axis varying 的 E8M0 scaleBI 检测的关键在于让行间差异可被感知。驱动使用 MX 量化场景构造输入BLOCK_SIZE 32MX 量化的 K-axis block sizen_pack (K BLOCK_SIZE * 2 - 1) // (BLOCK_SIZE * 2)MX 每 64 个 K 元素打包成 1 个 pack每 pack 含 2 个 slot每 slot 覆盖 32 个元素默认场景下x1_scale恒为0x7F即 1.0x2_scale沿 K 轴 varying——这是已知的 BI 触发条件bi_test_driver.pydef build_x2_scale_k_varying(n_pack, N): 构造 K-axis varying 的 E8M0 scale已知的 BI 触发条件。 bs torch.empty(n_pack, N, 2, dtypetorch.uint8) for p in range(n_pack): for c in range(N): for slot in (0, 1): bs[p, c, slot] 0x78 ((p * 2 slot) % 8) return bs另外提供build_x1_scale_varying让x1_scale沿 MK 轴 varying作为额外 BI 触发条件的覆盖bi_test_driver.pyx1_scale_varying通过cell_idx % X1_VARYING_MOD 0即每 4 个 cell 启用 1 个在 18-cell 矩阵中随机覆盖约 4-5 个 cell补强 x1_scale 维度的 BI 检测。4.4 Variant A/B 的 seed 策略不同 cell 使用不同 seed 对让不同数据分布触发不同的 cube/HCCL 路径bi_test_driver.pyshape_hash hash((M_total, K, N)) % 10000 seed_a 100 shape_hash seed_b 200 shape_hashA 与 B 使用不同的 seed 生成各自行内容但target_rowrow 0的内容在两个 variant 中被固定为同一份字节序列target_row_bytes由固定 seed 12345 生成见make_and_call。这样 A vs B 的 SHA 差异只能来自「其他行内容是否影响目标行」——即精确度量 BI。4.5 op 调用与 SHA 摘要make_and_call通过 torch 自定义算子入口torch.ops.npu.npu_quant_mm_reduce_scatter调用底层实现bi_test_driver.py并显式传入reduce_opsum、block_sizeBLOCK_SIZE32、comm_turn0x1_scale/x2_scaleE8M0 类型、y_dtype/x1_dtype/x2_dtype为 FP8 场景配置调用后torch_npu.npu.synchronize()确保计算落盘。SHA 摘要函数sha_row对目标行取row_idx行、转 uint8、计算 SHA256 的前 16 个 hex 字符bi_test_driver.pydef sha_row(t, row_idx): return hashlib.sha256( t[row_idx].cpu().view(torch.uint8).numpy().tobytes() ).hexdigest()[:16]4.6 结果统计与退出码main()按 rank 0 汇总五个状态计数bi_test_driver.pyresult: BI_PASS24 BI_FAIL0 NON_DET0 ERROR0 SKIP0BI_FAIL的 cell 会打印(M_total, K, N, A_sha, B_sha)NON_DET的 cell 会打印各 trial 的 SHA 列表仅BI_FAIL使进程以非零码退出sys.exit(0 if n_fail 0 else 1)NON_DET是「需进一步排查 HCCL state / 硬件因素」的信号不自动判定算子失败。5. 如何运行 BI 测试5.1 硬件与软件前提硬件V300Ascend 950PR/DT软件CANN 9.1、torch_npu2.9。5.2 启动命令cd mc2/matmul_reduce_scatter_v2/tests/bi/ bash run_bi_test.shrun_bi_test.sh会以 WS2/4/8 三次启动bi_test_driver.py每次打印 matmul_reduce_scatter_v2 BI rigorous WS2 N_WARMUP2 N_TRIALS3 Phase 0: HCCL warm-up... result: BI_PASS24 BI_FAIL0 NON_DET0 ERROR0 SKIP05.3 输出解读规则任何BI_FAIL 0→ 真 BI 违反算子需修复NON_DET 0→ 需排查测试系统HCCL state / 共享 NPU / 硬件而非算子ERROR→ 通常是 op-plugin 的 shape 拒绝或基础设施故障。5.4 启动脚本的可配置项run_bi_test.sh 提供了若干环境变量开关环境变量默认值作用HCCL_BUFFSIZE256HCCL 通信 buffer 大小默认 256MBHCCL_WHITELIST_DISABLE1关闭 HCCL 算法白名单限制BI_WS_LIST2 4 8自定义要跑的 WorldSize 列表BI_SLEEP_BETWEEN_WS15相邻 WS 之间的 sleep 秒数用于让 HCCL/NPU 清理上一轮 worker 的 device 资源脚本对每个 WS 的 device 映射为WS2 →0,1WS4 →0,1,2,3WS8 →0,1,2,3,4,5,6,7其他 WS 自动用seq生成。每个 WS 通过torch.distributed.run --nproc-per-node$WS --master-port$PORT端口为32700 WS启动并以ASCEND_RT_VISIBLE_DEVICES限定可见卡。任一轮失败会记录退出码并在最后以非零状态整体退出。6. 与 ATK 框架的集成待讨论当前实现作为 standalone Python driver不依赖 ATK。issue #2956 中讨论的 ATK 集成方案需要在standard字段新增bikeyexecutor 增加_run_bi_check钩子。集成时必须保留三个关键点这是方法学核心不是 boilerplaten_warmup和n_trials必须可配置——这是 BI 方法学的核心参数绝不能写死三态结果BI_PASS / BI_FAIL / NON_DETERMINISTIC需独立汇总不能合并——合并会丢失「非确定性」这一重要的中间语义NON_DETERMINISTIC 0不应该自动判 op 失败但要写入测试报告引起关注。集成方案待 maintainer 评估后再统一改造。本 PR 优先保证 BI 测试可独立运行作为方法学的可执行规范。7. 结语一套可执行的确定性-不变性测试范式matmul_reduce_scatter_v2/tests/bi/这套测试套件提供的价值超越了单个算子它是「文献级 batch-invariance」的可执行规范——把y[i] f(x[i], W)这一数学不变量翻译成了可重复、可判定的三态自动化测试它的三步方法学warm-up → 确定性基线 → A/B 对比是解决「真 bug vs 测试噪声」二义性的通用框架对任何涉及集合通信、共享 NPU、量化 scale 的 MC2 算子都适用它以matmul_reduce_scatter_v2作为 positive baseline、以 pre-PR-#6373 的AllGatherMatmulV2作为负例自证了方法学的灵敏度。如果你正在为 CANN ops-transformer 仓库中的其他 MC2 算子补充 BI 测试维度这个目录README、bi_test_driver.py、run_bi_test.sh就是最直接的参考模板配合 算子官方文档 与 arch35 kernel 源码即可完成从「测试怎么写」到「为什么这样写」的完整闭环理解。赞分享算子库人工智能深度学习Ascend【免费下载链接】ops-transformer本项目是CANN提供的transformer类大模型算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-transformer点击查看免费下载相关推荐CANN ops-transformer RainFusionAttention 测试套件全解析从用例设计到精度验证CANN ops transformer RainFusionAttention 测试套件全解析从用例设计到精度验证 RainFusionAttention算子库人工智能深度学习AscendCANN Runtime 组件隔离的风险识别与验证矩阵三步裁剪下的证据链、测试要求与 CI 判定CANN Runtime 组件隔离的风险识别与验证矩阵三步裁剪下的证据链、测试要求与 CI 判定 导读 在 CANN Runtime 中将某芯片或构建 tCANNAscend人工智能任务调度CANN ops-transformer 算子测试框架实践mixed_quant_sparse_flash_mla 的 pytest 全流程验证指南CANN ops transformer 算子测试框架实践mixed_quant_sparse_flash_mla 的 pytest 全流程验证指南 本文围绕算子库人工智能深度学习Ascend上一篇如何用Resilience4j构建坚不可摧的Java应用从熔断到限流的实战指南下一篇Nimx高级特性动画系统与事件处理的实战应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考