CANN社区竞赛Cumsum算子功能与精度验证报告
测试环境概述
本测试在搭载Ascend 910 AI处理器(型号ascend910_93)的服务器上完成,运行CANN 9.0.0-beta.2软件栈,操作系统为Ubuntu 22.04.5 LTS,主机架构基于Kunpeng 920 7280Z处理器。
一、算子语义解析
Cumsum 算子实现沿指定轴的前缀和计算。对于输入序列 x,输出第 i 个元素定义为:
即当前及之前所有元素之和。
接口参数说明
input:输入张量,支持多种数据类型,包括 FLOAT32、FLOAT16、BF16、INT32、INT64、INT8、UINT8 和 BOOL。axis:执行累加操作的维度,使用 INT64 类型表示,支持负数索引。output_dtype:可选目标输出类型,框架将自动进行类型转换。output:结果张量,形状与输入一致。
API 变体对比
| 接口名称 | 功能描述 |
|---|---|
| aclnnCumsum(input, axis, output_dtype, output) | 标准前缀和,逐元素累积 |
| aclnnCumsumV2(input, axis, exclusive, reverse, output) | 增强版本,支持排除当前值(exclusive)和逆序处理(reverse) |
底层执行路径分析
- 矩阵加速路径(l0op::CumsumCube):当批量大小 ≥12800 且通道数 ≥512 时启用,仅适用于浮点类型(FLOAT/BF16/FP16)。
- AiCore通用路径:适用于小尺寸张量或整数类型运算。
- AiCPU路径:特定类型组合如 bool 到 int64 转换时调用。
- 维度索引差异:axis=0 使用 INT64 存储维度信息,其他情况使用 INT32。
- V2特性组合:exclusive 与 reverse 四种逻辑状态均需覆盖。
数值误差特性
由于连续加法引入舍入误差,总误差随序列长度近似线性增长。单次浮点加法误差约为机器精度 ε,n 次累加后理论最大误差可达 n×ε。因此该算子是评估硬件数值稳定性的典型代表。
二、测试策略与用例设计
共构建32个测试案例,全面覆盖功能分支与边界场景。
参考基准(Oracle)设计
采用 CPU 端 double 精度实现作为黄金标准,对量化后的 float 输入执行精确前缀和计算,确保输入一致性,公式如下:
CpuBaseline(vector<double>)
容差设定原则
- FLOAT32:绝对误差 atol=1e-5,相对误差 rtol=1e-5(考虑累积效应适当放宽)
- FLOAT16:atol=1e-3,rtol=1e-3
- 整型类型(INT32/INT64等):严格逐位匹配
测试用例矩阵
| ID | 测试名称 | 覆盖重点 |
|---|---|---|
| TC01 | F32_1D_axis0 | 一维张量,首轴累加(INT64 dimTensor 分支) |
| TC02 | F32_2D_axis0 | 二维张量按列累加 |
| TC03 | F32_2D_axis1 | 二维张量按行累加(INT32 dimTensor) |
| TC04 | Int32 | 整型分块路径 |
| TC05 | Int64 | 长整型路径 |
| TC06 | Int8 | 有符号字节类型 |
| TC07 | Uint8 | 无符号字节类型 |
| TC08 | Float16 | 半精度浮点路径 |
| TC09 | Bool_to_Int64 | 布尔输入+类型转换 |
| TC10 | Int32toF32 | 整到浮点类型转换路径 |
| TC11 | V2_normal | 基础 V2 功能 |
| TC12 | V2_exclusive | 排除模式(首项为0) |
| TC13 | V2_reverse | 逆序累加 |
| TC14 | V2_excl_rev | 排除+逆序组合 |
| TC15 | AllZero | 全零输入 |
| TC16 | AllNegative | 全负数序列 |
| TC17 | AlternatePosNeg | 正负交替序列 |
| TC18 | SingleElement | 单元素特例 |
| TC19 | MediumSeq_512 | 中等长度(512) |
| TC20 | LargeSeq_4096 | 大尺寸触发多核分片 |
| TC21 | NullPtr_Rejected | 空指针异常检测 |
| TC22 | 3D_axis1 | 三维张量第二轴 |
| TC23 | V2_2D_excl_axis1 | 二维排除模式 |
| TC24 | V2_2D_rev_axis0 | 二维逆序模式 |
| TC25 | Precision_ErrorAcc | 万级长度误差累积 |
| TC26 | Precision_LargeSmall | 大数吞并小数现象 |
| TC27 | Precision_F16vsF32 | 半精度与单精度对比 |
| TC28 | Precision_Cancellation | 灾难性消去效应 |
| TC29 | Precision_0.1Acc | 0.1无法精确表示的累积 |
| TC30 | Int32_Overflow | 32位整型溢出行为 |
| TC31 | CubePath_12800x512_f32 | 矩阵加速路径(float32) |
| TC32 | CubePath_fp16 | 矩阵加速路径(fp16) |
三、代码覆盖率评估
全部32个用例均通过验证,无失败项。
各模块覆盖率统计(gcov插桩)
| 源文件 | 行覆盖率 | 分支覆盖率 | 总行数 | 备注 |
|---|---|---|---|---|
| op_api/aclnn_cumsum.cpp | 39.72% | 32.41% | 715 | V1/V2调度逻辑入口 |
| op_api/cumsum.cpp | 23.80% | 53.49% | 584 | 设备路由决策层 |
| op_host/arch35/cumsum_tiling.cpp | 83.55% | 55.26% | 468 | 核心分块逻辑,覆盖最广 |
| op_host/arch35/cumsum_tiling_ascendc_arch35.cpp | 48.30% | 54.61% | 1178 | 浮点型 AscendC 实现 |
| op_host/arch35/cumsum_tiling_ascendc_int_arch35.cpp | 38.94% | 39.44% | 796 | 整型 AscendC 实现 |
关键路径激活情况
- 矩阵加速路径:由 TC31 和 TC32 成功触发
- AiCore 浮点路径:TC01–TC03, TC08, TC11–TC24
- AiCPU 类型转换路径:TC04–TC07, TC09
- dtype 强制转换:TC09 (bool→int64), TC10 (int32→float32)
- exclusive 模式:TC12, TC14, TC23
- reverse 模式:TC13, TC14, TC24
- dim=0 路径:TC01, TC02
- dim=1 路径:TC03, TC22, TC23
- 异常处理路径:TC21(空指针拒绝)
四、数值精度深度分析
场景一:误差随长度累积(TC25)
输入:10000 个 1.0f 元素
观察结果:最终输出 ≈10000.0,最大偏差小于 1.0,在允许范围内。
理论分析:float32 的 ε≈1.19e-7,理论误差上限为 10000×ε≈1.19e-3。实际误差显著低于此值,表明硬件可能采用了补偿求和或树形归约等优化策略,使误差增长趋近 O(log n) 而非 O(n)。
场景二:大小数混合丢失(TC26)
输入:[1e8, 1e-6] 重复四次
结果:第二个元素仍为 1e8,微小增量被完全忽略。
原因:在 1e8 数量级下,float32 的最小可分辨单位(ULP)约为 8,远大于 1e-6,导致"大数吞噬小数"现象。这是浮点表示有效位数限制的直接体现。
场景三:FP16 vs FP32 对比(TC27)
输入:100 个 0.1f
输出对比:
- float32 结果 ≈10.000001,误差 ~1e-6
- float16 输出为 10.0(0x4900),误差为0(因10.0可在fp16中精确表示)
场景四:正负抵消(TC28)
输入:[1.0000001, -1.0] 重复500次
输出:奇数位置期望接近0,实测最大偏差 ~1e-7
机制解释:每次相减产生约 1e-7 的残差,属于正常 ULP 波动。此类"灾难性消去"会显著降低有效数字位数,是浮点计算中的经典问题。
场景五:0.1 的表示缺陷(TC29)
输入:1000 个 0.1f
输出:实际值 ≈100.000015,相对误差 ~1.5e-7
根源:0.1 在 float32 中实际存储为 0.10000000149,每步引入 ~1.49e-9 的量化误差,千次累加后总误差达 ~1.49e-6,与实测相符。说明误差不仅来自运算过程,也源于输入本身的离散化偏差。
场景六:整型溢出行为(TC30)
输入:四个 1e9 整数组成的向量
输出:
r[0]=1000000000
r[1]=2000000000
r[2]=-1294967296 // 3e9 > 2147483647,溢出
r[3]=-294967296 // 继续累加负值
结论:INT32 最大值为 2147483647,超出部分按补码截断,无异常抛出。开发者需自行预防溢出风险,必要时改用 INT64 或提前缩放数据。