news 2026/9/19 22:17:01

CANN ops-math 中 Pdist 算子迭代 3 验收报告深度解读:性能优化、双 API 覆盖与 97 用例全量回归

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANN ops-math 中 Pdist 算子迭代 3 验收报告深度解读:性能优化、双 API 覆盖与 97 用例全量回归

CANN ops-math 中 Pdist 算子迭代 3 验收报告深度解读:性能优化、双 API 覆盖与 97 用例全量回归

【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math

导读

本文基于 CANN ops-math 仓库中 Pdist 算子迭代 3 验收报告,完整解读 Pdist(p-范数成对距离)算子第三轮迭代的验收结果:13 项优化变更(向量化热点路径、动态 UB 预算、防溢出加固、Forward API 扩展)、97 条用例在 Real NPU 模式下 100% 通过、精度余量远超社区标准。阅读本文后,你将掌握 Pdist 算子的功能语义、双段式 ACLNN 接口调用方式、tilingKey 三分支计算路径、本轮优化背后的源码级原理,以及一套可复用的算子验收方法与 MERE/MARE 精度判定标准。

一、Pdist 算子是什么:功能语义与原型设计

1.1 功能说明与计算公式

Pdist 算子计算输入二维 tensor 各行之间的 p-范数成对距离,等价于 PyTorch 的torch.nn.functional.pdist(见 README.md)。输入为形状(N, M)的二维 tensor,输出为形状(N*(N-1)/2,)的一维 tensor,按上三角行优先顺序排列。

计算公式按 p 值分为三种情形(与 aclnnPdist.md 一致):

$$ \text{dist}(i, j) = \begin{cases} \left( \sum_{k=0}^{M-1} |x_{ik} - x_{jk}|^p \right)^{1/p} & 0 < p < \infty \ \sum_{k=0}^{M-1} \mathbb{1}(x_{ik} \neq x_{jk}) & p = 0 \ \max_{k=0}^{M-1} |x_{ik} - x_{jk}| & p = \infty \end{cases} $$

即:0 < p < ∞为闵可夫斯基距离(Minkowski),p=0为汉明距离(Hamming,不等元素计数),p=∞为切比雪夫距离(Chebyshev,最大绝对差)。输出数量为 C(N,2) = N·(N-1)/2 个元素。

1.2 算子原型与约束

参数名类别描述数据类型数据格式
x输入张量二维输入 tensor,形状 (N, M),N ≥ 2, M ≥ 1FLOAT16、FLOAT32ND
p属性距离参数,p ≥ 0,含 inf。可选,默认 2.0FLOAT-
y输出张量一维输出,形状 (N*(N-1)/2,)与 x 一致ND

约束说明:

  • 输入 x 必须为二维 tensor 且 dim(0) ≥ 2;
  • 输出 y 的数据类型必须与 x 一致;
  • p 取值范围为 [0, +∞];
  • N 上限为 65535computeNum使用 uint64_t 且新增MAX_ROWS=65535校验,N 过大时输出规模超出实际内存限制)。

产品支持方面,README 声明支持 Atlas A2 训练/推理系列产品与 Atlas A3 训练/推理系列产品;接口文档进一步明确 Ascend 950PR/Ascend 950DT、Atlas 200I/500 A2 推理产品、Atlas 推理系列产品、Atlas 训练系列产品均不支持。算子注册代码中AICore().AddConfig("ascend910b")AddConfig("ascend910_93")(见 pdist_def.cpp),与报告测试环境 Ascend910B (DAV_2201) 相互印证。

1.3 双 API 设计:aclnnPdist 与 aclnnPdistForward

迭代 3 的关键验收内容之一就是Forward API 扩展。两个接口功能一致,区别仅在于 p 参数的传入方式:

接口p 参数类型说明
aclnnPdistdouble p标量直接传入(见 aclnnPdist.md)
aclnnPdistForwardconst aclScalar *p通过aclCreateScalar创建 aclScalar 传入(见 aclnnPdistForward.md)

两个接口均采用 CANN 算子的两段式调用范式:先调用aclnnPdistGetWorkspaceSize/aclnnPdistForwardGetWorkspaceSize获取 workspace 大小与执行器,再调用aclnnPdist/aclnnPdistForward执行计算。第一段接口完成入参校验,错误码约定如下:

返回值错误码描述
ACLNN_ERR_PARAM_NULLPTR161001self、p、out 存在空指针
ACLNN_ERR_PARAM_INVALID161002self 的数据类型不在支持范围之内
ACLNN_ERR_PARAM_INVALID161002self 不是二维 tensor 或 N<2
ACLNN_ERR_PARAM_INVALID161002p < 0
ACLNN_ERR_PARAM_INVALID161002out 的 shape 与 N*(N-1)/2 不匹配

二、迭代 3 验收全景:97 用例全量通过

2.1 基本信息与验收状态

字段
算子名称Pdist
ACLNN 接口aclnnPdist / aclnnPdistForward
迭代编号3(含性能优化 + Forward API 扩展)
验收日期2026-05-13
测试方式C++ 原生测试(Real NPU + Mock CPU Golden)
测试文件test_aclnn_pdist.cpp
运行脚本run.sh

验收状态:通过。关键指标为:总用例 97(aclnnPdist 85 + aclnnPdistForward 12),通过 97,失败 0,通过率 100%。

2.2 多维度用例分布

按 API 分布:aclnnPdist 85/85(100%),aclnnPdistForward 12/12(100%)。

按级别分布:L0 门槛用例 8/8,L1 功能/精度用例 77/77,Forward API 用例 12/12。

按 dtype 分布:float32 63/63,float16 34/34(覆盖报告中的核心验收标准"全 dtype 用例通过")。

按 p 值分支分布——这是与算子内部实现直接对应的关键维度:

p 值分支用例数通过数
p=0不等计数(tilingKey=0)1818
p=0.5通用路径(tilingKey=1)77
p=1曼哈顿距离(tilingKey=1)1212
p=2欧氏距离(tilingKey=1)3030
p=3通用路径(tilingKey=1)66
p=10通用路径(tilingKey=1)44
p=infReduceMax(tilingKey=2)1717

按场景分布:核心功能直通(L0)8、p 值分支覆盖 14、dtype×p 全交叉 12、形状边界 7(N=2/M=1 最小、N=2/M=8、N=3/M=1、N=3/M=4、N=5/M=16)、多核切分 16(N=20 共 190 对、N=50 共 1225 对)、UB 分块大 M 16(M=256/1024/2048)、精度敏感 16(相同行、全零、全相同、负值、混合、fp16 边界、极小值、宽范围、大数值)、最小形状×p 组合 6、Forward API 覆盖 12。

2.3 精度验证:MERE/MARE 社区标准

验收采用 CANN 社区标准的**平均相对误差(MERE,Mean Relative Error)与最大相对误差(MARE,Max Relative Error)**判定:

dtypeMERE ThresholdMARE Threshold
float322^-13 = 1.22e-0410 × 2^-13 = 1.22e-03
float162^-10 = 9.77e-0410 × 2^-10 = 9.77e-03

Real NPU 代表性精度统计(测试代码中的精度比较逻辑可参见 test_aclnn_pdist.cpp,其中CompareResults对 NaN 对按 PyTorch 语义跳过、对 Inf 对要求符号一致,mare_threshold = 10.0 * threshold):

dtype场景最大MERE最大MARE阈值判定
float32L1_multicore_N50_M32_pinf (1225 elems)1.91e-075.01e-061.22e-04通过
float32L1_veryLargeM_N5_M2048_p2 (10 elems)1.43e-073.86e-071.22e-04通过
float32L1_largeN_N100_M16_p2 (4950 elems)5.45e-086.74e-071.22e-04通过
float32Fwd_multicore_N50_M32_p2 (1225 elems)5.13e-082.96e-071.22e-04通过
float32Fwd_largeM_N10_M1024_p2 (45 elems)8.10e-083.08e-071.22e-04通过
float16L1_multicore_N50_M32_p2 (1225 elems)1.86e-044.81e-049.77e-04通过
float16L1_largeM_N10_M1024_p2 (45 elems)2.13e-043.91e-049.77e-04通过

所有用例的 MERE 与 MARE 均远低于阈值,精度余量充足(约 2~4 个数量级)。

2.4 测试执行环境

项目
OSLinux 5.10.0-60.139.0.166.oe2203.aarch64
CANNcann-9.0.0-beta.2
芯片Ascend910B (DAV_2201)
编译器g++(std=c++17, -O2)
测试模式Real (NPU)

三、迭代 3 优化变更源码级解读:13 项改动逐一拆解

本轮验收的核心亮点是 13 项优化变更,全部在性能优化后完成全量回归且无退化。以下结合源码逐项拆解。

3.1 热点路径向量化(3 项,均在 op_kernel/pdist.h)

Hamming 距离向量化(p=0 路径):由标量循环改为CompareScalar + Select + ReduceSum全向量路径。对应源码 pdist.h 中tilingKey_ == 0分支:先CompareScalar(cmpLocal, src1, 0.0f, CMPMODE::NE, ...)对差值做不等比较生成 mask,再Duplicate(src2, 1.0f, ...)填充 1.0,Select按 mask 选择 1.0 或 0.0,最后ReduceSum求和得到不等元素计数。

ApplyInvP 向量化:对求和结果施加 1/p 次幂时,由标量 Ln/Exp 循环改为CompareScalar(mask) + Select + 向量 Ln/Muls/Exp。源码 pdist.h 中:先以CompareScalar(..., CMPMODE::GT, ...)生成"大于 0"掩码,Select将非正值替换为 1.0 以避免 Ln(0) 异常,再按 p 值选择 Sqrt(p=2)、Ln→Muls(invP)→Exp(通用路径),最后再次Select将原非正值恢复为 0.0——这保证了全零距离行(相同行)的输出严格为 0,与精度敏感用例中的"相同行/全零"场景对应。

WriteOutput fp16 向量化:fp16 输出路径由逐元素SetValue循环改为单条 Cast 指令。见 pdist.h:Cast(outFp16, outLocal, RoundMode::CAST_RINT, 8)一次完成 float→half 转换后DataCopyPad写出。

3.2 累加去同步与 workBuf 动态化

累加去同步ReduceSum结果改为用scalar 寄存器累加,每 k 仅 1 次SetValue。见 pdist.h:ProcessBlock中通过tempLocal.GetValue(0)同步取回 chunk 归约结果,在 scalar 层累加(accum += chunkVal,tilingKey==2 时取chunkVal > accum的最大值),每处理完一整行才执行一次outLocal.SetValue(k, accum),显著减少 UB 与 scalar 间的同步次数。

ReduceSum workBuf 动态化:硬编码 256 floats 改为GetReduceSumMaxMinTmpSize动态计算。Host 侧 pdist_tiling.cpp 的ComputeReduceBufSize同时查询 ReduceSum 与 ReduceMax 的最小临时尺寸并取较大者、按 32 字节对齐,Kernel 侧pipe.InitBuffer(workBuf, reduceBufSize_ + PDIST_SUM_TENSOR_SIZE * sizeof(float))(pdist.h)据此动态分配,使 UB 预算随分块大小自适应而非固定浪费。

3.3 健壮性加固(4 项)

computeNum 防溢出uint32_t → uint64_t,新增MAX_ROWS=65535校验。常量定义于 pdist_constants.h(PDIST_MAX_SUPPORTED_ROWS = 65535),Host 侧 pdist_tiling.cpp 在解析输入时校验,computeNum在 Tiling 中按static_cast<uint64_t>(rows) * (rows - 1) / 2计算(pdist_tiling.cpp),Kernel 侧PdistTilingData::computeNum亦为 uint64_t(见 pdist_tiling_data.h)。

attr p 语义修正REQUIRED → OPTIONAL,匹配默认值 2.0 语义。见 pdist_def.cpp:this->Attr("p").AttrType(OPTIONAL).Float(2.0f);,与 README"可选,默认 2.0"及 Tiling 中pValue = 2.0f的兜底逻辑(pdist_tiling.cpp)闭环一致——attrP->GetFloat(0)返回空指针时使用默认 2.0。

UB 预算防下溢ComputeUbBudget新增ubSize ≤ reservedBytes校验。见 pdist_tiling.cpp:先按 reduceBuf + 两段 SUM_TENSOR(fp16 再叠加 fp16 缓冲)计算 reservedBytes,若 UB 总量不足则直接报错返回,避免出现ubTensorEachLoop == 0的除零/下溢。

dead fields 清理:删除numEachCore / numEachLoop两个无用字段,PdistTilingData结构保持精简(见 pdist_tiling_data.h)。

3.4 多核切分预计算与核数收敛

多核切分预计算:由 Host 预算numBlockEachCore等切分参数,Kernel 侧零除法。见 pdist_tiling.cpp 的ComputeCoreSplit:按"每块 8 个输出元素"(PDIST_DATA_EACH_BLOCK = 8)将 computeNum 均匀切分到各核,剩余部分拆分为完整块(lastNumsBlocks)与不完整块(lastNumsNoneFullBlock)。Kernel 侧 pdist.h 的Process()完全基于这些预算值循环,无任何运行时除法。

核数收敛usedCores = min(cores, (computeNum+7)/8)。当输出对数不足时收敛实际使用核数,避免空核空转;neededCores至少为 1(pdist_tiling.cpp)。

共享常量头DATA_EACH_BLOCK / SUM_TENSOR_SIZE / MAX_ROWS统一收敛到 pdist_constants.h,Host 与 Kernel 共用,消除魔法数字漂移。

3.5 Forward API 支持

修正 aclnn_pdist_forward.h 的 include 路径并新增 ST 用例覆盖(12 条),使aclnnPdistForwardaclnnPdist达到同等验收标准。接口层入口可参见 pdist.cpp:l0op::Pdist通过INFER_SHAPE推导输出形状、ADD_TO_LAUNCHER_LIST_AICORE注册到 AICore 执行器。

四、Kernel 三分支计算路径:tilingKey 的源码映射

验收报告中"按 p 值分支分布"的 tilingKey(0/1/2)与算子实现一一对应。Host 侧 pdist_tiling.cpp 依据 p 值决定 tilingKey:

  • pValue == 0.0ftilingKey=0:Hamming 不等计数(CompareScalar(NE)+Select+ReduceSum);
  • std::isinf(pValue)tilingKey=2:Chebyshev 切比雪夫(Abs+ReduceMax,见 pdist.h);
  • 其余 →tilingKey=1:通用闵可夫斯基路径,其中 p=1 为曼哈顿(仅Abs后直接ReduceSum)、p=2 为欧氏(Abs+Mul平方 +ReduceSum)、其余 p 走Abs → Ln → Muls(p) → Exp → ReduceSum(pdist.h)。

值得注意的工程细节是 p=2 与 p=1 的专用快速路径被显式特判,避免通用 Ln/Exp 幂运算的开销与精度损失——这正是报告"p 值分支覆盖 14 用例"(p in {0, 0.5, 1, 2, 3, 10, inf} × fp16/fp32)要验证的。此外,行列索引反解使用GetIJFromIndex(pdist.h):将线性输出下标 idx 映射回 (rowI, rowJ),采用整数牛顿迭代求解三角形序号反函数,全程无浮点除法,是"kernel 零除法"优化的一部分。

五、迭代 3 验收标准达成情况与结论

验收标准达成状态说明
全 dtype 用例通过通过float32: 63/63, float16: 34/34
边界用例通过通过最小形状(N=2,M=1)、大N(N=100)、大M(M=2048)、fp16 边界值
双 API 覆盖通过aclnnPdist: 85/85, aclnnPdistForward: 12/12
累计通过率 = 100%通过97/97 = 100%,无回归
性能优化无回归通过向量化/去同步/动态 workBuf 等优化后全量回归通过

结论:迭代 3 验收通过。全部 97 个用例(aclnnPdist: 85 + aclnnPdistForward: 12)在 Real NPU 模式下均 100% 通过、无回归;本轮 13 项优化变更(向量化热点路径、动态 UB 预算、防溢出加固、Forward API 扩展等)经全量回归验证,精度远优于社区标准阈值。

六、延伸阅读:如何在仓库中复现与验证

Pdist 算子位于 experimental/math/pdist 目录,完整目录结构见 README.md,可按以下线索深入:

  • 接口调用示例:test_aclnn_pdist.cpp(p 以 float 传入)与 test_aclnn_pdist_forward.cpp(p 以 aclScalar 传入),二者均为可直接编译运行的两段式调用完整样例;
  • 验收测试:tests/st/test_aclnn_pdist.cpp(97 条用例源码,内含 CPU Golden 参考实现ComputeGoldenPdist与 MERE/MARE 比较器)及 tests/st/run.sh(运行脚本);
  • 单元测试:tests/ut/op_host(Tiling + InferShape UT 14 cases)、tests/ut/op_api(ACLNN 接口 UT 9 cases)、tests/ut/op_kernel(Kernel CPU 模拟器 UT,含数据生成脚本pdist_data/gen_data.py);
  • 接口文档:aclnnPdist.md 与 aclnnPdistForward.md 提供完整函数原型、参数表与错误码表。

报告本身的测试方式为"Real NPU + Mock CPU Golden":Golden 参考实现在 test_aclnn_pdist.cpp 中以 double 精度计算三种 p 分支,与 NPU 输出按 MERE/MARE 阈值比对,NaN/Inf 对按 PyTorch 语义特判——这一"Golden 对照 + 相对误差阈值"的验收方法论同样适用于其他数学算子的质量把关。

【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 22:16:26

Mamba架构深度解析:从状态空间模型到选择性SSM的工程实践

1. 从Transformer的痛点说起&#xff1a;为什么会有Mamba如果你这两年一直在跟进序列建模这个方向&#xff0c;大概率会有一种感觉&#xff1a;Transformer 已经把能做的都做了&#xff0c;从 NLP 一路杀到视觉、语音、时序预测&#xff0c;好像没什么它搞不定的。但真正把 Tra…

作者头像 李华
网站建设 2026/9/19 22:15:37

基于TMS320VC5402的定点指纹识别系统设计与优化

简介&#xff1a;基于TMS320VC5402 DSP的指纹识别系统设计文档&#xff0c;面向嵌入式系统、生物识别技术方向的工程师及在校学生&#xff0c;可服务于课程设计、毕业设计或项目预研。资源为docx格式&#xff0c;共1个文件&#xff0c;压缩包大小553KB&#xff0c;内容围绕指纹…

作者头像 李华
网站建设 2026/9/19 22:12:24

Spring5核心容器深度解析:Bean装配、生命周期与事务失效排查

1. 从"会用"到"用对"&#xff1a;Spring5 核心容器到底在管什么很多人学 Spring 到第五篇的时候&#xff0c;心里其实有个坎&#xff1a;前面几篇把 IoC、DI、Bean 生命周期、AOP 都过了一遍&#xff0c;代码也能跑起来&#xff0c;但一旦遇到真实项目里的…

作者头像 李华
网站建设 2026/9/19 22:11:05

谷歌浏览器截全屏长图全攻略:从开发者工具到手机端

谷歌浏览器如何截全屏长图&#xff08;非常实用&#xff0c;手机和电脑都适用&#xff09;平时截图截到想摔鼠标的经历&#xff0c;大家应该都有过。尤其是打开一个网页想完整保存整个页面内容&#xff0c;或者做资料整理、给同事反馈问题时&#xff0c;怎么截都只能截到当前屏…

作者头像 李华
网站建设 2026/9/19 22:09:45

Unity AssetBundle崩溃排查:LoadAsset_Internal并非内存溢出元凶

1. 崩溃日志里那个被冤枉的"内存溢出"如果你在Unity项目里排查过崩溃问题&#xff0c;大概率见过这样的场景&#xff1a;玩家反馈游戏突然闪退&#xff0c;你拿到日志一看&#xff0c;满屏都是Out of Memory或者Could not allocate memory&#xff0c;第一反应就是&q…

作者头像 李华