CANN-Ascend-C调试技巧-昇腾NPU算子出了bug怎么查
Ascend C 算子跑在 AI Core 上没有 printf、没有 gdb、没有 core dump。算子输出的结果不对你怎么知道错在哪这篇整理了三种调试方法从快到慢排列。方法一跟标准实现逐层对比最快的排障方式。把你的 Ascend C 算子替换成等价的 PyTorch 标准实现逐步缩小差异范围。importtorchimporttorch_npu# 你的自定义算子out_customtorch_npu.npu.my_op(x,y)# 标准实现out_refxy# 或者更复杂的等价实现# 逐元素对比diff(out_custom-out_ref).abs()print(f最大误差:{diff.max().item()})print(f平均误差:{diff.mean().item()})print(f误差 0.01 的比例:{(diff0.01).float().mean().item()})如果误差全在前半段问题可能在 Tiling 的前几个 tile。如果误差集中在某些行可能是指针偏移算错了。方法二DUMP 中间结果Ascend C 算子内部不能 print但可以把中间结果写到 HBM推理结束后读出来classMyKernel{// 添加一个 debug 输出 buffer__aicore__inlinevoidProcess(){// ... 正常计算 ...// Debug: 把中间结果写到 HBM// 需要在 Init 里额外申请一个 GM_ADDR debug_bufDataCopy(debug_gm_[tile_idx*block_len_],intermediate_local_,block_len_);}};# Python 端读取 debug 输出debug_datatorch.empty(expected_size,devicenpu,dtypetorch.float16)# 传入 debug buffer 的地址outtorch_npu.npu.my_op(x,y,debug_bufdebug_data)print(debug_data.cpu())# 查看中间结果这个方法需要在算子接口里加一个额外的输出参数改了算子签名。调试完后删掉。方法三CPU 模拟执行CANN 提供了 Ascend C 的 CPU 模拟器可以在 CPU 上逐步执行算子支持打印和断点# 用 CPU 模式编译ascendc--chipAscend910B2--modecpu add_custom_kernel.cpp-obuild/CPU 模式下可以在 kernel 里加printf__aicore__inlinevoidProcess(){// CPU 模式下可以用 printfprintf(tile %d: block_len%d\n,tile_idx,block_len_);// 计算后打印结果for(inti0;i8;i){printf(z[%d] %f\n,i,(float)z_local.GetValue(i));}}CPU 模式的执行速度比 NPU 慢 100-1000 倍但能精确定位问题。注意CPU 模式的结果跟 NPU 模式可能有微小差异float 精度差异但逻辑错误是一致的。常见 Bug 模式Bug 1Tiling 参数溢出// ❌ int32 溢出int32_ttotal_elemsM*N;// M4096, N4096 → 16Mint32 没问题int32_ttotal_bytestotal_elems*sizeof(half);// 32M还 OK// 但如果 M65536, N65536 → 4G elements → int32 溢出// ✅ 用 int64_tint64_ttotal_elems(int64_t)M*N;Bug 2Global Buffer 偏移算错// ❌ 按 element 偏移但用了 byte 偏移DataCopy(dst,src_gm_[tile*block_len*sizeof(half)],block_len);// ✅ 按 element 偏移SetGlobalBuffer 时已经指定了类型DataCopy(dst,src_gm_[tile*block_len],block_len);Bug 3Cube-Vector 同步缺失// ❌ Cube 还没写完Vector 就开始读了MatMul(qk,q,kt);Softmax(attn,qk);// qk 可能还没写完// ✅ 加同步MatMul(qk,q,kt);SetFlagPIPE_M(PIPE_V);WaitFlagPIPE_M(PIPE_V);Softmax(attn,qk);Bug 4Local Buffer 越界// ❌ block_len 超过 Local Buffer 容量uint32_tblock_len512*1024;// 512K elements 1MBfp16// Local Buffer 只有 256KB// ✅ 确认 block_len 在 Local Buffer 范围内// 256KB / sizeof(half) 128K elementsuint32_tmax_block128*1024;uint32_tblock_lenstd::min(requested_len,max_block);调试流程总结1. 跟标准实现对比 → 确认有 bug 2. 缩小输入规模 → 只用 [2, 16] 的小矩阵测试 3. CPU 模拟执行 → 加 printf 定位出错行 4. 修复 → 回到 NPU 模式验证 5. 恢复原始输入规模 → 确认修复完整Ascend C 调试最大的困难是没有直接的 print 和 debug 工具。但 CPU 模拟器 中间结果 DUMP 组合起来大部分 bug 都能定位。关键是先把输入规模缩到最小减少变量。仓库在这里https://atomgit.com/cann/opbase