- 算子库
- 人工智能
- CANN
【免费下载链接】ops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
导读
本文围绕 CANN 数学基础算子库ops-math中Polar(极坐标复数构造)算子的 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.dim与angle.dim可不一致);out为complex64(COMPLEX64),shape 为input与angle广播后的 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());即:先对angle求sin/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 | 逐字复用 S8 | EXEC_NPU_CMD通用胶水(算子无关,动态 dlopenaclnnPolar) |
setup.py/get_time.py | 逐字复用 S8 | pybind wheel 构建 / msprofop_summary*.csv取 [20:40] 均值 |
extension/custom_op.cpp | Polar 适配 | EXEC_NPU_CMD(aclnnPolar, input, angle, result),result=complex64、shape=broadcast(input,angle),50 轮供 msprof 取平均 |
test_op.py | Polar 适配 | 8 个 case + 复数 verify(view_as_real拆 real/imag 当 fp32,rtol/atol=1e-4) |
run.sh | Polar 适配 | 重建 wheel → msprof 计时 → get_time → 基线校验;LD 指向custom_mathvendor |
注:与 pybind 目录(
experimental/math/polar/tests/pybind/)相比,本pybind_baseline目录的差异仅在common/pytorch_npu_helper.hpp的GetOpApiFuncAddr实现上,详见下文"基线变体"一节。
前置准备:编译并安装自定义 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的完整执行流水线为:
- 每次 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 不匹配; - msprof 计时:
timeout 180 msprof --application="python3 test_op.py $1",超时(退出码 124)即判失败退出; - 提取耗时:
time_use=$(($(python3 get_time.py))),解析 msprof 生成的op_summary*.csv; - 基线校验:当前
time_base=9999999999999为哨兵值,仅做正确性门禁(time_use=0直接报[ERROR] Performance not achieved);待硬件实测出 l0 参考实现耗时后回填真实基线; - 输出结论:
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_LIST的DT_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)}:
| case | input shape | angle 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 1:
input取uniform(0,10)、angle取uniform(-3.14,3.14),覆盖常规同 shape 路径; - case 2:
[4,1,8] × [4,5,8],input低维向angle高维广播,是新增广播功能的主战场; - case 3:
input=[1](numel=1)广播到[3,4,5],验证标量输入路径,angle范围放大到±6.28; - case 4:
[8,1] × [1,7]双向广播到[8,7],input取uniform(-10,10)覆盖负模长; - case 5:
[4096,4096]大 shape,input取uniform(0,100)拉大模长量级,用于性能采集; - case 6:
[3,5,17,269]高维,最后一维 269 使每行 fp32 数据为 1076B,非 32B 对齐,考验 kernel 的访存/对齐处理; - case 7:
input=[2.5]、angle=[π/4],1 元素边界,angle 用np.pi/4便于人工核对out ≈ 2.5·(√2/2 + i·√2/2); - case 8:
angle取uniform(-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()) # complex643.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.cpp为custom_ops_lib扩展,并显式追加 torch_npu 安装路径下的 ACL 头文件目录-I <torch_npu>/include/third_party/acl/inc。wheel 名为custom_ops(version 1.0),run.sh用pip3 install dist/custom_ops*.whl --force-reinstall强制重装。
5.common/pytorch_npu_helper.hpp:EXEC_NPU_CMD 与"基线变体"
该头文件是 S8 逐字复用的算子无关胶水层,核心是EXEC_NPU_CMD(aclnn_api, ...)宏。它通过GetOpApiFuncAddr动态解析aclnnPolar、aclnnPolarGetWorkspaceSize、InitHugeMemThreadLocal等符号,完成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 的差异(仅构建层)
| 维度 | S8 | Polar(本框架) |
|---|---|---|
| 构建方式 | 每算子自带S8/<Op>/build.sh,auto_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 目录内脚本独立构建。
注意事项与基线回填指引
- 精度阈值:当前为 fp32
rtol/atol=1e-4;任务要求采用"AscendOpTest 默认阈值",待 AscendOpTest 工具实际阈值确认后,在test_op.py:verify_result回填(阈值调整点为rtol, atol = 1e-4, 1e-4一处); - 性能基线:
run.sh:time_base当前为哨兵值9999999999999(仅正确性门禁);硬件实测出 l0 参考实现耗时后需回填真实基线(任务要求自研实现性能 ≥ 基线 95%,即time_use <= time_base / 0.95量级的门禁语义),回填点即run.sh中time_base=...一处; - 运行环境:框架只能在 NPU 环境运行(依赖
torch_npu、msprof),本地无法自检——与 S8 相同; - 每次运行都会重建 wheel 并清理
PROF*目录,若手动跑python3 test_op.py N单测正确性,请先保证 wheel 已安装。
小结
pybind_baseline目录为 Polar 算子提供了一个与 S8 框架同构、与 l0 参考实现严格对齐的 NPU 测试闭环:custom_op.cpp负责以 pybind 扩展形式反复触发aclnnPolar,test_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上加速计算。
相关推荐
CANN ops-math AssignSub 算子深度解析:从算子定义到 aclnn 调用实践
CANN ops math AssignSub 算子深度解析:从算子定义到 aclnn 调用实践 导读 AssignSub 是 CANN ops math 数学
算子库人工智能CANNCANN ops-math 算子贡献指南:从新算子提交到合入的完整流程
CANN ops math 算子贡献指南:从新算子提交到合入的完整流程 本指南以 CANN ops math 开源数学算子库的 CONTRIBUTING.md
算子库人工智能CANNCANN ops-transformer quant_sparse_flash_mla 算子 pytest 测试框架实战指南
CANN ops transformer quant_sparse_flash_mla 算子 pytest 测试框架实战指南 本篇技术指南围绕 quant_sp
算子库人工智能深度学习Ascend
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考