CANN/cann-competitions Add算子测试报告
2026/5/9 12:09:33 网站建设 项目流程

===== 元信息(请如实填写,此区块将由组委会脚本自动解析,请保持字段名不变)=====

【免费下载链接】cann-competitions本仓库用于 CANN 开源社区各类竞赛、开源课题、社区任务等课题发布、开发者作品提交和展示。项目地址: https://gitcode.com/cann/cann-competitions

team_name: "不队" team_members:

  • "杨金鹏-广州大学"
  • "刘畅-广州大学" operator_name: "Add" operator_library: "cann-ops-math" report_date: "2026-04-25"

Add 算子测试报告

一、算子理解

Add 算子用于执行两个张量的逐元素加法,其在 CANN 库中的广义数学定义为out[i] = x1[i] + alpha * x2[i](其中alpha为标量乘子)。除了支持基础的张量相加,Add 算子家族在 API 层面提供了极其丰富的变体:

  • 计算模式:支持基础相加(aclnnAdd/aclnnAddV3)、张量与标量相加(aclnnAdds),以及对应的原地(Inplace)更新版本。
  • 数据类型与混合精度:不仅支持 FP32, FP16, BF16, INT32, INT64, INT8, UINT8, BOOL 等常用类型,还支持类似x1为 FP32 而x2为 FP16 的异构(Mixed Dtype)加法。
  • Broadcasting(广播):支持将不同形状的张量(如[4, 1][4, 4])隐式扩展为相同形状再进行计算。

值得关注的数学性质与底层硬件路径: Add 虽然看似简单,但在底层 Tiling 与内核实现上,极度依赖输入类型和alpha的值。例如,当alpha = 1.0时,可能走直接的AddWithoutCastCompute路径;当alpha != 1.0且类型满足条件时,会触发特定硬件的AxpyAxpyV2(即 $Y = \alpha X + Y$)路径;若不支持 Axpy,则退化为Mul + Add路径。此外,浮点加法的“大数吃小数”(吸收效应)、相近数相减的“灾难性相消”、以及整数的“溢出回绕”,都是测试和业务使用中极易踩坑的重点。


二、测试策略与用例设计

考虑到 Add 算子底层庞大的分发路由与硬件特化路径,我们在test_aclnn_addv8.cpp中设计了多达 133 个测试用例,采用白盒与黑盒相结合的策略,重点覆盖了以下几个维度:

1. 极高密度的 API 变体覆盖同时对aclnnAdd,aclnnAdds,aclnnInplaceAdd,aclnnInplaceAdds,aclnnAddV3,aclnnInplaceAddV36 套 API 进行全面测试。特别是针对新增的 V3 接口,通过引入self标量乘子,设计了相应的正向逻辑与异常校验逻辑。

2. 核心路由与 Tiling 分支的精准触发通过调整dtypealpha的组合,精准打穿 Host 侧与 Device 侧的特化逻辑:

  • 直接加法路径:如FP32+alpha=1INT32+alpha=1AddWithoutCastCompute)。
  • Axpy / AxpyV2 硬件加速路径:构造如FP32+alpha=1.5INT32+alpha=3,验证AXPY_DTYPE_SUPPORT_LISTARCH_REGBASE_AXPY_DTYPE_SUPPORT_LIST的分发情况。
  • 退化路径 (Mul + Add):对于不在 Axpy 列表的类型(如 V3 接口下的INT8+alpha=2),验证乘加分离运算的准确性。
  • 混合精度路径:构造FP16 + FP32FP32 + FP16,验证AddMixDtypeCompute分支。
  • 布尔特化路径:测试BOOL + BOOL,触发AddBoolCompute<int8_t>的按位或(OR)逻辑转换。

3. 异常与防御性编程(Error-Path)设计了 TC-57 至 TC-91 的 30+ 个连续异常探测用例,对 6 个 API 的GetWorkspaceSize进行轰炸。包括:传入nullptr、维度超过 8 维(dim_exceed_8用 9D 张量测试)、输出类型不支持(promote_outcast_BOOL)、不支持的数据类型(如UINT32)以及Shape不匹配,验证 API 拦截机制。

4. 精度基准(Oracle)的降维打击设计CPU 侧的基准参考坚决避免与被测算子使用相同的float类型:

  • 浮点数(FP32/FP16/BF16):全部提升至double进行高精度累加CpuAddF,以避免标准库float自身带来的舍入掩盖了 NPU 算子的问题。
  • 整数(INT32):对于溢出测试,若直接用 C++int32_t相加触发溢出,会引发未定义行为(UB)。我们的 Oracle 显式将其转为uint32_t执行运算后再强转回int32_tCpuAddI32),完美模拟 CANN 算子在底层基于二进制补码的低位截断(回绕)行为。

三、覆盖率分析

以下是基于流水线针对题目规定评分文件及底层算子实现提取的综合覆盖率数据:

目标文件核心代码行数行覆盖率分支覆盖率说明 / 未覆盖归因
aclnn_add_v3.cpp7794.81%63.85% (272/426)API V3 层。行覆盖率极高,分支未满主要是多条件判断及底层极小概率的校验抛错未触发。
aclnn_add.cpp30366.01%45.86% (709/1546)V1/V2 基础 API 与类型推导校验层。庞大的宏展开与分支校验导致分子分母基数极大,其中包含针对其他硬件架构的历史兼容校验逻辑未被走到。
add.cpp5955.93%23.48% (62/264)设备路由层 (AiCore/AiCpu)。异常资源处理及部分备选硬件路径未能全部触达。
add_tiling_arch35.cpp9389.25%57.29% (110/192)算子 Tiling 算法层。行覆盖率接近满分,说明基础的 Block 切分、Axpy 路由等核心数学调度已充分激活。

覆盖率总结:我们在aclnn_add_v3.cppadd_tiling_arch35.cpp取得了优异的行覆盖率(>89%),这得益于我们针对 Dtype 和 Alpha 的正交测试。较低的 API 层分支覆盖率符合预期,主要由系统级的防御性代码(如aclrtMalloc失败、极罕见的数据类型组合检查)带来。


四、精度分析

浮点运算的本质决定了“算子实现正确并不等于结果符合绝对数学真值”。我们专门设计了 TC-27 至 TC-33 等极端的精度场景进行剖析:

场景一:大数吃小数(吸收效应)

  • 测试输入 (TC-27):FP32 下1e10 + 1e-5
  • 实测输出与分析:在 FP32 中,1e10对应的 ULP (Unit in the Last Place) 大约是1024。因此,加入1e-5就像往海里滴水,结果的有效位完全无法表达这个增量。实测输出与理论真值的误差极大(大于1e-4)。这不是 CANN 的 Bug,而是浮点精度的硬件极限表现,测试给出了预期内的[PASS] No loss判定。

场景二:灾难性相消(Catastrophic Cancellation)

  • 测试输入 (TC-28):FP32 下1.0000001 + (-1.0)
  • 实测输出与分析:两个极为相近的数值相减,会导致前导有效位全部抵消,剩余的小数部分被放大。在此测试中,计算结果完美对齐了经量化后输入的 CPUdouble参考值(误差小于1e-6),说明 NPU 加法在局部精度的保持上非常稳健。

场景三:Alpha 标量的量化折损

  • 测试输入 (TC-29)X + (1/3) * Y
  • 实测输出与分析1/3在二进制中是无限循环小数。我们将已带有浮点量化误差的float av = 1.0f / 3.0f作为 alpha 传入。测试验证 NPU 计算是否引入了额外的截断。结果表明,实测值与使用同等量化av运算的 CPU reference 对比误差为0,证明了乘法累加计算链路上没有额外掉精度。

场景四:异常值传染(Inf 与 NaN)

  • 测试输入 (TC-30, TC-31)3.4e38 + 3.4e38(溢出);NaN + 1.0
  • 实测输出与分析:实测结果正确上溢输出+inf;对于 NaN 测试,实测结果res[0]正确传递了NaN标志,并且没有影响到同一个 tensor 中的res[1](即1.0 + 2.0 = 3.0)。符合 IEEE-754 标准,证明底层矢量运算管线处理异常值规范。

场景五:整数溢出的无感回绕(UB 陷阱)

  • 测试输入 (TC-32):INT32 下2^30 + 2^30(总和2147483648,超出 INT32_MAX)。
  • 实测输出与分析:实测 NPU 输出-2147483648。算子对整数溢出不报错,而是走底层 ALU 的二进制补码截断。我们在 CPU Oracle 端采用uint32_t模拟此行为。对于业务方,这警示我们在处理如 Offset 加法时需严格留意数据量级,必要时需提升为 INT64。

五、反思与改进

  1. 复杂的分发路由隐患:通过测试发现,Add 算子的实现不仅与数据类型挂钩,还极度受alpha值影响(例如,决定是否走AxpyMul+Add)。这要求我们在未来的融合算子开发中,务必对参数值为1.0,0.0,-1.0和一般小数的分支进行穷举测试
  2. Oracle 陷阱的规避:针对 INT32 溢出的设计是一个深刻的经验教训。如果直接在 C++ 用int32_t累加作为基准,在-O3优化下编译器可能因 UB 产生非预期的常量折叠。因此,CPU 的基准实现必须显式地规避编译器的 UB,本次我们通过类型强制转换实现的回绕,大大提升了测试代码的健壮性。
  3. 环境差异与异步 Hang 死:在测试执行阶段(参看 Log),我们发现部分涉及AiCpu路径(如 INT16 乘加)和复杂广播(如TC-17)在当前实机环境中存在挂死风险并进行了跳过([SKIP])。这提示我们底层框架针对部分边缘场景的 Tiling 切分可能与特定驱动或硬件版本存在兼容性隐患,建议 CANN 底层进一步完善在不匹配架构上的平滑降级(Fallback)机制。

【免费下载链接】cann-competitions本仓库用于 CANN 开源社区各类竞赛、开源课题、社区任务等课题发布、开发者作品提交和展示。项目地址: https://gitcode.com/cann/cann-competitions

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询