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),仅供参考

相关新闻

华硕Z890M主板真相与Ubuntu网卡驱动修复指南

华硕Z890M主板真相与Ubuntu网卡驱动修复指南

1. 这块Z890M主板根本不是“华硕官方型号”——先破除一个关键认知误区你搜到“华硕Z890M主板”,点开电商页面、论坛帖子甚至维修单,看到的几乎全是带华硕Logo、包装盒印着ASUS字样、BIOS界面是Aptio风格的板子——但我要直说:目前&#xff0…

2026/9/21 14:10:21 阅读更多 →
银河麒麟V10内网离线安装VLC:本地源配置与依赖处理全攻略

银河麒麟V10内网离线安装VLC:本地源配置与依赖处理全攻略

目录我每个月总会遇到几台要求装离线软件的终端。前段时间拿到一批银河麒麟V10的办公机器,本以为装个播放器是十分钟的事,结果发现软件商店空白、源不可用、预装播放器连最常见的H.264都放不顺,才算真正体会到什么叫“内网安装的麻烦”。捣鼓…

2026/9/23 3:44:11 阅读更多 →
网络安全入行越来越难?2026年从小白到安全工程师应该怎么学

网络安全入行越来越难?2026年从小白到安全工程师应该怎么学

2026年的网络安全行业,已经彻底告别了“报个培训班就能上岗”的时代。企业招聘JD里动辄要求“3年以上实战经验”“熟悉云原生安全与AI攻防”“能独立完成红蓝对抗复盘”,而应届生和转行者面对的是海量碎片化资料、日益内卷的初级岗位,以及AI工…

2026/9/22 14:51:59 阅读更多 →

最新新闻

Web端三通道支付集成:QQ/支付宝/Payment API最小可行方案

Web端三通道支付集成:QQ/支付宝/Payment API最小可行方案

简介:这是一套面向Web开发初学者与中级工程师的多支付网关集成源码,聚焦QQ支付与支付宝(Alipay)H5/扫码支付的前端后端完整实现,解决电商类网站或SaaS系统快速接入主流国内支付渠道的技术落地难题。资源共219个文件&am…

2026/9/23 22:12:17 阅读更多 →
SEO外链管理系统源码部署与一键优化实战指南

SEO外链管理系统源码部署与一键优化实战指南

简介:一款面向SEO从业者与网站管理员的工具型源码,借助自动化方式集中管理外部链接,解决人工维护外链耗时、易失效的问题,适合想提升站点排名与权重的中初级用户。压缩包共18个文件,主要包含3个PHP脚本用于网站配置和核…

2026/9/23 22:12:17 阅读更多 →
C++课设实战:EasyX仿超级马里奥源码拆解与二次开发指南

C++课设实战:EasyX仿超级马里奥源码拆解与二次开发指南

简介:这是一份基于C与EasyX图形库还原经典超级马里奥的完整游戏源码,面向计算机、通信、自动化等专业的学生与开发者,可直接用作毕业设计、课程设计或期末大作业。项目已实现1-1、1-2、1-3三个完整关卡,涵盖移动、跳跃、加速发射火…

2026/9/23 22:12:17 阅读更多 →
2024大厂前端面试攻略:从基础原理到项目实战的完整备战指南

2024大厂前端面试攻略:从基础原理到项目实战的完整备战指南

咱们开篇先把话说透:2024年还在传“前端已死”的人,要么没在认真找前端工作,要么看的招聘信息不超过十条。这一行的真相是——初级前端的确在卷学历、卷实习,但真正能解决业务问题、有系统设计能力、能扛起一个产品线渲染与体验责…

2026/9/23 22:12:17 阅读更多 →
Python实现设备剩余使用寿命RUL预测与故障诊断

Python实现设备剩余使用寿命RUL预测与故障诊断

简介:本资源是一套面向工业智能运维领域的Python剩余使用寿命(RUL)预测与故障诊断代码框架,适用于具备基础Python和机器学习能力的工程师、研究生及科研人员,解决设备退化建模、早期故障识别与预测性维护中的核心算法实…

2026/9/23 22:12:17 阅读更多 →
vLLM-Omni 新 TTS 模型怎么接?从跑通第一句音频到合入主线的完整实战路径

vLLM-Omni 新 TTS 模型怎么接?从跑通第一句音频到合入主线的完整实战路径

vLLM-Omni 新 TTS 模型怎么接?从跑通第一句音频到合入主线的完整实战路径 【免费下载链接】vllm-omni A framework for efficient model inference with omni-modality models 项目地址: https://gitcode.com/GitHub_Trending/vl/vllm-omni 你手上有一个 Hug…

2026/9/23 22:11:15 阅读更多 →

日新闻

3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A…

2026/9/23 0:00:23 阅读更多 →
2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我 刚把开发环境的显示器从1080P换到2K,跑老项目直接报错,版本升级后 API…

2026/9/23 0:01:25 阅读更多 →
3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点 官方文档翻了三遍还是云里雾里?别急,美眉图在实战项目中常被用来做数据可视化,但它的原理比你想的简单。今天咱们直接上手,用一个完整的小项目把美眉图跑通,不再死磕那些冗长的理论说明。…

2026/9/23 0:01:25 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/23 4:55:02 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/23 4:49:06 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/23 9:53:41 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/23 9:53:40 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/23 9:53:40 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/23 9:53:40 阅读更多 →