)
算子库人工智能CANN【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-math点击查看免费下载导读本文围绕 CANN ops-math 仓库中experimental/math/polar算子的aclnnPolar两段式接口展开系统讲解该接口的功能语义由模长 abs 与幅角 angle 构造极坐标复数张量对应 PyTorch 的torch.polar、产品支持矩阵、函数原型、参数约束、返回码含义以及完整可运行的调用示例。通过本文读者可以掌握如何在昇腾 NPU 上通过 aclnn 接口完成 Polar 算子的 workspace 申请、执行器创建与任务下发并理解该算子从算子定义、shape 推导、tiling 切分到 SIMT 内核计算的完整实现链路。接口功能与计算公式aclnnPolar的接口功能为由模长abs与幅角angle构造极坐标复数张量对应 PyTorch 接口torch.polar(abs, angle)。计算公式为$$ out_i input_i \times (\cos(angle_i) i \cdot \sin(angle_i)) $$其中input为极坐标模长分量angle为极坐标幅角单位弧度。每个输出元素是一个 COMPLEX64 复数实部为abs * cos(angle)虚部为abs * sin(angle)。该算子同时存在非实验态实现math/polar/README.md其功能描述为构建一个复数张量其元素为对应于极坐标的笛卡尔坐标其中绝对值为 abs角度为 angle两者数学语义一致。产品支持情况aclnnPolarexperimental 版本的支持情况如下表产品是否支持Atlas A3 训练系列产品 / Atlas A3 推理系列产品√Atlas A2 训练系列产品 / Atlas A2 推理系列产品√Ascend 950PR / Ascend 950DT×Atlas 200I/500 A2 推理产品×Atlas 推理系列产品×Atlas 训练系列产品×需要注意的是仓库中正式目录 math/polar 下的 Polar 实现面向 arch35Ascend 950 系列产品线其支持矩阵与 experimental 版本恰好相反Ascend 950PR/950DT 支持、Atlas A3/A2 不支持详见 math/polar/README.md。两套实现面向不同硬件平台使用前请务必以实际部署产品对应的支持矩阵为准。两段式接口设计每个 aclnn 算子都采用两段式接口设计aclnnPolar也不例外。必须先调用aclnnPolarGetWorkspaceSize接口获取入参并根据计算流程计算所需 workspace 大小随后调用aclnnPolar接口执行计算。第一段接口入参校验与 workspace 计算aclnnStatus aclnnPolarGetWorkspaceSize( const aclTensor* input, const aclTensor* angle, aclTensor* out, uint64_t* workspaceSize, aclOpExecutor** executor)第二段接口执行计算aclnnStatus aclnnPolar( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, const aclrtStream stream)两段式设计的好处在于第一段接口在 Host 侧完成参数校验并确定任务执行所需的临时内存大小第二段接口由用户自行管理 workspace 内存通过aclrtMalloc在 Device 侧申请从而让用户对内存分配时机与生命周期拥有完全控制权。aclnnPolarGetWorkspaceSize 参数说明参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续 Tensorinput输入公式中的 input极坐标模长分量数据类型需与 angle 一致input 与 angle 的 shape 满足 broadcast 关系FLOATND0-8√angle输入公式中的 angle极坐标幅角弧度数据类型需与 input 一致input 与 angle 的 shape 满足 broadcast 关系FLOATND0-8√out输出公式中的 outout 的 shape 与 input、angle 做 broadcast 后的 shape 一致数据类型恒为 COMPLEX64COMPLEX64ND0-8√workspaceSize输出返回需要在 Device 侧申请的 workspace 大小-----executor输出返回 op 执行器包含了算子计算流程-----关键要点解读输入数据类型input与angle均仅支持FLOAT且两者数据类型必须一致。Broadcast 语义两个输入支持 NumPy 风格的广播broadcast关系输出 shape 为两者广播后的 shape。关于广播关系的详细定义可参考 broadcast_relationship.md。输出类型固定out的数据类型恒为COMPLEX64与 input 的数据类型无关。COMPLEX64 在内存中每个元素由两个 float 组成实部 虚部申请与读取内存时需按 2 倍元素数计算。维度范围两个输入与输出均支持 0-8 维。非连续 Tensor三个 Tensor 均支持非连续strided存储。返回值返回类型为aclnnStatus具体状态码含义参见 aclnn 返回码。第一段接口完成入参校验出现如下场景时报错返回值错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 input、angle、out 是空指针。ACLNN_ERR_PARAM_INVALID161002input、angle 的数据类型或数据格式不在支持的范围之内。ACLNN_ERR_PARAM_INVALID161002input 与 angle 的数据类型不一致。ACLNN_ERR_PARAM_INVALID161002input 或 angle 的 shape 超过 8 维。ACLNN_ERR_PARAM_INVALID161002input 与 angle 的 shape 不满足 broadcast 推导关系。ACLNN_ERR_PARAM_INVALID161002out 的 shape 与 input、angle 做 broadcast 后的 shape 不一致或 out 的数据类型不是 COMPLEX64。aclnnPolar 参数说明参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址。workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclnnPolarGetWorkspaceSize 获取。executor输入op 执行器包含了算子计算流程。stream输入指定执行任务的 Stream。返回值同样为aclnnStatus状态码参见 aclnn 返回码。约束说明input 与 angle 的数据类型必须一致均为 FLOAT。out 的数据类型恒为 COMPLEX64与 input 无关。input、angle 的维度数不超过 8 维数据格式仅支持 ND。确定性计算aclnnPolar 默认确定性实现确定性计算相关概念参见 determinism_compute.md。源码级实现原理算子注册与 shape 推导在 math/polar/op_host/polar_def.cpp 中算子Polar通过OpDef注册输入abs与angle均为必选参数、ge::DT_FLOAT、ge::FORMAT_ND输出y为ge::DT_COMPLEX64、ge::FORMAT_ND。配置项开启动态 rank、动态 shape 支持并指定内核文件为polar_apt。Shape 推导实现在 math/polar/op_host/polar_infershape.cpp 的InferShape4Polar中直接调用BroadcastShape(in_shape1, in_shape2, out_shape)完成两个输入 shape 的广播合并广播失败即返回GRAPH_FAILED与第一段接口中shape 不满足 broadcast 推导关系的报错逻辑一一对应。接口层实现math/polar/op_api/polar.cpp 中的l0op::Polar展示了接口层的完整逻辑调用BroadcastInferShape计算两个输入广播后的 shape通过executor-AllocTensor(broadcastShape, op::DataType::DT_COMPLEX64)分配输出 Tensor数据类型固定为 COMPLEX64通过IsAiCoreSupport校验输入数据类型一致且为DT_FLOAT对应AICORE_DTYPE_SUPPORT_LIST满足条件后调用PolarAiCore通过ADD_TO_LAUNCHER_LIST_AICORE将算子加入 AiCore 启动列表。接口声明位于 math/polar/op_api/aclnn_polar.h即文档所示aclnnPolarGetWorkspaceSize与aclnnPolar两个对外 API。Tiling 切分math/polar/op_host/arch35/polar_tiling.cpp 实现了 tiling 逻辑包含三个核心步骤CheckDtype校验 abs、angle 均为DT_FLOATy 为DT_COMPLEX64CheckBroadcastAndMergeShape将两个输入按右对齐方式对齐维度逐维校验可广播性absDim ! angleDim absDim ! 1 angleDim ! 1时报错并将合并后的 shape 存入mergedShape_维度数上限即POLAR_MAX_DIM 8见 math/polar/op_kernel/arch35/polar_struct.hCalcStride对每个维度计算 merged/abs/angle/y 的 stride其中维度大小为 1 的输入 stride 置 0实现广播的零拷贝访问。DoOpTiling按核数coreNum对总元素数做均分elementsPerCore totalElements / coreNum、formerCore totalElements % coreNum前formerCore个核各多处理 1 个元素PostTiling中将 workspace 大小置 0Polar 算子无需额外临时内存并将PolarTilingData写入原始 tiling 数据。SIMT 内核计算内核入口 math/polar/op_kernel/polar_apt.cpp 中注册polar_apt内核实例化PolarOp::PolarSimtfloat并调用Init与Process。真正执行计算的 math/polar/op_kernel/arch35/polar_simt.h 中Process根据GetBlockIdx()计算当前核负责的元素区间startIdx与count随后通过asc_vf_call以POLAR_THREAD_DIM 1024线程启动 SIMT 计算。核心计算逻辑为U absVal absGm[absBase]; U cosVal Simt::Cos(angleGm[angleBase]); U sinVal Simt::Sin(angleGm[angleBase]); yGm[2 * yBase] absVal * cosVal; yGm[2 * yBase 1] absVal * sinVal;可以清晰地看到实部写入yGm[2 * yBase]即abs * cos(angle)虚部写入yGm[2 * yBase 1]即abs * sin(angle)与文档给出的计算公式完全一致这也印证了 COMPLEX64 输出在内存中每元素 实部 float 虚部 float的排布方式对应调用示例中按2 * outNum个 float 处理输出缓冲的做法。各维度坐标通过 stride 分解v / ms[d]、vv - c * ms[d]实现任意维度 shape 的通用寻址其中维度为 1 的输入 stride 为 0天然完成广播。完整调用示例示例代码如下具体编译和执行过程请参考编译与运行样例。仓库中 math/polar/examples/test_aclnn_polar.cpp 与 math/polar/tests/ut/op_api/test_aclnn_polar.cpp 提供了可对照的工程化测试样例。#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_polar.h #define CHECK_RET(cond, return_expr) \ do { \ if (!(cond)) { \ return_expr; \ } \ } while (0) #define LOG_PRINT(message, ...) \ do { \ printf(message, ##__VA_ARGS__); \ } while (0) int64_t GetShapeSize(const std::vectorint64_t shape) { int64_t shapeSize 1; for (auto i : shape) { shapeSize * i; } return shapeSize; } int Init(int32_t deviceId, aclrtStream* stream) { // 固定写法资源初始化 auto ret aclInit(nullptr); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclInit failed. ERROR: %d\n, ret); return ret); ret aclrtSetDevice(deviceId); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSetDevice failed. ERROR: %d\n, ret); return ret); ret aclrtCreateStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtCreateStream failed. ERROR: %d\n, ret); return ret); return 0; } template typename T int CreateAclTensor(const std::vectorT hostData, const std::vectorint64_t shape, void** deviceAddr, aclDataType dataType, aclTensor** tensor) { auto size GetShapeSize(shape) * sizeof(T); // 调用aclrtMalloc申请device侧内存 auto ret aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMalloc failed. ERROR: %d\n, ret); return ret); // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上 ret aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMemcpy failed. ERROR: %d\n, ret); return ret); // 计算连续tensor的strides std::vectorint64_t strides(shape.size(), 1); for (int64_t i shape.size() - 2; i 0; i--) { strides[i] shape[i 1] * strides[i 1]; } // 调用aclCreateTensor接口创建aclTensor *tensor aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); return 0; } int main() { // 1. 固定写法device/stream初始化参考acl API手册 int32_t deviceId 0; aclrtStream stream; auto ret Init(deviceId, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); // 2. 构造输入与输出需要根据API的接口自定义构造 std::vectorint64_t inputShape {4, 2}; std::vectorint64_t angleShape {4, 2}; std::vectorint64_t outShape {4, 2}; void* inputDeviceAddr nullptr; void* angleDeviceAddr nullptr; void* outDeviceAddr nullptr; aclTensor* input nullptr; aclTensor* angle nullptr; aclTensor* out nullptr; std::vectorfloat inputHostData {1, 1, 1, 1, 2, 2, 2, 2}; std::vectorfloat angleHostData {0, 1.5707964f, 3.1415927f, -1.5707964f, 0, 1.5707964f, 3.1415927f, -1.5707964f}; // 创建input/angle aclTensorFLOAT ret CreateAclTensor(inputHostData, inputShape, inputDeviceAddr, aclDataType::ACL_FLOAT, input); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(angleHostData, angleShape, angleDeviceAddr, aclDataType::ACL_FLOAT, angle); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建out aclTensorCOMPLEX64每元素 real/imag 两个 float int64_t outNum GetShapeSize(outShape); std::vectorfloat outHostData(outNum * 2, 0); size_t outByte static_castsize_t(outNum) * 2 * sizeof(float); ret aclrtMalloc(outDeviceAddr, outByte, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMalloc failed. ERROR: %d\n, ret); return ret); ret aclrtMemcpy(outDeviceAddr, outByte, outHostData.data(), outByte, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMemcpy failed. ERROR: %d\n, ret); return ret); std::vectorint64_t outStrides(outShape.size(), 1); for (int64_t i outShape.size() - 2; i 0; i--) { outStrides[i] outShape[i 1] * outStrides[i 1]; } out aclCreateTensor(outShape.data(), outShape.size(), aclDataType::ACL_COMPLEX64, outStrides.data(), 0, aclFormat::ACL_FORMAT_ND, outShape.data(), outShape.size(), outDeviceAddr); // 3. 调用CANN算子库API uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnPolar第一段接口 ret aclnnPolarGetWorkspaceSize(input, angle, out, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnPolarGetWorkspaceSize failed. ERROR: %d\n, ret); return ret); // 根据第一段接口计算出的workspaceSize申请device内存 void* workspaceAddr nullptr; if (workspaceSize 0) { ret aclrtMalloc(workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(allocate workspace failed. ERROR: %d\n, ret); return ret); } // 调用aclnnPolar第二段接口 ret aclnnPolar(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnPolar failed. ERROR: %d\n, ret); return ret); // 4. 固定写法同步等待任务执行结束 ret aclrtSynchronizeStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSynchronizeStream failed. ERROR: %d\n, ret); return ret); // 5. 获取输出的值complex64每元素 real/imag 两个 float std::vectorfloat resultData(outNum * 2, 0); ret aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, outByte, ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(copy result from device to host failed. ERROR: %d\n, ret); return ret); for (int64_t i 0; i outNum; i) { LOG_PRINT(out[%ld] %f %fi\n, i, resultData[2 * i], resultData[2 * i 1]); } // 6. 释放aclTensor需要根据具体API的接口定义修改 aclDestroyTensor(input); aclDestroyTensor(angle); aclDestroyTensor(out); // 7. 释放device资源 aclrtFree(inputDeviceAddr); aclrtFree(angleDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }示例关键点解读资源初始化第 1 步aclInit→aclrtSetDevice→aclrtCreateStream为固定写法任何 aclnn 算子调用前都必须完成。Tensor 构造第 2 步输入input、angle用ACL_FLOAT创建输出out必须用ACL_COMPLEX64创建。特别注意 COMPLEX64 的内存布局示例中outHostData与outByte均按outNum * 2个 float 计算因为每个复数元素在内存中占实部、虚部两个 float。两段式调用第 3 步先调用aclnnPolarGetWorkspaceSize得到workspaceSize与executor再按需aclrtMalloc申请 workspace 后调用aclnnPolar执行。由于 Polar 的 tiling 实现中将 workspace 置 0实际运行中workspaceSize通常为 0但代码仍保留了通用判断逻辑以保证健壮性。结果回读第 5 步同样按2 * outNum个 float 读取resultData[2*i]为实部、resultData[2*i1]为虚部。以示例数据为例input1, angle0时输出1.0 0.0iinput1, angleπ/2时输出≈0.0 1.0iinput2, angleπ时输出≈-2.0 0.0i可据此快速验证计算正确性。资源释放第 6、7 步依次释放aclTensor、device 内存、stream并调用aclrtResetDevice与aclFinalize收尾。常见问题排查现象可能原因与处理建议返回 ACLNN_ERR_PARAM_NULLPTR161001input、angle、out 存在空指针检查aclCreateTensor是否成功创建。返回 ACLNN_ERR_PARAM_INVALID161002依次核对输入数据类型是否为 FLOAT 且在支持范围内input 与 angle 数据类型是否一致shape 是否超过 8 维两个输入是否满足 broadcast 关系out 的 shape 是否等于广播后 shape、类型是否为 COMPLEX64。输出复数数值异常检查 angle 单位是否为弧度示例中 1.5707964f 即 π/2检查输出缓冲是否按2 * outNum个 float 申请与读取避免实部/虚部错位。编译无法找到头文件aclnnop/aclnn_polar.h确认 CANN 环境变量如ASCEND_HOME_PATH与 include 路径配置正确并参考编译与运行样例组织工程。延伸阅读两段式接口说明aclnn 算子统一的两段式调用规范broadcast 关系input 与 angle 的广播规则aclnn 返回码aclnnStatus状态码全集编译与运行样例样例工程的组织、编译与运行方式Polar 算子总览仓库中 arch35Ascend 950 系列版本的支持矩阵与调用方式非实验态工程化样例math/polar/examples/test_aclnn_polar.cpp、math/polar/tests/ut/op_api/test_aclnn_polar.cpp、math/polar/tests/ut/op_host/test_polar_infershape.cpp赞分享算子库人工智能CANN【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-math点击查看免费下载相关推荐CANN ops-math 算子 aclnnClampTensor 接口使用指南基于 ClipByValueV2 的张量限幅Clamp计算CANN ops math 算子 aclnnClampTensor 接口使用指南基于 ClipByValueV2 的张量限幅Clamp计算 本指南以 CA算子库人工智能CANNCANN ops-math SplitV 算子完全指南基于 size_splits 的张量切分与 aclnn 双接口调用实战CANN ops math SplitV 算子完全指南基于 size_splits 的张量切分与 aclnn 双接口调用实战 SplitV 是 CANN op算子库人工智能CANNCANN ops-math 的 DiagFlat 算子详解基于 aclnnDiagFlat 的对角线张量生成指南CANN ops math 的 DiagFlat 算子详解基于 aclnnDiagFlat 的对角线张量生成指南 DiagFlat 是 CANN ops ma算子库人工智能CANN创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考