Core concepts
Precision debugging = understand + analyze + locate + fix
- Understand data type limits: FP16 has about 3-4 significant digits, FP32 about 6-7.
- Identify numerical stability issues: large numbers swallowing small numbers, catastrophic cancellation.
- Master a scientific debugging method: from minimal reproduction to root cause analysis.
When to use
Applicable: precision verification failure (rtol/atol not met), output all 0 or random values, FP16 worse than FP32, large errors in specific numerical ranges, pipeline synchronization issues, DataCopy alignment issues.
Debugging prerequisites ⭐⭐⭐
Before debuggingmustcomplete the following three steps
1. Fix a minimal reproducible case
| Item | Description | Example |
|---|---|---|
| Shape | tensor shape | {8, 16} |
| Dtype | data type | float16 |
| fixed values | specific values | [1.0, 2.0, -0.5, ...] |
Selection principles: prefer simple → prefer 32-byte alignment → prefer FP32 → cover boundary values
💡 Recommended practice: during debugging, it is recommended to validate on at least two dtypes (such as FP16 and FP32) withthe same shape + the same data. If one dtype passes and the other fails, you can quickly narrow the scope according to the corresponding diagnostic mode below.
2. Search asc-devkit ⭐
Do not modify code based on intuition
Search order:
- Search
asc-devkit/examples/to find similar operators. - Check the
asc-devkit/docs/api/context/API documentation. - Compare the official implementation with the current implementation.
3. Clean up caches and temporary files
rm -rf build input output
mkdir -p build/input build/output
Quick decision tree
[前置检查] 已固定用例?已检索API?已清理缓存?
│
└─ 否 → 先完成前置步骤
└─ 是 → 继续
│
├─ [第0步] ⭐ 代码修改后输出完全不变?
│ ├─ 是 → 清理 build/ 和 kernel_cache 后重试
│ └─ 否 → 继续
│
├─ [第0.5步] ⭐ 多 dtype 交叉验证
│ ├─ FP32通过但FP16/BF16失败 → 精度不足,见下方诊断模式
│ ├─ BF16通过但FP16/FP32失败 → API fallback 路径差异 或 精度阈值差异,见下方诊断模式
│ └─ 全部通过/全部失败 → 继续
│
├─ [第一步] 排查数据搬运 ⭐⭐⭐
│ ├─ 输出是否全为 0 或随机错误?
│ │ ├─ 是 → 检查流水线同步(EnQue / DeQue)⭐⭐⭐
│ │ │ └─ DataCopy 后直接计算?→ 添加 EnQue/DeQue
│ │ │ └─ 临时验证:加 PipeBarrier,若正确则确认同步问题
│ │ ├─ 检查 DataCopy 是否 32 字节对齐
│ │ │ └─ 非对齐 → 改用 DataCopyPad
│ │ └─ 检查是否使用 GlobalTensor.SetValue
│ │ └─ 是 → 改用 LocalTensor.SetValue + DataCopyPad 搬出到 GM
│ └─ 验证:用 "CopyIn → CopyOut" 测试搬运
│
├─ [第二步] 对比分析
│ └─ 对比官方示例与当前实现 → 发现差异
│
└─ [第三步] 诊断问题类型
├─ 所有结果都差 → 公式/常量/API选择
├─ 个别值错误 → 边界条件/除零/溢出
└─ 误差整体偏大 → FP16精度不足 → 尝试FP32中间计算
Symptom-cause quick reference table
| Symptom | Possible cause | Diagnostic direction |
|---|---|---|
| Output all 0 or random errors | Missing pipeline synchronization / DataCopy unaligned / GlobalTensor.SetValue | Check EnQue / DeQue, data alignment, switch to LocalTensor.SetValue + DataCopyPad ⭐⭐⭐ |
sum=0, max_err=输入级别 |
Output not written out | Check output queue type (VECIN vs VECOUT) |
sum=0, max_err≈0 |
Output all 0/uninitialized | Check UB overflow, buffer allocation |
特定参数范围失败 |
Threshold/boundary error | Verify threshold calculation, check branch conditions |
非对齐数据失败 |
DataCopy alignment issue | Switch to DataCopyPad |
FP16 差但 FP32 好 |
Insufficient precision | Use FP32 for intermediate calculations |
Cast 后数据错误 |
RoundMode error | half → float use CAST_NONE, float → half use CAST_ROUND |
| BF16 passes but FP16/FP32 fails | (1) Some APIs do not support BF16, and BF16 takes a simpler fallback path, which happens to be correct; (2) BF16 and FP16/FP32 have different precision characteristics (BF16 mantissa 7bit vs FP16 10bit), resulting in differences in precision thresholds or overflow behavior | First investigate the differences in API fallback branches, then check whether the precision thresholds (rtol/atol) are adapted to each dtype |
| FP32 passes but FP16/BF16 fails | Insufficient precision in half-precision intermediate computations | Increase precision: Cast → compute in FP32 → Cast back to half precision |
| Output is completely unchanged after modifying the code | Binary not updated / compiler cache | Clean build/ and $HOME/atc_data/kernel_cache/ then retry |
Diagnostic pattern: "FP32 passes but FP16/BF16 fails"
This is the most common precision diagnostic signal: FP16/BF16 usually share the same Cast-to-FP32 computation path in Ascend C (if constexpr (std::is_same_v<T, bfloat16_t> || std::is_same_v<T, half>)), FP32 passing indicates that the core algorithm is correct, and the problem lies in the half-precision conversion step.
确认: FP32通过,FP16/BF16失败
│
├─ 检查中间计算精度
│ ├─ FP16/BF16 Cast→FP32 计算路径是否正确?
│ ├─ 是否有未升精度的中间运算(如直接 Add<half>)?
│ └─ 验证:将 FP16 路径的中间计算全部用 FP32,观察结果
│
├─ 检查 Cast RoundMode
│ ├─ half→float 应使用 CAST_NONE
│ ├─ float→half 应使用 CAST_ROUND
│ └─ 验证:对比 Cast 前后的数值
│
├─ 检查 Pipeline 同步
│ ├─ FP16/BF16 路径有额外的 Cast 操作,Cast 后是否 EnQue/DeQue?
│ ├─ FP32 路径无 Cast,天然无此同步问题
│ └─ 验证:在 Cast 后加 PipeBarrier,若正确则确认同步问题
│
└─ 检查 Buffer 大小
├─ FP16/BF16 路径需额外 Cast buffer(2× innerDim × sizeof(float))
├─ FP32 路径不需要 Cast buffer
└─ 验证:检查 Tiling 中 castBuf 大小计算
Diagnostic pattern: "BF16 passes, but FP16/FP32 fails"
This is a valuable diagnostic signal and may be caused by two types of reasons:
Reason 1: API not supported leading to differences in fallback paths. Some Ascend C arithmetic/reduction APIs do not support BF16, and developers implement a simpler fallback path for BF16. The BF16 fallback passing indicates that the logic is correct, and the problem lies in the complex APIs called in the FP16/FP32 path.
Reason 2: Differences in precision thresholds / numerical ranges. BF16 (mantissa 7bit, exponent range same as FP32) and FP16 (mantissa 10bit, smaller exponent range) have different precision characteristics: BF16 is less prone to overflow but has lower mantissa precision, while FP16 is prone to overflow but has higher mantissa precision. If the verification thresholds (rtol / atol) are not differentiated and adapted, or FP16 overflows while BF16 does not, BF16 will pass but FP16 will fail.
确认: BF16通过,FP16/FP32失败
│
├─ [原因1] API fallback 路径差异
│ ├─ 搜索代码中 if constexpr (std::is_same_v<T, bfloat16_t>) 分支
│ ├─ BF16 走的 fallback 路径 vs FP16/FP32 走的主路径,差异在哪里?
│ ├─ 列出 FP16 / FP32 路径中使用但 BF16 路径未使用的 API
│ ├─ 逐个验证差异 API 的参数(mask、repeatTime、stride)
│ └─ 临时将 FP16 路径改为与 BF16 相同的 fallback 实现,观察是否通过
│
├─ [原因2] 精度阈值 / 数值范围差异
│ ├─ FP16 是否溢出?(FP16 max ≈ 65504,BF16 指数范围同 FP32)
│ ├─ 验证阈值(rtol/atol)是否按 dtype 区分?
│ │ ├─ BF16: rtol=1e-2 级别(mantissa 仅 7bit)
│ │ └─ FP16: rtol=1e-3 级别(mantissa 10bit)
│ └─ 检查中间计算是否因 FP16 的更高精度要求暴露了算法缺陷
│
└─ 交叉验证
├─ 检查 asc-devkit 文档确认相关 API 是否支持 BF16
├─ 如果 API 不支持 BF16 → 优先按原因1排查
└─ 如果 API 支持 BF16(BF16/FP16 走相同路径)→ 优先按原因2排查
Common pitfalls quick reference
| Pitfall | Symptom | Solution |
|---|---|---|
| Missing pipeline synchronization | Output all 0 or random errors | EnQue/DeQue synchronization is required after DataCopy ⭐⭐⭐ |
| DataCopy unaligned | Small-scale data all 0/abnormal | Use DataCopyPad ⭐⭐⭐ |
| GlobalTensor.SetValue | Output is all 0 | Switch to LocalTensor.SetValue + DataCopyPad to move out to GM ⭐⭐⭐ |
| Cast RoundMode | Data is garbled after Cast | Use CAST_NONE for half→float, CAST_ROUND for float→half ⭐ |
| Insufficient FP16 precision | Even simple calculations have errors | Use FP32 for key intermediate values |
| exp/log overflow | Inf or NaN appears | Subtract the maximum value before computing |
| Subtractive cancellation | Large error in a-b when a≈b | Use a numerically stable equivalent formula |
| Reduce error | Reduce results have larger error than element-wise | Use an FP32 accumulator |
| Division-by-zero risk | NaN or abnormally large values | Add epsilon protection |
Pipeline synchronization debugging
Core issue: DataCopy / DataCopyPad are asynchronous DMA operations, and performing Vector computations directly on the copied data may read incomplete data!
// ❌ 错误:AllocTensor 后直接用
LocalTensor<T> x = inQueue.AllocTensor<T>();
DataCopy(x, gm, size);
Compute(x); // 错!可能读到未完成搬运的数据
// ✅ 正确:DeQue 后再计算
LocalTensor<T> x = inQueue.AllocTensor<T>();
DataCopy(x, gm, size);
inQueue.EnQue(x);
LocalTensor<T> xIn = inQueue.DeQue<T>(); // 等待搬运完成
Compute(xIn);
Temporary debugging method:
DataCopy(x, gm, size);
PipeBarrier<PIPE_ALL>(); // 临时加,如果结果正确说明是同步问题
Compute(x);
If PipeBarrier can solve the problem, it indicates a synchronization issue → Fix: switch to the EnQue/DeQue mechanism
| Misconception | Correct understanding |
|---|---|
| Data is available after AllocTensor | AllocTensor only allocates memory and does not wait for the copy |
| DataCopy is synchronous | DataCopy is asynchronous DMA and returns immediately |
| It works without EnQue/DeQue | Synchronization with EnQue/DeQue or PipeBarrier is required |
| PipeBarrier has good performance | PipeBarrier is a full pipeline stall and has poor performance |
For details, see references/common-traps.md
Debugging strategy levels
调试方法
│
├─ 快速方法(优先尝试,≤7次)
│ ├─ 误差分布分析 → 识别误差模式
│ ├─ Printf 特定位置 → 缩小范围
│ └─ 常见陷阱排查 → 对症下药
│
└─ 二分调试(保底手段)
└─ 快速方法尝试≥7次或方法穷尽时立即切换
Important principle: do not blindly try more than 7 times
Problem localization methods
1. Comparison method (compare with working code)
Find code that works normally and compare differences line by line
2. Boundary bisection method
Record the passing/failing threshold and analyze the branch selection
3. Numerical verification method
Do not trust estimation formulas; use code to compute actual values
4. Buffer debugging key points
| Problem | Symptom | Solution |
|---|---|---|
| VECIN used for output | Output equals input | Output must use the VECOUT queue |
| Double Buffer miscalculation | Incorrect threshold | Multiply by 2 when calculating the threshold |
For the detailed localization process, see references/diagnosis-workflow.md
Precision standard source priority
- Priority 1: precision requirements explicitly specified in the operator development Plan
- Priority 2: Huawei Ascend official precision standard documentation
- Priority 3: default value of this Skill (fallback only)
| Data type | rtol | atol |
|---|---|---|
| FP16 | 1e-3 | 1e-4 |
| FP32 | 1e-5 | 1e-6 |
| INT | - | 0 |
Agent Usage Guide
Debugging count rules
计数器 = 0
每次尝试快速方法(误差分析/Printf/陷阱排查)→ 计数器+1
当 计数器 >= 7 或 快速方法穷尽 → 立即切换二分调试
💡 Experience recommendation: if multiple attempts have made no progress (the number of failing cases has not decreased), it is recommended to:
- Check whether the compilation cache has been cleared (
rm -rf build/ $HOME/atc_data/kernel_cache/)- Verify whether the sha256 of the binary actually changed after modification
- Switch to a completely different debugging strategy (e.g., degrade from bisection to a minimal working path)
Debugging summary requirements
After each successful debugging stepmustform a summary:
- Places where the documentation description is unclear
- Places that need improvement and recommended solutions
- Points that cause confusion for the Agent
- Debugging process record
Summary template:references/debug-summary-template.md
Checklist
Debugging phase:
- A minimal reproducible case has been fixed
- asc-devkit has been searched to confirm API usage ⭐
- Cache and temporary files have been cleared
- Pipeline synchronization issues have been investigated(DataCopy 后是否 EnQue/DeQue)⭐⭐⭐
- The issue of output being all 0 has been investigated(DataCopy 对齐 / GlobalTensor.SetValue → LocalTensor.SetValue + DataCopyPad)⭐⭐⭐
- Official examples have been compared with the current implementation
- Number of attempts < 7
- Switch to bisection debugging immediately upon reaching the threshold
Reference Materials
Workflow
- diagnosis-workflow.md - Complete Diagnostic Workflow
- binary-search-debug.md - Detailed Guide to Binary Debugging
Problem Diagnosis
- common-traps.md - Detailed Explanation of Common Precision Pitfalls
- best-practices.md - Best Practices
Debugging Tools
- printf-debug.md - Printf Debugging Method
- data-comparison.md - Data Comparison Method
- tools-reference.md - Tools and Commands Reference
Hands-on Case Studies
- case-studies.md - Hands-on Debugging Case Studies
- debug-summary-template.md - Debugging Summary Template