CANN ops-nn 算子开发指南:aclnnSigmoid 与 aclnnInplaceSigmoid 两段式接口详解
【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn
本文以 CANN ops-nn 仓库中experimental/activation/sigmoid_v2实验性算子的对外 ACLNN 接口文档为骨架,系统讲解aclnnSigmoid/aclnnInplaceSigmoid两段式接口的功能语义、函数原型、参数约束、错误码、底层实现与调用示例。读完本文,你将能够在 NPU 上独立完成 Sigmoid(含原地版本)算子的 ACLNN 两段式编程:先通过GetWorkspaceSize接口完成入参校验与执行器构建,再通过执行接口在指定 Stream 上异步下发计算任务,并掌握基于仓库示例与测试的验证方法。
产品支持情况
experimental/activation/sigmoid_v2提供的aclnnSigmoid/aclnnInplaceSigmoid接口在 CANN ops-nn 中支持的硬件产品如下:
| 产品 | 是否支持 |
|---|---|
| Ascend 950PR/Ascend 950DT | √ |
| Atlas A3 训练系列产品/Atlas A3 推理系列产品 | √ |
| Atlas A2 训练系列产品/Atlas A2 推理系列产品 | √ |
| Atlas 200I/500 A2 推理产品 | × |
| Atlas 推理系列产品 | √ |
| Atlas 训练系列产品 | √ |
需要注意:Atlas 200I/500 A2 推理产品当前不支持该接口,其他主流的 Atlas 训练/推理系列及 Ascend 950 系列均可用。
功能说明
aclnnSigmoid:对输入 Tensor 执行 Sigmoid 计算,并将结果写入独立分配的输出 Tensor。aclnnInplaceSigmoid:对输入 Tensor 原地执行 Sigmoid 计算,计算结果直接覆盖输入 Tensor 所在的内存,无需额外输出 Tensor。experimental/activation/sigmoid_v2目录对外导出的 ACLNN 接口名与原实现保持一致,便于上层框架无缝切换。
Sigmoid 的计算公式为:
$$ out = \frac{1}{1 + e^{-input}} $$
从公式可以看出,Sigmoid 将任意实数输入映射到开区间 (0, 1),是神经网络中常用的非线性激活函数,常用于二分类输出层或作为门控单元的基础运算。
两段式接口与调用流程
每个 ACLNN 算子都采用两段式接口设计,aclnnSigmoid系列同样如此:
- 第一段:调用
aclnnSigmoidGetWorkspaceSize或aclnnInplaceSigmoidGetWorkspaceSize,完成入参校验、构建 op 执行器,并返回执行所需的 Device 侧 workspace 大小。 - 第二段:调用
aclnnSigmoid或aclnnInplaceSigmoid,传入第一段获取的 workspace、workspaceSize、executor 以及目标 Stream,在 NPU 上异步执行计算。
上图展示了 ACLNN 算子调用的完整流程:初始化 → 运行时资源申请 → 单算子调用(申请输入/输出内存 → 传输数据 → 计算 workspace 大小并申请内存 → 执行算子 → 同步等待 → 释放内存)→ 运行时资源释放 → 去初始化。其中"计算 workspace 大小并申请内存"与"执行算子"两步,正是两段式接口的核心环节。aclnnSigmoid的完整调用生命周期可参考 examples/test_aclnn_sigmoid.cpp,原地版本可参考 examples/test_aclnn_inplace_sigmoid.cpp。
函数原型
四个对外接口的函数原型如下(声明位于 op_host/op_api/aclnn_sigmoid.h):
非原地版本两段式接口:
aclnnStatus aclnnSigmoidGetWorkspaceSize( const aclTensor *self, aclTensor *out, uint64_t *workspaceSize, aclOpExecutor **executor);aclnnStatus aclnnSigmoid( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, const aclrtStream stream);原地版本两段式接口:
aclnnStatus aclnnInplaceSigmoidGetWorkspaceSize( aclTensor *selfRef, uint64_t *workspaceSize, aclOpExecutor **executor);aclnnStatus aclnnInplaceSigmoid( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, const aclrtStream stream);两段式接口必须配对使用:第二段执行接口依赖第一段返回的executor与workspaceSize,跳步调用会导致执行失败。
aclnnSigmoidGetWorkspaceSize 参数说明
参数说明
| 参数名 | 输入/输出 | 描述 | 使用说明 | 数据类型 | 数据格式 | 维度(shape) | 非连续Tensor |
|---|---|---|---|---|---|---|---|
| self(aclTensor*) | 输入 | 待进行 Sigmoid 计算的入参,公式中的 input | 支持空 Tensor;shape 必须与 out 完全一致;数据类型必须与 out 完全一致 | BFLOAT16、FLOAT16、FLOAT32 | ND | 0-8 | √ |
| out(aclTensor*) | 输出 | 计算的出参 | 支持空 Tensor;shape 必须与 self 完全一致;数据类型必须与 self 完全一致 | BFLOAT16、FLOAT16、FLOAT32 | ND | 0-8 | √ |
| workspaceSize(uint64_t*) | 输出 | 返回需要在 Device 侧申请的 workspace 大小 | - | - | - | - | - |
| executor(aclOpExecutor**) | 输出 | 返回 op 执行器,包含算子计算流程 | - | - | - | - | - |
注意:文档中数据类型使用FLOAT32描述,与仓库 README 及源码中的FLOAT(即DT_FLOAT,对应 ACL 的ACL_FLOAT)指代同一类型。
返回值
返回aclnnStatus状态码,具体参见 aclnn 返回码。
第一段接口会完成入参校验,出现以下场景时报错:
| 返回码 | 错误码 | 描述 |
|---|---|---|
| ACLNN_ERR_PARAM_NULLPTR | 161001 | 传入的 self 或 out 是空指针 |
| ACLNN_ERR_PARAM_INVALID | 161002 | self 或 out 的数据类型不在支持范围内 |
| ACLNN_ERR_PARAM_INVALID | 161002 | self 和 out 的数据类型不一致 |
| ACLNN_ERR_PARAM_INVALID | 161002 | self 和 out 的 shape 不一致 |
| ACLNN_ERR_PARAM_INVALID | 161002 | self 或 out 的维度大于 8 |
从源码 op_host/op_api/aclnn_sigmoid.cpp 可以看到校验逻辑与文档完全对应:CheckParams依次执行CheckNotNull(空指针检查)、CheckDtypeValid(dtype 是否在{DT_FLOAT, DT_FLOAT16, DT_BF16}支持列表内、self 与 out dtype 是否一致)、CheckShape(shape 是否相等、维度是否超过常量MAX_DIM_LEN = 8)。
aclnnSigmoid 参数说明
参数说明
| 参数名 | 输入/输出 | 描述 |
|---|---|---|
| workspace | 输入 | 在 Device 侧申请的 workspace 内存地址 |
| workspaceSize | 输入 | 在 Device 侧申请的 workspace 大小,由第一段接口 aclnnSigmoidGetWorkspaceSize 获取 |
| executor | 输入 | op 执行器,包含算子计算流程 |
| stream | 输入 | 指定执行任务的 Stream |
返回值
返回aclnnStatus状态码,具体参见 aclnn 返回码。第二段接口内部通过CommonOpExecutorRun统一完成执行器的调度运行。
aclnnInplaceSigmoidGetWorkspaceSize 参数说明
参数说明
| 参数名 | 输入/输出 | 描述 | 使用说明 | 数据类型 | 数据格式 | 维度(shape) | 非连续Tensor |
|---|---|---|---|---|---|---|---|
| selfRef(aclTensor*) | 输入/输出 | 原地计算的输入输出张量 | 支持空 Tensor;原地计算后 shape 和 dtype 不变 | BFLOAT16、FLOAT16、FLOAT32 | ND | 0-8 | √ |
| workspaceSize(uint64_t*) | 输出 | 返回需要在 Device 侧申请的 workspace 大小 | - | - | - | - | - |
| executor(aclOpExecutor**) | 输出 | 返回 op 执行器,包含算子计算流程 | - | - | - | - | - |
返回值
返回aclnnStatus状态码,具体参见 aclnn 返回码。
第一段接口会完成入参校验,出现以下场景时报错:
| 返回码 | 错误码 | 描述 |
|---|---|---|
| ACLNN_ERR_PARAM_NULLPTR | 161001 | 传入的 selfRef 是空指针 |
| ACLNN_ERR_PARAM_INVALID | 161002 | selfRef 的数据类型不在支持范围内 |
| ACLNN_ERR_PARAM_INVALID | 161002 | selfRef 的维度大于 8 |
原地版本不需要单独的out张量,selfRef同时充当输入与输出。源码中aclnnInplaceSigmoidGetWorkspaceSize在完成CheckInplaceParams校验后,直接以selfRef同时作为 self 和 out 复用内部的执行逻辑,从而保证原地语义。
aclnnInplaceSigmoid 参数说明
参数说明
| 参数名 | 输入/输出 | 描述 |
|---|---|---|
| workspace | 输入 | 在 Device 侧申请的 workspace 内存地址 |
| workspaceSize | 输入 | 在 Device 侧申请的 workspace 大小,由第一段接口 aclnnInplaceSigmoidGetWorkspaceSize 获取 |
| executor | 输入 | op 执行器,包含算子计算流程 |
| stream | 输入 | 指定执行任务的 Stream |
返回值
返回aclnnStatus状态码,具体参见 aclnn 返回码。
约束说明
使用本接口时需要严格遵守以下约束:
- 输入 dtype 仅支持
FLOAT(FLOAT32)、FLOAT16、BFLOAT16。 aclnnSigmoid中,self和out的 dtype 必须一致。aclnnSigmoid中,self和out的 shape 必须一致。- 维度范围为 0 到 8(0 维即标量 Tensor)。
- 支持空 Tensor:当输入为空 Tensor 时,第一段接口直接返回
workspaceSize = 0,无需下发实际计算。 - 支持非连续 Tensor:接口内部会先执行
Contiguous将输入规整为连续内存,计算完成后再通过ViewCopy将结果写回目标输出,保证非连续输入/输出的正确性。 FLOAT16和BFLOAT16路径在 kernel 内部采用升精度计算:先将数据提升到 FLOAT32 完成 Sigmoid 运算,再回写到原 dtype,以降低低精度下的计算误差。
从源码看,空 Tensor 与连续化的处理位于 op_host/op_api/aclnn_sigmoid.cpp 的ExecSigmoidGetWorkspaceSize:当self->IsEmpty()时立即返回;否则依次构造l0op::Contiguous(self)→l0op::Sigmoid(selfContiguous)→l0op::ViewCopy(sigmoidOpOut, out)的执行链,最终通过uniqueExecutor->GetWorkspaceSize()汇总 workspace 需求。
源码实现剖析
Host 侧:ACLNN 接口入口
- op_host/op_api/aclnn_sigmoid.cpp:对外提供四个 ACLNN 两段式接口。第一段接口负责参数校验、空 Tensor 短路、构建
Contiguous → Sigmoid → ViewCopy的 l0op 执行链并汇总 workspace;第二段接口统一走CommonOpExecutorRun在指定 Stream 上异步执行。同时通过L2_DFX_PHASE_1/L2_DFX_PHASE_2宏埋点,用于算子调用的 DFX 日志与性能观测。 - op_host/op_api/sigmoid.h 与 op_host/op_api/sigmoid.cpp:提供内部
l0op::Sigmoid封装,当前由aclnn_sigmoid.cpp直接调用。该封装通过OP_TYPE_REGISTER(Sigmoid)注册算子类型,内部按输入 Tensor 的 view shape 与 dtype 分配输出 Tensor(ND 格式),并通过ADD_TO_LAUNCHER_LIST_AICORE将算子加入 AICore 启动器列表。 - op_host/sigmoid_tiling.cpp:tiling 实现。获取平台信息(AIV core 数量、UB 内存大小),校验输入 dtype(同样限定
DT_FLOAT / DT_FLOAT16 / DT_BF16),按 core 数将总元素数切分为formerNum / formerLength / tailLength / tileLength,并申请系统库要求的 workspace 大小。
Kernel 侧:AICore 计算实现
- op_kernel/sigmoid.cpp:kernel 入口,根据
sizeof(D_T_X) == sizeof(float)在编译期选择两条计算路径:- FLOAT32 路径:
NsSigmoid::KernelSigmoid<D_T_X>。 - FLOAT16 / BFLOAT16 路径:
NsSigmoid::KernelSigmoidUpcast<D_T_X>,即文档约束中所述的"升精度计算"。
- FLOAT32 路径:
- op_kernel/sigmoid.h:核心模板实现。
KernelSigmoidBase完成基于 tiling 数据的 core 分块、DataCopyPad搬入搬出与双缓冲流水(BUFFER_NUM = 2)。KernelSigmoid::ComputeBase在 FLOAT32 域内执行Muls(-1) → Exp → Adds(1) → Div(1/x)完成1/(1+e^{-x});KernelSigmoidUpcast::ComputeBase先Cast到 FLOAT32 执行同样的计算序列,最后以CAST_RINT舍入模式回写到原 dtype。sigmoid_tiling_data.h与sigmoid_tiling_key.h定义 tiling 数据结构与 key。
调用示例
仓库在 examples 目录提供了可直接编译运行的完整示例。下面以test_aclnn_sigmoid.cpp为例,梳理两段式调用的关键步骤(为便于阅读做了精简,完整代码见仓库):
#include "acl/acl.h" #include "aclnnop/aclnn_sigmoid.h" // 1. 初始化 ACL 并创建 Stream aclInit(nullptr); aclrtSetDevice(0); aclrtCreateStream(&stream); // 2. 在 Device 侧申请输入/输出内存并搬运数据 aclrtMalloc(&input_device, bytes, ACL_MEM_MALLOC_HUGE_FIRST); aclrtMalloc(&output_device, bytes, ACL_MEM_MALLOC_HUGE_FIRST); aclrtMemcpy(input_device, bytes, input_host.data(), bytes, ACL_MEMCPY_HOST_TO_DEVICE); // 3. 通过 aclCreateTensor 构建 aclTensor(ND 格式,显式传入 shape/strides) aclCreateTensor(shape.data(), shape.size(), config.acl_dtype, strides.data(), 0, ACL_FORMAT_ND, shape.data(), shape.size(), input_device); // 4. 第一段接口:获取 workspace 大小并构建 executor aclnnSigmoidGetWorkspaceSize(input_tensor, output_tensor, &workspace_size, &executor); // 5. 按需申请 workspace if (workspace_size > 0) { aclrtMalloc(&workspace, workspace_size, ACL_MEM_MALLOC_HUGE_FIRST); } // 6. 第二段接口:在指定 Stream 上执行 aclnnSigmoid(workspace, workspace_size, executor, stream); // 7. 同步等待并回拷结果 aclrtSynchronizeStream(stream); aclrtMemcpy(output_host.data(), bytes, output_device, bytes, ACL_MEMCPY_DEVICE_TO_HOST); // 8. 依次释放 tensor / workspace / 内存 / stream,并 ResetDevice、aclFinalizetest_aclnn_sigmoid.cpp还内置了一个冒烟用例(smoke case):输入{0.0, 1.0, 2.0, 3.0}(shape 为{2, 2},FLOAT32),逐元素与 CPU 参考值1.0f / (1.0f + std::exp(-x))比对,容差1e-6。同时它支持命令行传参形式运行:test_aclnn_sigmoid <dtype> <shape_csv> <input.bin> <output.bin> [device_id],例如test_aclnn_sigmoid fp16 2,3 /tmp/in.bin /tmp/out.bin 0,便于批量验证不同 dtype 与 shape 组合。
原地版本示例 test_aclnn_inplace_sigmoid.cpp 的结构更简洁:只申请一块 Device 内存构建self,依次调用aclnnInplaceSigmoidGetWorkspaceSize与aclnnInplaceSigmoid后,直接从原地址回拷结果并打印result[0..3],验证原地覆盖语义。
编译与运行
仓库提供了编译运行脚本 examples/run.sh。先确保 custom run 包已安装并加载 CANN 环境:
source /usr/local/Ascend/cann-8.5.0-beta.1/set_env.sh export LD_LIBRARY_PATH=/usr/local/Ascend/cann-8.5.0-beta.1/opp/vendors/customize_nn/op_api/lib:${LD_LIBRARY_PATH} cd experimental/activation/sigmoid_v2/examples bash run.shrun.sh使用g++ -std=c++17分别编译test_aclnn_sigmoid.cpp与test_aclnn_inplace_sigmoid.cpp,链接-lcust_opapi -lnnopbase -lascendcl三个库,并通过-Wl,-rpath指定运行时库路径;脚本执行set -euo pipefail,任一环节失败即终止。示例默认检查输入[[0, 1], [2, 3]]的输出是否与 CPU 参考值一致。
测试与验证
op_api 单元测试
仓库在 tests/ut/op_api/test_aclnn_sigmoid.cpp 中提供了基于 gtest 的 op_api 单元测试,覆盖多种 dtype 与精度要求:
case_001_float:shape{2, 3, 4},FLOAT32,输入取值范围 [-8, 8],精度容差 0.0001。case_002_float16:shape{2, 3, 4},FLOAT16,输入取值范围 [-8, 8],精度容差 0.001。case_003_bfloat16:shape{2, 3, 4},BF16,输入取值范围 [-8, 8],精度容差 0.01。- 后续用例还覆盖了
aclnnInplaceSigmoid原地接口的 float16 等场景。
用例通过OP_API_UT(aclnnSigmoid, INPUT(selfDesc), OUTPUT(outDesc))构造两段式调用,先断言TestGetWorkspaceSize返回ACL_SUCCESS,再执行TestPrecision完成精度校验。
ATK 标准化测试
仓库还提供了适用于 ATK 的小规模标准化测试集:用例配置见 tests/st/aclnnSigmoid/all_aclnnSigmoid.json,CPU benchmark 执行器见 tests/st/aclnnSigmoid/executor_aclnnSigmoid.py。运行方式(需先加载 CANN 环境与 ATK 虚拟环境):
export ATK_BIND_CPU_TYPE=2 source /usr/local/Ascend/cann/set_env.sh atk node --backend npu --devices 2 \ node --backend cpu task --task accuracy \ -c experimental/activation/sigmoid_v2/tests/st/aclnnSigmoid/all_aclnnSigmoid.json \ -p experimental/activation/sigmoid_v2/tests/st/aclnnSigmoid/executor_aclnnSigmoid.py该命令以 NPU 为被测后端、CPU 为参考后端执行精度比对任务,可对多组用例批量校验算子正确性。
总结
aclnnSigmoid/aclnnInplaceSigmoid是 CANN ops-nn 在experimental/activation/sigmoid_v2下导出的实验性两段式 ACLNN 接口,二者共享同一套 kernel 与 tiling 实现,区别仅在于原地接口通过selfRef复用同一块内存完成"输入即输出"。使用时需要重点关注三类约束:dtype 仅限 FLOAT/FLOAT16/BFLOAT16(低精度走升精度计算路径)、非原地接口要求 self 与 out 的 dtype/shape 完全一致、维度不超过 8 且支持空 Tensor 与非连续 Tensor。通过仓库自带的 examples(含 run.sh 一键编译运行)与 UT/ATK 两级测试,开发者可以快速完成接口验证,并将其迁移到自己的上层框架或推理引擎中。
【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考