ARTICLE DETAIL

资讯详情

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

CANN ops-nn aclnnRelu 与 aclnnInplaceRelu 单算子 API 完整使用指南

CANN ops-nn aclnnRelu 与 aclnnInplaceRelu 单算子 API 完整使用指南 CANN ops-nn aclnnRelu 与 aclnnInplaceRelu 单算子 API 完整使用指南【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn本文基于 CANN 神经网络算子库 ops-nn 中 Relu 算子的官方接口文档系统讲解 aclnnRelu 与 aclnnInplaceRelu 两套单算子 API 的产品支持情况、两段式调用机制、参数与返回码语义、底层计算流程与调用示例帮助开发者在 Atlas/Ascend 系列 NPU 上快速完成 Relu 激活计算的推理与训练集成。一、算子与接口总览ReluRectified Linear Unit是神经网络中最常用的激活函数之一其计算规则为输入张量中数值大于等于 0 的元素保持原值小于 0 的元素置为 0输出张量与输入张量具有完全相同的 shape。本仓库中该算子的接口说明文档位于 activation/relu/docs/aclnnReluaclnnInplaceRelu.md配套的算子源码位于 activation/relu 目录。在 ops-nn 仓库中Relu 算子对外提供两套功能等价、语义略有差异的 aclnn 接口aclnnRelu非原地out-of-place版本。需要调用方显式新建一个输出张量对象out来存放计算结果输入张量 self 在计算后保持不变。aclnnInplaceRelu原地in-place版本。无需新建输出张量直接在输入张量 selfRef 的内存中覆写计算结果可省去一次输出张量的内存申请。从源码实现看两者共享同一个内部执行函数。在 activation/relu/op_api/aclnn_relu.cpp 中aclnnInplaceReluGetWorkspaceSize直接以ExecReluGetWorkspaceSize(selfRef, selfRef, workspaceSize, executor)的方式复用非原地版本的实现即输入即输出。计算公式$$ relu(self) \begin{cases} self, self\gt 0 \ 0, self\le 0 \end{cases} $$产品支持情况依据文档中的产品支持矩阵aclnnRelu 与 aclnnInplaceRelu 在以下产品上可用产品支持情况Ascend 950PR/Ascend 950DT支持Atlas A3 训练系列产品/Atlas A3 推理系列产品支持Atlas A2 训练系列产品/Atlas A2 推理系列产品支持Atlas 200I/500 A2 推理产品不支持Atlas 推理系列产品不支持Atlas 训练系列产品支持注意文档中Atlas 推理系列产品与Atlas 200I/500 A2 推理产品不支持但 activation/relu/README.md 中Atlas 推理系列产品一栏标注为支持两处口径存在差异实际部署时请以所安装 CANN 版本与芯片型号对应的最终能力为准。二、两段式接口调用机制与 CANN 其他单算子 API 一致aclnnRelu 与 aclnnInplaceRelu 都采用两段式接口详见 docs/zh/context/two_phase_api.md的调用方式第一段接口GetWorkspaceSize先调用aclnnReluGetWorkspaceSize或aclnnInplaceReluGetWorkspaceSize完成入参校验并依据算子计算流程计算出本次调用所需的 workspace 大小同时返回封装了算子计算流程的 op 执行器aclOpExecutor。按 workspaceSize 申请 NPU 内存根据第一段接口返回的 workspaceSize通过aclrtMalloc在 Device 侧申请对应大小的临时内存。第二段接口执行调用aclnnRelu或aclnnInplaceRelu传入 workspace 地址、workspaceSize、executor 与 stream真正在 NPU 上执行计算。其中 workspace 是指除输入/输出张量外算子在 NPU 上完成计算所需的临时内存workspaceSize 表示该临时内存的大小。需要特别注意的是第二段接口不能重复调用即不允许出现GetWorkspaceSize → 执行 → 再次执行的用法否则会产生异常每次执行前都必须重新获取 workspace 与 executor。函数原型aclnnRelu 两段接口原型aclnnStatus aclnnReluGetWorkspaceSize( const aclTensor* self, const aclTensor* out, uint64_t* workspaceSize, aclOpExecutor **executor)aclnnStatus aclnnRelu( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, const aclrtStream stream)aclnnInplaceRelu 两段接口原型aclnnStatus aclnnInplaceReluGetWorkspaceSize( aclTensor* selfRef, uint64_t *workspaceSize, aclOpExecutor **executor)aclnnStatus aclnnInplaceRelu( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, const aclrtStream stream)aclnnInplaceRelu 的第一段接口没有 out 参数只有唯一的输入输出共用参数 selfRef两个算子的第二段接口参数完全一致workspace、workspaceSize、executor、stream。三、aclnnReluGetWorkspaceSize 参数与校验规则参数说明参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续TensorselfaclTensor*输入待进行 Relu 计算的入参即公式中的输入 self支持空 Tensorshape 需与 out 一致FLOAT、FLOAT16、INT8、INT32、INT64、BFLOAT16、UINT8ND0-8√outaclTensor*输出计算的出参shape 需与 self 一致FLOAT、FLOAT16、INT8、INT32、INT64、BFLOAT16、UINT8ND0-8√workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小-----executoraclOpExecutor**输出返回 op 执行器包含算子计算流程-----对于 Atlas 训练系列产品数据类型支持范围为 FLOAT、FLOAT16、INT8、INT32、INT64、UINT8即不含 BFLOAT16。对照源码 activation/relu/op_api/aclnn_relu.cpp 可知接口内部维护了两份数据类型支持列表DTYPE_SUPPORT_LISTFLOAT、FLOAT16、INT8、UINT8、INT32、INT64与DTYPE_SUPPORT_LIST_INCLUDE_BF16额外包含 BF16。是否允许 BF16 由CheckSocVersionIsSupportBf16()依据当前 NPU 架构DAV_2201、DAV_3510 等动态判定这与文档中Atlas 训练系列产品不支持 BFLOAT16的说明互为印证。返回值与第一段接口的入参校验第一段接口返回aclnnStatus状态码完整状态码表见 docs/zh/context/aclnn_return_code.md并在内部完成入参校验。aclnnReluGetWorkspaceSize 在以下场景会报错返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 self 或 out 是空指针。ACLNN_ERR_PARAM_INVALID161002self 和 out 的数据类型不在支持的范围之内。ACLNN_ERR_PARAM_INVALID161002self 和 out 的数据类型不相同。ACLNN_ERR_PARAM_INVALID161002self 和 out 的 shape 超过 8 维或者两者 shape 不一致。这些校验逻辑在源码中均有对应实现CheckNotNull检查空指针对应 aclnn_relu.cppCheckDtypeValid检查数据类型及 self/out 类型一致性aclnn_relu.cppCheckShape检查维度上限与 shape 一致性aclnn_relu.cpp。单元测试 activation/relu/tests/ut/op_api/test_aclnn_relu.cpp 中对这些非法路径均有断言覆盖例如 BOOL 类型入参、self/out 类型不一致、9 维 shape、空指针等场景均期望返回ACLNN_ERR_PARAM_INVALID或ACLNN_ERR_PARAM_NULLPTR。四、aclnnRelu 第二段接口参数说明参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址。workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclnnReluGetWorkspaceSize 获取。executor输入op 执行器包含了算子计算流程。stream输入指定执行任务的 Stream。返回值同样是aclnnStatusACLNN_SUCCESS表示执行成功其余错误码可通过aclGetRecentErrMsg接口获取详细的异常信息以辅助定位问题。五、aclnnInplaceReluGetWorkspaceSize 参数与校验规则参数说明参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续TensorselfRefaclTensor*输入待进行 Relu 计算的入参支持空 TensorFLOAT、FLOAT16、INT8、INT32、INT64、BFLOAT16、UINT8ND0-8√workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小-----executoraclOpExecutor**输出返回 op 执行器包含算子计算流程-----与 aclnnRelu 相同Atlas 训练系列产品上数据类型支持 FLOAT、FLOAT16、INT8、INT32、INT64、UINT8。返回值与入参校验返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 self 是空指针。ACLNN_ERR_PARAM_INVALID161002self 的数据类型不在支持的范围之内。ACLNN_ERR_PARAM_INVALID161002self 的 shape 超过 8 维。由于原地版本不存在独立的 out 张量因此校验规则也比非原地版本更少不涉及self 与 out 类型不一致shape 不一致这两类检查。六、aclnnInplaceRelu 第二段接口参数说明参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址。workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclnnInplaceReluGetWorkspaceSize 获取。executor输入op 执行器包含了算子计算流程。stream输入指定执行任务的 Stream。七、约束说明与底层执行路径文档中的约束说明指出aclnnRelu 与 aclnnInplaceRelu 均为确定性计算默认采用确定性实现关于确定性计算的更多背景可参考 docs/zh/context/determinism_compute.md。从 API 头文件 activation/relu/op_api/aclnn_relu.h 的注释可知aclnnRelu 的计算流程遵循一条清晰的 L0 算子流水线具体在 activation/relu/op_api/aclnn_relu.cpp 中体现为以下细节空 Tensor 与特殊分支当 self 为空 Tensor或 self 与 out 为同一指针且数据类型为 UINT8 时直接返回 workspaceSize0跳过实际计算。由于 UINT8 全为非负数Relu 运算退化为恒等映射因此无需调用 Relu kernelself-GetDataType() ! op::DataType::DT_UINT8时才下发l0op::Relu计算。Contiguous 转换通过l0op::Contiguous将输入 self 转换为连续 Tensor这正是文档参数表中支持非连续 Tensor的底层实现关于非连续 Tensor 可参见 docs/zh/context/non_contiguous_tensor.md。Relu 计算调用 L0 层算子l0op::Relu完成逐元素计算L0 实现位于 activation/relu/op_api/relu.cpp其通过ADD_TO_LAUNCHER_LIST_AICORE(Relu, ...)将任务加入 AICore 启动列表。ViewCopy 回写通过l0op::ViewCopy将计算结果写回可能为非连续的 out 张量。workspace 计算最终通过uniqueExecutor-GetWorkspaceSize()汇总本次调用所需 workspace 大小并调用uniqueExecutor.ReleaseTo(executor)将执行器移交出去。此外算子 Host 侧的定义见 activation/relu/op_host/relu_def.cpp它声明了 x/y 两个 ND 格式的输入输出并分别注册了 ascend950 与 ascend350 两个架构的 AICore 配置支持动态 shape 与动态 rank这解释了为何接口能够支持 0-8 维、任意 shape 的 Tensor。八、完整调用示例8.1 环境准备编译执行算子 API 前需确认基础环境已搭建完成包括驱动、固件、CANN 软件包、ops 包等完整流程可参考 docs/zh/context/compile_and_run_sample.md。编译时需链接 CANN 运行时与算子 API 库CMakeLists.txt 中的核心配置大致如下实际路径请按环境调整cmake_minimum_required(VERSION 3.14) project(ACLNN_EXAMPLE) add_compile_options(-stdc11) if(NOT $ENV{ASCEND_CUSTOM_PATH} STREQUAL ) set(ASCEND_PATH $ENV{ASCEND_CUSTOM_PATH}) else() set(ASCEND_PATH /usr/local/Ascend/cann) endif() set(INCLUDE_BASE_DIR ${ASCEND_PATH}/include) include_directories(${INCLUDE_BASE_DIR} ${INCLUDE_BASE_DIR}/aclnn) add_executable(opapi_test test_relu.cpp) target_link_libraries(opapi_test PRIVATE ${ASCEND_PATH}/lib64/libascendcl.so ${ASCEND_PATH}/lib64/libnnopbase.so ${ASCEND_PATH}/lib64/libopapi_math.so ${ASCEND_PATH}/lib64/libopapi_nn.so)环境变量配置与编译运行命令# 配置 CANN 环境变量 source ${INSTALL_DIR}/set_env.sh # 进入 CMakeLists.txt 所在目录编译并运行 mkdir -p build cd build cmake .. make ./opapi_test8.2 aclnnRelu 接口调用示例以下示例来自 activation/relu/examples/test_aclnn_relu.cpp对 shape 为 {2, 2}、取值为 {0, 1, 2, 3} 的 FLOAT 张量执行 Relu输入中无负数输出恒等于输入如需观察置零效果可自行将部分元素改为负值#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_relu.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名称 // aclnnRelu接口调用示例 uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnRelu第一段接口 ret aclnnReluGetWorkspaceSize(self, out, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnReluGetWorkspaceSize 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); } // 调用aclnnRelu第二段接口 ret aclnnRelu(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnRelu 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(aclnnRelu result[%ld] 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; }8.3 aclnnInplaceRelu 接口调用示例原地版本与 8.2 的区别集中在第 2、3、5 步不需要创建 out 张量out 相关代码可保留也可移除关键是第一段接口只传 self计算直接写回 self 所在内存因此第 5 步回拷结果时读取的是 selfDeviceAddr。核心调用片段如下完整源码见 activation/relu/examples/test_aclnn_inplace_relu.cpp#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_relu.h // ... CHECK_RET / LOG_PRINT / GetShapeSize / Init / CreateAclTensor 与 8.2 相同此处省略 ... int main() { // 1. 固定写法device/stream初始化参考acl API手册 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. 构造输入inplace版本无需构造独立输出 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}; ret CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_FLOAT, self); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(outHostData, outShape, outDeviceAddr, aclDataType::ACL_FLOAT, out); CHECK_RET(ret ACL_SUCCESS, return ret); // 3. 调用CANN算子库API // aclnnInplaceRelu接口调用示例 uint64_t inplaceWorkspaceSize 0; aclOpExecutor* inplaceExecutor; // 调用aclnnInplaceRelu第一段接口只传selfRef ret aclnnInplaceReluGetWorkspaceSize(self, inplaceWorkspaceSize, inplaceExecutor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnInplaceReluGetWorkspaceSize 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); } // 调用aclnnInplaceRelu第二段接口 ret aclnnInplaceRelu(inplaceWorkspaceAddr, inplaceWorkspaceSize, inplaceExecutor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnInplaceRelu 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. 获取输出的值inplace结果直接读取selfDeviceAddr auto inplaceSize GetShapeSize(selfShape); std::vectorfloat inplaceResultData(inplaceSize, 0); ret aclrtMemcpy(inplaceResultData.data(), inplaceResultData.size() * sizeof(inplaceResultData[0]), selfDeviceAddr, inplaceSize * sizeof(inplaceResultData[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 inplaceSize; i) { LOG_PRINT(aclnnInplaceRelu result[%ld] is: %f\n, i, inplaceResultData[i]); } // 6. 释放aclTensor和aclScalar需要根据具体API的接口定义修改 aclDestroyTensor(self); aclDestroyTensor(out); // 7. 释放device资源需要根据具体API的接口定义修改 aclrtFree(selfDeviceAddr); aclrtFree(outDeviceAddr); if (inplaceWorkspaceSize 0) { aclrtFree(inplaceWorkspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }8.4 调用要点小结选型若原输入张量后续不再被引用优先选择 aclnnInplaceRelu 以节省一份输出张量的 Device 内存否则使用 aclnnRelu 保留原输入。两段式顺序不可颠倒必须先 GetWorkspaceSize 再执行且第二段接口只能调用一次。内存管理workspaceSize 可能为 0例如空 Tensor 或 UINT8 恒等分支申请 workspace 前务必判空。结果回读非原地版本从 outDeviceAddr 读取原地版本从 selfDeviceAddr即被覆写的原输入读取。九、测试与验证ops-nn 仓库为 Relu 算子提供了完整的测试体系可用于验证接口行为OP API 单元测试activation/relu/tests/ut/op_api/test_aclnn_relu.cpp 覆盖了数据类型非法ACL_BOOL、self/out 类型不一致、空 Tensorshape 含 0 维度、正常 FLOAT/UINT8 路径、空指针ACLNN_ERR_PARAM_NULLPTR、9 维 shape 超限ACLNN_ERR_PARAM_INVALID等场景直接对应本文第三、五节的校验规则表。算子定义与 shape 推导测试activation/relu/tests/ut/op_host/test_relu_infershape.cpp 与 activation/relu/tests/ut/op_host/arch35/test_relu_tiling.cpp 分别验证 Host 侧 shape 推导与 tiling 逻辑。端到端调用样例activation/relu/examples/test_aclnn_relu.cpp 与 activation/relu/examples/test_aclnn_inplace_relu.cpp 是可直接编译运行的完整样例。算子的图模式 IRactivation/relu/op_graph/relu_proto.h 定义了 Relu 的算子 IR 描述支持通过构图方式调用 Relu 算子图模式。十、参考文档aclnnReluaclnnInplaceRelu 接口说明本文档原始出处Relu 算子 README两段式接口说明aclnn 返回码说明编译与运行样例非连续 Tensor 说明确定性计算说明【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表