已合并
新增 ReLU 实验算子 #4478
cwzhang创建于 4月30日
新增 ReLU 实验算子 #4478
已合并
共 24 个文件变更+2394-0
| @@ -0,0 +1,19 @@ | |||
| 1 | +# ---------------------------------------------------------------------------- | ||
| 2 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | +# ---------------------------------------------------------------------------- | ||
| 10 | + | ||
| 11 | +file(GLOB CURRENT_DIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*) | ||
| 12 | +if(NOT ENABLE_TEST) | ||
| 13 | + list(REMOVE_ITEM CURRENT_DIRS tests) | ||
| 14 | +endif() | ||
| 15 | +foreach(SUB_DIR ${CURRENT_DIRS}) | ||
| 16 | + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUB_DIR}/CMakeLists.txt") | ||
| 17 | + add_subdirectory(${SUB_DIR}) | ||
| 18 | + endif() | ||
| 19 | +endforeach() | ||
| @@ -0,0 +1,176 @@ | |||
| 1 | +# Relu | ||
| 2 | + | ||
| 3 | +## 产品支持情况 | ||
| 4 | + | ||
| 5 | +| 产品 | 是否支持 | | ||
| 6 | +| :--- | :---: | | ||
| 7 | +| <term>Ascend 950PR/Ascend 950DT</term> | √ | | ||
| 8 | +| <term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term> | √ | | ||
| 9 | +| <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term> | √ | | ||
| 10 | +| <term>Atlas 200I/500 A2 推理产品</term> | × | | ||
| 11 | +| <term>Atlas 推理系列产品</term> | √ | | ||
| 12 | +| <term>Atlas 训练系列产品</term> | √ | | ||
| 13 | + | ||
| 14 | +## 功能说明 | ||
| 15 | + | ||
| 16 | +- 算子功能:对输入 Tensor 完成 ReLU 运算。 | ||
| 17 | + | ||
| 18 | +计算公式: | ||
| 19 | + | ||
| 20 | +$$ | ||
| 21 | +\operatorname{relu}(x) = \max(x, 0) | ||
| 22 | +$$ | ||
| 23 | + | ||
| 24 | +- 目录 `experimental/activation/relu` 对外导出 `aclnnRelu` 和 `aclnnInplaceRelu` 两段式 ACLNN 接口。 | ||
| 25 | +- `op_host/op_api/aclnn_relu.cpp` 是对外 ACLNN 接口入口。 | ||
| 26 | +- `op_host/op_api/relu.h` 和 `op_host/op_api/relu.cpp` 提供内部 `l0op::Relu` 封装,当前由 ACLNN 接口直接调用。 | ||
| 27 | + | ||
| 28 | +## 调用方式 | ||
| 29 | + | ||
| 30 | +| 调用方式 | 是否支持 | | ||
| 31 | +| :------- | :------: | | ||
| 32 | +| ACLNN 调用 | 是 | | ||
| 33 | +| ACLNN 原地调用 | 是 | | ||
| 34 | + | ||
| 35 | +## ACLNN 接口 | ||
| 36 | + | ||
| 37 | +### 函数原型 | ||
| 38 | + | ||
| 39 | +当前 experimental Relu 提供两段式 ACLNN 接口: | ||
| 40 | + | ||
| 41 | +```cpp | ||
| 42 | +aclnnStatus aclnnReluGetWorkspaceSize( | ||
| 43 | + const aclTensor *self, | ||
| 44 | + const aclTensor *out, | ||
| 45 | + uint64_t *workspaceSize, | ||
| 46 | + aclOpExecutor **executor); | ||
| 47 | + | ||
| 48 | +aclnnStatus aclnnRelu( | ||
| 49 | + void *workspace, | ||
| 50 | + uint64_t workspaceSize, | ||
| 51 | + aclOpExecutor *executor, | ||
| 52 | + const aclrtStream stream); | ||
| 53 | +``` | ||
| 54 | + | ||
| 55 | +原地版本接口如下: | ||
| 56 | + | ||
| 57 | +```cpp | ||
| 58 | +aclnnStatus aclnnInplaceReluGetWorkspaceSize( | ||
| 59 | + aclTensor *selfRef, | ||
| 60 | + uint64_t *workspaceSize, | ||
| 61 | + aclOpExecutor **executor); | ||
| 62 | + | ||
| 63 | +aclnnStatus aclnnInplaceRelu( | ||
| 64 | + void *workspace, | ||
| 65 | + uint64_t workspaceSize, | ||
| 66 | + aclOpExecutor *executor, | ||
| 67 | + const aclrtStream stream); | ||
| 68 | +``` | ||
| 69 | + | ||
| 70 | +详细参数说明见 [docs/aclnnRelu&aclnnInplaceRelu.md](docs/aclnnRelu&aclnnInplaceRelu.md)。 | ||
| 71 | + | ||
| 72 | +## 参数说明 | ||
| 73 | + | ||
| 74 | +<table style="undefined;table-layout: fixed; width: 1393px"><colgroup> | ||
| 75 | +<col style="width: 171px"> | ||
| 76 | +<col style="width: 115px"> | ||
| 77 | +<col style="width: 260px"> | ||
| 78 | +<col style="width: 220px"> | ||
| 79 | +<col style="width: 200px"> | ||
| 80 | +<col style="width: 104px"> | ||
| 81 | +</colgroup> | ||
| 82 | +<thead> | ||
| 83 | + <tr> | ||
| 84 | + <th>参数名</th> | ||
| 85 | + <th>输入/输出</th> | ||
| 86 | + <th>描述</th> | ||
| 87 | + <th>数据类型</th> | ||
| 88 | + <th>数据格式</th> | ||
| 89 | + </tr> | ||
| 90 | +</thead> | ||
| 91 | +<tbody> | ||
| 92 | + <tr> | ||
| 93 | + <td>self</td> | ||
| 94 | + <td>输入</td> | ||
| 95 | + <td>待进行 ReLU 计算的入参。</td> | ||
| 96 | + <td>FLOAT、FLOAT16、BFLOAT16、INT8、INT32、INT64</td> | ||
| 97 | + <td>ND</td> | ||
| 98 | + </tr> | ||
| 99 | + <tr> | ||
| 100 | + <td>out</td> | ||
| 101 | + <td>输出</td> | ||
| 102 | + <td>非原地接口的计算出参。</td> | ||
| 103 | + <td>FLOAT、FLOAT16、BFLOAT16、INT8、INT32、INT64</td> | ||
| 104 | + <td>ND</td> | ||
| 105 | + </tr> | ||
| 106 | + <tr> | ||
| 107 | + <td>selfRef</td> | ||
| 108 | + <td>输入/输出</td> | ||
| 109 | + <td>原地接口的输入输出张量。</td> | ||
| 110 | + <td>FLOAT、FLOAT16、BFLOAT16、INT8、INT32、INT64</td> | ||
| 111 | + <td>ND</td> | ||
| 112 | + </tr> | ||
| 113 | +</tbody> | ||
| 114 | +</table> | ||
| 115 | + | ||
| 116 | +## 约束说明 | ||
| 117 | + | ||
| 118 | +- 输入仅支持 `FLOAT`、`FLOAT16`、`BFLOAT16`、`INT8`、`INT32`、`INT64`。 | ||
| 119 | +- `BFLOAT16` 仅在 `Ascend910B` 及后续同代 SoC 上支持。 | ||
| 120 | +- 输入和输出的 dtype 必须一致。 | ||
| 121 | +- 输入和输出的 shape 必须完全一致。 | ||
| 122 | +- 支持 0 到 8 维 Tensor。 | ||
| 123 | +- 支持空 Tensor。 | ||
| 124 | +- 支持非连续 Tensor,接口内部会在需要时做 `Contiguous` 和 `ViewCopy`。 | ||
| 125 | +- `FLOAT`、`FLOAT16`、`INT32` 路径直接使用 AscendC `Relu` 计算。 | ||
| 126 | +- `BFLOAT16` 路径在 kernel 中升精度到 `float32` 计算后回写。 | ||
| 127 | +- `INT8` 路径在 kernel 中升精度到 `float16` 计算后回写。 | ||
| 128 | +- `INT64` 路径按已验证基线走逐元素标量语义。 | ||
| 129 | + | ||
| 130 | +## 目录说明 | ||
| 131 | + | ||
| 132 | +| 路径 | 说明 | | ||
| 133 | +| :--- | :--- | | ||
| 134 | +| [examples/test_aclnn_relu.cpp](examples/test_aclnn_relu.cpp) | `aclnnRelu` 两段式调用示例。 | | ||
| 135 | +| [examples/test_aclnn_inplace_relu.cpp](examples/test_aclnn_inplace_relu.cpp) | `aclnnInplaceRelu` 两段式调用示例。 | | ||
| 136 | +| [examples/run.sh](examples/run.sh) | 编译并运行 example 的脚本。 | | ||
| 137 | +| [docs/aclnnRelu&aclnnInplaceRelu.md](docs/aclnnRelu&aclnnInplaceRelu.md) | `aclnnRelu` / `aclnnInplaceRelu` 接口文档。 | | ||
| 138 | +| [tests/ut/op_api/test_aclnn_relu.cpp](tests/ut/op_api/test_aclnn_relu.cpp) | `op_api` 单元测试。 | | ||
| 139 | +| [tests/st/aclnnRelu/all_aclnnRelu.json](tests/st/aclnnRelu/all_aclnnRelu.json) | 适用于 ATK 的小规模标准化测试集。 | | ||
| 140 | +| [tests/st/aclnnRelu/executor_aclnnRelu.py](tests/st/aclnnRelu/executor_aclnnRelu.py) | ATK CPU benchmark 执行器。 | | ||
| 141 | + | ||
| 142 | +## Example 运行 | ||
| 143 | + | ||
| 144 | +先确保 custom run 包已经安装,并加载 CANN 环境: | ||
| 145 | + | ||
| 146 | +```bash | ||
| 147 | +source /usr/local/Ascend/cann-8.5.0-beta.1/set_env.sh | ||
| 148 | +export LD_LIBRARY_PATH=/usr/local/Ascend/cann-8.5.0-beta.1/opp/vendors/customize_nn/op_api/lib:${LD_LIBRARY_PATH} | ||
| 149 | +cd /root/src/cann/ops-nn/experimental/activation/relu/examples | ||
| 150 | +bash run.sh | ||
| 151 | +``` | ||
| 152 | + | ||
| 153 | +示例会编译并运行 `test_aclnn_relu.cpp` 与 `test_aclnn_inplace_relu.cpp`。 | ||
| 154 | + | ||
| 155 | +## Tests 运行 | ||
| 156 | + | ||
| 157 | +### 1. op_api 单元测试 | ||
| 158 | + | ||
| 159 | +```bash | ||
| 160 | +source /usr/local/Ascend/cann/set_env.sh | ||
| 161 | +cd /root/src/cann/ops-nn | ||
| 162 | +bash build.sh --experimental --ops=relu -u --opapi -j8 -O2 | ||
| 163 | +``` | ||
| 164 | + | ||
| 165 | +### 2. ATK 小规模标准化测试 | ||
| 166 | + | ||
| 167 | +```bash | ||
| 168 | +export ATK_BIND_CPU_TYPE=2 | ||
| 169 | +source /usr/local/Ascend/cann/set_env.sh | ||
| 170 | +source /root/src/kernel/ascend-kernel/.venv/bin/activate | ||
| 171 | +cd /root/src/testcase | ||
| 172 | +atk node --backend npu --devices 2 \ | ||
| 173 | + node --backend cpu task --task accuracy \ | ||
| 174 | + -c /root/src/cann/ops-nn/experimental/activation/relu/tests/st/aclnnRelu/all_aclnnRelu.json \ | ||
| 175 | + -p /root/src/cann/ops-nn/experimental/activation/relu/tests/st/aclnnRelu/executor_aclnnRelu.py | ||
| 176 | +``` | ||
| @@ -0,0 +1,370 @@ | |||
| 1 | +# aclnnRelu 与 aclnnInplaceRelu | ||
| 2 | + | ||
| 3 | +## 产品支持情况 | ||
| 4 | + | ||
| 5 | +|产品|是否支持| | ||
| 6 | +|:---|:---:| | ||
| 7 | +|<term>Ascend 950PR/Ascend 950DT</term>|√| | ||
| 8 | +|<term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term>|√| | ||
| 9 | +|<term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term>|√| | ||
| 10 | +|<term>Atlas 200I/500 A2 推理产品</term>|×| | ||
| 11 | +|<term>Atlas 推理系列产品</term>|√| | ||
| 12 | +|<term>Atlas 训练系列产品</term>|√| | ||
| 13 | + | ||
| 14 | +## 功能说明 | ||
| 15 | + | ||
| 16 | +- `aclnnRelu`:对输入 Tensor 执行 ReLU 计算,并将结果写入独立输出 Tensor。 | ||
| 17 | +- `aclnnInplaceRelu`:对输入 Tensor 原地执行 ReLU 计算,结果直接覆盖输入内存。 | ||
| 18 | +- `experimental/activation/relu` 目录对外导出的 ACLNN 接口名与原实现保持一致。 | ||
| 19 | + | ||
| 20 | +计算公式: | ||
| 21 | + | ||
| 22 | +$$ | ||
| 23 | +\operatorname{relu}(x) = \max(x, 0) | ||
| 24 | +$$ | ||
| 25 | + | ||
| 26 | +## 函数原型 | ||
| 27 | + | ||
| 28 | +每个算子分为[两段式接口](../../../docs/zh/context/两段式接口.md),必须先调用 `aclnnReluGetWorkspaceSize` 或 `aclnnInplaceReluGetWorkspaceSize` 获取执行器和 workspace 大小,再调用第二段接口执行计算。 | ||
| 29 | + | ||
| 30 | +```cpp | ||
| 31 | +aclnnStatus aclnnReluGetWorkspaceSize( | ||
| 32 | + const aclTensor *self, | ||
| 33 | + const aclTensor *out, | ||
| 34 | + uint64_t *workspaceSize, | ||
| 35 | + aclOpExecutor **executor); | ||
| 36 | +``` | ||
| 37 | + | ||
| 38 | +```cpp | ||
| 39 | +aclnnStatus aclnnRelu( | ||
| 40 | + void *workspace, | ||
| 41 | + uint64_t workspaceSize, | ||
| 42 | + aclOpExecutor *executor, | ||
| 43 | + const aclrtStream stream); | ||
| 44 | +``` | ||
| 45 | + | ||
| 46 | +```cpp | ||
| 47 | +aclnnStatus aclnnInplaceReluGetWorkspaceSize( | ||
| 48 | + aclTensor *selfRef, | ||
| 49 | + uint64_t *workspaceSize, | ||
| 50 | + aclOpExecutor **executor); | ||
| 51 | +``` | ||
| 52 | + | ||
| 53 | +```cpp | ||
| 54 | +aclnnStatus aclnnInplaceRelu( | ||
| 55 | + void *workspace, | ||
| 56 | + uint64_t workspaceSize, | ||
| 57 | + aclOpExecutor *executor, | ||
| 58 | + const aclrtStream stream); | ||
| 59 | +``` | ||
| 60 | + | ||
| 61 | +## aclnnReluGetWorkspaceSize | ||
| 62 | + | ||
| 63 | +- **参数说明:** | ||
| 64 | + | ||
| 65 | + <table style="undefined;table-layout: fixed; width: 1497px"><colgroup> | ||
| 66 | + <col style="width: 271px"> | ||
| 67 | + <col style="width: 115px"> | ||
| 68 | + <col style="width: 247px"> | ||
| 69 | + <col style="width: 300px"> | ||
| 70 | + <col style="width: 177px"> | ||
| 71 | + <col style="width: 104px"> | ||
| 72 | + <col style="width: 138px"> | ||
| 73 | + <col style="width: 145px"> | ||
| 74 | + </colgroup> | ||
| 75 | + <thead> | ||
| 76 | + <tr> | ||
| 77 | + <th>参数名</th> | ||
| 78 | + <th>输入/输出</th> | ||
| 79 | + <th>描述</th> | ||
| 80 | + <th>使用说明</th> | ||
| 81 | + <th>数据类型</th> | ||
| 82 | + <th>数据格式</th> | ||
| 83 | + <th>维度(shape)</th> | ||
| 84 | + <th>非连续Tensor</th> | ||
| 85 | + </tr> | ||
| 86 | + </thead> | ||
| 87 | + <tbody> | ||
| 88 | + <tr> | ||
| 89 | + <td>self(aclTensor*)</td> | ||
| 90 | + <td>输入</td> | ||
| 91 | + <td>待进行 ReLU 计算的输入张量。</td> | ||
| 92 | + <td><ul><li>支持空Tensor。</li><li>shape 必须与 out 完全一致。</li><li>数据类型必须与 out 完全一致。</li></ul></td> | ||
| 93 | + <td>Ascend910B 及同代 SoC:BFLOAT16、FLOAT16、FLOAT32、INT8、INT32、INT64;其他支持产品:FLOAT16、FLOAT32、INT8、INT32、INT64</td> | ||
| 94 | + <td>ND</td> | ||
| 95 | + <td>0-8</td> | ||
| 96 | + <td>√</td> | ||
| 97 | + </tr> | ||
| 98 | + <tr> | ||
| 99 | + <td>out(aclTensor*)</td> | ||
| 100 | + <td>输出</td> | ||
| 101 | + <td>计算的出参。</td> | ||
| 102 | + <td><ul><li>支持空Tensor。</li><li>shape 必须与 self 完全一致。</li><li>数据类型必须与 self 完全一致。</li></ul></td> | ||
| 103 | + <td>Ascend910B 及同代 SoC:BFLOAT16、FLOAT16、FLOAT32、INT8、INT32、INT64;其他支持产品:FLOAT16、FLOAT32、INT8、INT32、INT64</td> | ||
| 104 | + <td>ND</td> | ||
| 105 | + <td>0-8</td> | ||
| 106 | + <td>√</td> | ||
| 107 | + </tr> | ||
| 108 | + <tr> | ||
| 109 | + <td>workspaceSize(uint64_t*)</td> | ||
| 110 | + <td>输出</td> | ||
| 111 | + <td>返回需要在 Device 侧申请的 workspace 大小。</td> | ||
| 112 | + <td>-</td> | ||
| 113 | + <td>-</td> | ||
| 114 | + <td>-</td> | ||
| 115 | + <td>-</td> | ||
| 116 | + <td>-</td> | ||
| 117 | + </tr> | ||
| 118 | + <tr> | ||
| 119 | + <td>executor(aclOpExecutor**)</td> | ||
| 120 | + <td>输出</td> | ||
| 121 | + <td>返回 op 执行器,包含算子计算流程。</td> | ||
| 122 | + <td>-</td> | ||
| 123 | + <td>-</td> | ||
| 124 | + <td>-</td> | ||
| 125 | + <td>-</td> | ||
| 126 | + <td>-</td> | ||
| 127 | + </tr> | ||
| 128 | + </tbody> | ||
| 129 | + </table> | ||
| 130 | + | ||
| 131 | +- **返回值:** | ||
| 132 | + | ||
| 133 | + aclnnStatus:返回状态码,具体参见[aclnn返回码](../../../docs/zh/context/aclnn返回码.md)。 | ||
| 134 | + | ||
| 135 | + 第一段接口会完成入参校验,出现以下场景时报错: | ||
| 136 | + | ||
| 137 | + <table style="undefined;table-layout: fixed;width: 979px"><colgroup> | ||
| 138 | + <col style="width: 272px"> | ||
| 139 | + <col style="width: 103px"> | ||
| 140 | + <col style="width: 604px"> | ||
| 141 | + </colgroup> | ||
| 142 | + <thead> | ||
| 143 | + <tr> | ||
| 144 | + <th>返回码</th> | ||
| 145 | + <th>错误码</th> | ||
| 146 | + <th>描述</th> | ||
| 147 | + </tr> | ||
| 148 | + </thead> | ||
| 149 | + <tbody> | ||
| 150 | + <tr> | ||
| 151 | + <td>ACLNN_ERR_PARAM_NULLPTR</td> | ||
| 152 | + <td>161001</td> | ||
| 153 | + <td>传入的 self 或 out 是空指针。</td> | ||
| 154 | + </tr> | ||
| 155 | + <tr> | ||
| 156 | + <td rowspan="4">ACLNN_ERR_PARAM_INVALID</td> | ||
| 157 | + <td rowspan="4">161002</td> | ||
| 158 | + <td>self 或 out 的数据类型不在支持范围内。</td> | ||
| 159 | + </tr> | ||
| 160 | + <tr> | ||
| 161 | + <td>self 和 out 的数据类型不一致。</td> | ||
| 162 | + </tr> | ||
| 163 | + <tr> | ||
| 164 | + <td>self 和 out 的 shape 不一致。</td> | ||
| 165 | + </tr> | ||
| 166 | + <tr> | ||
| 167 | + <td>self 或 out 的维度大于 8。</td> | ||
| 168 | + </tr> | ||
| 169 | + </tbody></table> | ||
| 170 | + | ||
| 171 | +## aclnnRelu | ||
| 172 | + | ||
| 173 | +- **参数说明:** | ||
| 174 | + | ||
| 175 | + <table style="undefined;table-layout: fixed; width: 953px"><colgroup> | ||
| 176 | + <col style="width: 173px"> | ||
| 177 | + <col style="width: 112px"> | ||
| 178 | + <col style="width: 668px"> | ||
| 179 | + </colgroup> | ||
| 180 | + <thead> | ||
| 181 | + <tr> | ||
| 182 | + <th>参数名</th> | ||
| 183 | + <th>输入/输出</th> | ||
| 184 | + <th>描述</th> | ||
| 185 | + </tr> | ||
| 186 | + </thead> | ||
| 187 | + <tbody> | ||
| 188 | + <tr> | ||
| 189 | + <td>workspace</td> | ||
| 190 | + <td>输入</td> | ||
| 191 | + <td>在 Device 侧申请的 workspace 内存地址。</td> | ||
| 192 | + </tr> | ||
| 193 | + <tr> | ||
| 194 | + <td>workspaceSize</td> | ||
| 195 | + <td>输入</td> | ||
| 196 | + <td>在 Device 侧申请的 workspace 大小,由第一段接口 aclnnReluGetWorkspaceSize 获取。</td> | ||
| 197 | + </tr> | ||
| 198 | + <tr> | ||
| 199 | + <td>executor</td> | ||
| 200 | + <td>输入</td> | ||
| 201 | + <td>op 执行器,包含算子计算流程。</td> | ||
| 202 | + </tr> | ||
| 203 | + <tr> | ||
| 204 | + <td>stream</td> | ||
| 205 | + <td>输入</td> | ||
| 206 | + <td>指定执行任务的 Stream。</td> | ||
| 207 | + </tr> | ||
| 208 | + </tbody> | ||
| 209 | + </table> | ||
| 210 | + | ||
| 211 | +- **返回值:** | ||
| 212 | + | ||
| 213 | + aclnnStatus:返回状态码,具体参见[aclnn返回码](../../../docs/zh/context/aclnn返回码.md)。 | ||
| 214 | + | ||
| 215 | +## aclnnInplaceReluGetWorkspaceSize | ||
| 216 | + | ||
| 217 | +- **参数说明:** | ||
| 218 | + | ||
| 219 | + <table style="undefined;table-layout: fixed; width: 1355px"><colgroup> | ||
| 220 | + <col style="width: 271px"> | ||
| 221 | + <col style="width: 115px"> | ||
| 222 | + <col style="width: 297px"> | ||
| 223 | + <col style="width: 108px"> | ||
| 224 | + <col style="width: 177px"> | ||
| 225 | + <col style="width: 104px"> | ||
| 226 | + <col style="width: 138px"> | ||
| 227 | + <col style="width: 145px"> | ||
| 228 | + </colgroup> | ||
| 229 | + <thead> | ||
| 230 | + <tr> | ||
| 231 | + <th>参数名</th> | ||
| 232 | + <th>输入/输出</th> | ||
| 233 | + <th>描述</th> | ||
| 234 | + <th>使用说明</th> | ||
| 235 | + <th>数据类型</th> | ||
| 236 | + <th>数据格式</th> | ||
| 237 | + <th>维度(shape)</th> | ||
| 238 | + <th>非连续Tensor</th> | ||
| 239 | + </tr> | ||
| 240 | + </thead> | ||
| 241 | + <tbody> | ||
| 242 | + <tr> | ||
| 243 | + <td>selfRef(aclTensor*)</td> | ||
| 244 | + <td>输入/输出</td> | ||
| 245 | + <td>原地计算的输入输出张量。</td> | ||
| 246 | + <td><ul><li>支持空Tensor。</li><li>原地计算后 shape 和 dtype 不变。</li></ul></td> | ||
| 247 | + <td>BFLOAT16、FLOAT16、FLOAT32、INT8、INT32、INT64</td> | ||
| 248 | + <td>ND</td> | ||
| 249 | + <td>0-8</td> | ||
| 250 | + <td>√</td> | ||
| 251 | + </tr> | ||
| 252 | + <tr> | ||
| 253 | + <td>workspaceSize(uint64_t*)</td> | ||
| 254 | + <td>输出</td> | ||
| 255 | + <td>返回需要在 Device 侧申请的 workspace 大小。</td> | ||
| 256 | + <td>-</td> | ||
| 257 | + <td>-</td> | ||
| 258 | + <td>-</td> | ||
| 259 | + <td>-</td> | ||
| 260 | + <td>-</td> | ||
| 261 | + </tr> | ||
| 262 | + <tr> | ||
| 263 | + <td>executor(aclOpExecutor**)</td> | ||
| 264 | + <td>输出</td> | ||
| 265 | + <td>返回 op 执行器,包含算子计算流程。</td> | ||
| 266 | + <td>-</td> | ||
| 267 | + <td>-</td> | ||
| 268 | + <td>-</td> | ||
| 269 | + <td>-</td> | ||
| 270 | + <td>-</td> | ||
| 271 | + </tr> | ||
| 272 | + </tbody> | ||
| 273 | + </table> | ||
| 274 | + | ||
| 275 | +- **返回值:** | ||
| 276 | + | ||
| 277 | + aclnnStatus:返回状态码,具体参见[aclnn返回码](../../../docs/zh/context/aclnn返回码.md)。 | ||
| 278 | + | ||
| 279 | + 第一段接口会完成入参校验,出现以下场景时报错: | ||
| 280 | + | ||
| 281 | + <table style="undefined;table-layout: fixed;width: 979px"><colgroup> | ||
| 282 | + <col style="width: 272px"> | ||
| 283 | + <col style="width: 103px"> | ||
| 284 | + <col style="width: 604px"> | ||
| 285 | + </colgroup> | ||
| 286 | + <thead> | ||
| 287 | + <tr> | ||
| 288 | + <th>返回码</th> | ||
| 289 | + <th>错误码</th> | ||
| 290 | + <th>描述</th> | ||
| 291 | + </tr> | ||
| 292 | + </thead> | ||
| 293 | + <tbody> | ||
| 294 | + <tr> | ||
| 295 | + <td>ACLNN_ERR_PARAM_NULLPTR</td> | ||
| 296 | + <td>161001</td> | ||
| 297 | + <td>传入的 selfRef 是空指针。</td> | ||
| 298 | + </tr> | ||
| 299 | + <tr> | ||
| 300 | + <td rowspan="2">ACLNN_ERR_PARAM_INVALID</td> | ||
| 301 | + <td rowspan="2">161002</td> | ||
| 302 | + <td>selfRef 的数据类型不在支持范围内。</td> | ||
| 303 | + </tr> | ||
| 304 | + <tr> | ||
| 305 | + <td>selfRef 的维度大于 8。</td> | ||
| 306 | + </tr> | ||
| 307 | + </tbody></table> | ||
| 308 | + | ||
| 309 | +## aclnnInplaceRelu | ||
| 310 | + | ||
| 311 | +- **参数说明:** | ||
| 312 | + | ||
| 313 | + <table style="undefined;table-layout: fixed; width: 953px"><colgroup> | ||
| 314 | + <col style="width: 173px"> | ||
| 315 | + <col style="width: 112px"> | ||
| 316 | + <col style="width: 668px"> | ||
| 317 | + </colgroup> | ||
| 318 | + <thead> | ||
| 319 | + <tr> | ||
| 320 | + <th>参数名</th> | ||
| 321 | + <th>输入/输出</th> | ||
| 322 | + <th>描述</th> | ||
| 323 | + </tr> | ||
| 324 | + </thead> | ||
| 325 | + <tbody> | ||
| 326 | + <tr> | ||
| 327 | + <td>workspace</td> | ||
| 328 | + <td>输入</td> | ||
| 329 | + <td>在 Device 侧申请的 workspace 内存地址。</td> | ||
| 330 | + </tr> | ||
| 331 | + <tr> | ||
| 332 | + <td>workspaceSize</td> | ||
| 333 | + <td>输入</td> | ||
| 334 | + <td>在 Device 侧申请的 workspace 大小,由第一段接口 aclnnInplaceReluGetWorkspaceSize 获取。</td> | ||
| 335 | + </tr> | ||
| 336 | + <tr> | ||
| 337 | + <td>executor</td> | ||
| 338 | + <td>输入</td> | ||
| 339 | + <td>op 执行器,包含算子计算流程。</td> | ||
| 340 | + </tr> | ||
| 341 | + <tr> | ||
| 342 | + <td>stream</td> | ||
| 343 | + <td>输入</td> | ||
| 344 | + <td>指定执行任务的 Stream。</td> | ||
| 345 | + </tr> | ||
| 346 | + </tbody> | ||
| 347 | + </table> | ||
| 348 | + | ||
| 349 | +- **返回值:** | ||
| 350 | + | ||
| 351 | + aclnnStatus:返回状态码,具体参见[aclnn返回码](../../../docs/zh/context/aclnn返回码.md)。 | ||
| 352 | + | ||
| 353 | +## 约束说明 | ||
| 354 | + | ||
| 355 | +- 输入 dtype 仅支持 `FLOAT`、`FLOAT16`、`BFLOAT16`、`INT8`、`INT32`、`INT64`。 | ||
| 356 | +- `BFLOAT16` 仅在 `Ascend910B` 及后续同代 SoC 上支持。 | ||
| 357 | +- `aclnnRelu` 中,`self` 和 `out` 的 dtype 必须一致。 | ||
| 358 | +- `aclnnRelu` 中,`self` 和 `out` 的 shape 必须一致。 | ||
| 359 | +- 维度范围为 0 到 8。 | ||
| 360 | +- 支持空 Tensor。 | ||
| 361 | +- 支持非连续 Tensor,内部会执行 `Contiguous` 和 `ViewCopy`。 | ||
| 362 | + | ||
| 363 | +## 实现说明 | ||
| 364 | + | ||
| 365 | +- `op_host/op_api/aclnn_relu.cpp` 提供对外 ACLNN 两段式接口。 | ||
| 366 | +- `op_host/op_api/relu.h` 和 `op_host/op_api/relu.cpp` 提供内部 `l0op::Relu` 封装,当前由 `aclnn_relu.cpp` 直接调用。 | ||
| 367 | + | ||
| 368 | +## 调用示例 | ||
| 369 | + | ||
| 370 | +完整示例见 [../examples/test_aclnn_relu.cpp](../examples/test_aclnn_relu.cpp) 和 [../examples/test_aclnn_inplace_relu.cpp](../examples/test_aclnn_inplace_relu.cpp)。 | ||
| @@ -0,0 +1,45 @@ | |||
| 1 | +#!/usr/bin/env bash | ||
| 2 | +# ---------------------------------------------------------------------------- | ||
| 3 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 4 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 5 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 6 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 7 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 8 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 9 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 10 | +# ---------------------------------------------------------------------------- | ||
| 11 | + | ||
| 12 | +set -euo pipefail | ||
| 13 | + | ||
| 14 | +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) | ||
| 15 | +CANN_ROOT=${ASCEND_HOME_PATH:-/usr/local/Ascend/cann-8.5.0-beta.1} | ||
| 16 | + | ||
| 17 | +source "${CANN_ROOT}/set_env.sh" | ||
| 18 | +export LD_LIBRARY_PATH="${CANN_ROOT}/opp/vendors/customize_nn/op_api/lib:${LD_LIBRARY_PATH:-}" | ||
| 19 | + | ||
| 20 | +BUILD_DIR="${SCRIPT_DIR}/build" | ||
| 21 | +mkdir -p "${BUILD_DIR}" | ||
| 22 | + | ||
| 23 | +g++ -std=c++17 -O2 \ | ||
| 24 | + "${SCRIPT_DIR}/test_aclnn_relu.cpp" \ | ||
| 25 | + -I"${CANN_ROOT}/aarch64-linux/include" \ | ||
| 26 | + -I"${CANN_ROOT}/opp/vendors/customize_nn/op_api/include" \ | ||
| 27 | + -L"${CANN_ROOT}/lib64" \ | ||
| 28 | + -L"${CANN_ROOT}/opp/vendors/customize_nn/op_api/lib" \ | ||
| 29 | + -Wl,-rpath,"${CANN_ROOT}/lib64:${CANN_ROOT}/opp/vendors/customize_nn/op_api/lib" \ | ||
| 30 | + -lcust_opapi -lnnopbase -lascendcl \ | ||
| 31 | + -o "${BUILD_DIR}/test_aclnn_relu" | ||
| 32 | + | ||
| 33 | +"${BUILD_DIR}/test_aclnn_relu" | ||
| 34 | + | ||
| 35 | +g++ -std=c++17 -O2 \ | ||
| 36 | + "${SCRIPT_DIR}/test_aclnn_inplace_relu.cpp" \ | ||
| 37 | + -I"${CANN_ROOT}/aarch64-linux/include" \ | ||
| 38 | + -I"${CANN_ROOT}/opp/vendors/customize_nn/op_api/include" \ | ||
| 39 | + -L"${CANN_ROOT}/lib64" \ | ||
| 40 | + -L"${CANN_ROOT}/opp/vendors/customize_nn/op_api/lib" \ | ||
| 41 | + -Wl,-rpath,"${CANN_ROOT}/lib64:${CANN_ROOT}/opp/vendors/customize_nn/op_api/lib" \ | ||
| 42 | + -lcust_opapi -lnnopbase -lascendcl \ | ||
| 43 | + -o "${BUILD_DIR}/test_aclnn_inplace_relu" | ||
| 44 | + | ||
| 45 | +"${BUILD_DIR}/test_aclnn_inplace_relu" | ||
| @@ -0,0 +1,138 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software; you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + do { \ | ||
| 23 | + if (!(cond)) { \ | ||
| 24 | + expr; \ | ||
| 25 | + } \ | ||
| 26 | + } while (0) | ||
| 27 | + | ||
| 28 | +namespace { | ||
| 29 | +int64_t GetShapeSize(const std::vector<int64_t> &shape) | ||
| 30 | +{ | ||
| 31 | + int64_t shape_size = 1; | ||
| 32 | + for (int64_t dim : shape) { | ||
| 33 | + shape_size *= dim; | ||
| 34 | + } | ||
| 35 | + return shape_size; | ||
| 36 | +} | ||
| 37 | + | ||
| 38 | +std::vector<int64_t> MakeStrides(const std::vector<int64_t> &shape) | ||
| 39 | +{ | ||
| 40 | + if (shape.empty()) { | ||
| 41 | + return {}; | ||
| 42 | + } | ||
| 43 | + std::vector<int64_t> strides(shape.size(), 1); | ||
| 44 | + for (int64_t i = static_cast<int64_t>(shape.size()) - 2; i >= 0; --i) { | ||
| 45 | + strides[static_cast<size_t>(i)] = shape[static_cast<size_t>(i + 1)] * strides[static_cast<size_t>(i + 1)]; | ||
| 46 | + } | ||
| 47 | + return strides; | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +aclError CreateAclTensor(const std::vector<int64_t> &shape, aclDataType dtype, void *device_addr, aclTensor **tensor) | ||
| 51 | +{ | ||
| 52 | + std::vector<int64_t> strides = MakeStrides(shape); | ||
| 53 | + const int64_t *shape_ptr = shape.empty() ? nullptr : shape.data(); | ||
| 54 | + const int64_t *strides_ptr = strides.empty() ? nullptr : strides.data(); | ||
| 55 | + *tensor = aclCreateTensor( | ||
| 56 | + shape_ptr, shape.size(), dtype, strides_ptr, 0, ACL_FORMAT_ND, shape_ptr, shape.size(), device_addr); | ||
| 57 | + return *tensor == nullptr ? ACL_ERROR_FAILURE : ACL_SUCCESS; | ||
| 58 | +} | ||
| 59 | +} // namespace | ||
| 60 | + | ||
| 61 | +int main() | ||
| 62 | +{ | ||
| 63 | + const int32_t device_id = 0; | ||
| 64 | + std::vector<int64_t> shape = {2, 4}; | ||
| 65 | + std::vector<float> host_data = {-4.0f, -1.0f, 0.0f, 2.0f, 3.0f, -5.0f, 6.0f, -7.0f}; | ||
| 66 | + const size_t bytes = host_data.size() * sizeof(float); | ||
| 67 | + std::vector<float> result(host_data.size(), 0.0f); | ||
| 68 | + const std::vector<float> expected = {0.0f, 0.0f, 0.0f, 2.0f, 3.0f, 0.0f, 6.0f, 0.0f}; | ||
| 69 | + uint64_t workspace_size = 0; | ||
| 70 | + aclrtStream stream = nullptr; | ||
| 71 | + void *device_addr = nullptr; | ||
| 72 | + void *workspace = nullptr; | ||
| 73 | + aclTensor *self = nullptr; | ||
| 74 | + aclOpExecutor *executor = nullptr; | ||
| 75 | + bool acl_initialized = false; | ||
| 76 | + bool device_set = false; | ||
| 77 | + | ||
| 78 | + auto ret = aclInit(nullptr); | ||
| 79 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 80 | + acl_initialized = true; | ||
| 81 | + ret = aclrtSetDevice(device_id); | ||
| 82 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 83 | + device_set = true; | ||
| 84 | + ret = aclrtCreateStream(&stream); | ||
| 85 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 86 | + | ||
| 87 | + ret = aclrtMalloc(&device_addr, bytes, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 88 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 89 | + ret = aclrtMemcpy(device_addr, bytes, host_data.data(), bytes, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 90 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 91 | + ret = CreateAclTensor(shape, ACL_FLOAT, device_addr, &self); | ||
| 92 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 93 | + | ||
| 94 | + ret = aclnnInplaceReluGetWorkspaceSize(self, &workspace_size, &executor); | ||
| 95 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 96 | + if (workspace_size > 0) { | ||
| 97 | + ret = aclrtMalloc(&workspace, workspace_size, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 98 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + ret = aclnnInplaceRelu(workspace, workspace_size, executor, stream); | ||
| 102 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 103 | + ret = aclrtSynchronizeStream(stream); | ||
| 104 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 105 | + | ||
| 106 | + ret = aclrtMemcpy(result.data(), bytes, device_addr, bytes, ACL_MEMCPY_DEVICE_TO_HOST); | ||
| 107 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 108 | + | ||
| 109 | + for (size_t i = 0; i < expected.size(); ++i) { | ||
| 110 | + if (std::fabs(result[i] - expected[i]) > 1e-6f) { | ||
| 111 | + std::fprintf(stderr, "check failed at %zu: got %.6f expected %.6f\n", i, result[i], expected[i]); | ||
| 112 | + ret = ACL_ERROR_FAILURE; | ||
| 113 | + goto cleanup; | ||
| 114 | + } | ||
| 115 | + } | ||
| 116 | + std::printf("inplace relu example passed\n"); | ||
| 117 | + | ||
| 118 | +cleanup: | ||
| 119 | + if (self != nullptr) { | ||
| 120 | + aclDestroyTensor(self); | ||
| 121 | + } | ||
| 122 | + if (device_addr != nullptr) { | ||
| 123 | + aclrtFree(device_addr); | ||
| 124 | + } | ||
| 125 | + if (workspace != nullptr) { | ||
| 126 | + aclrtFree(workspace); | ||
| 127 | + } | ||
| 128 | + if (stream != nullptr) { | ||
| 129 | + aclrtDestroyStream(stream); | ||
| 130 | + } | ||
| 131 | + if (device_set) { | ||
| 132 | + aclrtResetDevice(device_id); | ||
| 133 | + } | ||
| 134 | + if (acl_initialized) { | ||
| 135 | + aclFinalize(); | ||
| 136 | + } | ||
| 137 | + return ret; | ||
| 138 | +} | ||
| @@ -0,0 +1,298 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * @file test_aclnn_relu.cpp | ||
| 13 | + * @brief ACLNN invocation example for experimental Relu operator | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + do { \ | ||
| 30 | + if (!(cond)) { \ | ||
| 31 | + expr; \ | ||
| 32 | + } \ | ||
| 33 | + } while (0) | ||
| 34 | + | ||
| 35 | +namespace { | ||
| 36 | +struct ReluConfig { | ||
| 37 | + aclDataType acl_dtype; | ||
| 38 | + size_t element_size; | ||
| 39 | + std::string name; | ||
| 40 | +}; | ||
| 41 | + | ||
| 42 | +int64_t GetShapeSize(const std::vector<int64_t> &shape) | ||
| 43 | +{ | ||
| 44 | + int64_t shape_size = 1; | ||
| 45 | + for (int64_t dim : shape) { | ||
| 46 | + shape_size *= dim; | ||
| 47 | + } | ||
| 48 | + return shape_size; | ||
| 49 | +} | ||
| 50 | + | ||
| 51 | +std::vector<int64_t> MakeStrides(const std::vector<int64_t> &shape) | ||
| 52 | +{ | ||
| 53 | + if (shape.empty()) { | ||
| 54 | + return {}; | ||
| 55 | + } | ||
| 56 | + std::vector<int64_t> strides(shape.size(), 1); | ||
| 57 | + for (int64_t i = static_cast<int64_t>(shape.size()) - 2; i >= 0; --i) { | ||
| 58 | + strides[static_cast<size_t>(i)] = shape[static_cast<size_t>(i + 1)] * strides[static_cast<size_t>(i + 1)]; | ||
| 59 | + } | ||
| 60 | + return strides; | ||
| 61 | +} | ||
| 62 | + | ||
| 63 | +bool ParseDtype(const std::string &dtype_name, ReluConfig *config) | ||
| 64 | +{ | ||
| 65 | + if (dtype_name == "fp16" || dtype_name == "float16") { | ||
| 66 | + *config = {ACL_FLOAT16, sizeof(uint16_t), "fp16"}; | ||
| 67 | + return true; | ||
| 68 | + } | ||
| 69 | + if (dtype_name == "fp32" || dtype_name == "float32") { | ||
| 70 | + *config = {ACL_FLOAT, sizeof(float), "fp32"}; | ||
| 71 | + return true; | ||
| 72 | + } | ||
| 73 | + if (dtype_name == "bf16" || dtype_name == "bfloat16") { | ||
| 74 | + *config = {ACL_BF16, sizeof(uint16_t), "bf16"}; | ||
| 75 | + return true; | ||
| 76 | + } | ||
| 77 | + if (dtype_name == "int8") { | ||
| 78 | + *config = {ACL_INT8, sizeof(int8_t), "int8"}; | ||
| 79 | + return true; | ||
| 80 | + } | ||
| 81 | + if (dtype_name == "int32") { | ||
| 82 | + *config = {ACL_INT32, sizeof(int32_t), "int32"}; | ||
| 83 | + return true; | ||
| 84 | + } | ||
| 85 | + if (dtype_name == "int64") { | ||
| 86 | + *config = {ACL_INT64, sizeof(int64_t), "int64"}; | ||
| 87 | + return true; | ||
| 88 | + } | ||
| 89 | + return false; | ||
| 90 | +} | ||
| 91 | + | ||
| 92 | +bool ParseShape(const std::string &shape_text, std::vector<int64_t> *shape) | ||
| 93 | +{ | ||
| 94 | + shape->clear(); | ||
| 95 | + if (shape_text.empty() || shape_text == "scalar") { | ||
| 96 | + return true; | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + size_t start = 0; | ||
| 100 | + while (start < shape_text.size()) { | ||
| 101 | + size_t end = shape_text.find(',', start); | ||
| 102 | + std::string token = shape_text.substr(start, end == std::string::npos ? std::string::npos : end - start); | ||
| 103 | + if (token.empty()) { | ||
| 104 | + return false; | ||
| 105 | + } | ||
| 106 | + shape->push_back(std::stoll(token)); | ||
| 107 | + if (end == std::string::npos) { | ||
| 108 | + break; | ||
| 109 | + } | ||
| 110 | + start = end + 1; | ||
| 111 | + } | ||
| 112 | + return true; | ||
| 113 | +} | ||
| 114 | + | ||
| 115 | +bool ReadFile(const std::string &path, std::vector<char> *buffer) | ||
| 116 | +{ | ||
| 117 | + std::ifstream stream(path, std::ios::binary); | ||
| 118 | + if (!stream.is_open()) { | ||
| 119 | + return false; | ||
| 120 | + } | ||
| 121 | + stream.seekg(0, std::ios::end); | ||
| 122 | + std::streamsize size = stream.tellg(); | ||
| 123 | + stream.seekg(0, std::ios::beg); | ||
| 124 | + if (size < 0) { | ||
| 125 | + return false; | ||
| 126 | + } | ||
| 127 | + buffer->resize(static_cast<size_t>(size)); | ||
| 128 | + return size == 0 || stream.read(buffer->data(), size).good(); | ||
| 129 | +} | ||
| 130 | + | ||
| 131 | +bool WriteFile(const std::string &path, const std::vector<char> &buffer) | ||
| 132 | +{ | ||
| 133 | + std::ofstream stream(path, std::ios::binary); | ||
| 134 | + if (!stream.is_open()) { | ||
| 135 | + return false; | ||
| 136 | + } | ||
| 137 | + stream.write(buffer.data(), static_cast<std::streamsize>(buffer.size())); | ||
| 138 | + return stream.good(); | ||
| 139 | +} | ||
| 140 | + | ||
| 141 | +aclError CreateAclTensor( | ||
| 142 | + const std::vector<int64_t> &shape, aclDataType dtype, void *device_addr, aclTensor **tensor) | ||
| 143 | +{ | ||
| 144 | + std::vector<int64_t> strides = MakeStrides(shape); | ||
| 145 | + const int64_t *shape_ptr = shape.empty() ? nullptr : shape.data(); | ||
| 146 | + const int64_t *strides_ptr = strides.empty() ? nullptr : strides.data(); | ||
| 147 | + *tensor = aclCreateTensor( | ||
| 148 | + shape_ptr, shape.size(), dtype, strides_ptr, 0, ACL_FORMAT_ND, shape_ptr, shape.size(), device_addr); | ||
| 149 | + return *tensor == nullptr ? ACL_ERROR_FAILURE : ACL_SUCCESS; | ||
| 150 | +} | ||
| 151 | + | ||
| 152 | +int RunRelu( | ||
| 153 | + const std::vector<char> &input_host, const std::vector<int64_t> &shape, const ReluConfig &config, | ||
| 154 | + std::vector<char> *output_host, int32_t device_id) | ||
| 155 | +{ | ||
| 156 | + aclrtStream stream = nullptr; | ||
| 157 | + void *input_device = nullptr; | ||
| 158 | + void *output_device = nullptr; | ||
| 159 | + void *workspace = nullptr; | ||
| 160 | + aclTensor *input_tensor = nullptr; | ||
| 161 | + aclTensor *output_tensor = nullptr; | ||
| 162 | + aclOpExecutor *executor = nullptr; | ||
| 163 | + uint64_t workspace_size = 0; | ||
| 164 | + const size_t bytes = static_cast<size_t>(GetShapeSize(shape)) * config.element_size; | ||
| 165 | + bool acl_initialized = false; | ||
| 166 | + bool device_set = false; | ||
| 167 | + | ||
| 168 | + auto ret = aclInit(nullptr); | ||
| 169 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 170 | + acl_initialized = true; | ||
| 171 | + ret = aclrtSetDevice(device_id); | ||
| 172 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 173 | + device_set = true; | ||
| 174 | + ret = aclrtCreateStream(&stream); | ||
| 175 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 176 | + | ||
| 177 | + if (bytes > 0) { | ||
| 178 | + ret = aclrtMalloc(&input_device, bytes, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 179 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 180 | + ret = aclrtMalloc(&output_device, bytes, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 181 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 182 | + | ||
| 183 | + ret = aclrtMemcpy(input_device, bytes, input_host.data(), bytes, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 184 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 185 | + std::vector<char> zero_buffer(bytes, 0); | ||
| 186 | + ret = aclrtMemcpy(output_device, bytes, zero_buffer.data(), bytes, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 187 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + ret = CreateAclTensor(shape, config.acl_dtype, input_device, &input_tensor); | ||
| 191 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 192 | + ret = CreateAclTensor(shape, config.acl_dtype, output_device, &output_tensor); | ||
| 193 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 194 | + | ||
| 195 | + ret = aclnnReluGetWorkspaceSize(input_tensor, output_tensor, &workspace_size, &executor); | ||
| 196 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 197 | + if (workspace_size > 0) { | ||
| 198 | + ret = aclrtMalloc(&workspace, workspace_size, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 199 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 200 | + } | ||
| 201 | + | ||
| 202 | + ret = aclnnRelu(workspace, workspace_size, executor, stream); | ||
| 203 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 204 | + ret = aclrtSynchronizeStream(stream); | ||
| 205 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 206 | + | ||
| 207 | + output_host->resize(bytes); | ||
| 208 | + if (bytes > 0) { | ||
| 209 | + ret = aclrtMemcpy(output_host->data(), bytes, output_device, bytes, ACL_MEMCPY_DEVICE_TO_HOST); | ||
| 210 | + CHECK_RET(ret == ACL_SUCCESS, goto cleanup); | ||
| 211 | + } | ||
| 212 | + | ||
| 213 | +cleanup: | ||
| 214 | + if (input_tensor != nullptr) { | ||
| 215 | + aclDestroyTensor(input_tensor); | ||
| 216 | + } | ||
| 217 | + if (output_tensor != nullptr) { | ||
| 218 | + aclDestroyTensor(output_tensor); | ||
| 219 | + } | ||
| 220 | + if (workspace != nullptr) { | ||
| 221 | + aclrtFree(workspace); | ||
| 222 | + } | ||
| 223 | + if (input_device != nullptr) { | ||
| 224 | + aclrtFree(input_device); | ||
| 225 | + } | ||
| 226 | + if (output_device != nullptr) { | ||
| 227 | + aclrtFree(output_device); | ||
| 228 | + } | ||
| 229 | + if (stream != nullptr) { | ||
| 230 | + aclrtDestroyStream(stream); | ||
| 231 | + } | ||
| 232 | + if (device_set) { | ||
| 233 | + aclrtResetDevice(device_id); | ||
| 234 | + } | ||
| 235 | + if (acl_initialized) { | ||
| 236 | + aclFinalize(); | ||
| 237 | + } | ||
| 238 | + return ret; | ||
| 239 | +} | ||
| 240 | + | ||
| 241 | +template <typename T> | ||
| 242 | +std::vector<char> ToBytes(const std::vector<T> &values) | ||
| 243 | +{ | ||
| 244 | + std::vector<char> buffer(values.size() * sizeof(T)); | ||
| 245 | + if (!buffer.empty()) { | ||
| 246 | + std::memcpy(buffer.data(), values.data(), buffer.size()); | ||
| 247 | + } | ||
| 248 | + return buffer; | ||
| 249 | +} | ||
| 250 | + | ||
| 251 | +int RunDefaultExample() | ||
| 252 | +{ | ||
| 253 | + ReluConfig config{ACL_FLOAT, sizeof(float), "fp32"}; | ||
| 254 | + std::vector<int64_t> shape = {4, 2}; | ||
| 255 | + std::vector<float> input = {-4.0f, -3.0f, -2.0f, 0.0f, 1.0f, 2.0f, 4.0f, 5.0f}; | ||
| 256 | + std::vector<char> output; | ||
| 257 | + auto ret = RunRelu(ToBytes(input), shape, config, &output, 0); | ||
| 258 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 259 | + | ||
| 260 | + const float *result = reinterpret_cast<const float *>(output.data()); | ||
| 261 | + const std::vector<float> expected = {0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 2.0f, 4.0f, 5.0f}; | ||
| 262 | + for (size_t i = 0; i < expected.size(); ++i) { | ||
| 263 | + if (result[i] != expected[i]) { | ||
| 264 | + std::fprintf(stderr, "default check failed at %zu: got %.8f expected %.8f\n", i, result[i], expected[i]); | ||
| 265 | + return 1; | ||
| 266 | + } | ||
| 267 | + } | ||
| 268 | + std::printf("default example passed\n"); | ||
| 269 | + return 0; | ||
| 270 | +} | ||
| 271 | +} // namespace | ||
| 272 | + | ||
| 273 | +int main(int argc, char **argv) | ||
| 274 | +{ | ||
| 275 | + if (argc == 1) { | ||
| 276 | + return RunDefaultExample(); | ||
| 277 | + } | ||
| 278 | + | ||
| 279 | + if (argc != 6) { | ||
| 280 | + std::fprintf(stderr, "Usage: %s <dtype> <shape|scalar> <input.bin> <output.bin> <device_id>\n", argv[0]); | ||
| 281 | + return 2; | ||
| 282 | + } | ||
| 283 | + | ||
| 284 | + ReluConfig config{}; | ||
| 285 | + CHECK_RET(ParseDtype(argv[1], &config), return 2); | ||
| 286 | + | ||
| 287 | + std::vector<int64_t> shape; | ||
| 288 | + CHECK_RET(ParseShape(argv[2], &shape), return 2); | ||
| 289 | + | ||
| 290 | + std::vector<char> input_host; | ||
| 291 | + CHECK_RET(ReadFile(argv[3], &input_host), return 3); | ||
| 292 | + | ||
| 293 | + std::vector<char> output_host; | ||
| 294 | + int ret = RunRelu(input_host, shape, config, &output_host, std::atoi(argv[5])); | ||
| 295 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 296 | + CHECK_RET(WriteFile(argv[4], output_host), return 4); | ||
| 297 | + return 0; | ||
| 298 | +} | ||
| @@ -0,0 +1,11 @@ | |||
| 1 | +# ---------------------------------------------------------------------------- | ||
| 2 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | +# ---------------------------------------------------------------------------- | ||
| 10 | + | ||
| 11 | +add_modules_sources(HOSTNAME ${OPHOST_NAME} MODE PRIVATE DIR ${CMAKE_CURRENT_SOURCE_DIR} OPTYPE relu ACLNNTYPE aclnn_exclude DISABLE_IN_OPP TRUE) | ||
| @@ -0,0 +1,159 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +using namespace op; | ||
| 21 | + | ||
| 22 | + | ||
| 23 | +extern "C" { | ||
| 24 | + | ||
| 25 | + | ||
| 26 | +constexpr size_t MAX_DIM_LEN = 8; | ||
| 27 | + | ||
| 28 | +static const std::initializer_list<op::DataType> ASCEND910_DTYPE_SUPPORT_LIST = { | ||
| 29 | + op::DataType::DT_FLOAT, | ||
| 30 | + op::DataType::DT_FLOAT16, | ||
| 31 | + op::DataType::DT_INT8, | ||
| 32 | + op::DataType::DT_INT32, | ||
| 33 | + op::DataType::DT_INT64}; | ||
| 34 | +static const std::initializer_list<op::DataType> ASCEND910B_DTYPE_SUPPORT_LIST = { | ||
| 35 | + op::DataType::DT_FLOAT, | ||
| 36 | + op::DataType::DT_FLOAT16, | ||
| 37 | + op::DataType::DT_BF16, | ||
| 38 | + op::DataType::DT_INT8, | ||
| 39 | + op::DataType::DT_INT32, | ||
| 40 | + op::DataType::DT_INT64}; | ||
| 41 | + | ||
| 42 | +static inline const std::initializer_list<op::DataType> &GetDtypeSupportList() | ||
| 43 | +{ | ||
| 44 | + if (GetCurrentPlatformInfo().GetSocVersion() >= SocVersion::ASCEND910B && | ||
| 45 | + GetCurrentPlatformInfo().GetSocVersion() <= SocVersion::ASCEND910E) { | ||
| 46 | + return ASCEND910B_DTYPE_SUPPORT_LIST; | ||
| 47 | + } | ||
| 48 | + if (Ops::NN::AclnnUtil::IsRegbase()) { | ||
| 49 | + return ASCEND910B_DTYPE_SUPPORT_LIST; | ||
| 50 | + } | ||
| 51 | + return ASCEND910_DTYPE_SUPPORT_LIST; | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +static bool CheckNotNull(const aclTensor *self, const aclTensor *out) | ||
| 55 | +{ | ||
| 56 | + OP_CHECK_NULL(self, return false); | ||
| 57 | + OP_CHECK_NULL(out, return false); | ||
| 58 | + return true; | ||
| 59 | +} | ||
| 60 | + | ||
| 61 | +static bool CheckDtypeValid(const aclTensor *self, const aclTensor *out) | ||
| 62 | +{ | ||
| 63 | + auto supportList = GetDtypeSupportList(); | ||
| 64 | + OP_CHECK_DTYPE_NOT_SUPPORT(self, supportList, return false); | ||
| 65 | + OP_CHECK_DTYPE_NOT_SUPPORT(out, supportList, return false); | ||
| 66 | + OP_CHECK_DTYPE_NOT_MATCH(self, out->GetDataType(), return false); | ||
| 67 | + return true; | ||
| 68 | +} | ||
| 69 | + | ||
| 70 | +static bool CheckShape(const aclTensor *self, const aclTensor *out) | ||
| 71 | +{ | ||
| 72 | + OP_CHECK_SHAPE_NOT_EQUAL(self, out, return false); | ||
| 73 | + OP_CHECK_MAX_DIM(self, MAX_DIM_LEN, return false); | ||
| 74 | + OP_CHECK_MAX_DIM(out, MAX_DIM_LEN, return false); | ||
| 75 | + return true; | ||
| 76 | +} | ||
| 77 | + | ||
| 78 | +static aclnnStatus CheckParams(const aclTensor *self, const aclTensor *out) | ||
| 79 | +{ | ||
| 80 | + CHECK_RET(CheckNotNull(self, out), ACLNN_ERR_PARAM_NULLPTR); | ||
| 81 | + CHECK_RET(CheckDtypeValid(self, out), ACLNN_ERR_PARAM_INVALID); | ||
| 82 | + CHECK_RET(CheckShape(self, out), ACLNN_ERR_PARAM_INVALID); | ||
| 83 | + return ACLNN_SUCCESS; | ||
| 84 | +} | ||
| 85 | + | ||
| 86 | +static aclnnStatus CheckInplaceParams(const aclTensor *self) | ||
| 87 | +{ | ||
| 88 | + OP_CHECK_NULL(self, return ACLNN_ERR_PARAM_NULLPTR); | ||
| 89 | + auto supportList = GetDtypeSupportList(); | ||
| 90 | + OP_CHECK_DTYPE_NOT_SUPPORT(self, supportList, return ACLNN_ERR_PARAM_INVALID); | ||
| 91 | + OP_CHECK_MAX_DIM(self, MAX_DIM_LEN, return ACLNN_ERR_PARAM_INVALID); | ||
| 92 | + return ACLNN_SUCCESS; | ||
| 93 | +} | ||
| 94 | + | ||
| 95 | +static aclnnStatus ExecReluGetWorkspaceSize( | ||
| 96 | + const aclTensor *self, const aclTensor *out, uint64_t *workspaceSize, aclOpExecutor **executor) | ||
| 97 | +{ | ||
| 98 | + auto uniqueExecutor = CREATE_EXECUTOR(); | ||
| 99 | + CHECK_RET(uniqueExecutor.get() != nullptr, ACLNN_ERR_INNER_CREATE_EXECUTOR); | ||
| 100 | + | ||
| 101 | + auto ret = CheckParams(self, out); | ||
| 102 | + CHECK_RET(ret == ACLNN_SUCCESS, ret); | ||
| 103 | + | ||
| 104 | + if (self->IsEmpty()) { | ||
| 105 | + *workspaceSize = 0; | ||
| 106 | + uniqueExecutor.ReleaseTo(executor); | ||
| 107 | + return ACLNN_SUCCESS; | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + auto selfContiguous = l0op::Contiguous(self, uniqueExecutor.get()); | ||
| 111 | + CHECK_RET(selfContiguous != nullptr, ACLNN_ERR_INNER_NULLPTR); | ||
| 112 | + | ||
| 113 | + auto reluOpOut = l0op::Relu(selfContiguous, uniqueExecutor.get()); | ||
| 114 | + CHECK_RET(reluOpOut != nullptr, ACLNN_ERR_INNER_NULLPTR); | ||
| 115 | + | ||
| 116 | + auto viewCopyResult = l0op::ViewCopy(reluOpOut, out, uniqueExecutor.get()); | ||
| 117 | + CHECK_RET(viewCopyResult != nullptr, ACLNN_ERR_INNER_NULLPTR); | ||
| 118 | + | ||
| 119 | + *workspaceSize = uniqueExecutor->GetWorkspaceSize(); | ||
| 120 | + uniqueExecutor.ReleaseTo(executor); | ||
| 121 | + return ACLNN_SUCCESS; | ||
| 122 | +} | ||
| 123 | + | ||
| 124 | +aclnnStatus aclnnReluGetWorkspaceSize( | ||
| 125 | + const aclTensor *self, const aclTensor *out, uint64_t *workspaceSize, aclOpExecutor **executor) | ||
| 126 | +{ | ||
| 127 | + OP_CHECK_COMM_INPUT(workspaceSize, executor); | ||
| 128 | + L2_DFX_PHASE_1(aclnnRelu, DFX_IN(self), DFX_OUT(out)); | ||
| 129 | + return ExecReluGetWorkspaceSize(self, out, workspaceSize, executor); | ||
| 130 | +} | ||
| 131 | + | ||
| 132 | +aclnnStatus aclnnInplaceReluGetWorkspaceSize( | ||
| 133 | + aclTensor *selfRef, uint64_t *workspaceSize, aclOpExecutor **executor) | ||
| 134 | +{ | ||
| 135 | + OP_CHECK_COMM_INPUT(workspaceSize, executor); | ||
| 136 | + L2_DFX_PHASE_1(aclnnInplaceRelu, DFX_IN(selfRef), DFX_OUT(selfRef)); | ||
| 137 | + | ||
| 138 | + auto ret = CheckInplaceParams(selfRef); | ||
| 139 | + CHECK_RET(ret == ACLNN_SUCCESS, ret); | ||
| 140 | + | ||
| 141 | + return ExecReluGetWorkspaceSize(selfRef, selfRef, workspaceSize, executor); | ||
| 142 | +} | ||
| 143 | + | ||
| 144 | +aclnnStatus aclnnRelu(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, const aclrtStream stream) | ||
| 145 | +{ | ||
| 146 | + L2_DFX_PHASE_2(aclnnRelu); | ||
| 147 | + return CommonOpExecutorRun(workspace, workspaceSize, executor, stream); | ||
| 148 | +} | ||
| 149 | + | ||
| 150 | +aclnnStatus aclnnInplaceRelu( | ||
| 151 | + void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, const aclrtStream stream) | ||
| 152 | +{ | ||
| 153 | + L2_DFX_PHASE_2(aclnnInplaceRelu); | ||
| 154 | + return CommonOpExecutorRun(workspace, workspaceSize, executor, stream); | ||
| 155 | +} | ||
| 156 | + | ||
| 157 | + | ||
| 158 | +} | ||
| 159 | + | ||
| @@ -0,0 +1,37 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | +extern "C" { | ||
| 19 | + | ||
| 20 | + | ||
| 21 | +ACLNN_API aclnnStatus aclnnReluGetWorkspaceSize( | ||
| 22 | + const aclTensor *self, const aclTensor *out, uint64_t *workspaceSize, aclOpExecutor **executor); | ||
| 23 | + | ||
| 24 | +ACLNN_API aclnnStatus aclnnRelu( | ||
| 25 | + void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, const aclrtStream stream); | ||
| 26 | + | ||
| 27 | +ACLNN_API aclnnStatus aclnnInplaceReluGetWorkspaceSize( | ||
| 28 | + aclTensor *selfRef, uint64_t *workspaceSize, aclOpExecutor **executor); | ||
| 29 | + | ||
| 30 | +ACLNN_API aclnnStatus aclnnInplaceRelu( | ||
| 31 | + void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, const aclrtStream stream); | ||
| 32 | + | ||
| 33 | + | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + | ||
| @@ -0,0 +1,37 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | +using namespace op; | ||
| 20 | + | ||
| 21 | +namespace l0op { | ||
| 22 | + | ||
| 23 | +OP_TYPE_REGISTER(Relu); | ||
| 24 | + | ||
| 25 | +const aclTensor *Relu(const aclTensor *self, aclOpExecutor *executor) | ||
| 26 | +{ | ||
| 27 | + L0_DFX(Relu, self); | ||
| 28 | + auto reluOut = executor->AllocTensor(self->GetStorageShape(), self->GetDataType(), self->GetStorageFormat()); | ||
| 29 | + CHECK_RET(reluOut != nullptr, nullptr); | ||
| 30 | + | ||
| 31 | + auto ret = ADD_TO_LAUNCHER_LIST_AICORE(Relu, OP_INPUT(self), OP_OUTPUT(reluOut)); | ||
| 32 | + OP_CHECK(ret == ACLNN_SUCCESS, OP_LOGE(ACLNN_ERR_INNER_NULLPTR, "Relu ADD_TO_LAUNCHER_LIST_AICORE failed."), | ||
| 33 | + return nullptr); | ||
| 34 | + return reluOut; | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +} // namespace l0op | ||
| @@ -0,0 +1,21 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +namespace l0op { | ||
| 18 | +const aclTensor *Relu(const aclTensor *self, aclOpExecutor *executor); | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | + | ||
| @@ -0,0 +1,47 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | +namespace ops { | ||
| 14 | +class Relu : public OpDef { | ||
| 15 | +public: | ||
| 16 | + explicit Relu(const char *name) : OpDef(name) | ||
| 17 | + { | ||
| 18 | + this->Input("x") | ||
| 19 | + .ParamType(REQUIRED) | ||
| 20 | + .DataType({ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_INT8, ge::DT_INT32, ge::DT_INT64}) | ||
| 21 | + .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 22 | + .UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, | ||
| 23 | + ge::FORMAT_ND}) | ||
| 24 | + .AutoContiguous(); | ||
| 25 | + | ||
| 26 | + this->Output("y") | ||
| 27 | + .ParamType(REQUIRED) | ||
| 28 | + .DataType({ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_INT8, ge::DT_INT32, ge::DT_INT64}) | ||
| 29 | + .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 30 | + .UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, | ||
| 31 | + ge::FORMAT_ND}) | ||
| 32 | + .AutoContiguous(); | ||
| 33 | + | ||
| 34 | + OpAICoreConfig aicoreConfig; | ||
| 35 | + aicoreConfig.DynamicCompileStaticFlag(true) | ||
| 36 | + .DynamicFormatFlag(false) | ||
| 37 | + .DynamicRankSupportFlag(true) | ||
| 38 | + .DynamicShapeSupportFlag(true) | ||
| 39 | + .NeedCheckSupportFlag(false) | ||
| 40 | + .PrecisionReduceFlag(true) | ||
| 41 | + .ExtendCfgInfo("opFile.value", "relu"); | ||
| 42 | + this->AICore().AddConfig("ascend910b", aicoreConfig); | ||
| 43 | + } | ||
| 44 | +}; | ||
| 45 | + | ||
| 46 | +OP_ADD(Relu); | ||
| 47 | +} // namespace ops | ||
| @@ -0,0 +1,22 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +namespace ops { | ||
| 15 | + | ||
| 16 | +static ge::graphStatus InferShape4Relu(gert::InferShapeContext *context) | ||
| 17 | +{ | ||
| 18 | + return Ops::Base::InferShape4Elewise(context); | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | +IMPL_OP_INFERSHAPE(Relu).InferShape(InferShape4Relu); | ||
| 22 | +} // namespace ops | ||
| @@ -0,0 +1,180 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | +namespace optiling { | ||
| 22 | + | ||
| 23 | +using namespace Ops::NN::OpTiling; | ||
| 24 | + | ||
| 25 | +constexpr int64_t CACHE_LINE_BYTE_LENGTH = 512; | ||
| 26 | +constexpr int64_t MIN_BYTES_PER_CORE = 16 * 1024; | ||
| 27 | + | ||
| 28 | +struct ReluCompileInfo {}; | ||
| 29 | + | ||
| 30 | +static const gert::Shape SCALAR_SHAPE = {1}; | ||
| 31 | + | ||
| 32 | +static const gert::Shape &EnsureNotScalar(const gert::Shape &shape) | ||
| 33 | +{ | ||
| 34 | + if (shape.GetDimNum() == 0) { | ||
| 35 | + return SCALAR_SHAPE; | ||
| 36 | + } | ||
| 37 | + return shape; | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +static ge::graphStatus GetPlatformInfo(gert::TilingContext *context, uint64_t &ubSize, int64_t &coreNum) | ||
| 41 | +{ | ||
| 42 | + auto *platformInfoPtr = context->GetPlatformInfo(); | ||
| 43 | + OP_CHECK_NULL_WITH_CONTEXT(context, platformInfoPtr); | ||
| 44 | + auto ascendcPlatform = platform_ascendc::PlatformAscendC(platformInfoPtr); | ||
| 45 | + coreNum = static_cast<int64_t>(ascendcPlatform.GetCoreNumAiv()); | ||
| 46 | + OP_CHECK_IF(coreNum <= 0, OP_LOGE(context, "coreNum is invalid"), return ge::GRAPH_FAILED); | ||
| 47 | + ascendcPlatform.GetCoreMemSize(platform_ascendc::CoreMemType::UB, ubSize); | ||
| 48 | + OP_CHECK_IF(ubSize == 0, OP_LOGE(context, "ubSize is 0"), return ge::GRAPH_FAILED); | ||
| 49 | + return ge::GRAPH_SUCCESS; | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +static ge::graphStatus GetShapeAttrsInfo(gert::TilingContext *context, int64_t &totalLength, ge::DataType &dataType) | ||
| 53 | +{ | ||
| 54 | + auto *inputX = context->GetInputShape(0); | ||
| 55 | + OP_CHECK_NULL_WITH_CONTEXT(context, inputX); | ||
| 56 | + totalLength = EnsureNotScalar(inputX->GetStorageShape()).GetShapeSize(); | ||
| 57 | + | ||
| 58 | + auto *inputDesc = context->GetInputDesc(0); | ||
| 59 | + OP_CHECK_NULL_WITH_CONTEXT(context, inputDesc); | ||
| 60 | + dataType = inputDesc->GetDataType(); | ||
| 61 | + const std::set<ge::DataType> supportedDtype = { | ||
| 62 | + ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_INT8, ge::DT_INT32, ge::DT_INT64}; | ||
| 63 | + OP_CHECK_IF(supportedDtype.count(dataType) == 0, OP_LOGE(context, "invalid dtype"), return ge::GRAPH_FAILED); | ||
| 64 | + | ||
| 65 | + return ge::GRAPH_SUCCESS; | ||
| 66 | +} | ||
| 67 | + | ||
| 68 | +static ge::graphStatus GetWorkspaceSize(gert::TilingContext *context) | ||
| 69 | +{ | ||
| 70 | + auto ascendcPlatform = platform_ascendc::PlatformAscendC(context->GetPlatformInfo()); | ||
| 71 | + uint32_t sysWorkspaceSize = ascendcPlatform.GetLibApiWorkSpaceSize(); | ||
| 72 | + size_t *currentWorkspace = context->GetWorkspaceSizes(1); | ||
| 73 | + OP_CHECK_NULL_WITH_CONTEXT(context, currentWorkspace); | ||
| 74 | + currentWorkspace[0] = sysWorkspaceSize; | ||
| 75 | + return ge::GRAPH_SUCCESS; | ||
| 76 | +} | ||
| 77 | + | ||
| 78 | +static void FillDefaultTiling(ReluTilingData *tiling, uint32_t dTypeX, gert::TilingContext *context) | ||
| 79 | +{ | ||
| 80 | + tiling->formerNum = 0; | ||
| 81 | + tiling->formerLength = 0; | ||
| 82 | + tiling->tailLength = 0; | ||
| 83 | + tiling->tileLength = 1; | ||
| 84 | + context->SetBlockDim(1); | ||
| 85 | + ASCENDC_TPL_SEL_PARAM(context, dTypeX); | ||
| 86 | +} | ||
| 87 | + | ||
| 88 | +static void FillNormalTiling(ReluTilingData *tiling, int64_t totalLength, uint64_t ubSize, int64_t coreNum, | ||
| 89 | + ge::DataType dataType, uint32_t dTypeX, gert::TilingContext *context) | ||
| 90 | +{ | ||
| 91 | + uint32_t typeLength = 0; | ||
| 92 | + ge::TypeUtils::GetDataTypeLength(dataType, typeLength); | ||
| 93 | + OP_CHECK_IF(typeLength == 0, OP_LOGE(context, "typeLength is 0"), return); | ||
| 94 | + | ||
| 95 | + int64_t dtypeSize = static_cast<int64_t>(typeLength); | ||
| 96 | + int64_t totalBytes = totalLength * dtypeSize; | ||
| 97 | + int64_t targetCoreNum = 1; | ||
| 98 | + if (dataType == ge::DT_INT64) { | ||
| 99 | + targetCoreNum = coreNum; | ||
| 100 | + } else { | ||
| 101 | + targetCoreNum = (totalBytes + MIN_BYTES_PER_CORE - 1) / MIN_BYTES_PER_CORE; | ||
| 102 | + targetCoreNum = std::max<int64_t>(1, targetCoreNum); | ||
| 103 | + targetCoreNum = std::min<int64_t>(targetCoreNum, coreNum); | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + int64_t cacheLineElements = std::max<int64_t>(1, CACHE_LINE_BYTE_LENGTH / dtypeSize); | ||
| 107 | + int64_t totalLengthCore = (totalLength + targetCoreNum - 1) / targetCoreNum; | ||
| 108 | + int64_t totalLengthCoreAlign = | ||
| 109 | + ((totalLengthCore + cacheLineElements - 1) / cacheLineElements) * cacheLineElements; | ||
| 110 | + | ||
| 111 | + int64_t usedCoreNum = (totalLength + totalLengthCoreAlign - 1) / totalLengthCoreAlign; | ||
| 112 | + usedCoreNum = std::max<int64_t>(1, usedCoreNum); | ||
| 113 | + int64_t formerNum = usedCoreNum - 1; | ||
| 114 | + int64_t formerLength = totalLengthCoreAlign; | ||
| 115 | + int64_t tailLength = totalLength - formerNum * formerLength; | ||
| 116 | + | ||
| 117 | + int64_t bufferCoefficient = 8; | ||
| 118 | + if (dataType == ge::DT_FLOAT16) { | ||
| 119 | + bufferCoefficient = 4; | ||
| 120 | + } else if (dataType == ge::DT_BF16) { | ||
| 121 | + bufferCoefficient = 8; | ||
| 122 | + } else if (dataType == ge::DT_INT8) { | ||
| 123 | + bufferCoefficient = 4; | ||
| 124 | + } else if (dataType == ge::DT_INT64) { | ||
| 125 | + bufferCoefficient = 32; | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + int64_t maxTileElements = static_cast<int64_t>(ubSize) / bufferCoefficient; | ||
| 129 | + int64_t alignElements = std::max<int64_t>(1, 32 / dtypeSize); | ||
| 130 | + int64_t tileLength = (maxTileElements / alignElements) * alignElements; | ||
| 131 | + if (tileLength <= 0) { | ||
| 132 | + tileLength = alignElements; | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + tiling->formerNum = formerNum; | ||
| 136 | + tiling->formerLength = formerLength; | ||
| 137 | + tiling->tailLength = tailLength; | ||
| 138 | + tiling->tileLength = tileLength; | ||
| 139 | + | ||
| 140 | + context->SetBlockDim(static_cast<uint32_t>(usedCoreNum)); | ||
| 141 | + ASCENDC_TPL_SEL_PARAM(context, dTypeX); | ||
| 142 | +} | ||
| 143 | + | ||
| 144 | +static ge::graphStatus ReluTilingFunc(gert::TilingContext *context) | ||
| 145 | +{ | ||
| 146 | + uint64_t ubSize = 0; | ||
| 147 | + int64_t coreNum = 0; | ||
| 148 | + OP_CHECK_IF(GetPlatformInfo(context, ubSize, coreNum) != ge::GRAPH_SUCCESS, | ||
| 149 | + OP_LOGE(context, "GetPlatformInfo error"), return ge::GRAPH_FAILED); | ||
| 150 | + | ||
| 151 | + int64_t totalLength = 0; | ||
| 152 | + ge::DataType dataType; | ||
| 153 | + OP_CHECK_IF(GetShapeAttrsInfo(context, totalLength, dataType) != ge::GRAPH_SUCCESS, | ||
| 154 | + OP_LOGE(context, "GetShapeAttrsInfo error"), return ge::GRAPH_FAILED); | ||
| 155 | + | ||
| 156 | + OP_CHECK_IF(GetWorkspaceSize(context) != ge::GRAPH_SUCCESS, | ||
| 157 | + OP_LOGE(context, "GetWorkspaceSize error"), return ge::GRAPH_FAILED); | ||
| 158 | + | ||
| 159 | + auto *tiling = context->GetTilingData<ReluTilingData>(); | ||
| 160 | + OP_CHECK_NULL_WITH_CONTEXT(context, tiling); | ||
| 161 | + OP_CHECK_IF(memset_s(tiling, sizeof(ReluTilingData), 0, sizeof(ReluTilingData)) != EOK, | ||
| 162 | + OP_LOGE(context, "set tiling data error"), return ge::GRAPH_FAILED); | ||
| 163 | + | ||
| 164 | + uint32_t dTypeX = static_cast<uint32_t>(dataType); | ||
| 165 | + if (totalLength <= 0) { | ||
| 166 | + FillDefaultTiling(tiling, dTypeX, context); | ||
| 167 | + return ge::GRAPH_SUCCESS; | ||
| 168 | + } | ||
| 169 | + | ||
| 170 | + FillNormalTiling(tiling, totalLength, ubSize, coreNum, dataType, dTypeX, context); | ||
| 171 | + return ge::GRAPH_SUCCESS; | ||
| 172 | +} | ||
| 173 | + | ||
| 174 | +static ge::graphStatus TilingParseForRelu([[maybe_unused]] gert::TilingParseContext *context) | ||
| 175 | +{ | ||
| 176 | + return ge::GRAPH_SUCCESS; | ||
| 177 | +} | ||
| 178 | + | ||
| 179 | +IMPL_OP_OPTILING(Relu).Tiling(ReluTilingFunc).TilingParse<ReluCompileInfo>(TilingParseForRelu); | ||
| 180 | +} // namespace optiling | ||
| @@ -0,0 +1,38 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +template <typename D_T_X> | ||
| 16 | +__global__ __aicore__ void relu(GM_ADDR x, GM_ADDR y, GM_ADDR workspace, GM_ADDR tiling) | ||
| 17 | +{ | ||
| 18 | + REGISTER_TILING_DEFAULT(ReluTilingData); | ||
| 19 | + GET_TILING_DATA_WITH_STRUCT(ReluTilingData, tilingData, tiling); | ||
| 20 | + | ||
| 21 | + if constexpr (std::is_same_v<D_T_X, float> || std::is_same_v<D_T_X, half> || std::is_same_v<D_T_X, int32_t>) { | ||
| 22 | + NsRelu::KernelRelu<D_T_X> op; | ||
| 23 | + op.Init(x, y, &tilingData); | ||
| 24 | + op.Process(); | ||
| 25 | + } else if constexpr (std::is_same_v<D_T_X, bfloat16_t>) { | ||
| 26 | + NsRelu::KernelReluUpcast<D_T_X, float> op; | ||
| 27 | + op.Init(x, y, &tilingData); | ||
| 28 | + op.Process(); | ||
| 29 | + } else if constexpr (std::is_same_v<D_T_X, int8_t>) { | ||
| 30 | + NsRelu::KernelReluUpcast<D_T_X, half> op; | ||
| 31 | + op.Init(x, y, &tilingData); | ||
| 32 | + op.Process(); | ||
| 33 | + } else { | ||
| 34 | + NsRelu::KernelReluScalarInt64<D_T_X> op; | ||
| 35 | + op.Init(x, y, &tilingData); | ||
| 36 | + op.Process(); | ||
| 37 | + } | ||
| 38 | +} | ||
| @@ -0,0 +1,245 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | +namespace NsRelu { | ||
| 20 | + | ||
| 21 | +using namespace AscendC; | ||
| 22 | + | ||
| 23 | +constexpr int32_t BUFFER_NUM = 2; | ||
| 24 | +constexpr int32_t QUEUE_DEPTH = 1; | ||
| 25 | + | ||
| 26 | +template <typename T> | ||
| 27 | +__aicore__ inline void CopyInFromGm(TQue<TPosition::VECIN, BUFFER_NUM> &inQueueX, GlobalTensor<T> &xGm, | ||
| 28 | + int64_t progress, int64_t tileLength, int64_t curTileLength) | ||
| 29 | +{ | ||
| 30 | + LocalTensor<T> xLocal = inQueueX.AllocTensor<T>(); | ||
| 31 | + DataCopyExtParams copyParams{1, static_cast<uint32_t>(curTileLength * sizeof(T)), 0, 0, 0}; | ||
| 32 | + DataCopyPadExtParams<T> padParams{false, 0, 0, 0}; | ||
| 33 | + DataCopyPad(xLocal, xGm[progress * tileLength], copyParams, padParams); | ||
| 34 | + inQueueX.EnQue(xLocal); | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +template <typename T> | ||
| 38 | +__aicore__ inline void CopyOutToGm(TQue<TPosition::VECOUT, BUFFER_NUM> &outQueueY, GlobalTensor<T> &yGm, | ||
| 39 | + int64_t progress, int64_t tileLength, int64_t curTileLength) | ||
| 40 | +{ | ||
| 41 | + LocalTensor<T> yLocal = outQueueY.DeQue<T>(); | ||
| 42 | + DataCopyExtParams copyParams{1, static_cast<uint32_t>(curTileLength * sizeof(T)), 0, 0, 0}; | ||
| 43 | + DataCopyPad(yGm[progress * tileLength], yLocal, copyParams); | ||
| 44 | + outQueueY.FreeTensor(yLocal); | ||
| 45 | +} | ||
| 46 | + | ||
| 47 | +template <typename T> | ||
| 48 | +__aicore__ inline void CopyInFromGm( | ||
| 49 | + TQueBind<TPosition::VECIN, TPosition::VECOUT, QUEUE_DEPTH> &inOutQueue, GlobalTensor<T> &xGm, int64_t progress, | ||
| 50 | + int64_t tileLength, int64_t curTileLength) | ||
| 51 | +{ | ||
| 52 | + LocalTensor<T> xLocal = inOutQueue.AllocTensor<T>(); | ||
| 53 | + DataCopyExtParams copyParams{1, static_cast<uint32_t>(curTileLength * sizeof(T)), 0, 0, 0}; | ||
| 54 | + DataCopyPadExtParams<T> padParams{false, 0, 0, 0}; | ||
| 55 | + DataCopyPad(xLocal, xGm[progress * tileLength], copyParams, padParams); | ||
| 56 | + inOutQueue.template EnQue<QuePosition::GM, QuePosition::VECIN, T>(xLocal); | ||
| 57 | +} | ||
| 58 | + | ||
| 59 | +template <typename T> | ||
| 60 | +__aicore__ inline void CopyOutToGm( | ||
| 61 | + TQueBind<TPosition::VECIN, TPosition::VECOUT, QUEUE_DEPTH> &inOutQueue, GlobalTensor<T> &yGm, | ||
| 62 | + int64_t progress, int64_t tileLength, int64_t curTileLength) | ||
| 63 | +{ | ||
| 64 | + LocalTensor<T> yLocal = inOutQueue.template DeQue<QuePosition::VECOUT, QuePosition::GM, T>(); | ||
| 65 | + DataCopyExtParams copyParams{1, static_cast<uint32_t>(curTileLength * sizeof(T)), 0, 0, 0}; | ||
| 66 | + DataCopyPad(yGm[progress * tileLength], yLocal, copyParams); | ||
| 67 | + inOutQueue.FreeTensor(yLocal); | ||
| 68 | +} | ||
| 69 | + | ||
| 70 | +template <typename T> | ||
| 71 | +__aicore__ inline void InitBlockGm(GlobalTensor<T> &xGm, GlobalTensor<T> &yGm, int64_t &blockLength, | ||
| 72 | + int64_t &tileLength, GM_ADDR x, GM_ADDR y, const ReluTilingData *tilingData) | ||
| 73 | +{ | ||
| 74 | + int64_t blockIdx = GetBlockIdx(); | ||
| 75 | + if (blockIdx < tilingData->formerNum) { | ||
| 76 | + blockLength = tilingData->formerLength; | ||
| 77 | + int64_t offset = tilingData->formerLength * blockIdx; | ||
| 78 | + xGm.SetGlobalBuffer((__gm__ T *)x + offset, tilingData->formerLength); | ||
| 79 | + yGm.SetGlobalBuffer((__gm__ T *)y + offset, tilingData->formerLength); | ||
| 80 | + } else { | ||
| 81 | + blockLength = tilingData->tailLength; | ||
| 82 | + int64_t offset = tilingData->formerLength * tilingData->formerNum; | ||
| 83 | + xGm.SetGlobalBuffer((__gm__ T *)x + offset, tilingData->tailLength); | ||
| 84 | + yGm.SetGlobalBuffer((__gm__ T *)y + offset, tilingData->tailLength); | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + tileLength = tilingData->tileLength; | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +template <typename Kernel> | ||
| 91 | +__aicore__ inline void ProcessTiles(Kernel &kernel, int64_t blockLength, int64_t tileLength) | ||
| 92 | +{ | ||
| 93 | + int64_t tileNum = (blockLength + tileLength - 1) / tileLength; | ||
| 94 | + if (tileNum == 0) { | ||
| 95 | + return; | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + int64_t tailTileLength = blockLength - (tileNum - 1) * tileLength; | ||
| 99 | + for (int64_t i = 0; i < tileNum - 1; ++i) { | ||
| 100 | + kernel.CopyIn(i, tileLength); | ||
| 101 | + kernel.Compute(tileLength); | ||
| 102 | + kernel.CopyOut(i, tileLength); | ||
| 103 | + } | ||
| 104 | + kernel.CopyIn(tileNum - 1, tailTileLength); | ||
| 105 | + kernel.Compute(tailTileLength); | ||
| 106 | + kernel.CopyOut(tileNum - 1, tailTileLength); | ||
| 107 | +} | ||
| 108 | + | ||
| 109 | +template <typename Derived, typename T> | ||
| 110 | +class KernelReluBase { | ||
| 111 | +public: | ||
| 112 | + __aicore__ inline void Init(GM_ADDR x, GM_ADDR y, const ReluTilingData *tilingData) | ||
| 113 | + { | ||
| 114 | + InitBlockGm(xGm_, yGm_, blockLength_, tileLength_, x, y, tilingData); | ||
| 115 | + pipe_.InitBuffer(inOutQueue_, BUFFER_NUM, tileLength_ * sizeof(T)); | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + __aicore__ inline void Process() | ||
| 119 | + { | ||
| 120 | + ProcessTiles(static_cast<Derived &>(*this), blockLength_, tileLength_); | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + __aicore__ inline void CopyIn(int64_t progress, int64_t curTileLength) | ||
| 124 | + { | ||
| 125 | + CopyInFromGm(inOutQueue_, xGm_, progress, tileLength_, curTileLength); | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + __aicore__ inline void CopyOut(int64_t progress, int64_t curTileLength) | ||
| 129 | + { | ||
| 130 | + CopyOutToGm(inOutQueue_, yGm_, progress, tileLength_, curTileLength); | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | +protected: | ||
| 134 | + TPipe pipe_; | ||
| 135 | + TQueBind<TPosition::VECIN, TPosition::VECOUT, QUEUE_DEPTH> inOutQueue_; | ||
| 136 | + GlobalTensor<T> xGm_; | ||
| 137 | + GlobalTensor<T> yGm_; | ||
| 138 | + int64_t blockLength_ = 0; | ||
| 139 | + int64_t tileLength_ = 0; | ||
| 140 | +}; | ||
| 141 | + | ||
| 142 | +template <typename T> | ||
| 143 | +class KernelRelu : public KernelReluBase<KernelRelu<T>, T> { | ||
| 144 | +public: | ||
| 145 | + __aicore__ inline KernelRelu() {} | ||
| 146 | + | ||
| 147 | + __aicore__ inline void Compute(int64_t curTileLength) | ||
| 148 | + { | ||
| 149 | + LocalTensor<T> xLocal = this->inOutQueue_.template DeQue<QuePosition::GM, QuePosition::VECIN, T>(); | ||
| 150 | + Relu(xLocal, xLocal, curTileLength); | ||
| 151 | + this->inOutQueue_.template EnQue<QuePosition::VECOUT, QuePosition::GM, T>(xLocal); | ||
| 152 | + } | ||
| 153 | +}; | ||
| 154 | + | ||
| 155 | +template <typename T, typename MidT> | ||
| 156 | +class KernelReluUpcast : public KernelReluBase<KernelReluUpcast<T, MidT>, T> { | ||
| 157 | +public: | ||
| 158 | + __aicore__ inline KernelReluUpcast() {} | ||
| 159 | + | ||
| 160 | + __aicore__ inline void Init(GM_ADDR x, GM_ADDR y, const ReluTilingData *tilingData) | ||
| 161 | + { | ||
| 162 | + KernelReluBase<KernelReluUpcast<T, MidT>, T>::Init(x, y, tilingData); | ||
| 163 | + this->pipe_.InitBuffer(tmpBufX_, this->tileLength_ * sizeof(MidT)); | ||
| 164 | + } | ||
| 165 | + | ||
| 166 | + __aicore__ inline void Compute(int64_t curTileLength) | ||
| 167 | + { | ||
| 168 | + LocalTensor<T> xLocal = this->inOutQueue_.template DeQue<QuePosition::GM, QuePosition::VECIN, T>(); | ||
| 169 | + LocalTensor<MidT> xMid = tmpBufX_.Get<MidT>(); | ||
| 170 | + Cast(xMid, xLocal, RoundMode::CAST_NONE, curTileLength); | ||
| 171 | + Relu(xMid, xMid, curTileLength); | ||
| 172 | + Cast(xLocal, xMid, RoundMode::CAST_RINT, curTileLength); | ||
| 173 | + this->inOutQueue_.template EnQue<QuePosition::VECOUT, QuePosition::GM, T>(xLocal); | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | +private: | ||
| 177 | + TBuf<TPosition::VECCALC> tmpBufX_; | ||
| 178 | +}; | ||
| 179 | + | ||
| 180 | +template <typename T> | ||
| 181 | +class KernelReluScalarInt64 { | ||
| 182 | +public: | ||
| 183 | + __aicore__ inline KernelReluScalarInt64() {} | ||
| 184 | + | ||
| 185 | + __aicore__ inline void Init(GM_ADDR x, GM_ADDR y, const ReluTilingData *tilingData) | ||
| 186 | + { | ||
| 187 | + InitBlockGm(xGm_, yGm_, blockLength_, tileLength_, x, y, tilingData); | ||
| 188 | + pipe_.InitBuffer(inQueueX_, BUFFER_NUM, tileLength_ * sizeof(T)); | ||
| 189 | + pipe_.InitBuffer(outQueueY_, BUFFER_NUM, tileLength_ * sizeof(T)); | ||
| 190 | + } | ||
| 191 | + | ||
| 192 | + __aicore__ inline void Process() | ||
| 193 | + { | ||
| 194 | + int64_t tileNum = (blockLength_ + tileLength_ - 1) / tileLength_; | ||
| 195 | + if (tileNum == 0) { | ||
| 196 | + return; | ||
| 197 | + } | ||
| 198 | + | ||
| 199 | + int64_t tailTileLength = blockLength_ - (tileNum - 1) * tileLength_; | ||
| 200 | + for (int64_t i = 0; i < tileNum - 1; ++i) { | ||
| 201 | + CopyIn(i, tileLength_); | ||
| 202 | + Compute(tileLength_); | ||
| 203 | + CopyOut(i, tileLength_); | ||
| 204 | + } | ||
| 205 | + CopyIn(tileNum - 1, tailTileLength); | ||
| 206 | + Compute(tailTileLength); | ||
| 207 | + CopyOut(tileNum - 1, tailTileLength); | ||
| 208 | + } | ||
| 209 | + | ||
| 210 | +private: | ||
| 211 | + __aicore__ inline void CopyIn(int64_t progress, int64_t curTileLength) | ||
| 212 | + { | ||
| 213 | + CopyInFromGm(inQueueX_, xGm_, progress, tileLength_, curTileLength); | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + __aicore__ inline void Compute(int64_t curTileLength) | ||
| 217 | + { | ||
| 218 | + LocalTensor<T> xLocal = inQueueX_.DeQue<T>(); | ||
| 219 | + LocalTensor<T> yLocal = outQueueY_.AllocTensor<T>(); | ||
| 220 | + for (int64_t index = 0; index < curTileLength; ++index) { | ||
| 221 | + T value = xLocal.GetValue(index); | ||
| 222 | + yLocal.SetValue(index, value > static_cast<T>(0) ? value : static_cast<T>(0)); | ||
| 223 | + } | ||
| 224 | + outQueueY_.EnQue<T>(yLocal); | ||
| 225 | + inQueueX_.FreeTensor(xLocal); | ||
| 226 | + } | ||
| 227 | + | ||
| 228 | + __aicore__ inline void CopyOut(int64_t progress, int64_t curTileLength) | ||
| 229 | + { | ||
| 230 | + CopyOutToGm(outQueueY_, yGm_, progress, tileLength_, curTileLength); | ||
| 231 | + } | ||
| 232 | + | ||
| 233 | +private: | ||
| 234 | + TPipe pipe_; | ||
| 235 | + TQue<TPosition::VECIN, BUFFER_NUM> inQueueX_; | ||
| 236 | + TQue<TPosition::VECOUT, BUFFER_NUM> outQueueY_; | ||
| 237 | + GlobalTensor<T> xGm_; | ||
| 238 | + GlobalTensor<T> yGm_; | ||
| 239 | + int64_t blockLength_ = 0; | ||
| 240 | + int64_t tileLength_ = 0; | ||
| 241 | +}; | ||
| 242 | + | ||
| 243 | +} // namespace NsRelu | ||
| 244 | + | ||
| 245 | + | ||
| @@ -0,0 +1,23 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +struct ReluTilingData { | ||
| 17 | + int64_t formerNum; | ||
| 18 | + int64_t formerLength; | ||
| 19 | + int64_t tailLength; | ||
| 20 | + int64_t tileLength; | ||
| 21 | +}; | ||
| 22 | + | ||
| 23 | + | ||
| @@ -0,0 +1,30 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +ASCENDC_TPL_ARGS_DECL( | ||
| 17 | + Relu, | ||
| 18 | + ASCENDC_TPL_DATATYPE_DECL(D_T_X, C_DT_FLOAT, C_DT_FLOAT16, C_DT_BF16, C_DT_INT8, C_DT_INT32, C_DT_INT64, | ||
| 19 | + ASCENDC_TPL_INPUT(0))); | ||
| 20 | + | ||
| 21 | +ASCENDC_TPL_SEL( | ||
| 22 | + ASCENDC_TPL_ARGS_SEL(ASCENDC_TPL_DATATYPE_SEL(D_T_X, C_DT_FLOAT)), | ||
| 23 | + ASCENDC_TPL_ARGS_SEL(ASCENDC_TPL_DATATYPE_SEL(D_T_X, C_DT_FLOAT16)), | ||
| 24 | + ASCENDC_TPL_ARGS_SEL(ASCENDC_TPL_DATATYPE_SEL(D_T_X, C_DT_BF16)), | ||
| 25 | + ASCENDC_TPL_ARGS_SEL(ASCENDC_TPL_DATATYPE_SEL(D_T_X, C_DT_INT8)), | ||
| 26 | + ASCENDC_TPL_ARGS_SEL(ASCENDC_TPL_DATATYPE_SEL(D_T_X, C_DT_INT32)), | ||
| 27 | + ASCENDC_TPL_ARGS_SEL(ASCENDC_TPL_DATATYPE_SEL(D_T_X, C_DT_INT64)), | ||
| 28 | +); | ||
| 29 | + | ||
| 30 | + | ||
| @@ -0,0 +1,16 @@ | |||
| 1 | +# ---------------------------------------------------------------------------- | ||
| 2 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | +# ---------------------------------------------------------------------------- | ||
| 10 | + | ||
| 11 | +file(GLOB CURRENT_DIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*) | ||
| 12 | +foreach(SUB_DIR ${CURRENT_DIRS}) | ||
| 13 | + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUB_DIR}/CMakeLists.txt") | ||
| 14 | + add_subdirectory(${SUB_DIR}) | ||
| 15 | + endif() | ||
| 16 | +endforeach() | ||
| @@ -0,0 +1,75 @@ | |||
| 1 | +[ | ||
| 2 | + { | ||
| 3 | + "id": 1, | ||
| 4 | + "name": "torch.relu fp16", | ||
| 5 | + "aclnn_name": "aclnnRelu", | ||
| 6 | + "version": "v1.0", | ||
| 7 | + "expected_error_msg": null, | ||
| 8 | + "api": "pytorch", | ||
| 9 | + "api_type": "aclnn_relu", | ||
| 10 | + "aclnn_api_type": "aclnn_function", | ||
| 11 | + "backward": false, | ||
| 12 | + "standard": { | ||
| 13 | + "acc": "cv_fused_double_benchmark", | ||
| 14 | + "perf": "not_key" | ||
| 15 | + }, | ||
| 16 | + "outputs": null, | ||
| 17 | + "inputs": [ | ||
| 18 | + { | ||
| 19 | + "name": "x", | ||
| 20 | + "type": "tensor", | ||
| 21 | + "required": true, | ||
| 22 | + "dtype": "fp16", | ||
| 23 | + "shape": [ | ||
| 24 | + 128 | ||
| 25 | + ], | ||
| 26 | + "range_values": [ | ||
| 27 | + -10, | ||
| 28 | + 10 | ||
| 29 | + ], | ||
| 30 | + "backward": false, | ||
| 31 | + "align_32B": null | ||
| 32 | + } | ||
| 33 | + ], | ||
| 34 | + "acl_json": "", | ||
| 35 | + "method_inputs": null, | ||
| 36 | + "tensor_input": null | ||
| 37 | + }, | ||
| 38 | + { | ||
| 39 | + "id": 2, | ||
| 40 | + "name": "torch.relu bf16", | ||
| 41 | + "aclnn_name": "aclnnRelu", | ||
| 42 | + "version": "v1.0", | ||
| 43 | + "expected_error_msg": null, | ||
| 44 | + "api": "pytorch", | ||
| 45 | + "api_type": "aclnn_relu", | ||
| 46 | + "aclnn_api_type": "aclnn_function", | ||
| 47 | + "backward": false, | ||
| 48 | + "standard": { | ||
| 49 | + "acc": "cv_fused_double_benchmark", | ||
| 50 | + "perf": "not_key" | ||
| 51 | + }, | ||
| 52 | + "outputs": null, | ||
| 53 | + "inputs": [ | ||
| 54 | + { | ||
| 55 | + "name": "x", | ||
| 56 | + "type": "tensor", | ||
| 57 | + "required": true, | ||
| 58 | + "dtype": "bf16", | ||
| 59 | + "shape": [ | ||
| 60 | + 64, | ||
| 61 | + 256 | ||
| 62 | + ], | ||
| 63 | + "range_values": [ | ||
| 64 | + -10, | ||
| 65 | + 10 | ||
| 66 | + ], | ||
| 67 | + "backward": false, | ||
| 68 | + "align_32B": null | ||
| 69 | + } | ||
| 70 | + ], | ||
| 71 | + "acl_json": "", | ||
| 72 | + "method_inputs": null, | ||
| 73 | + "tensor_input": null | ||
| 74 | + } | ||
| 75 | +] | ||
| @@ -0,0 +1,29 @@ | |||
| 1 | +#!/usr/bin/env python3 | ||
| 2 | +# -*- coding: utf-8 -*- | ||
| 3 | +# ---------------------------------------------------------------------------- | ||
| 4 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 5 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 6 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 7 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 8 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 9 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 10 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 11 | +# | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +import torch | ||
| 15 | +from atk.configs.dataset_config import InputDataset | ||
| 16 | +from atk.tasks.api_execute import register | ||
| 17 | +from atk.tasks.api_execute.base_api import BaseApi | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +def relu_reference(x: torch.Tensor) -> torch.Tensor: | ||
| 21 | + zeros = torch.zeros_like(x) | ||
| 22 | + return torch.where(x > 0, x, zeros) | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | +class TorchRelu(BaseApi): | ||
| 27 | + def __call__(self, input_data: InputDataset, with_output: bool = False): | ||
| 28 | + x = input_data.kwargs["x"] | ||
| 29 | + return relu_reference(x) | ||
| @@ -0,0 +1,16 @@ | |||
| 1 | +# ---------------------------------------------------------------------------- | ||
| 2 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | +# ---------------------------------------------------------------------------- | ||
| 10 | + | ||
| 11 | +file(GLOB CURRENT_SOURCE_DIRS LIST_DIRECTORIES true ${CMAKE_CURRENT_SOURCE_DIR}/*) | ||
| 12 | +foreach(SUB_DIR ${CURRENT_SOURCE_DIRS}) | ||
| 13 | + if(EXISTS "${SUB_DIR}/CMakeLists.txt") | ||
| 14 | + add_subdirectory(${SUB_DIR}) | ||
| 15 | + endif() | ||
| 16 | +endforeach() | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | +# ---------------------------------------------------------------------------- | ||
| 2 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | +# ---------------------------------------------------------------------------- | ||
| 10 | + | ||
| 11 | +file(GLOB CURRENT_DIR RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*) | ||
| 12 | +if(UT_TEST_ALL OR OP_API_UT) | ||
| 13 | + add_modules_ut_sources(HOSTNAME ${OP_API_MODULE_NAME} MODE PRIVATE DIR ${CMAKE_CURRENT_SOURCE_DIR}) | ||
| 14 | +endif() | ||
| @@ -0,0 +1,348 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | +using namespace op; | ||
| 19 | + | ||
| 20 | +class l2_relu_test : public testing::Test { | ||
| 21 | + protected: | ||
| 22 | + static void SetUpTestCase() | ||
| 23 | + { | ||
| 24 | + std::cout << "relu_test SetUp" << std::endl; | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + static void TearDownTestCase() | ||
| 28 | + { | ||
| 29 | + std::cout << "relu_test TearDown" << std::endl; | ||
| 30 | + } | ||
| 31 | +}; | ||
| 32 | + | ||
| 33 | +TEST_F(l2_relu_test, case_001_float) | ||
| 34 | +{ | ||
| 35 | + auto selfDesc = TensorDesc({2, 4}, ACL_FLOAT, ACL_FORMAT_ND).ValueRange(-10, 10); | ||
| 36 | + auto outDesc = TensorDesc({2, 4}, ACL_FLOAT, ACL_FORMAT_ND).Precision(0.0001, 0.0001); | ||
| 37 | + | ||
| 38 | + auto ut = OP_API_UT(aclnnRelu, INPUT(selfDesc), OUTPUT(outDesc)); | ||
| 39 | + | ||
| 40 | + uint64_t workspaceSize = 0; | ||
| 41 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 42 | + EXPECT_EQ(aclRet, ACL_SUCCESS); | ||
| 43 | + | ||
| 44 | + ut.TestPrecision(); | ||
| 45 | +} | ||
| 46 | + | ||
| 47 | +TEST_F(l2_relu_test, case_002_float16) | ||
| 48 | +{ | ||
| 49 | + auto selfDesc = TensorDesc({2, 4}, ACL_FLOAT16, ACL_FORMAT_ND).ValueRange(-10, 10); | ||
| 50 | + auto outDesc = TensorDesc({2, 4}, ACL_FLOAT16, ACL_FORMAT_ND).Precision(0.001, 0.001); | ||
| 51 | + | ||
| 52 | + auto ut = OP_API_UT(aclnnRelu, INPUT(selfDesc), OUTPUT(outDesc)); | ||
| 53 | + | ||
| 54 | + uint64_t workspaceSize = 0; | ||
| 55 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 56 | + EXPECT_EQ(aclRet, ACL_SUCCESS); | ||
| 57 | + | ||
| 58 | + ut.TestPrecision(); | ||
| 59 | +} | ||
| 60 | + | ||
| 61 | +TEST_F(l2_relu_test, case_003_bfloat16) | ||
| 62 | +{ | ||
| 63 | + auto selfDesc = TensorDesc({2, 4}, ACL_BF16, ACL_FORMAT_ND).ValueRange(-10, 10); | ||
| 64 | + auto outDesc = TensorDesc({2, 4}, ACL_BF16, ACL_FORMAT_ND).Precision(0.01, 0.01); | ||
| 65 | + | ||
| 66 | + auto ut = OP_API_UT(aclnnRelu, INPUT(selfDesc), OUTPUT(outDesc)); | ||
| 67 | + | ||
| 68 | + uint64_t workspaceSize = 0; | ||
| 69 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
S | |||
| 70 | + EXPECT_EQ(aclRet, ACL_SUCCESS); | ||
| 71 | + | ||
| 72 | + ut.TestPrecision(); | ||
| 73 | +} | ||
| 74 | + | ||
| 75 | +TEST_F(l2_relu_test, case_004_int8) | ||
| 76 | +{ | ||
| 77 | + auto selfDesc = TensorDesc({1024}, ACL_INT8, ACL_FORMAT_ND).ValueRange(-20, 20); | ||
| 78 | + auto outDesc = TensorDesc({1024}, ACL_INT8, ACL_FORMAT_ND); | ||
| 79 | + | ||
| 80 | + auto ut = OP_API_UT(aclnnRelu, INPUT(selfDesc), OUTPUT(outDesc)); | ||
| 81 | + | ||
| 82 | + uint64_t workspaceSize = 0; | ||
| 83 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 84 | + EXPECT_EQ(aclRet, ACL_SUCCESS); | ||
| 85 | + | ||
| 86 | + ut.TestPrecision(); | ||
| 87 | +} | ||
| 88 | + | ||
| 89 | +TEST_F(l2_relu_test, case_005_int32) | ||
| 90 | +{ | ||
| 91 | + auto selfDesc = TensorDesc({512}, ACL_INT32, ACL_FORMAT_ND).ValueRange(-20, 20); | ||
| 92 | + auto outDesc = TensorDesc({512}, ACL_INT32, ACL_FORMAT_ND); | ||
| 93 | + | ||
| 94 | + auto ut = OP_API_UT(aclnnRelu, INPUT(selfDesc), OUTPUT(outDesc)); | ||
| 95 | + | ||
| 96 | + uint64_t workspaceSize = 0; | ||
| 97 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 98 | + EXPECT_EQ(aclRet, ACL_SUCCESS); | ||
| 99 | + | ||
| 100 | + ut.TestPrecision(); | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | +TEST_F(l2_relu_test, case_006_int64) | ||
| 104 | +{ | ||
| 105 | + auto selfDesc = TensorDesc({257}, ACL_INT64, ACL_FORMAT_ND).ValueRange(-20, 20); | ||
| 106 | + auto outDesc = TensorDesc({257}, ACL_INT64, ACL_FORMAT_ND); | ||
| 107 | + | ||
| 108 | + auto ut = OP_API_UT(aclnnRelu, INPUT(selfDesc), OUTPUT(outDesc)); | ||
| 109 | + | ||
| 110 | + uint64_t workspaceSize = 0; | ||
| 111 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 112 | + EXPECT_EQ(aclRet, ACL_SUCCESS); | ||
| 113 | + | ||
| 114 | + ut.TestPrecision(); | ||
| 115 | +} | ||
| 116 | + | ||
| 117 | +TEST_F(l2_relu_test, case_007_empty_tensor) | ||
| 118 | +{ | ||
| 119 | + auto selfDesc = TensorDesc({2, 0}, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 120 | + auto outDesc = TensorDesc({2, 0}, ACL_FLOAT, ACL_FORMAT_ND).Precision(0.0001, 0.0001); | ||
| 121 | + | ||
| 122 | + auto ut = OP_API_UT(aclnnRelu, INPUT(selfDesc), OUTPUT(outDesc)); | ||
| 123 | + | ||
| 124 | + uint64_t workspaceSize = 0; | ||
| 125 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 126 | + EXPECT_EQ(aclRet, ACL_SUCCESS); | ||
| 127 | + | ||
| 128 | + ut.TestPrecision(); | ||
| 129 | +} | ||
| 130 | + | ||
| 131 | +TEST_F(l2_relu_test, case_008_not_contiguous) | ||
| 132 | +{ | ||
| 133 | + auto selfDesc = TensorDesc({5, 4}, ACL_FLOAT, ACL_FORMAT_ND, {1, 5}, 0, {4, 5}).ValueRange(-10, 10); | ||
| 134 | + auto outDesc = TensorDesc({5, 4}, ACL_FLOAT, ACL_FORMAT_ND, {1, 5}, 0, {4, 5}).Precision(0.0001, 0.0001); | ||
| 135 | + | ||
| 136 | + auto ut = OP_API_UT(aclnnRelu, INPUT(selfDesc), OUTPUT(outDesc)); | ||
| 137 | + | ||
| 138 | + uint64_t workspaceSize = 0; | ||
| 139 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 140 | + EXPECT_EQ(aclRet, ACL_SUCCESS); | ||
| 141 | + | ||
| 142 | + ut.TestPrecision(); | ||
| 143 | +} | ||
| 144 | + | ||
| 145 | +TEST_F(l2_relu_test, case_009_invalid_input_dtype) | ||
| 146 | +{ | ||
| 147 | + auto selfDesc = TensorDesc({2, 3}, ACL_BOOL, ACL_FORMAT_ND); | ||
| 148 | + auto outDesc = TensorDesc({2, 3}, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 149 | + | ||
| 150 | + auto ut = OP_API_UT(aclnnRelu, INPUT(selfDesc), OUTPUT(outDesc)); | ||
| 151 | + | ||
| 152 | + uint64_t workspaceSize = 0; | ||
| 153 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 154 | + EXPECT_EQ(aclRet, ACLNN_ERR_PARAM_INVALID); | ||
| 155 | +} | ||
| 156 | + | ||
| 157 | +TEST_F(l2_relu_test, case_010_invalid_output_dtype) | ||
| 158 | +{ | ||
| 159 | + auto selfDesc = TensorDesc({2, 3}, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 160 | + auto outDesc = TensorDesc({2, 3}, ACL_BOOL, ACL_FORMAT_ND); | ||
| 161 | + | ||
| 162 | + auto ut = OP_API_UT(aclnnRelu, INPUT(selfDesc), OUTPUT(outDesc)); | ||
| 163 | + | ||
| 164 | + uint64_t workspaceSize = 0; | ||
| 165 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 166 | + EXPECT_EQ(aclRet, ACLNN_ERR_PARAM_INVALID); | ||
| 167 | +} | ||
| 168 | + | ||
| 169 | +TEST_F(l2_relu_test, case_011_dtype_mismatch) | ||
| 170 | +{ | ||
| 171 | + auto selfDesc = TensorDesc({2, 3}, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 172 | + auto outDesc = TensorDesc({2, 3}, ACL_FLOAT16, ACL_FORMAT_ND); | ||
| 173 | + | ||
| 174 | + auto ut = OP_API_UT(aclnnRelu, INPUT(selfDesc), OUTPUT(outDesc)); | ||
| 175 | + | ||
| 176 | + uint64_t workspaceSize = 0; | ||
| 177 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 178 | + EXPECT_EQ(aclRet, ACLNN_ERR_PARAM_INVALID); | ||
| 179 | +} | ||
| 180 | + | ||
| 181 | +TEST_F(l2_relu_test, case_012_nullptr) | ||
| 182 | +{ | ||
| 183 | + auto outDesc = TensorDesc({2, 4}, ACL_INT8, ACL_FORMAT_ND); | ||
| 184 | + | ||
| 185 | + auto ut = OP_API_UT(aclnnRelu, INPUT(nullptr), OUTPUT(outDesc)); | ||
| 186 | + | ||
| 187 | + uint64_t workspaceSize = 0; | ||
| 188 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 189 | + EXPECT_EQ(aclRet, ACLNN_ERR_PARAM_NULLPTR); | ||
| 190 | +} | ||
| 191 | + | ||
| 192 | +TEST_F(l2_relu_test, case_013_shape_mismatch) | ||
| 193 | +{ | ||
| 194 | + auto selfDesc = TensorDesc({1, 2, 3, 4}, ACL_INT8, ACL_FORMAT_ND); | ||
| 195 | + auto outDesc = TensorDesc({1, 2, 3, 3}, ACL_INT8, ACL_FORMAT_ND); | ||
| 196 | + | ||
| 197 | + auto ut = OP_API_UT(aclnnRelu, INPUT(selfDesc), OUTPUT(outDesc)); | ||
| 198 | + | ||
| 199 | + uint64_t workspaceSize = 0; | ||
| 200 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 201 | + EXPECT_EQ(aclRet, ACLNN_ERR_PARAM_INVALID); | ||
| 202 | +} | ||
| 203 | + | ||
| 204 | +TEST_F(l2_relu_test, case_014_max_dim) | ||
| 205 | +{ | ||
| 206 | + auto selfDesc = TensorDesc({1, 2, 3, 4, 5, 6, 7, 8, 9}, ACL_INT8, ACL_FORMAT_ND); | ||
| 207 | + auto outDesc = TensorDesc({1, 2, 3, 4, 5, 6, 7, 8, 9}, ACL_INT8, ACL_FORMAT_ND); | ||
| 208 | + | ||
| 209 | + auto ut = OP_API_UT(aclnnRelu, INPUT(selfDesc), OUTPUT(outDesc)); | ||
| 210 | + | ||
| 211 | + uint64_t workspaceSize = 0; | ||
| 212 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 213 | + EXPECT_EQ(aclRet, ACLNN_ERR_PARAM_INVALID); | ||
| 214 | +} | ||
| 215 | + | ||
| 216 | +TEST_F(l2_relu_test, case_015_inplace_float) | ||
| 217 | +{ | ||
| 218 | + auto selfDesc = TensorDesc({2, 4}, ACL_FLOAT, ACL_FORMAT_ND).ValueRange(-10, 10); | ||
| 219 | + | ||
| 220 | + auto ut = OP_API_UT(aclnnInplaceRelu, INPUT(selfDesc), OUTPUT()); | ||
| 221 | + | ||
| 222 | + uint64_t workspaceSize = 0; | ||
| 223 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 224 | + EXPECT_EQ(aclRet, ACL_SUCCESS); | ||
| 225 | + | ||
| 226 | + ut.TestPrecision(); | ||
| 227 | +} | ||
| 228 | + | ||
| 229 | +TEST_F(l2_relu_test, case_016_inplace_int64) | ||
| 230 | +{ | ||
| 231 | + auto selfDesc = TensorDesc({257}, ACL_INT64, ACL_FORMAT_ND).ValueRange(-20, 20); | ||
| 232 | + | ||
| 233 | + auto ut = OP_API_UT(aclnnInplaceRelu, INPUT(selfDesc), OUTPUT()); | ||
| 234 | + | ||
| 235 | + uint64_t workspaceSize = 0; | ||
| 236 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 237 | + EXPECT_EQ(aclRet, ACL_SUCCESS); | ||
| 238 | + | ||
| 239 | + ut.TestPrecision(); | ||
| 240 | +} | ||
| 241 | + | ||
| 242 | +TEST_F(l2_relu_test, case_017_inplace_invalid_dtype) | ||
| 243 | +{ | ||
| 244 | + auto selfDesc = TensorDesc({2, 3}, ACL_BOOL, ACL_FORMAT_ND); | ||
| 245 | + | ||
| 246 | + auto ut = OP_API_UT(aclnnInplaceRelu, INPUT(selfDesc), OUTPUT()); | ||
| 247 | + | ||
| 248 | + uint64_t workspaceSize = 0; | ||
| 249 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 250 | + EXPECT_EQ(aclRet, ACLNN_ERR_PARAM_INVALID); | ||
| 251 | +} | ||
| 252 | + | ||
| 253 | +TEST_F(l2_relu_test, case_018_inplace_nullptr) | ||
| 254 | +{ | ||
| 255 | + uint64_t workspaceSize = 0; | ||
| 256 | + auto ut = OP_API_UT(aclnnInplaceRelu, INPUT(static_cast<aclTensor *>(nullptr)), OUTPUT()); | ||
| 257 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 258 | + EXPECT_EQ(aclRet, ACLNN_ERR_PARAM_NULLPTR); | ||
| 259 | +} | ||
| 260 | + | ||
| 261 | +TEST_F(l2_relu_test, case_019_inplace_max_dim) | ||
| 262 | +{ | ||
| 263 | + auto selfDesc = TensorDesc({1, 2, 3, 4, 5, 6, 7, 8, 9}, ACL_INT8, ACL_FORMAT_ND); | ||
| 264 | + | ||
| 265 | + auto ut = OP_API_UT(aclnnInplaceRelu, INPUT(selfDesc), OUTPUT()); | ||
| 266 | + | ||
| 267 | + uint64_t workspaceSize = 0; | ||
| 268 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 269 | + EXPECT_EQ(aclRet, ACLNN_ERR_PARAM_INVALID); | ||
| 270 | +} | ||
| 271 | + | ||
| 272 | +TEST_F(l2_relu_test, case_020_inplace_float16) | ||
| 273 | +{ | ||
| 274 | + auto selfDesc = TensorDesc({2, 4}, ACL_FLOAT16, ACL_FORMAT_ND).ValueRange(-10, 10); | ||
| 275 | + | ||
| 276 | + auto ut = OP_API_UT(aclnnInplaceRelu, INPUT(selfDesc), OUTPUT()); | ||
| 277 | + | ||
| 278 | + uint64_t workspaceSize = 0; | ||
| 279 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 280 | + EXPECT_EQ(aclRet, ACL_SUCCESS); | ||
| 281 | + | ||
| 282 | + ut.TestPrecision(); | ||
| 283 | +} | ||
| 284 | + | ||
| 285 | +TEST_F(l2_relu_test, case_021_inplace_bfloat16) | ||
| 286 | +{ | ||
| 287 | + auto selfDesc = TensorDesc({2, 4}, ACL_BF16, ACL_FORMAT_ND).ValueRange(-10, 10); | ||
| 288 | + | ||
| 289 | + auto ut = OP_API_UT(aclnnInplaceRelu, INPUT(selfDesc), OUTPUT()); | ||
| 290 | + | ||
| 291 | + uint64_t workspaceSize = 0; | ||
| 292 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 293 | + EXPECT_EQ(aclRet, ACL_SUCCESS); | ||
| 294 | + | ||
| 295 | + ut.TestPrecision(); | ||
| 296 | +} | ||
| 297 | + | ||
| 298 | +TEST_F(l2_relu_test, case_022_inplace_not_contiguous) | ||
| 299 | +{ | ||
| 300 | + auto selfDesc = TensorDesc({5, 4}, ACL_FLOAT, ACL_FORMAT_ND, {1, 5}, 0, {4, 5}).ValueRange(-10, 10); | ||
| 301 | + | ||
| 302 | + auto ut = OP_API_UT(aclnnInplaceRelu, INPUT(selfDesc), OUTPUT()); | ||
| 303 | + | ||
| 304 | + uint64_t workspaceSize = 0; | ||
| 305 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 306 | + EXPECT_EQ(aclRet, ACL_SUCCESS); | ||
| 307 | + | ||
| 308 | + ut.TestPrecision(); | ||
| 309 | +} | ||
| 310 | + | ||
| 311 | +TEST_F(l2_relu_test, case_023_inplace_empty_tensor) | ||
| 312 | +{ | ||
| 313 | + auto selfDesc = TensorDesc({2, 0}, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 314 | + | ||
| 315 | + auto ut = OP_API_UT(aclnnInplaceRelu, INPUT(selfDesc), OUTPUT()); | ||
| 316 | + | ||
| 317 | + uint64_t workspaceSize = 0; | ||
| 318 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 319 | + EXPECT_EQ(aclRet, ACL_SUCCESS); | ||
| 320 | + | ||
| 321 | + ut.TestPrecision(); | ||
| 322 | +} | ||
| 323 | + | ||
| 324 | +TEST_F(l2_relu_test, case_024_inplace_int8) | ||
| 325 | +{ | ||
| 326 | + auto selfDesc = TensorDesc({1024}, ACL_INT8, ACL_FORMAT_ND).ValueRange(-20, 20); | ||
| 327 | + | ||
| 328 | + auto ut = OP_API_UT(aclnnInplaceRelu, INPUT(selfDesc), OUTPUT()); | ||
| 329 | + | ||
| 330 | + uint64_t workspaceSize = 0; | ||
| 331 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 332 | + EXPECT_EQ(aclRet, ACL_SUCCESS); | ||
| 333 | + | ||
| 334 | + ut.TestPrecision(); | ||
| 335 | +} | ||
| 336 | + | ||
| 337 | +TEST_F(l2_relu_test, case_025_inplace_int32) | ||
| 338 | +{ | ||
| 339 | + auto selfDesc = TensorDesc({512}, ACL_INT32, ACL_FORMAT_ND).ValueRange(-20, 20); | ||
| 340 | + | ||
| 341 | + auto ut = OP_API_UT(aclnnInplaceRelu, INPUT(selfDesc), OUTPUT()); | ||
| 342 | + | ||
| 343 | + uint64_t workspaceSize = 0; | ||
| 344 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 345 | + EXPECT_EQ(aclRet, ACL_SUCCESS); | ||
| 346 | + | ||
| 347 | + ut.TestPrecision(); | ||
| 348 | +} | ||


PR 描述里把这个用例写成 BFLOAT16 精度验证,但这里只调用了 TestGetWorkspaceSize,没有调用 TestPrecision 做数值比对。如果确实要覆盖 BF16 精度,建议像 fp16、fp32 那样补上 TestPrecision;如果暂时不打算做精度校验,建议同步改 PR 描述或测试清单里的措辞,避免对外宣称的测试内容和代码不一致。