ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

CANN ops-math 中 Atanh 算子的原理与调用指南:从 aclnn 两段式接口到 AICore/AICPU 双实现

CANN ops-math 中 Atanh 算子的原理与调用指南:从 aclnn 两段式接口到 AICore/AICPU 双实现 算子库人工智能CANN【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-math点击查看免费下载导读Atanh反双曲正切inverse hyperbolic tangent是 CANN ops-math 数学基础算子库中提供的一类逐元素element-wise激活类算子用于在 NPU 上对输入张量中的每个元素执行atanh运算广泛应用于归一化、自注意力打分、数学变换等网络场景。本文以 math/atanh/README.md 为主体结合算子定义、形状推导、L0/L2 接口实现、AICore 与 AICPU 内核代码完整讲解 Atanh 算子的数学定义、参数约束、aclnn 两段式调用与图模式调用方法、完整可运行的 C 调用示例以及底层的调度与计算原理帮助读者在 CANN 环境中独立完成 Atanh 算子的编译、运行与验证。功能说明与数学原理Atanh 算子对输入张量中的每一个元素独立计算其反双曲正切值是典型的逐元素算子。其计算公式为$$ y \text{atanh}(x) \frac{1}{2} \ln\left(\frac{1x}{1-x}\right) $$从公式可以看出atanh的定义域为开区间 (-1, 1)当x超出该值域时结果具有特殊边界行为详见后文约束说明。该算子在 ops-math 中属于 math/atanh 目录贡献记录显示该算子由 CANN-BOT SIMT 于 2026/05/22 新增。产品支持情况README 中列出的产品支持矩阵如下所有列出产品均支持 Atanh 算子产品是否支持Ascend 950PR/Ascend 950DT√Atlas A3 训练系列产品/Atlas A3 推理系列产品√Atlas A2 训练系列产品/Atlas A2 推理系列产品√Atlas 200I/500 A2 推理产品√Atlas 推理系列产品√Atlas 训练系列产品√需要说明的是aclnn 接口文档 math/atanh/docs/aclnnAtanhaclnnInplaceAtanh.md 中的支持矩阵与 README 略有差异该文档标注 Atlas 200I/500 A2 推理产品 为不支持实际支持情况以所安装 CANN 版本的接口文档为准。参数说明Atanh 算子共两个 Tensor 参数输入x与输出y均为 ND 格式参数名输入/输出/属性描述数据类型数据格式x输入待进行 atanh 计算的入参公式中的 x。输入值域为 (-1, 1)。FLOAT16、FLOAT、BF16NDy输出atanh 计算的结果公式中的 y。FLOAT16、FLOAT、BF16ND这一参数约束在算子定义源码 math/atanh/op_host/atanh_def.cpp 中有完全对应的实现Input(x)与Output(y)的DataType均声明为{ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16}Format均为FORMAT_ND且输入输出都设置了AutoContiguous()表示框架会自动将非连续 Tensor 处理为连续 Tensor 后参与计算。约束说明README 对 Atanh 算子给出如下三条核心约束输入x的值域为 (-1, 1)当|x| 1时输出 NaN当x ±1时输出 ±Inf。这是由atanh数学定义含对数、除法在定义域边界上的行为决定的。输出 shape 与输入 shape 完全相同。输出 dtype 与输入 dtype 相同。输出 shape 与输入相同这一约束在形状推导实现 math/atanh/op_host/atanh_infershape.cpp 中逐维拷贝实现InferShapeAtanh读取输入 shape 的维数xShapeSize先SetDimNum再逐维SetDim写回输出即yShape[i] xShape[i]。调用说明README 给出两种调用方式对应仓库中的两个独立可编译示例调用方式调用样例说明aclnn 调用test_aclnn_atanh参见 算子调用 完成算子编译和验证。图模式调用test_geir_atanh参见 算子调用 完成算子编译和验证。图模式调用对应仓库中的图适配实现 math/atanh/op_graph/atanh_graph_infer.cpp配合atanh_proto.h中的算子原型以及 TensorFlow 侧插件 math/atanh/framework/atanh_tf_plugin.cpp用于在图编译框架GEIR中完成算子的节点构建与推导。aclnn 接口详解aclnnAtanh 与 aclnnInplaceAtanh接口选择aclnn 调用方式提供两个功能完全相同的接口详见 math/atanh/docs/aclnnAtanhaclnnInplaceAtanh.mdaclnnAtanh需新建一个输出张量对象存储计算结果in-place 之外的常规模式aclnnInplaceAtanh无需新建输出张量对象直接在输入张量的内存中存储计算结果可节省一次输出 Tensor 的申请与拷贝。两个接口均为两段式接口参见 两段式接口说明必须先调用*GetWorkspaceSize获取计算所需 workspace 大小以及包含算子计算流程的执行器再调用第二段接口执行计算。函数原型aclnnStatus aclnnAtanhGetWorkspaceSize( const aclTensor* input, aclTensor* out, uint64_t* workspaceSize, aclOpExecutor** executor) aclnnStatus aclnnAtanh( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream) aclnnStatus aclnnInplaceAtanhGetWorkspaceSize( aclTensor* inputRef, uint64_t* workspaceSize, aclOpExecutor** executor) aclnnStatus aclnnInplaceAtanh( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream)aclnnAtanhGetWorkspaceSize 参数说明参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续 TensorinputaclTensor*输入输入 tensor进行反双曲正切运算。shape 需要与 out 一致。INT8、INT16、INT32、INT64、UINT8、BOOL、FLOAT、FLOAT16、DOUBLEND不超过 8 维√outaclTensor*输出输出 tensor存储计算结果。shape 需要与 input 一致。FLOAT、FLOAT16、DOUBLEND-√workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小。-----executoraclOpExecutor**输出返回 op 执行器包含了算子计算流程。-----平台扩展说明在 Atlas A3 训练/推理系列产品上input和out的数据类型额外支持 COMPLEX64、COMPLEX128、BFLOAT16。返回值错误码第一段接口完成入参校验返回aclnnStatus状态码具体参见 aclnn 返回码。出现如下场景时报错返回值错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 input 或 out 是空指针。ACLNN_ERR_PARAM_INVALID161002input 或 out 的数据类型不在支持的范围之内。ACLNN_ERR_PARAM_INVALID161002input 和 out 的 shape 不一致。ACLNN_ERR_PARAM_INVALID161002input 或 out 的维数大于 8。aclnnAtanh 参数说明参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址。workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclnnAtanhGetWorkspaceSize 获取。executor输入op 执行器包含了算子计算流程。stream输入指定执行任务的 Stream。aclnnInplaceAtanhGetWorkspaceSize 参数说明参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续 TensorinputRefaclTensor*输入/输出输入输出 tensor进行反双曲正切运算计算结果存储在 inputRef 中。-FLOAT、FLOAT16、DOUBLEND不超过 8 维√workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小。-----executoraclOpExecutor**输出返回 op 执行器包含了算子计算流程。-----平台扩展说明在 Atlas A3 训练/推理系列产品上inputRef数据类型额外支持 COMPLEX64、COMPLEX128、BFLOAT16。in-place 接口的校验错误场景返回值错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 inputRef 是空指针。ACLNN_ERR_PARAM_INVALID161002inputRef 的数据类型不在支持的范围之内。ACLNN_ERR_PARAM_INVALID161002inputRef 的维数大于 8。确定性约束aclnnAtanh 与 aclnnInplaceAtanh 默认均为确定性实现同一输入在多次运行中产生确定一致的结果。完整调用示例以下示例代码源自 math/atanh/docs/aclnnAtanhaclnnInplaceAtanh.md 与仓库样例 math/atanh/examples/test_aclnn_atanh.cpp完整演示了两段式接口的标准用法编译与运行请参考 编译与运行样例#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_atanh.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手册根据实际device填写deviceId 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 selfShape {4, 2}; std::vectorint64_t outShape {4, 2}; void* selfDeviceAddr nullptr; void* outDeviceAddr nullptr; aclTensor* self nullptr; aclTensor* out nullptr; std::vectorfloat selfHostData {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8}; std::vectorfloat outHostData {0, 0, 0, 0, 0, 0, 0, 0}; // 创建self aclTensor ret CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_FLOAT, self); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建out aclTensor ret CreateAclTensor(outHostData, outShape, outDeviceAddr, aclDataType::ACL_FLOAT, out); CHECK_RET(ret ACL_SUCCESS, return ret); // 3. 调用CANN算子库APIaclnnAtanh两段式 uint64_t workspaceSize 0; aclOpExecutor* executor; ret aclnnAtanhGetWorkspaceSize(self, out, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnAtanhGetWorkspaceSize 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); } // 调用aclnnAtanh第二段接口 ret aclnnAtanh(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnAtanh failed. ERROR: %d\n, ret); return ret); // 3. aclnnInplaceAtanh接口调用示例结果直接写回self uint64_t inplaceWorkspaceSize 0; aclOpExecutor* inplaceExecutor; ret aclnnInplaceAtanhGetWorkspaceSize(self, inplaceWorkspaceSize, inplaceExecutor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnInplaceAtanhGetWorkspaceSize failed. ERROR: %d\n, ret); return ret); void* inplaceWorkspaceAddr nullptr; if (inplaceWorkspaceSize 0) { ret aclrtMalloc(inplaceWorkspaceAddr, inplaceWorkspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(allocate workspace failed. ERROR: %d\n, ret); return ret); } ret aclnnInplaceAtanh(inplaceWorkspaceAddr, inplaceWorkspaceSize, inplaceExecutor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnInplaceAtanh 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. 将device侧内存上的结果拷贝至host侧 auto size GetShapeSize(outShape); std::vectorfloat resultData(size, 0); ret aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(resultData[0]), 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 size; i) { LOG_PRINT(result[%ld] is: %f\n, i, resultData[i]); } // 6. 释放aclTensor aclDestroyTensor(self); aclDestroyTensor(out); // 7. 释放device资源 aclrtFree(selfDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }对 shape 为{4, 2}、输入{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8}的示例输出约为{0.100335, 0.202733, 0.309520, 0.423649, 0.549306, 0.693147, 0.867301, 1.098612}。源码级实现原理L2 接口层入参校验、自动类型转换与工作流编排L2 层接口实现在 math/atanh/op_api/aclnn_atanh.cpp其工作流体现了 CANN 单算子 API 的通用编排模式dtype 支持列表L2 层支持比 L0/内核更宽泛的输入类型FLOAT、FLOAT16、DOUBLE、INT8/16/32/64、UINT8、BOOL、COMPLEX64/128910B 系额外支持 BF16输出类型支持 FLOAT、FLOAT16、DOUBLE、COMPLEX64、COMPLEX128、BF16。自动 cast 链路对于 INT8/INT16/INT32/INT64/BOOL/UINT8 等输入NEED_CAST_DTYPE_LIST_ATANH实现先调用l0op::Cast转成 FLOAT 再执行 atanh计算完成后再次l0op::Cast回目标输出类型从而让 L0 层只需聚焦浮点计算。连续化与结果写回输入先经l0op::Contiguous保证连续计算结果最后通过l0op::ViewCopy写回可能非连续的输出out上。workspace 获取第一段接口通过CREATE_EXECUTOR()创建执行器、完成上述节点编排后以uniqueExecutor-GetWorkspaceSize()返回 workspace 大小并将 executor 转移给调用方第二段接口aclnnAtanh/aclnnInplaceAtanh通过CommonOpExecutorRun在指定 stream 上真正执行。aclnnInplaceAtanhGetWorkspaceSize则直接复用ExecAtanhGetWorkspaceSize(inputRef, inputRef, ...)将输入同时作为输出实现 in-place 语义。空 Tensor 处理当input-IsEmpty()时第一段接口直接返回workspaceSize 0无需下发计算。L0 层AICore/AICPU 双路调度L0 层实现在 math/atanh/op_api/atanh.cpp核心是IsAiCoreSupport的按平台 dtype 的调度决策在 ASCEND910B、ASCEND910_93 以及 RegBase寄存器级底座平台上支持DT_FLOAT、DT_FLOAT16、DT_BF16走 AICore其他平台仅DT_FLOAT、DT_FLOAT16走 AICore不满足条件的输入回退到 AICPU 路径AtanhAiCpu保证算子在所有产品上的可用性。AICore 路径通过宏ADD_TO_LAUNCHER_LIST_AICORE将算子加入任务队列AICPU 路径通过ADD_TO_LAUNCHER_LIST_AICPU创建AicpuTaskSpace任务二者共享executor-AllocTensor(input-GetViewShape(), input-GetDataType())分配的输出 Tensor——这从实现上印证了输出 shape/dtype 与输入一致的约束。AICore 内核与 tilingAICore 内核入口为 math/atanh/op_kernel/atanh_apt.cpp通过AtanhTilingKeyFP320、FP161、BF162在编译期展开为三个特化分支分别调用NsAtanh::AtanhSimt::Processfloat|half|bfloat16_tSIMT 实现见 math/atanh/op_kernel/arch35/atanh_simt.h配合 math/atanh/op_host/arch35/atanh_tiling_arch35.cpp 完成 arch35 平台的 tiling 切分tiling 数据结构见 math/atanh/op_kernel/arch35/atanh_tiling_data.h。算子定义 math/atanh/op_host/atanh_def.cpp 中为ascend950配置了DynamicShapeSupportFlag(true)、DynamicRankSupportFlag(true)、PrecisionReduceFlag(true)等能力开关说明该算子在 AICore 上支持动态 shape 与动态 rank。AICPU 内核AICPU 回退路径实现于 math/atanh/op_kernel_aicpu/atanh_aicpu.cpp要点如下标量计算调用标准库std::atanh对Eigen::half特化为先转float计算再转回半精度支持的数据类型比 AICore 更宽DT_FLOAT16、DT_FLOAT、DT_DOUBLE、DT_COMPLEX64、DT_COMPLEX128对应 L2 层 A3 平台扩展的复数支持数据量超过kAtanhParallelNum64×1024 个元素时按 CPU 核数cores - 2保底切分区间通过CpuKernelUtils::ParallelFor并行计算否则单线程串行执行计算前完成数据指针、输入输出 dtype 与数据大小一致性校验。图模式适配图模式GEIR路径由 math/atanh/op_graph/atanh_graph_infer.cpp 提供形状推导钩子配合 math/atanh/framework/atanh_tf_plugin.cpp 在 TensorFlow 前端完成算子注册映射使 Atanh 可像原生算子一样出现在计算图中。示例程序见 math/atanh/examples/test_geir_atanh.cpparch35 平台版本位于 math/atanh/examples/arch35。测试与验证仓库为 Atanh 算子提供了覆盖 L2 API、AICPU 内核与端到端ST的多层测试L2 API 单测math/atanh/tests/ut/op_api/test_aclnn_atanh.cpp 覆盖 FLOAT、FLOAT16、DOUBLE、BF16 四种数据类型1 维/3 维/5 维/空 Tensor 等多种 shape输入值域统一控制在 (-0.9, 0.9) 内精度容差 1e-4以及nullptr输入/输出返回ACLNN_ERR_PARAM_NULLPTR的异常路径AICPU 内核单测math/atanh/tests/ut/op_kernel_aicpu/test_atanh.cppST 场景math/atanh/tests/st/aclnnAtanh/atk_aclnnAtanh.json 定义端到端场景math/atanh/tests/assets/golden.py 提供 golden 数据生成逻辑用于结果比对arch35 的 kernel 级用例见 math/atanh/tests/st/arch35/ttk_kernel_atanh_st.csv。常见问题与注意事项值域越界行为输入严格限制在 (-1, 1) 内。由于实现直接调用数学库/标准库的atanh|x| 1时自然产生 NaNx ±1时产生 ±Inf属于符合 IEEE 语义的预期行为业务侧需在调用前做好数据裁剪。平台差异AICore 与 AICPU 支持的 dtype 集合不同如复数、DOUBLE 仅在 AICPU 路径支持BF16 仅在部分平台走 AICore最终由 L0 层IsAiCoreSupport自动选择执行路径用户无需感知但若需保证跨平台一致的精度表现建议在目标平台上执行 math/atanh/tests 下对应用例。两段式接口不可省略必须先调用GetWorkspaceSize获取 executor再以aclnnrtMalloc按返回大小申请 workspace大小为 0 时可跳过申请后调用第二段接口最后通过aclrtSynchronizeStream同步等待任务完成再拷贝结果回 Host。赞分享算子库人工智能CANN【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-math点击查看免费下载相关推荐CANN ops-math MaskedFill 算子深度解析从 aclnn 两段式接口到 AICore 广播填充实现CANN ops math MaskedFill 算子深度解析从 aclnn 两段式接口到 AICore 广播填充实现 MaskedFill 是 CANN o算子库人工智能CANNCANN ops-math LogicalNot 算子aclnn 两段式调用接口、实现原理与源码深度解析CANN ops math LogicalNot 算子aclnn 两段式调用接口、实现原理与源码深度解析 本篇技术指南围绕 CANN 开源算子库 ops ma算子库人工智能CANNCANN ops-math 算子实战aclnnCircularPad3dBackward 两段式接口原理与调用指南CANN ops math 算子实战aclnnCircularPad3dBackward 两段式接口原理与调用指南 本文围绕 CANN ops math 开源算子库人工智能CANN上一篇Angular Query 的 injectIsMutating 注入选项解析掌握 InjectIsMutatingOptions.injector 与全局 mutation 状态追踪下一篇Ice 使用指南5分钟让 Mac 菜单栏回归整洁创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表