ascendc-operator-precision-debug
AscendC 算子精度问题调试与根因定位。当算子精度测试失败(allclose 不通过、结果偏差、输出全零/NaN 等)时使用。流程:误差分布分析 → 代码易错点审查 → 实验隔离 → printf/DumpTensor 插桩 → 修复验证。关键词:精度调试、精度问题、结果不一致、误差定位、allclose 失败、输出偏差、NaN、全零、precision debug。
How do I install this agent skill?
npx skills add https://github.com/ascend/agent-skills --skill ascendc-operator-precision-debugIs this agent skill safe to install?
- Gen Agent Trust Hubpass
The skill provides a structured methodology and automation tools for debugging precision errors in AscendC operators. It uses code analysis and a Python-based testing framework to identify common NPU programming errors. No security risks or malicious behaviors were found.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
What does this agent skill do?
AscendC 算子精度调试
按「由浅入深」五阶段定位根因:先看数据分布,再查代码易错点,然后实验隔离,最后插桩定位。
Phase 1: 误差分析 → Phase 2: 代码审查 → Phase 3: 实验隔离 → Phase 4: 插桩定位 → Phase 5: 修复验证
Phase 1:误差分析
原则:先看数据,再看代码。 先搞清楚「错在哪、错多少、错成什么样」。
收集失败用例的 shape、dtype、MaxAbsErr/MeanAbsErr/CosineSim,然后基于 scripts/debug_precision_template.py 创建 csrc/ops/<op_name>/test/debug_<op_name>_precision.py(替换占位符后运行),自动分析:
- 误差统计:MaxAbsErr、MeanAbsErr、MaxRelErr
- 首个错误元素:多维坐标 + 线性下标 + NPU 值 vs 参考值
- 错误分布:错误元素数量/占比、错误间隔是否呈周期性
- 特殊值:输出是否全零、含 NaN/Inf
- 自动对照:固定输入 vs 随机输入、缩小 shape 二分
误差特征 → 初步判断
| 现象 | 最可能原因 | 下一步 |
|---|---|---|
| FP16 失败,FP32 通过 | 未升精度到 FP32 计算 | Phase 2 查 Cast |
| 输出全零 | CopyOut 未执行 / GM 偏移错 | Phase 2 查 CopyOut |
| 输出含 NaN/Inf | 除零 / log 负数 / 溢出 | Phase 2 查 Compute |
| 全部偏差,CosineSim≈1 | 系统性精度损失 | Phase 2 查升精度 |
| 周期性/条纹状错误 | tile 边界 / 搬运偏移 | Phase 3 实验 |
| 仅尾部元素错 | 尾 tile 长度 / 对齐 | Phase 2 查尾 tile |
| 多次运行结果不同 | 异步同步不足 | Phase 3 实验 B |
| 小 shape 过、大 shape 挂 | 多核/tiling 边界 | Phase 3 实验 A |
| 固定输入过、随机挂 | 地址/stride/偏移错 | Phase 3 实验 C |
Phase 2:代码审查
MANDATORY:读取 op_host/<op_name>.cpp、op_kernel/<op_name>.cpp、design.md(若存在),按以下清单由浅入深排查。
第一层:基本正确性(最高频)
- FP16/BF16 未升精度:Compute 中半精度是否先
Cast到 FP32 计算再Cast回?这是最高频精度 bug。 - 计算公式错误:API 调用序列与设计文档/PyTorch 逐步对照——运算顺序、标量符号、是否缺步骤。
- GM 偏移单位混淆:
xGm[progress * tileLength]是元素偏移,不要多乘sizeof(T)。 - tileLength vs curTileLength:偏移用
tileLength,计算/搬运用curTileLength(尾 tile 可能更小)。
第二层:搬运与对齐
- DataCopyPad copyLen:
DataCopyExtParams的 copyLen 是字节数 =curTileLength * sizeof(T)。 - 尾 tile 对齐:尾 tile 不满足 32B 对齐时,
alignedTailLen计算及使用是否正确。 - 多输入偏移不一致:多输入 tensor shape 不同时(如 RoPE 的 x vs cos/sin),各自的偏移计算是否正确。
第三层:Tiling 与多核
- Host/Kernel tiling 不一致:同一符号(如
tileLength)在 host 和 kernel 中含义是否一致。 - 核间边界重叠/遗漏:formerNum × formerLength + tailNum × tailLength 是否恰好覆盖全部数据。
- bufferCoefficient 错误:与设计文档 UB 分配表核对,错误的系数会导致 tileLength 偏差。
第四层:API 陷阱
- ReduceSum/Max 修改源数据:归约可能改写源 tensor,后续若复用需先
Adds(backup, src, 0.0f, len)备份。 - AllocTensor/FreeTensor 未配对:与 EnQue/DeQue 需严格配对,否则缓冲区泄漏。
- 向量长度参数:AscendC 向量 API 长度是元素个数,非字节数。
第五层:边界情况
- 除零 / 定义域越界:Div、Reciprocal 防零;Ln 要求正数;Sqrt 要求非负。
- tiling 整数溢出:乘法是否可能溢出 int32?建议 int64_t。
检查点:输出审查报告——疑似问题列表(按可能性排序)。若已锁定根因,跳到 Phase 5;否则进入 Phase 3。
Phase 3:实验隔离
Phase 2 无法直接锁定根因时,通过控制变量实验缩小范围。每次只改一个变量。
实验 A:block_dim → 1(多核隔离)
在 op_host 临时硬编码 blockDim = 1,重编译测试。可配合缩小 shape。
| 结果 | 结论 |
|---|---|
| 单核过、多核挂 | 核间问题:GM 区间重叠 / tiling 映射 / 核间同步 |
| 单核也挂 | 非多核问题 → 实验 B |
实验 B:PipeBarrier<PIPE_ALL>(同步隔离)
将 kernel Process 中所有同步临时替换为 AscendC::PipeBarrier<PIPE_ALL>()(CopyIn / Compute / CopyOut 之间各加一个)。
| 结果 | 结论 |
|---|---|
| 全屏障后过 | 核内同步不足 → 逐步恢复细粒度同步定位 |
| 仍失败 | 非同步问题 → 实验 C |
PIPE_ALL仅用于实验隔离,绝不可作为最终方案。
实验 C:固定/规律输入(地址隔离)
分别用全 1、等差序列(torch.arange)、随机输入测试。
| 结果 | 结论 |
|---|---|
| 全 1 过、等差/随机挂 | 地址/偏移/stride 错误(常数输入掩盖了偏移问题) |
| 全都挂 | 计算逻辑或全局 tiling 错误 |
| 全都过 | 特定数值范围触发精度问题 → 查边界值/极值 |
实验 D:缩小 shape(边界隔离)
shape=(32,) → (tileLength,) → (tileLength*2,) → 原始 shape,定位恰好开始失败的分界点,反推 tile/核边界。
首错下标 + tiling 反推
首错线性下标 → 第几个 tile → 哪个核 → 该核 GM 起始偏移 → 搬运预期字节数
周期 = tileLength → 搬运/偏移问题;周期 = 向量宽度 → 计算流程问题;与核边界对齐 → 多核/offset 问题。
Phase 4:插桩定位
问题范围已收敛到某阶段/某 tile 后,用 AscendC::printf 和 AscendC::DumpTensor 精确定位。
核心规则
- 仅 0 核打印:每个核计算逻辑一致时,加
if (AscendC::GetBlockIdx() == 0)减少输出量。 - 同步后再读:在
DeQue/PipeBarrier之后才能读LocalTensor,否则读到未完成搬运的脏数据。 - FP16 先转 float:
AscendC::printf("v=%.6f\n", static_cast<float>(tensor.GetValue(idx)));,直接打 half 会乱码。 - 用 desc 区分阶段:DumpTensor 的 desc 参数(0=CopyIn 后, 1=Compute 中间, 2=CopyOut 前)。
- 小量起步:DumpTensor 的 dumpSize 从小值开始,过大会导致缓冲满或截断。
printf vs DumpTensor 选择
| 场景 | 工具 |
|---|---|
| 标量、分支判断、单个下标 | AscendC::printf |
| 连续一段 tensor 快速扫 | AscendC::DumpTensor(tensor, desc, dumpSize) |
| 全量逐元素对比 | 不在 kernel 内做 — Host 读 GM + Python 脚本 |
插桩策略
在 Compute 函数内 DeQue 之后,逐步骤插桩,与 Python 侧用相同输入手算的中间结果逐步对比。第一个出现偏差的步骤即为根因所在。
// 示意:0 核、第 0 个 tile
if (AscendC::GetBlockIdx() == 0 && progress == 0) {
AscendC::printf("[step1] tmp[0]=%.6f\n", static_cast<float>(tmp.GetValue(0)));
}
Phase 5:修复验证
常见修复模式
| 根因 | 修复 |
|---|---|
| FP16 未升精度 | 添加 Cast(fp16→fp32) + 计算 + Cast(fp32→fp16) |
| GM 偏移错 | 修正偏移公式(元素 vs 字节) |
| 尾 tile 长度错 | 计算/搬运用 curTileLength,偏移用 tileLength |
| tiling 参数错 | 修正 host 端 tiling 计算 |
| 同步缺失 | 添加正确的 EnQue/DeQue 或 PipeBarrier |
| ReduceSum 覆盖源 | 先 Adds 备份再 ReduceSum |
| 搬运长度错 | 修正 DataCopyExtParams 的 copyLen |
修复后
- 移除所有调试插桩(printf/DumpTensor),或用
#ifdef DEBUG_PRECISION包裹 - 重新编译安装
- 运行原失败用例 + 完整精度测试
- 仍失败 → 回到 Phase 1(最多 3 轮),3 轮后仍失败则报告用户
输出要求(MANDATORY)
调试完成后 MUST 在对话中展示:问题摘要、根因分析、修复内容、验证结果、≥2 条关键经验。NEVER 仅回复「已修复」。
典型案例(按需加载)
定位到疑似根因后,加载对应案例了解完整排查过程:
| 误差现象 | 案例文件 | 何时加载 |
|---|---|---|
| FP16 挂 FP32 过,全部偏差 | examples/fp16-no-upcast.md | 怀疑升精度缺失 |
| 首错在 tile 边界,周期 = tileLength | examples/gm-offset-error.md | 怀疑 GM 偏移错误 |
| 仅尾部少量元素错 | examples/tail-tile-misalign.md | 怀疑尾 tile 处理 |
| block_dim=1 过,多核挂 | examples/multicore-tiling-overlap.md | 怀疑核间 tiling |
| 多次运行结果不同 | examples/async-sync-missing.md | 怀疑同步缺失 |
不要一次性加载所有案例。 仅在误差特征匹配时加载对应案例。
反模式(NEVER)
- NEVER 不分析误差分布就直接改代码
- NEVER 在 kernel 中 printf 循环打全量 tensor — 用 DumpTensor 或 Host 侧对比
- NEVER 多核同时大量打印 — 加
GetBlockIdx() == 0仅 0 核打印 - NEVER 在未同步位置读 LocalTensor — 必须在 DeQue/PipeBarrier 之后
- NEVER 用
PIPE_ALL作为最终修复 — 仅用于实验隔离 - NEVER 修复后不移除调试代码
- NEVER 仅修复已知失败用例而不跑完整精度测试
- NEVER 超过 3 轮仍失败时继续尝试 — 应报告用户
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/ascend/agent-skills/ascendc-operator-precision-debug">View ascendc-operator-precision-debug on skillZs</a>