拓冰建站拓冰建站
首页 / 资讯中心 / 正文

CANN ops-nn Sigmoid 算子接入指南:aclnnSigmoid 与 aclnnInplaceSigmoid 两段式接口详解

CANN ops-nn Sigmoid 算子接入指南aclnnSigmoid 与 aclnnInplaceSigmoid 两段式接口详解【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn导读本文以 CANN ops-nn 仓库中 Sigmoid 算子文档 为核心系统讲解在 Ascend NPU 上通过aclnnSigmoid与aclnnInplaceSigmoid两组接口完成 Sigmoid 激活计算的完整链路从产品支持矩阵、数学定义、两段式接口函数原型到每个参数的约束与返回码、底层 AICore/AICPU 分派与 Kernel 实现原理再到可直接编译运行的两个完整 C 调用示例与测试验证方法。读完本文你可以独立完成 Sigmoid 算子在 CANN 环境下的单算子调用Single Op编程并理解非 inplace 与 inplace 两种模式的选型差异。一、算子概述与产品支持情况Sigmoid 是神经网络中最基础的激活函数之一其输出被压缩在(0, 1)区间常用于二分类输出层与门控机制。在 CANN ops-nn 仓库中Sigmoid 位于 activation/sigmoid 目录下以单算子Single Op形式对外提供aclnnAscend CANN Lite NN系列 C 接口同时支持图模式构图调用见 算子 IR 定义兼容 TensorFlow 的 Sigmoid 算子语义。按照算子文档与 Sigmoid README产品支持情况如下产品是否支持Ascend 950PR/Ascend 950DT支持Atlas A3 训练系列产品 / Atlas A3 推理系列产品支持Atlas A2 训练系列产品 / Atlas A2 推理系列产品支持Atlas 200I/500 A2 推理产品不支持Atlas 推理系列产品支持Atlas 训练系列产品支持二、功能说明与数学原理接口功能对输入 Tensor 逐元素element-wise完成 Sigmoid 运算计算公式为$$ out {\frac{1} {1{e}^{-input}}} $$即对输入的每个元素x输出1 / (1 exp(-x))。这一公式在 Kernel 实现中被原样落地从 sigmoid_f32.h 可以看到AICore Kernel 先对输入向量乘以-1Reg::Muls随后调用Reg::Exp计算指数、Reg::Adds加 1、最后Reg::Div做除法得到结果与文档公式逐条对应。值得说明的是aclnnSigmoid与aclnnInplaceSigmoid实现相同的数学功能区别仅在于结果存放位置aclnnSigmoid需新建一个输出张量对象存储计算结果输入self、输出out是两个独立的aclTensoraclnnInplaceSigmoid无需新建输出张量对象直接在输入张量的内存中存储计算结果接口中只有selfRef一个张量参数输入/输出二合一。选型建议若后续仍需要原始输入例如做反向传播求梯度应使用非 inplace 的aclnnSigmoid若输入张量不再被使用、希望节省一份输出内存可选用aclnnInplaceSigmoid。三、两段式接口设计Single Op 调用范式aclnnSigmoid与aclnnInplaceSigmoid均遵循 CANN 单算子调用的两段式接口设计详见仓库文档 两段式接口说明第一段GetWorkspaceSize调用aclnnSigmoidGetWorkspaceSize或aclnnInplaceSigmoidGetWorkspaceSize传入输入张量完成入参校验并由框架根据计算流程计算所需的 workspace 大小同时返回封装了算子计算流程的executor第二段执行根据第一段返回的workspaceSize在 Device 侧申请内存后调用aclnnSigmoid或aclnnInplaceSigmoid传入 workspace 与 executor在指定的stream上真正执行计算。函数原型如下// aclnnSigmoid 第一段接口 aclnnStatus aclnnSigmoidGetWorkspaceSize( const aclTensor *self, aclTensor *out, uint64_t *workspaceSize, aclOpExecutor **executor)// aclnnSigmoid 第二段接口 aclnnStatus aclnnSigmoid( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, const aclrtStream stream)// aclnnInplaceSigmoid 第一段接口 aclnnStatus aclnnInplaceSigmoidGetWorkspaceSize( aclTensor* selfRef, uint64_t* workspaceSize, aclOpExecutor** executor)// aclnnInplaceSigmoid 第二段接口 aclnnStatus aclnnInplaceSigmoid( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, const aclrtStream stream)从源码实现看两组接口共用同一套执行逻辑在 aclnn_sigmoid.cpp 中aclnnInplaceSigmoidGetWorkspaceSize在完成 inplace 专属参数校验后将selfRef同时作为输入与输出auto out const_castaclTensor*(selfRef);转发给公共的ExecSigmoidGetWorkspaceSize第二段接口则统一通过CommonOpExecutorRun完成计算因此两者在底层 Kernel 层面完全等价。四、aclnnSigmoid 接口参数详解4.1 aclnnSigmoidGetWorkspaceSize 参数说明参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续TensorselfaclTensor*输入待进行 Sigmoid 计算的入参公式中的 input支持空 Tensorshape 需要与 out 一致FLOAT、FLOAT16、DOUBLE、INT8、INT16、INT32、INT64、UINT8、BOOL、COMPLEX64、COMPLEX128、BFLOAT16ND0-8√outaclTensor*输出计算的出参shape 需要与 self 一致FLOAT、FLOAT16、DOUBLE、COMPLEX64、COMPLEX128、BFLOAT16ND0-8√workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小-----executoraclOpExecutor**输出返回 op 执行器包含了算子计算流程-----参数要点解读结合源码self的 12 种数据类型与 aclnn_sigmoid.cpp 中的DTYPE_SUPPORT_LIST完全一致out的 6 种类型对应DTYPE_OUT_LIST当self的数据类型不在输出支持列表内时如 INT8/BOOL 等API 层会先通过l0op::Cast将输入转换为out的类型完成 Sigmoid 后再Cast回out的类型并ViewCopy到输出见ExecSigmoidGetWorkspaceSize的构图逻辑因此self与out允许数据类型不同shape 支持 0~8 维且允许空 Tensorself-IsEmpty()时直接返回 workspace 大小不走 Kernel 计算对应 单测 case_2非连续 Tensor 支持API 层会先通过l0op::Contiguous将输入规整为连续张量输出侧通过ViewCopy处理非连续输出开发者无需手动搬移数据BFLOAT16 支持与芯片强相关源码中CheckDtypeValid通过CheckSocVersionIsSupportBf16判断仅 Ascend 950DAV_2201等支持 BF16 的架构放行其他 SoC 上传入 BF16 的self会返回ACLNN_ERR_PARAM_INVALID。4.2 aclnnSigmoidGetWorkspaceSize 返回值与错误码第一段接口会完成入参校验出现以下场景时报错返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 self 或 out 是空指针ACLNN_ERR_PARAM_INVALID161002self 和 out 的数据类型和数据格式不在支持的范围之内ACLNN_ERR_PARAM_INVALID161002self 和 out 的 shape 不匹配以上错误码与源码中CheckParams的三步校验一一对应先检查空指针CheckNotNull再检查数据类型CheckDtypeValid最后检查 shapeCheckShape要求self与outshape 相等且self维度不超过 8。完整返回码含义可参考仓库文档 aclnn 返回码说明。4.3 aclnnSigmoid第二段参数说明参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclnnSigmoidGetWorkspaceSize 获取executor输入op 执行器包含了算子计算流程stream输入指定执行任务的 Stream返回值同样为aclnnStatus状态码。第二段接口本身不再做参数校验其正确性依赖第一段接口返回的executor与workspaceSize因此必须严格按先 GetWorkspaceSize、再执行的顺序调用。五、aclnnInplaceSigmoid 接口参数详解5.1 aclnnInplaceSigmoidGetWorkspaceSize 参数说明参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续TensorselfRefaclTensor*输入/输出计算的入参-FLOAT、FLOAT16、DOUBLE、COMPLEX64、COMPLEX128、BFLOAT16ND1-8√workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小-----executoraclOpExecutor**输出返回 op 执行器包含了算子计算流程-----与 aclnnSigmoid 的关键差异inplace 版本只有一个张量参数selfRef既是输入又是输出计算完成后原输入内存被结果覆盖数据类型只支持FLOAT、FLOAT16、DOUBLE、COMPLEX64、COMPLEX128、BFLOAT16DTYPE_OUT_LIST不支持 INT8/BOOL 等整型——inplace 模式下无法借助 Cast 中间张量规避类型限制因此输入必须本身就是可参与 Sigmoid 计算的类型shape 范围为 1-8 维非 inplace 版本支持 0-8 维0 维空 Tensor 场景在 inplace 模式下无意义。产品相关的类型差异重要对于 Atlas 推理系列产品、Atlas 训练系列产品selfRef实际支持的数据类型为 FLOAT、FLOAT16、DOUBLE、COMPLEX64、COMPLEX128不含 BFLOAT16。这一约束与源码中的GetSelfRefDtypeList一致当芯片架构为DAV_2201Ascend 950 系列或支持 regbase 时返回含 BF16 的完整列表其他架构如 Atlas 训练/推理系列返回不含 BF16 的ASCEND910_OUTPUT_DTYPE_SUPPORT_LIST。5.2 aclnnInplaceSigmoidGetWorkspaceSize 返回值与错误码返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 selfRef 是空指针ACLNN_ERR_PARAM_INVALID161002selfRef 的数据类型不在支持的范围之内5.3 aclnnInplaceSigmoid第二段参数说明参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclnnInplaceSigmoidGetWorkspaceSize 获取executor输入op 执行器包含了算子计算流程stream输入指定执行任务的 Stream六、约束说明确定性计算aclnnSigmoid与aclnnInplaceSigmoid均为默认确定性实现同一输入多次调用结果一致便于调试与结果比对。其余约束数据类型、维度、格式、BF16 的 SoC 限制已在上述参数表中说明此外在 sigmoid_def.cpp 中可以看到AICore 侧算子原型x/y仅声明 FLOAT16、BF16、FLOAT 三种类型其余类型由 API 层的 Cast 链路兜底这与参数表self 支持 12 种类型、out 支持 6 种类型并不矛盾。七、底层实现原理从 API 到 Kernel 的完整调用链理解调用链有助于排查性能与报错问题。Sigmoid 算子的执行路径可分为四层1. API 层Host 侧构图aclnn_sigmoid.cpp 中的ExecSigmoidGetWorkspaceSize依次完成参数校验 → 输入Contiguous规整 → 如需要Cast到计算类型 →l0op::Sigmoid构图 → 结果Cast回输出类型 →ViewCopy到输出最后通过executor-GetWorkspaceSize()汇总所需 workspace。2. 算子分派层sigmoid.cpp 中l0op::Sigmoid根据输入数据类型决定计算路径AICORE_DTYPE_SUPPORT_LIST {DT_FLOAT, DT_FLOAT16, DT_BF16}命中时走 AICore 向量核SigmoidAiCore通过ADD_TO_LAUNCHER_LIST_AICORE入队其他类型走 AICPU 兜底路径SigmoidAiCpu通过ADD_TO_LAUNCHER_LIST_AICPU入队。这也解释了为什么 INT8/BOOL/DOUBLE/COMPLEX 等类型能够支持——它们在 API 层被 Cast 为 FLOAT 类类型后送入 AICore或在 AICPU 上完成计算。3. Tiling 与 Shape 推导Host 侧sigmoid_infershape.cpp 复用InferShape4Elewise做逐元素算子的 shape 推导sigmoid_tiling_arch35.cpp 根据 x/y 数据类型生成 tiling keyF16/BF16/F32 三种并结合 AIV 核数、UB 内存大小做数据切分。4. Kernel 层Device 侧sigmoid_apt.cpp 是 AICore 核函数入口按SIGMOID_F16_TILING_KEY/SIGMOID_BF16_TILING_KEY/SIGMOID_F32_TILING_KEY分发到对应的SigmoidF16/SigmoidBf16/SigmoidF32算子类以 sigmoid_f32.h 为例其Process采用UB 双缓冲DOUBLE_BUFFER 多核分块 尾块处理流水Compute 阶段用RegTensor寄存器向量指令完成-x→exp→1→ 除法 的公式计算。八、完整调用示例以下两个示例分别演示aclnnSigmoid非 inplace与aclnnInplaceSigmoidinplace的完整调用流程代码与文档示例一致仓库中还提供了同款可编译样例 test_aclnn_sigmoid.cpp 与 test_aclnn_inplace_sigmoid.cpp。具体编译和执行过程请参考仓库文档 编译与运行样例。8.1 aclnnSigmoid 接口调用示例#include cinttypes #include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_sigmoid.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 {2, 2}; std::vectorint64_t outShape {2, 2}; void* selfDeviceAddr nullptr; void* outDeviceAddr nullptr; aclTensor* self nullptr; aclTensor* out nullptr; std::vectorfloat selfHostData {0, 1, 2, 3}; std::vectorfloat outHostData {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; // 调用aclnnSigmoid第一段接口 ret aclnnSigmoidGetWorkspaceSize(self, out, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnSigmoidGetWorkspaceSize 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); } // 调用aclnnSigmoid第二段接口 ret aclnnSigmoid(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnSigmoid 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(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[% PRId64 ] is: %f\n, i, resultData[i]); } // 6. 释放aclTensor和aclScalar需要根据具体API的接口定义修改 aclDestroyTensor(self); aclDestroyTensor(out); // 7. 释放device资源需要根据具体API的接口定义参数 aclrtFree(selfDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }预期输出输入{0, 1, 2, 3}result[0] is: 0.500000、result[1] is: 0.731059、result[2] is: 0.880797、result[3] is: 0.952574即sigmoid(0)0.5随输入增大趋近于 1。8.2 aclnnInplaceSigmoid 接口调用示例#include cinttypes #include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_sigmoid.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 {2, 2}; void* selfDeviceAddr nullptr; aclTensor* self nullptr; std::vectorfloat selfHostData {0, 1, 2, 3}; // 创建self aclTensor ret CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_FLOAT, self); CHECK_RET(ret ACL_SUCCESS, return ret); // 3. 调用CANN算子库API需要修改为具体的API名称 uint64_t inplaceWorkspaceSize 0; aclOpExecutor* inplaceExecutor; // 调用aclnnInplaceSigmoid第一段接口 ret aclnnInplaceSigmoidGetWorkspaceSize(self, inplaceWorkspaceSize, inplaceExecutor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnInplaceSigmoidGetWorkspaceSize failed. ERROR: %d\n, ret); return ret); // 根据第一段接口计算出的workspaceSize申请device内存 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); } // 调用aclnnInplaceSigmoid第二段接口 ret aclnnInplaceSigmoid(inplaceWorkspaceAddr, inplaceWorkspaceSize, inplaceExecutor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnInplaceSigmoid 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 inplaceSize GetShapeSize(selfShape); std::vectorfloat inplaceResultData(inplaceSize, 0); ret aclrtMemcpy(inplaceResultData.data(), inplaceResultData.size() * sizeof(inplaceResultData[0]), selfDeviceAddr, inplaceSize * 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 inplaceSize; i) { LOG_PRINT(inplaceResult[% PRId64 ] is: %f\n, i, inplaceResultData[i]); } // 6. 释放aclTensor和aclScalar需要根据具体API的接口定义修改 aclDestroyTensor(self); // 7. 释放device资源需要根据具体API的接口定义参数 aclrtFree(selfDeviceAddr); if (inplaceWorkspaceSize 0) { aclrtFree(inplaceWorkspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }对比两个示例可以看出 inplace 模式的省事之处无需为out单独申请内存、创建aclTensor结果直接读回selfDeviceAddr即可但代价是原始输入{0,1,2,3}被覆盖为{0.5, 0.731, 0.881, 0.953}后续无法再使用原始数据。示例中的通用注意点deviceId需根据实际设备填写多卡环境请通过环境变量或aclrtSetDevice指定正确的卡号workspaceSize可能为 0Sigmoid 的 AICore 路径通常无需额外 workspace见 tiling 中WORKSPACE_SIZE 0的常量设计此时跳过aclrtMalloc即可但调用第二段接口时仍须按示例写法传入aclrtMalloc使用ACL_MEM_MALLOC_HUGE_FIRST标志申请大块连续内存优先若计算数据量较大建议将aclrtMemcpy与计算放在不同阶段双 stream 异步示例中为便于理解采用同步等待方式。九、测试与验证方法仓库为 Sigmoid 算子提供了完整的多层测试可作为自验证的参照OP API 单测tests/ut/op_api/test_aclnn_sigmoid.cpp 覆盖了常规 shape如{1,16,1,1}精度阈值 0.0001、空 Tensor{0}、空指针入参CheckNotNull等场景同时验证TestGetWorkspaceSize与TestPrecision两条测试路径Kernel 单测tests/ut/op_kernel/test_sigmoid_apt.cpp 直接验证sigmoid核函数入口Tiling 单测tests/ut/op_host/arch35/test_sigmoid_tiling.cpp 与 tests/ut/op_host/test_sigmoid_infershape.cpp 分别校验 tiling 参数生成与 shape 推导系统测试STtests/st/aclnnSigmoid/executor_aclnnSigmoid.py 使用torch.nn.Sigmoid()作为基准实现golden通过 ATK 框架对 AICore 输出做数值比对证明算子行为与 PyTorch 语义一致golden.py 提供参考实现atk_aclnnSigmoid.json定义测试用例配置。若你在自有工程中集成最简验证方式即用 8.1/8.2 节示例代码将输出与1.0 / (1.0 exp(-x))的 Host 侧计算结果比对误差在 float 精度范围内即可认为接入成功。十、常见问题与排查指引现象可能原因处理建议第一段接口返回ACLNN_ERR_PARAM_NULLPTR161001self/out/selfRef指针未初始化或为 nullptr检查aclCreateTensor是否成功返回见示例第 2 步第一段接口返回ACLNN_ERR_PARAM_INVALID161002self与out的 shape 不一致、维度超过 8或self为 BF16 但当前 SoC 不支持核对参数表第 4.1/5.1 节的类型与 shape 约束第二段接口执行后结果全为 0 或未更新未调用aclrtSynchronizeStream就读取结果或 inplace 场景读错了内存地址按示例顺序先aclrtSynchronizeStream再从正确地址aclrtMemcpy回 Host编译报找不到aclnnop/aclnn_sigmoid.h头文件搜索路径未包含 CANN 算子库 include 目录确认编译环境已配置 CANN 安装目录下的include并链接对应算子库workspace 申请失败多次调用第一段接口但未在每次执行后释放旧的 workspace每次执行完毕后aclrtFree(workspaceAddr)避免内存泄漏见示例第 7 步结语aclnnSigmoid与aclnnInplaceSigmoid是 CANN ops-nn 中接入 Sigmoid 激活最直接的两条通道前者输出独立、适合需要保留输入的场景后者原地计算、省内存省拷贝。两者共享同一套两段式调用范式与底层 Kernel 实现掌握其中任意一个即可快速迁移到另一个。本文给出的参数约束、错误码、源码链路与可运行示例覆盖了从看懂文档到跑通算例的完整路径若需在图模式下使用可进一步参考 算子 IR 定义 与 Sigmoid 模块 README。【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门