CANN ops-math 正切算子 aclnnTan / aclnnInplaceTan 两段式接口详解与调用指南

发布时间:2026/9/21 14:11:20
CANN ops-math 正切算子 aclnnTan / aclnnInplaceTan 两段式接口详解与调用指南 CANN ops-math 正切算子 aclnnTan / aclnnInplaceTan 两段式接口详解与调用指南【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math导读aclnnTan与aclnnInplaceTan是 CANN ops-math 数学算子库中用于逐元素计算输入张量正切值的两对单算子Single Op接口分别对应新建输出张量与原位改写输入内存两种使用场景。本文以 math/tan/docs/aclnnTanaclnnInplaceTan.md 为骨架结合 math/tan 目录下的 op_api、op_host、op_kernel、op_kernel_aicpu 源码与测试用例完整讲解算子功能、两段式接口的函数原型与全部参数语义、错误码校验规则、AI Core 与 AICPU 双路径实现原理并给出可直接编译运行的两个 C 调用示例。读完本文你将能够在昇腾 NPU 上正确使用这两套接口完成张量正切计算并理解其从 host 侧参数校验、tiling 切核到 kernel 分发的完整执行链路。一、产品支持情况aclnnTan与aclnnInplaceTan在同一份接口文档中发布二者的产品支持情况完全一致如下表所示产品系列是否支持Ascend 950PR / Ascend 950DT支持Atlas A3 训练系列产品 / Atlas A3 推理系列产品支持Atlas A2 训练系列产品 / Atlas A2 推理系列产品支持Atlas 200I/500 A2 推理产品不支持Atlas 推理系列产品支持Atlas 训练系列产品支持注意aclnnTan/aclnnInplaceTan的接口文档math/tan/docs/aclnnTanaclnnInplaceTan.md中标注Atlas 200I/500 A2 推理产品不支持而算子级 READMEmath/tan/README.md中该产品标注为支持两者差异来自发布口径的不同实际使用时请以目标环境的产品规格为准。二、功能说明2.1 算子功能与计算公式该算子对输入张量self中的每个元素计算正切值结果逐元素写入输出张量out输出张量的 shape 与输入张量保持一致。计算公式为$$ out[i] \tan(self[i]) $$其中self[i]表示输入张量self的第i个元素out[i]表示输出张量out的第i个元素。2.2 计算示例以一个 2×2 的整型输入为例输入self tensor([[1, 2], [3, 4]]) 输出out tensor([[ 1.5574, -2.1850], [-0.1425, 1.1578]])即tan(1) ≈ 1.5574、tan(2) ≈ -2.1850、tan(3) ≈ -0.1425、tan(4) ≈ 1.1578。需要注意tan是以 π 为周期的周期函数在 π/2 附近存在间断点输入元素越靠近π/2 kπ结果的绝对值越大、精度越敏感。2.3 平台相关的实现差异从源码看Tan 算子在 math/tan/op_api/tan.cpp 中按平台与数据类型分流到不同计算路径AI Core 路径IsAiCoreSupport()根据当前 SoC 版本选择支持列表。默认平台含 Atlas 训练/推理系列等支持FLOAT16、FLOAT、INT32Ascend 910B / Ascend 910_93 / 寄存器化RegBase平台额外支持BFLOAT16。AI Core 路径通过ADD_TO_LAUNCHER_LIST_AICORE下发 AscendC kernelmath/tan/op_kernel/tan_apt.cpp。AICPU 路径DOUBLE、COMPLEX64、COMPLEX128等 AI Core 不支持的浮点/复数类型走 AICPU 计算math/tan/op_kernel_aicpu/tan_aicpu.cpp使用 Eigen 的Eigen::numext::tanT实现并在元素数超过阈值时通过ParallelFor多核并行。此外从 math/tan/op_host/tan_def.cpp 的算子定义看AI Core 侧原生算子注册的数据类型为FLOAT16、FLOAT、BF16、INT32且声明了动态 shape、动态 rank 支持以及PrecisionReduceFlag(true)精度优化开关。三、两段式接口与函数原型3.1 两段式调用模型与 CANN 其他单算子接口一致aclnnTan与aclnnInplaceTan采用两段式接口详细规范参见 两段式接口说明第一段GetWorkspaceSize调用aclnnTanGetWorkspaceSize或aclnnInplaceTanGetWorkspaceSize完成入参校验、构图与 workspace 大小计算返回计算所需的workspaceSizeDevice 侧临时内存大小以及封装了算子计算流程的executoraclOpExecutor 执行器。第二段执行根据第一段返回的workspaceSize在 Device 侧申请内存后调用aclnnTan或aclnnInplaceTan传入 workspace、executor 与 stream 真正执行计算。3.2 aclnnTan 与 aclnnInplaceTan 的差异两对接口实现完全相同的数学功能区别仅在于结果的存放方式请根据实际场景选择aclnnTan需要额外新建一个输出张量对象out来存储计算结果输入self保持不变。aclnnInplaceTan无需新建输出张量对象直接在输入张量selfRef的 Device 内存上原地写入计算结果可节省一份输出内存。由于是原地改写调用前需确认输入数据不再被其他逻辑使用。3.3 四个函数原型aclnnStatus aclnnTanGetWorkspaceSize( const aclTensor* self, aclTensor* out, uint64_t* workspaceSize, aclOpExecutor** executor)aclnnStatus aclnnTan( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, const aclrtStream stream)aclnnStatus aclnnInplaceTanGetWorkspaceSize( const aclTensor* selfRef, uint64_t* workspaceSize, aclOpExecutor** executor)aclnnStatus aclnnInplaceTan( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, const aclrtStream stream)四、第一段接口参数详解4.1 aclnnTanGetWorkspaceSize参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续TensorselfaclTensor*输入公式中的输入 selfshape 需要与 out 一致和 out 的数据类型满足互推导关系FLOAT、FLOAT16、DOUBLE、INT8、INT16、INT32、INT64、UINT8、BOOL、COMPLEX64、COMPLEX128、BFLOAT16ND不大于 8√outaclTensor*输出公式中的 outshape 需要与 self 一致和 self 的数据类型满足互推导关系FLOAT、FLOAT16、DOUBLE、COMPLEX64、COMPLEX128、BFLOAT16ND-√workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小-----executoraclOpExecutor**输出返回 op 执行器包含了算子计算流程-----平台附加限制Atlas 训练系列产品、Atlas 推理系列产品上不支持 BFLOAT16、COMPLEX32、COMPLEX64。关于参数语义可以结合 math/tan/op_api/aclnn_tan.cpp 的CheckParams与Compute实现进一步理解类型扩展与互推导self侧允许INT8/INT16/INT32/INT64/UINT8/BOOL等整型与布尔类型其原因是这些类型无法直接参与浮点三角运算。源码中NEED_CAST_TO_FLOAT_DTYPE_LIST将这些类型统一视为可cast 到 FLOAT在Compute阶段先通过l0op::Cast转成DT_FLOAT计算最后再Cast回out的目标数据类型。out侧仅允许浮点/复数类型且必须能与self满足互推导关系。shape 与维度约束CheckShape中通过OP_CHECK_MAX_DIM(self, MAX_SUPPORT_DIMS_NUMS)校验维度不大于 8并通过OP_CHECK_SHAPE_NOT_EQUAL校验self与outshape 完全一致。格式约束算子仅支持 ND 格式。CheckParams中对非 ND 存储格式打印警告日志且在寄存器化RegBase架构上直接拒绝私有格式Private format输入。非连续 Tensor接口标注支持非连续输入Compute中首先调用l0op::Contiguous将非连续输入规整为连续张量再执行 Tan最后用l0op::ViewCopy将结果拷贝回用户提供的out。空张量短路当self或out为空张量如 shape 为{0}时aclnnTanGetWorkspaceSize直接返回workspaceSize 0与合法 executor不构造计算图math/tan/op_api/aclnn_tan.cpp 中self-IsEmpty() || out-IsEmpty()分支对应 UT 用例case_empty_tensortests/ut/op_host/op_api/test_aclnn_tan.cpp。4.2 aclnnInplaceTanGetWorkspaceSize参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续TensorselfRefaclTensor*输入/输出公式中的 self-FLOAT、FLOAT16、DOUBLE、COMPLEX64、COMPLEX128、BFLOAT16ND不大于 8√workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小-----executoraclOpExecutor**输出返回 op 执行器包含了算子计算流程-----平台附加限制Atlas 训练系列产品、Atlas 推理系列产品上不支持 BFLOAT16、COMPLEX32、COMPLEX64。从源码看math/tan/op_api/aclnn_tan.cpp 中aclnnInplaceTanGetWorkspaceSize原位版本没有独立的out参数CheckParamsInplace(selfRef, selfRef)将输入同时作为校验目标随后auto out const_castaclTensor*(selfRef)让计算结果直接复用输入张量对象即计算与输出共用同一块 Device 内存。因此selfRef被标记为输入/输出其数据类型列表与aclnnTan的out一致仅限浮点/复数整型输入需要用户先自行转换。五、第二段接口参数详解aclnnTan与aclnnInplaceTan第二段接口的参数完全一致参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口aclnnTanGetWorkspaceSize / aclnnInplaceTanGetWorkspaceSize获取executor输入op 执行器包含了算子计算流程由第一段接口返回stream输入指定执行任务的 Stream第二段接口的实现非常薄从源码看aclnnTan与aclnnInplaceTan主体只是调用CommonOpExecutorRun(workspace, workspaceSize, executor, stream)由统一的 executor 运行时负责把第一段构图时记录的算子计算流程真正下发到指定 Streammath/tan/op_api/aclnn_tan.cpp。workspaceSize为 0 时传入nullptr作为 workspace 是合法的示例代码中仅在workspaceSize 0时才申请内存。六、返回值与错误码两个第一段接口的返回值类型均为aclnnStatus完整状态码定义参见 aclnn返回码。第一段接口会完成全部入参校验校验失败的典型场景与返回码如下aclnnTanGetWorkspaceSize返回值错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 self 或 out 是空指针时ACLNN_ERR_PARAM_INVALID161002以下任一场景1. self 的数据类型不在支持的范围之内2. self 不能转为 out 的数据类型或 self 和 out 的数据类型不满足互推导关系3. self 和 out 的维度大于 84. self 和 out 的 shape 不一致aclnnInplaceTanGetWorkspaceSize返回值错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 selfRef 是空指针时ACLNN_ERR_PARAM_INVALID161002selfRef 的数据类型不在支持的范围之内这些错误码与 math/tan/op_api/aclnn_tan.cpp 中的校验流程一一对应CheckNotNull失败返回ACLNN_ERR_PARAM_NULLPTRCheckDtypeValid、CheckShape、CheckDtypeCanCast、CheckFormat任一失败均返回ACLNN_ERR_PARAM_INVALID。仓库 UTtests/ut/op_host/op_api/test_aclnn_tan.cpp中有case_nullptr_input/case_nullptr_output用例直接断言空指针入参返回ACLNN_ERR_PARAM_NULLPTR可作为行为参考。七、约束说明确定性计算aclnnTan与aclnnInplaceTan默认即为确定性实现多次调用在相同输入下结果可复现。数值范围Atlas A3 训练/推理系列产品FLOAT、BFLOAT16、FLOAT16、INT32、INT64 数据类型的输入数据范围为[-65504, 65504]时满足精度要求超过该数值范围无法保证精度请使用 CPU 进行计算。执行超时风险Atlas A3 训练/推理系列产品如果计算量过大可能导致算子执行超时报错类型为 aicore errorerrorStr为timeout or trap error。典型触发场景为最后 2 轴合轴小于 16而前面的轴合轴超大。此时应拆分输入张量或调整数据布局后再执行。NaN/Inf 行为Ascend 950PR/950DT从 math/tan/README.md 的约束说明看Ascend 950 系列 AI Core 实现中输入为 NaN、Inf 或绝对值大于等于 1e7 时输出为 NaN。八、底层实现链路源码级解读8.1 host 侧参数校验 → 构图 → workspace 计算第一段接口的执行链路集中在 math/tan/op_api/aclnn_tan.cppCREATE_EXECUTOR()创建唯一的 aclOpExecutorCheckParams(self, out)依次完成空指针、数据类型、shape、类型互推导、私有格式校验详见第六节空张量短路返回workspaceSize 0Compute(self, out, executor)以l0op低级算子原语构图Contiguous规整非连续输入→ 若为整型/布尔则Cast到 FLOAT →l0op::Tan真正计算→Cast回out数据类型 →ViewCopy写回输出从 executor 获取构图后的总workspaceSize返回给调用方。构图的核心计算原语l0op::Tan定义在 math/tan/op_api/tan.cpp先按输入 shape/数据类型分配输出张量y再由IsAiCoreSupport(x)决定走 AI CoreADD_TO_LAUNCHER_LIST_AICORE还是 AICPUADD_TO_LAUNCHER_LIST_AICPU路径。8.2 设备侧tiling 切核与 kernel 分发tilingAscend 950arch35架构的 tiling 实现在 math/tan/op_host/arch35/tan_tiling_arch35.cpp。TanTilingFunc动态获取 AIV 核数以THREAD_NUM(512) × MIN_ELEMENTS_PER_THREAD(4)为每核元素粒度估算所需核数并取 min 到可用核数随后把totalElements / perCoreElements / needCoreNum / dataType写入TanTilingData并依据数据类型FLOAT16/FLOAT/BF16/INT32设置TAN_TPL_SCH_MODE_0~3对应的 tiling key 与 blockDim同时将局部内存设置为 128KB、workspace 设置为 0。kernelAI Core kernel 入口 math/tan/op_kernel/tan_apt.cpp 通过编译期if constexpr按 tiling key 把数据类型映射到half、float、bfloat16_t、int32_t统一调用 arch35 的NsTan::ProcessT(x, y, tilingData)完成逐元素正切计算math/tan/op_kernel/arch35/tan_simt.h。AICPU kernel对 DOUBLE/COMPLEX64/COMPLEX128 等类型math/tan/op_kernel_aicpu/tan_aicpu.cpp 先校验输入输出数据类型、数据大小、维度完全一致再按元素总数决定是否多核并行使用 Eigen 的Eigen::numext::tanT逐元素计算。8.3 图模式GE入口除 aclnn 单算子接口外Tan 算子还支持 GE 图模式通过算子 IR math/tan/op_graph/tan_proto.h 构图shape 推导与数据类型推导实现在 math/tan/op_graph/tan_graph_infer.cpp输出数据类型继承输入示例见 examples/test_geir_tan.cpp。框架侧另有 TensorFlow 插件入口 math/tan/framework/tan_tf_plugin.cpp。九、调用示例aclnnTan以下代码摘自 math/tan/docs/aclnnTanaclnnInplaceTan.md 的调用示例完整展示了从资源初始化、构造 aclTensor、两段式调用、同步取回结果到资源释放的全流程。编译与运行方式请参考 编译与运行样例。#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_tan.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 shape_size 1; for (auto i : shape) { shape_size * i; } return shape_size; } 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根据自己的需要处理 CHECK_RET(ret 0, 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, 2, 3, 4, 5, 6, 7}; 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算子库API需要修改为具体的API uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnTan第一段接口 ret aclnnTanGetWorkspaceSize(self, out, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnTanGetWorkspaceSize 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;); } // 调用aclnnTan第二段接口 ret aclnnTan(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnTan 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侧需要根据具体API的接口定义修改 auto size GetShapeSize(outShape); std::vectorfloat resultData(size, 0); ret aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(float), 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需要根据具体API的接口定义修改 aclDestroyTensor(self); aclDestroyTensor(out); // 7.释放device资源需要根据具体API的接口定义修改 aclrtFree(selfDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }对示例代码的几个要点说明对 shape 为{4, 2}、8 个float元素0~7的输入输出应为tan(0)~tan(7)的近似值其中tan(0)0可用来快速核对结果是否合理示例采用裸指针 手动释放的写法。仓库 examples/test_aclnn_tan.cpp 中另提供了一份使用std::unique_ptr自定义 deleter管理aclrtStream、Device 内存与aclTensor生命周期的 RAII 风格版本异常安全更好推荐在实际工程中参考aclnnTanGetWorkspaceSize返回ACLNN_ERR_PARAM_INVALID161002时可按第六节的错误场景逐一排查 dtype、shape 与格式问题。十、调用示例aclnnInplaceTan原位版本的调用流程与aclnnTan基本一致差异在于不需要构造out张量selfRef同时充当输入与输出因此最后直接从selfRef的 Device 内存中拷贝结果。#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_tan.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 shape_size 1; for (auto i : shape) { shape_size * i; } return shape_size; } 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根据自己的需要处理 CHECK_RET(ret 0, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); // 2.构造输入与输出需要根据API的接口自定义构造 std::vectorint64_t selfRefShape {4, 2}; void* selfRefDeviceAddr nullptr; aclTensor* selfRef nullptr; std::vectorfloat selfRefHostData {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8}; // 创建self aclTensor ret CreateAclTensor(selfRefHostData, selfRefShape, selfRefDeviceAddr, aclDataType::ACL_FLOAT, selfRef); CHECK_RET(ret ACL_SUCCESS, return ret); // 3.调用CANN算子库API需要修改为具体的API uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnInplaceTan第一段接口 ret aclnnInplaceTanGetWorkspaceSize(selfRef, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnInplaceTanGetWorkspaceSize 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;); } // 调用aclnnInplaceTan第二段接口 ret aclnnInplaceTan(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnInplaceTan 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侧需要根据具体API的接口定义修改 auto size GetShapeSize(selfRefShape); std::vectorfloat resultData(size, 0); ret aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), selfRefDeviceAddr, size * sizeof(float), 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需要根据具体API的接口定义修改 aclDestroyTensor(selfRef); // 7.释放device资源需要根据具体API的接口定义修改 aclrtFree(selfRefDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }使用aclnnInplaceTan时请特别注意由于结果是原地写入selfRef的原数据会被覆盖如果后续还需要原始输入请先做数据备份或者改用非原位的aclnnTan。十一、测试与验证仓库为 Tan 算子提供了从单测到端到端的完整验证手段可用于验证本文描述的行为op_api UTtests/ut/op_host/op_api/test_aclnn_tan.cpp 覆盖了aclnnTan的 FP32 精度测试shape{1,16,1,1}、输入范围 [-2,2]、精度容差 1e-4、空张量用例以及空指针入参返回ACLNN_ERR_PARAM_NULLPTR的错误路径。AICPU UTtests/ut/op_kernel_aicpu/test_tan.cpp 覆盖 DOUBLE 等 AICPU 路径数据类型的计算。ST 用例tests/st/aclnnTan/atk_aclnnTan.jsonATK 算子测试描述与 tests/st/arch35/ttk_kernel_tan_st.csvarch35 内核场景用于端到端场景验证。示例程序examples/test_aclnn_tan.cppaclnn 方式与 examples/test_geir_tan.cppGE 图模式提供可直接编译运行的参考实现。十二、总结aclnnTan与aclnnInplaceTan是 CANN ops-math 中实现逐元素正切计算的标准单算子接口二者功能等价、仅在输出内存的分配方式上不同前者需要显式创建输出张量后者在输入内存上原地计算、可节省一份显存。两套接口都遵循GetWorkspaceSize 执行的两段式调用模型第一段完成参数校验与构图并返回 workspace 大小第二段在指定 Stream 上执行。底层实现上算子根据平台与数据类型在 AI CoreAscendC kernel按 tiling key 分派与 AICPUEigen 实现支持 DOUBLE/复数两条路径间自动选择host 侧还会对整型输入自动执行 cast 到 FLOAT 的预处理。需要进一步了解的读者可以继续阅读接口文档原文 math/tan/docs/aclnnTanaclnnInplaceTan.md、算子级说明 math/tan/README.md 以及 两段式接口说明、编译与运行样例 等配套文档。【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询