CANN ops-math Polar 算子 pybind 基线测试框架:从 case 设计到 msprof 性能门禁的完整实践
2026/9/19 12:51:38 网站建设 项目流程
  • 算子库
  • 人工智能
  • CANN

【免费下载链接】ops-math

本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。

项目地址:https://gitcode.com/cann/ops-math
点击查看免费下载

导读

本文围绕 CANN 数学基础算子库ops-mathPolar(极坐标复数构造)算子的 pybind 基线测试框架展开,该框架位于experimental/math/polar/tests/pybind_baseline/,用于在 NPU 环境下对自定义 Polar 算子进行正确性验证与性能基线校验。读完本文,你将掌握:Polar 算子的数学语义与 l0 参考实现(sin/cos/mul/complex拼接链)的对齐方式、测试框架六个文件的职责划分与调用关系、8 个 case 的广播/边界/性能覆盖设计、msprof计时与get_time.py均值提取的完整流程,以及如何新增 case、如何将性能基线从哨兵值回填为真实门禁值。

框架定位:验证什么,对齐什么

Polar 算子的功能定义如下:

out = input · (cos(angle) + i·sin(angle))
  • input(极坐标模长,abs)与angle(幅角,弧度)均为fp32(FLOAT),且支持 NumPy 广播input.dimangle.dim可不一致);
  • outcomplex64(COMPLEX64),shape 为inputangle广播后的 shape。

该框架的对齐基准是 ops-math 仓中的 l0 拼接版参考实现 math/polar/op_api/aclnn_polar.cpp。从源码可以看到,该 aclnn 入口在非 RegBase 平台上正是用 l0 算子拼接实现 Polar 的:

// math/polar/op_api/aclnn_polar.cpp 中 aclnnPolarGetWorkspaceSize 的核心计算链 auto angleSin = l0op::Sin(angleContiguous, uniqueExecutor.get()); auto angleCos = l0op::Cos(angleContiguous, uniqueExecutor.get()); auto absSin = l0op::Mul(angleSin, inputContiguous, uniqueExecutor.get()); auto absCos = l0op::Mul(angleCos, inputContiguous, uniqueExecutor.get()); output = l0op::Complex(absCos, absSin, out->GetDataType(), uniqueExecutor.get());

即:先对anglesin/cos,再分别与input相乘得到虚部/实部,最后通过Complex组装出复数,并通过l0op::ViewCopy拷贝到非连续的out上。参数检查方面,该文件还通过DTYPE_SUPPORT_LIST = {DT_FLOAT}OUTPUT_DTYPE_SUPPORT_LIST = {DT_COMPLEX64}约束输入输出类型一致,通过OP_CHECK_BROADCAST_AND_INFER_SHAPE约束广播语义、MAX_DIM_LEN = 8约束维度上限(约束说明 中亦明确:维度不超过 8 维)。这意味着 pybind 基线测试的输入构造必须覆盖"广播""fp32→complex64""高维"这些参考实现同样处理的场景,测试才有对齐意义。

文件构成与职责

框架目录结构与 S8case_910b/<Op>/完全一致,共 6 个文件,其中 3 个逐字复用 S8(算子无关),3 个为 Polar 适配:

文件来源说明
common/pytorch_npu_helper.hpp逐字复用 S8EXEC_NPU_CMD通用胶水(算子无关,动态 dlopenaclnnPolar
setup.py/get_time.py逐字复用 S8pybind wheel 构建 / msprofop_summary*.csv取 [20:40] 均值
extension/custom_op.cppPolar 适配EXEC_NPU_CMD(aclnnPolar, input, angle, result),result=complex64、shape=broadcast(input,angle),50 轮供 msprof 取平均
test_op.pyPolar 适配8 个 case + 复数 verify(view_as_real拆 real/imag 当 fp32,rtol/atol=1e-4)
run.shPolar 适配重建 wheel → msprof 计时 → get_time → 基线校验;LD 指向custom_mathvendor

注:与 pybind 目录(experimental/math/polar/tests/pybind/)相比,本pybind_baseline目录的差异仅在common/pytorch_npu_helper.hppGetOpApiFuncAddr实现上,详见下文"基线变体"一节。

前置准备:编译并安装自定义 Polar 算子

框架只能在 NPU 环境运行(依赖torch_npu),本地无法自检——这与 S8 相同。在第一次运行或修改 kernel 后重做以下步骤:

# 在 ops-math 仓编译并安装自定义 Polar 算子 bash build.sh --pkg --soc=ascend910b --ops=polar -j16 # A3: --soc=ascend910_93 ./build_out/cann-ops-math-*linux*.run

构建产物会安装到${ASCEND_HOME_PATH}/opp/vendors/custom_math/,即算子的 op_api 库(libcust_opapi.so)与 kernel 实现均落在custom_mathvendor 下。run.sh中正是通过如下 LD 配置让测试进程能找到该 vendor 下的 op_api 动态库:

# run.sh 中的 vendor 路径注入(custom_math 为主,customize 兜底) _OPP="${ASCEND_OPP_PATH:-${ASCEND_HOME_PATH}/opp}" export LD_LIBRARY_PATH="$_OPP/vendors/custom_math/op_api/lib/:$_OPP/vendors/customize/op_api/lib/:$LD_LIBRARY_PATH"

用法:一条命令完成"重建 wheel → 计时 → 校验"

cd case_910b/Polar # 即本框架目录 experimental/math/polar/tests/pybind_baseline/ bash run.sh 2 # 跑 case2(广播主战场)

run.sh的完整执行流水线为:

  1. 每次 run 都重建 wheel:删除./dist ./build ./custom_ops.egg-info,执行python3 setup.py build bdist_wheel,再pip3 install dist/custom_ops*.whl --force-reinstall。这样避免上次跑别的 op 留下的 wheel signature 不匹配;
  2. msprof 计时timeout 180 msprof --application="python3 test_op.py $1",超时(退出码 124)即判失败退出;
  3. 提取耗时time_use=$(($(python3 get_time.py))),解析 msprof 生成的op_summary*.csv
  4. 基线校验:当前time_base=9999999999999为哨兵值,仅做正确性门禁(time_use=0直接报[ERROR] Performance not achieved);待硬件实测出 l0 参考实现耗时后回填真实基线;
  5. 输出结论echo "Operator performance and accuracy have passed"

预期输出为:xxx verify result pass!(逐 case 打印)+time_base = ... time_use = ...

核心实现深读

1.extension/custom_op.cpp:Polar 适配点

该文件是唯一需要按算子改写的 pybind 扩展:

// 关键调用链:50 轮重复执行,供 msprof 取平均 auto out_shape = at::infer_size(input.sizes(), angle.sizes()); // numpy 广播规则推导输出 shape auto round = 50; // msprof 取平均:重复 50 次 for (size_t i = 0; i < round; i++) { result = at::empty(out_shape, input.options().dtype(c10::kComplexFloat)); EXEC_NPU_CMD(aclnnPolar, input, angle, result); } return result;

要点:

  • at::infer_size(input.sizes(), angle.sizes())在扩展侧按 NumPy 广播规则推导输出 shape,与参考实现中的OP_CHECK_BROADCAST_AND_INFER_SHAPE语义对齐;
  • 输出张量以c10::kComplexFloat创建,对应OUTPUT_DTYPE_SUPPORT_LISTDT_COMPLEX64
  • 重复 50 轮使 msprof 能采集到足够多的执行样本,供get_time.py取 [20:40] 区间均值;
  • 对外注册myops.my_op(Tensor input, Tensor angle) -> Tensor并绑定PrivateUse1(NPU)后端实现,PYBIND11_MODULE暴露custom_op函数供 Python 侧custom_ops_lib.custom_op(input_npu, angle_npu)调用。

2.test_op.py:8 个 case 与复数精度比对

case 数据通过case_data字典维护,新增 case 直接加键即可(与 S8 习惯一致),键名形如'caseN',值为{'input': np.ndarray(fp32), 'angle': np.ndarray(fp32)}

caseinput shapeangle shape考点
1[2,6,10][2,6,10]同 shape 基础
2[4,1,8][4,5,8]广播:低维→高维(新增功能主战场)
3[1][3,4,5]广播:标量 input
4[8,1][1,7]广播:双向 → [8,7]
5[4096,4096][4096,4096]大 shape(性能)
6[3,5,17,269]高维 + inner 非 32B 对齐(1076B/行)
7[1][1]1 元素边界
8[64,1024][64,1024]大角度(Sin/Cos 范围归约压力)

case 设计意图与数据细节:

  • case 1inputuniform(0,10)angleuniform(-3.14,3.14),覆盖常规同 shape 路径;
  • case 2[4,1,8] × [4,5,8]input低维向angle高维广播,是新增广播功能的主战场
  • case 3input=[1](numel=1)广播到[3,4,5],验证标量输入路径,angle范围放大到±6.28
  • case 4[8,1] × [1,7]双向广播到[8,7]inputuniform(-10,10)覆盖负模长;
  • case 5[4096,4096]大 shape,inputuniform(0,100)拉大模长量级,用于性能采集;
  • case 6[3,5,17,269]高维,最后一维 269 使每行 fp32 数据为 1076B,非 32B 对齐,考验 kernel 的访存/对齐处理;
  • case 7input=[2.5]angle=[π/4],1 元素边界,angle 用np.pi/4便于人工核对out ≈ 2.5·(√2/2 + i·√2/2)
  • case 8angleuniform(-10000,10000)大角度,专门施加 Sin/Cos 的范围归约压力

复数结果的 verify 逻辑(verify_result)是框架的关键设计:因为 AscendOpTest 对复数采用"拆实虚部当 fp32 比对"的同口径,这里用torch.view_as_real把 complex64 拆成最后一维为 2(real/imag)的 fp32 张量再逐元素比对:

real_f32 = torch.view_as_real(real_result.to(torch.complex64)).to(torch.float32) golden_f32 = torch.view_as_real(golden.to(torch.complex64)).to(torch.float32) rtol, atol = 1e-4, 1e-4

比对采用abs_diff <= atol | rel_diff <= rtol的宽松判据(相对误差分母取max(|real|, |golden|),为 0 时以10e-10兜底),并显式容忍双方同为 NaN 的情况。失败时打印前 10 个错误点的索引、NPU 值、CPU golden 值与绝对误差便于定位。golden 由torch.polar生成,且先经torch.broadcast_tensors显式广播再 contiguous,确保 golden 与广播语义一致:

a, b = torch.broadcast_tensors(t_in, t_ang) return torch.polar(a.contiguous(), b.contiguous()) # complex64

3.get_time.py:msprof 结果提取与均值

遍历当前目录下所有op_summary*.csv,取每行Task Duration(us)列,转换为纳秒int(float(time_use) * 1000000))后对time_use_list[20:40]求均值并打印。取 [20:40] 区间是为了避开前 20 轮(包含首轮初始化、JIT/编译开销、cache 冷启动等异常样本),用中间稳定的 20 个样本代表稳态性能。

4.setup.py:pybind wheel 构建

通过torch_npu.utils.cpp_extension.NpuExtension编译./extension/custom_op.cppcustom_ops_lib扩展,并显式追加 torch_npu 安装路径下的 ACL 头文件目录-I <torch_npu>/include/third_party/acl/inc。wheel 名为custom_ops(version 1.0),run.shpip3 install dist/custom_ops*.whl --force-reinstall强制重装。

5.common/pytorch_npu_helper.hpp:EXEC_NPU_CMD 与"基线变体"

该头文件是 S8 逐字复用的算子无关胶水层,核心是EXEC_NPU_CMD(aclnn_api, ...)宏。它通过GetOpApiFuncAddr动态解析aclnnPolaraclnnPolarGetWorkspaceSizeInitHugeMemThreadLocal等符号,完成GetWorkspaceSize → 申请 workspace → CommonOpExecutorRun三步调用,并把参数(at::Tensor/at::Scalar等)统一ConvertType成 ACL 侧的aclTensor/aclScalar/aclIntArray等句柄,运行结束后ReleaseConvertTypes释放。

本框架的关键差异点(也是pybind_baseline的命名由来)在GetOpApiFuncAddr

// [BASELINE 变体] 强制只解析系统 libopapi.so(l0 参考实现),跳过 libcust_opapi.so, // 故即使自定义 Polar 已安装,本测试调用的也是系统 aclnnPolar = l0 拼接参考基线。 static auto opApiHandler = GetOpApiLibHandler(GetOpApiLibName()); // libopapi.so

即使自定义 Polar 已安装到custom_mathvendor,基线测试也强制只从系统libopapi.so解析aclnnPolar,从而保证被测对象始终是l0 拼接参考实现,与 math/polar/op_api/aclnn_polar.cpp 形成严格对齐。与之对比,experimental/math/polar/tests/pybind/目录下的同名头文件走的是常规解析路径(libcust_opapi.so优先),用于测自定义实现本身——两套目录一个测"自研实现 vs 系统基线",一个测"自研实现本身的正确性/性能",建议结合使用。

与 S8 的差异(仅构建层)

维度S8Polar(本框架)
构建方式每算子自带S8/<Op>/build.shauto_submit.sh直接 build构建走ops-math 仓级build.sh --pkg --ops=polar,与本测试框架解耦
upload/run 阶段auto_submit.sh -o <Op>直接可用auto_submit.sh -o Polar仍可复用其 upload/run 阶段;但 build 阶段需替换为 ops-math 命令(待算子工程落地后再接线)

这是"仅构建层"的差异:测试用例、计时提取、基线校验的编排逻辑与 S8 保持一致,只是 Polar 的算子包由 ops-math 仓级构建产出(安装到custom_mathvendor),而不是由 S8 目录内脚本独立构建。

注意事项与基线回填指引

  1. 精度阈值:当前为 fp32rtol/atol=1e-4;任务要求采用"AscendOpTest 默认阈值",待 AscendOpTest 工具实际阈值确认后,在test_op.py:verify_result回填(阈值调整点为rtol, atol = 1e-4, 1e-4一处);
  2. 性能基线run.sh:time_base当前为哨兵值9999999999999(仅正确性门禁);硬件实测出 l0 参考实现耗时后需回填真实基线(任务要求自研实现性能 ≥ 基线 95%,即time_use <= time_base / 0.95量级的门禁语义),回填点即run.shtime_base=...一处;
  3. 运行环境:框架只能在 NPU 环境运行(依赖torch_npumsprof),本地无法自检——与 S8 相同;
  4. 每次运行都会重建 wheel 并清理PROF*目录,若手动跑python3 test_op.py N单测正确性,请先保证 wheel 已安装。

小结

pybind_baseline目录为 Polar 算子提供了一个与 S8 框架同构、与 l0 参考实现严格对齐的 NPU 测试闭环:custom_op.cpp负责以 pybind 扩展形式反复触发aclnnPolartest_op.py用 8 个精心设计的 case(同 shape/三种广播形态/大 shape/非对齐高维/边界/大角度)覆盖正确性,run.sh + msprof + get_time.py完成性能采样,pytorch_npu_helper.hpp的基线变体保证被测对象锁定为系统 l0 拼接版。若要在仓库中继续深入,可对照阅读 l0 参考实现、Polar 算子总览(含参数表与约束说明)、同结构的 pybind 目录 以及 AOT 测试。

  • 算子库
  • 人工智能
  • CANN

【免费下载链接】ops-math

本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。

项目地址:https://gitcode.com/cann/ops-math
点击查看免费下载

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

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

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

立即咨询