已合并
[Requirement|需求建议]: 【社区任务】Relu算子新增INT16数据类型支持 #6387
cxj01创建于 6月23日
[Requirement|需求建议]: 【社区任务】Relu算子新增INT16数据类型支持 #6387
已合并
cxj01创建于 6月23日
共 47 个文件变更+5664-1174
Nice tryNice try8月17日

此空文件需要删除

likedislike
The file is empty
Nice tryNice try8月17日

看评论的意思是无需保留activattion下的relu的新增实现,提交涉及文件应该是只在experimental/activation/relu_v2

likedislike
@@ -1,20 +1,15 @@
1# ----------------------------------------------------------------------------1# ----------------------------------------------------------------------------
2-# This program is free software, you can redistribute it and/or modify.2+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
3-# Copyright (c) 2025 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-# This file is a part of the CANN Open Software.4+# CANN Open Software License Agreement Version 2.0 (the "License").
5-# Licensed under 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.5# 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, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.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.8# See LICENSE in the root of the software repository for the full text of the License.
9# ----------------------------------------------------------------------------9# ----------------------------------------------------------------------------
10 10 
11-file(GLOB CURRENT_DIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*)11+# 设置算子定义时支持的芯片类型
12-if(NOT ENABLE_TEST)12+set(SUPPORT_COMPUTE_UNIT "ascend950" "mc62cm12a" "ascend910b" "ascend910_93")
13- list(REMOVE_ITEM CURRENT_DIRS tests)13+# 设置每种芯片类型对应的tiling文件目录,即采用op_host目录下哪个文件夹下的tiling文件编译
14-endif()14+set(SUPPORT_TILING_DIR "arch35" "arch35" "ascend910b" "ascend910b")
15-foreach(SUB_DIR ${CURRENT_DIRS})15+add_modules_sources(HOSTNAME ${OPHOST_NAME} MODE PRIVATE DIR ${CMAKE_CURRENT_SOURCE_DIR} OPTYPE relu_v2 ACLNNTYPE aclnn_exclude COMPUTE_UNIT ${SUPPORT_COMPUTE_UNIT} TILING_DIR ${SUPPORT_TILING_DIR} DISABLE_IN_OPP TRUE)
16- if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUB_DIR}/CMakeLists.txt")
17- add_subdirectory(${SUB_DIR})
18- endif()
19-endforeach()
20- 
@@ -0,0 +1,70 @@
1+# Relu_V2
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+ 
12+## 功能说明
13+ 
14+- 算子功能:激活函数,返回与输入tensor shape相同的tensor,tensor中value大于等于0时,取该value,小于0,取0。
15+- 计算公式:
16+ 
17+$$
18+relu(self) = \begin{cases} self, & self\gt 0 \\ 0, & self\le 0 \end{cases}
19+$$
20+ 
21+## 参数说明
22+ 
23+ <table style="undefined;table-layout: fixed; width: 1305px"><colgroup>
24+ <col style="width: 171px">
25+ <col style="width: 115px">
26+ <col style="width: 247px">
27+ <col style="width: 108px">
28+ <col style="width: 177px">
29+ <col style="width: 104px">
30+ <col style="width: 238px">
31+ <col style="width: 145px">
32+ </colgroup>
33+ <thead>
34+ <tr>
35+ <th>参数名</th>
36+ <th>输入/输出</th>
37+ <th>描述</th>
38+ <th>数据类型</th>
39+ <th>数据格式</th>
40+ </tr></thead>
41+ <tbody>
42+ <tr>
43+ <td>x</td>
44+ <td>输入</td>
45+ <td>待进行Relu计算的入参。</td>
46+ <td>FLOAT、FLOAT16、INT8、INT32、INT64、BFLOAT16</td>
47+ <td>ND</td>
48+ </tr>
49+ <tr>
50+ <td>y</td>
51+ <td>输出</td>
52+ <td>进行Relu计算后的输出</td>
53+ <td>FLOAT、FLOAT16、INT8、INT32、INT64、BFLOAT16</td>
54+ <td>ND</td>
55+ </tr>
56+ </tbody>
57+ </table>
58+ 
59+## 约束说明
60+ 
61+- 无
62+ 
63+## 调用说明
64+ 
65+| 调用方式 | 调用样例 | 说明 |
66+|--------------|------------------------------------------------------------------------|--------------------------------------------------------------|
67+| aclnn调用 | [test_aclnn_relu](examples/test_aclnn_relu.cpp) | 通过[aclnnRelu&aclnnInplaceRelu](docs/aclnnRelu&aclnnInplaceRelu.md)接口方式调用Relu算子。 |
68+| aclnn调用 | [test_aclnn_inplace_relu](examples/test_aclnn_inplace_relu.cpp) | 通过[aclnnRelu&aclnnInplaceRelu](docs/aclnnRelu&aclnnInplaceRelu.md)接口方式调用Relu算子。 |
69+ 
70+| 图模式 | - | 通过[算子IR](op_graph/relu_v2_proto.h)构图方式调用Relu算子。 |
@@ -0,0 +1,602 @@
1+# aclnnAddRelu&aclnnInplaceAddRelu
2+ 
3+[📄 查看源码](https://gitcode.com/cann/ops-nn/tree/master/activation/relu)
4+ 
5+## 产品支持情况
6+ 
7+|产品 | 是否支持 |
8+|:-------------------------|:----------:|
9+| <term>Ascend 950PR/Ascend 950DT</term> | √ |
10+| <term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term> | √ |
11+| <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term> | √ |
12+| <term>Atlas 200I/500 A2 推理产品</term> | × |
13+| <term>Atlas 推理系列产品</term> | × |
14+| <term>Atlas 训练系列产品</term> | √ |
15+ 
16+## 功能说明
17+ 
18+- 接口功能:完成加法计算后得到结果再进行激活。
19+- 计算公式:
20+ 
21+ $$
22+ out_i = self_i+alpha \times other_i
23+ $$
24+ 
25+ $$
26+ relu(self) = \begin{cases} self, & self\gt 0 \\ 0, & self\le 0 \end{cases}
27+ $$
28+ 
29+## 函数原型
30+ 
31+- aclnnAddRelu和aclnnInplaceAddRelu实现相同的功能,使用区别如下,请根据自身实际场景选择合适的算子。
32+ 
33+ - aclnnAddRelu:需新建一个输出张量对象存储计算结果。
34+ - aclnnInplaceAddRelu:无需新建输出张量对象,直接在输入张量的内存中存储计算结果。
35+ 
36+- 每个算子分为[两段式接口](../../../../docs/zh/context/two_phase_api.md),必须先调用“aclnnAddReluGetWorkspaceSize”或者“aclnnInplaceAddReluGetWorkspaceSize”接口获取计算所需workspace大小以及包含了算子计算流程的执行器,再调用“aclnnAddRelu”或者“aclnnInplaceAddRelu”接口执行计算。
37+ 
38+ ```Cpp
39+ aclnnStatus aclnnAddReluGetWorkspaceSize(
40+ const aclTensor *self,
41+ const aclTensor *other,
42+ aclScalar *alpha,
43+ aclTensor *out,
44+ uint64_t *workspaceSize,
45+ aclOpExecutor **executor)
46+ ```
47+ 
48+ ```Cpp
49+ aclnnStatus aclnnAddRelu(
50+ void *workspace,
51+ uint64_t workspaceSize,
52+ aclOpExecutor *executor,
53+ aclrtStream stream)
54+ ```
55+ 
56+ ```Cpp
57+ aclnnStatus aclnnInplaceAddReluGetWorkspaceSize(
58+ aclTensor *selfRef,
59+ const aclTensor *other,
60+ aclScalar *alpha,
61+ uint64_t *workspaceSize,
62+ aclOpExecutor **executor)
63+ ```
64+ 
65+ ```Cpp
66+ aclnnStatus aclnnInplaceAddRelu(
67+ void *workspace,
68+ uint64_t workspaceSize,
69+ aclOpExecutor *executor,
70+ aclrtStream stream)
71+ ```
72+ 
73+## aclnnAddReluGetWorkspaceSize
74+ 
75+- **参数说明:**
76+ 
77+ <table style="undefined;table-layout: fixed; width: 1478px"><colgroup>
78+ <col style="width: 249px">
79+ <col style="width: 121px">
80+ <col style="width: 164px">
81+ <col style="width: 253px">
82+ <col style="width: 262px">
83+ <col style="width: 148px">
84+ <col style="width: 135px">
85+ <col style="width: 146px">
86+ </colgroup>
87+ <thead>
88+ <tr>
89+ <th>参数名</th>
90+ <th>输入/输出</th>
91+ <th>描述</th>
92+ <th>使用说明</th>
93+ <th>数据类型</th>
94+ <th>数据格式</th>
95+ <th>维度(shape)</th>
96+ <th>非连续Tensor</th>
97+ </tr></thead>
98+ <tbody>
99+ <tr>
100+ <td>self(aclTensor*)</td>
@@ -0,0 +1,611 @@
1+# aclnnRelu&aclnnInplaceRelu
2+ 
3+[📄 查看源码](https://gitcode.com/cann/ops-nn/tree/master/activation/relu)
4+ 
5+## 产品支持情况
6+ 
7+|产品 | 是否支持 |
8+|:-------------------------|:----------:|
9+| <term>Ascend 950PR/Ascend 950DT</term> | √ |
10+| <term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term> | √ |
11+| <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term> | √ |
12+| <term>Atlas 200I/500 A2 推理产品</term> | × |
13+| <term>Atlas 推理系列产品</term> | × |
14+| <term>Atlas 训练系列产品</term> | √ |
15+ 
16+## 功能说明
17+ 
18+- 接口功能:激活函数,返回与输入tensor shape相同的tensor,tensor中value大于等于0时,取该value,小于0,取0。
19+- 计算公式:
20+ 
21+$$
22+relu(self) = \begin{cases} self, & self\gt 0 \\ 0, & self\le 0 \end{cases}
23+$$
24+ 
25+## 函数原型
26+ 
27+- aclnnRelu和aclnnInplaceRelu实现相同的功能,使用区别如下,请根据自身实际场景选择合适的算子。
28+ - aclnnRelu:需新建一个输出张量对象存储计算结果。
29+ - aclnnInplaceRelu:无需新建输出张量对象,直接在输入张量的内存中存储计算结果。
30+ 
31+- 每个算子分为[两段式接口](../../../../docs/zh/context/two_phase_api.md),必须先调用“aclnnReluGetWorkspaceSize”或者“aclnnInplaceReluGetWorkspaceSize”接口获取入参并根据计算流程计算所需workspace大小,再调用“aclnnRelu”或者“aclnnInplaceRelu”接口执行计算。
32+ 
33+```Cpp
34+aclnnStatus aclnnReluGetWorkspaceSize(
35+ const aclTensor* self,
36+ const aclTensor* out,
37+ uint64_t* workspaceSize,
38+ aclOpExecutor **executor)
39+```
40+ 
41+```Cpp
42+aclnnStatus aclnnRelu(
43+ void* workspace,
44+ uint64_t workspaceSize,
45+ aclOpExecutor* executor,
46+ const aclrtStream stream)
47+```
48+ 
49+```Cpp
50+aclnnStatus aclnnInplaceReluGetWorkspaceSize(
51+ aclTensor* selfRef,
52+ uint64_t *workspaceSize,
53+ aclOpExecutor **executor)
54+```
55+ 
56+```Cpp
57+aclnnStatus aclnnInplaceRelu(
58+ void *workspace,
59+ uint64_t workspaceSize,
60+ aclOpExecutor *executor,
61+ const aclrtStream stream)
62+```
63+ 
64+## aclnnReluGetWorkspaceSize
65+ 
66+- **参数说明:**
67+ 
68+ <table style="undefined;table-layout: fixed; width: 1370px"><colgroup>
69+ <col style="width: 271px">
70+ <col style="width: 115px">
71+ <col style="width: 200px">
72+ <col style="width: 220px">
73+ <col style="width: 177px">
74+ <col style="width: 104px">
75+ <col style="width: 138px">
76+ <col style="width: 145px">
77+ </colgroup>
78+ <thead>
79+ <tr>
80+ <th>参数名</th>
81+ <th>输入/输出</th>
82+ <th>描述</th>
83+ <th>使用说明</th>
84+ <th>数据类型</th>
85+ <th>数据格式</th>
86+ <th>维度(shape)</th>
87+ <th>非连续Tensor</th>
88+ </tr></thead>
89+ <tbody>
90+ <tr>
91+ <td>self(aclTensor*)</td>
92+ <td>输入</td>
93+ <td>待进行Relu计算的入参,公式中的输入self。</td>
94+ <td><ul><li>支持空Tensor。</li><li>shape需要与out一致。</li></ul></td>
95+ <td>FLOAT、FLOAT16、INT8、INT32、INT64、BFLOAT16</td>
96+ <td>ND</td>
97+ <td>0-8</td>
98+ <td>√</td>
99+ </tr>
100+ <tr>
Rexperimental/activation/relu_v2/examples/test_aclnn_relu_v2.cpp→experimental/activation/relu_v2/examples/test_aclnn_add_relu.cpp+72-88
@@ -1,30 +1,17 @@
1/**1/**
2- * This file is part of the OpenBOAT project at Harbin Institute of Technology (HIT)2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3- * and is contributed to the CANN Open Software.3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4- *4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5- * Copyright (c) 2025 AISS Group, Harbin Institute of Technology (HIT).5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6- * All Rights Reserved.6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7- *
8- * Authors (accounts):
9- 
10- * - Tu Yuanhang <@TuYHAAAAAA>
11- * - Su Tonghua <@sutonghua>
12- *
13- * This program is free software: you can redistribute it and/or modify it.
14- * Licensed under the CANN Open Software License Agreement Version 2.0 (the "License").
15- * You may not use this file except in compliance with the License.
16- * See the LICENSE file at the root of the repository for the full text of the License.
17- *
18- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED,
19 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.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.
20 */9 */
21#include <iostream>10#include <iostream>
22-#include <memory>
23#include <vector>11#include <vector>
24#include "acl/acl.h"12#include "acl/acl.h"
25-#include "aclnn_relu_v2.h"13+#include "aclnnop/aclnn_add_relu.h"
26-// 修改测试数据类型14+ 
27-using DataType = int16_t;
28#define CHECK_RET(cond, return_expr) \15#define CHECK_RET(cond, return_expr) \
29 do { \16 do { \
30 if (!(cond)) { \17 if (!(cond)) { \
@@ -46,22 +33,9 @@ int64_t GetShapeSize(const std::vector<int64_t>& shape)
46 return shapeSize;33 return shapeSize;
47}34}
48 35 
49-void PrintOutResult(std::vector<int64_t>& shape, void** deviceAddr)
50-{
51- auto size = GetShapeSize(shape);
52- std::vector<DataType> resultData(size, 0);
53- auto ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), *deviceAddr,
54- size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST);
55- CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return );
56- for (int64_t i = 0; i < size; i++) {
57- // LOG_PRINT("mean result[%ld] is: %f\n", i, resultData[i]); // float
58- LOG_PRINT("mean result[%ld] is: %d\n", i, resultData[i]); // int
59- }
60-}
61- 
62int Init(int32_t deviceId, aclrtStream* stream)36int Init(int32_t deviceId, aclrtStream* stream)
63{37{
64- // 固定写法,初始化38+ // 固定写法,资源初始化
65 auto ret = aclInit(nullptr);39 auto ret = aclInit(nullptr);
66 CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);40 CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
67 ret = aclrtSetDevice(deviceId);41 ret = aclrtSetDevice(deviceId);
@@ -76,10 +50,10 @@ int CreateAclTensor(const std::vector<T>& hostData, const std::vector<int64_t>&
76 aclDataType dataType, aclTensor** tensor)50 aclDataType dataType, aclTensor** tensor)
77{51{
78 auto size = GetShapeSize(shape) * sizeof(T);52 auto size = GetShapeSize(shape) * sizeof(T);
79- // 2. 申请device侧内存53+ // 调用aclrtMalloc申请device侧内存
80 auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);54 auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
81 CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);55 CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
82- // 3. 调用aclrtMemcpy将host侧数据拷贝到device侧内存上56+ // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上
83 ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);57 ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
84 CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);58 CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);
85 59 
@@ -95,81 +69,91 @@ int CreateAclTensor(const std::vector<T>& hostData, const std::vector<int64_t>&
95 return 0;69 return 0;
96}70}
97 71 
98-int aclnnReluV2Test(int32_t deviceId, aclrtStream& stream)72+int main()
99{73{
100- // 1. 调用acl进行device/stream初始化74+ // 1. (固定写法)device/stream初始化,参考acl API手册
75+ // 根据自己的实际device填写deviceId
76+ int32_t deviceId = 0;
77+ aclrtStream stream;
101 auto ret = Init(deviceId, &stream);78 auto ret = Init(deviceId, &stream);
102 CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);79 CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
103 80 
104 // 2. 构造输入与输出,需要根据API的接口自定义构造81 // 2. 构造输入与输出,需要根据API的接口自定义构造
105- aclTensor* selfX = nullptr;82+ std::vector<int64_t> selfShape = {4, 2};
106- void* selfXDeviceAddr = nullptr;83+ std::vector<int64_t> otherShape = {4, 2};
107- std::vector<int64_t> selfXShape = {123, 7, 7};84+ std::vector<int64_t> outShape = {4, 2};
108- std::vector<DataType> selfXHostData(6027, 7);85+ void* selfDeviceAddr = nullptr;
109- ret = CreateAclTensor(selfXHostData, selfXShape, &selfXDeviceAddr, aclDataType::ACL_INT16, &selfX);86+ void* otherDeviceAddr = nullptr;
110- // 当前样例算子未进行shape、dtype全泛化,其他输入场景可能存在不支持情况
111- // 通过智能指针自动释放aclTensor和device资源
112- std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor*)> selfXPtr(selfX, aclDestroyTensor);
113- std::unique_ptr<void, aclError (*)(void*)> selfXDeviceAddrPtr(selfXDeviceAddr, aclrtFree);
114- CHECK_RET(ret == ACL_SUCCESS, return ret);
115- 
116- aclTensor* out = nullptr;
117 void* outDeviceAddr = nullptr;87 void* outDeviceAddr = nullptr;
118- std::vector<int64_t> outShape = {123, 7, 7};88+ aclTensor* self = nullptr;
119- std::vector<DataType> outHostData(6027, 0);89+ aclTensor* other = nullptr;
120- ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_INT16, &out);90+ aclScalar* alpha = nullptr;
121- std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor*)> outPtr(out, aclDestroyTensor);91+ aclTensor* out = nullptr;
122- std::unique_ptr<void, aclError (*)(void*)> outDeviceAddrPtr(outDeviceAddr, aclrtFree);92+ std::vector<float> selfHostData = {0, 1, 2, 3, 4, 5, 6, 7};
93+ std::vector<float> otherHostData = {1, 1, 1, 2, 2, 2, 3, 3};
94+ std::vector<float> outHostData(8, 0);
95+ float alphaValue = 1.2f;
96+ // 创建self aclTensor
97+ ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_FLOAT, &self);
98+ CHECK_RET(ret == ACL_SUCCESS, return ret);
99+ // 创建other aclTensor
100+ ret = CreateAclTensor(otherHostData, otherShape, &otherDeviceAddr, aclDataType::ACL_FLOAT, &other);
101+ CHECK_RET(ret == ACL_SUCCESS, return ret);
102+ // 创建alpha aclScalar
103+ alpha = aclCreateScalar(&alphaValue, aclDataType::ACL_FLOAT);
104+ CHECK_RET(alpha != nullptr, return ret);
105+ // 创建out aclTensor
106+ ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT, &out);
123 CHECK_RET(ret == ACL_SUCCESS, return ret);107 CHECK_RET(ret == ACL_SUCCESS, return ret);
124 108 
125- // 3. 调用CANN算子库API,需要修改为具体的Api名称
126 uint64_t workspaceSize = 0;109 uint64_t workspaceSize = 0;
127 aclOpExecutor* executor;110 aclOpExecutor* executor;
128 111 
129- LOG_PRINT("Before GetWorkspaceSize: selfX=%p, out=%p\n", (void*)selfX, (void*)out);112+ // aclnnAddRelu接口调用示例
130- LOG_PRINT("Before GetWorkspaceSize: selfXDeviceAddr=%p, selfYDeviceAddr=%p, outDeviceAddr=%p\n", selfXDeviceAddr,113+ // 3. 调用CANN算子库API
131- outDeviceAddr);114+ // 调用aclnnAddRelu第一段接口
132- // 4. 调用aclnnReluV2第一段接口115+ ret = aclnnAddReluGetWorkspaceSize(self, other, alpha, out, &workspaceSize, &executor);
133- ret = aclnnReluV2GetWorkspaceSize(selfX, out, &workspaceSize, &executor);116+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAddReluGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
134- LOG_PRINT("aclnnReluV2GetWorkspaceSize returned %d, workspaceSize=%llu, executor=%p\n", ret,
135- (unsigned long long)workspaceSize, (void*)executor);
136- CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnReluV2ExampleGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
137- 
138 // 根据第一段接口计算出的workspaceSize申请device内存117 // 根据第一段接口计算出的workspaceSize申请device内存
139 void* workspaceAddr = nullptr;118 void* workspaceAddr = nullptr;
140- std::unique_ptr<void, aclError (*)(void*)> workspaceAddrPtr(nullptr, aclrtFree);119+ if (workspaceSize > 0) {
141- if (workspaceSize > static_cast<uint64_t>(0)) {
142 ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);120 ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
143 CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);121 CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
144- workspaceAddrPtr.reset(workspaceAddr);
145 }122 }
123+ // 调用aclnnAddRelu第二段接口
124+ ret = aclnnAddRelu(workspaceAddr, workspaceSize, executor, stream);
125+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAddRelu failed. ERROR: %d\n", ret); return ret);
146 126 
147- // 5. 调用aclnnAddExample第二段接口127+ // 4. (固定写法)同步等待任务执行结束
148- ret = aclnnReluV2(workspaceAddr, workspaceSize, executor, stream);
149- CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAddExample failed. ERROR: %d\n", ret); return ret);
150- 
151- // 6. (固定写法)同步等待任务执行结束
152 ret = aclrtSynchronizeStream(stream);128 ret = aclrtSynchronizeStream(stream);
153 CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);129 CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
154 130 
155- // 7. 获取输出的值,将device侧内存上的结果拷贝至host侧,需要根据具体API的接口定义修改131+ // 5. 获取输出的值,将device侧内存上的结果拷贝至host侧,需要根据具体API的接口定义修改
156- std::vector<int64_t> outShape1 = {8};132+ auto size = GetShapeSize(outShape);
157- PrintOutResult(outShape1, &outDeviceAddr);133+ std::vector<float> resultData(size, 0);
158- return ACL_SUCCESS;134+ ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr,
159-}135+ size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST);
136+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);
137+ for (int64_t i = 0; i < size; i++) {
138+ LOG_PRINT("result[%ld] is: %f\n", i, resultData[i]);
139+ }
160 140 
161-int main()141+ // 6. 释放aclTensor和aclScalar,需要根据具体API的接口定义修改
162-{142+ aclDestroyTensor(self);
163- int32_t deviceId = 0;143+ aclDestroyTensor(other);
164- aclrtStream stream;144+ aclDestroyScalar(alpha);
145+ aclDestroyTensor(out);
165 146 
166- auto ret = aclnnReluV2Test(deviceId, stream);147+ // 7. 释放Device资源,需要根据具体API的接口定义修改
167- // 释放device资源以及acl去初始化148+ aclrtFree(selfDeviceAddr);
149+ aclrtFree(otherDeviceAddr);
150+ aclrtFree(outDeviceAddr);
151+ if (workspaceSize > 0) {
152+ aclrtFree(workspaceAddr);
153+ }
168 aclrtDestroyStream(stream);154 aclrtDestroyStream(stream);
169 aclrtResetDevice(deviceId);155 aclrtResetDevice(deviceId);
170 aclFinalize();156 aclFinalize();
171 157 
172- CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnReluV2Test failed. ERROR: %d\n", ret); return ret);
173- 
174 return 0;158 return 0;
175-}159+}
@@ -0,0 +1,161 @@
1+/**
2+ * Copyright (c) 2025 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+#include <iostream>
11+#include <vector>
12+#include "acl/acl.h"
13+#include "aclnnop/aclnn_add_relu.h"
14+ 
15+#define CHECK_RET(cond, return_expr) \
16+ do { \
17+ if (!(cond)) { \
18+ return_expr; \
19+ } \
20+ } while (0)
21+ 
22+#define LOG_PRINT(message, ...) \
23+ do { \
24+ printf(message, ##__VA_ARGS__); \
25+ } while (0)
26+ 
27+int64_t GetShapeSize(const std::vector<int64_t>& shape)
28+{
29+ int64_t shapeSize = 1;
30+ for (auto i : shape) {
31+ shapeSize *= i;
32+ }
33+ return shapeSize;
34+}
35+ 
36+int Init(int32_t deviceId, aclrtStream* stream)
37+{
38+ // 固定写法,资源初始化
39+ auto ret = aclInit(nullptr);
40+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
41+ ret = aclrtSetDevice(deviceId);
42+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
43+ ret = aclrtCreateStream(stream);
44+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
45+ return 0;
46+}
47+ 
48+template <typename T>
49+int CreateAclTensor(const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr,
50+ aclDataType dataType, aclTensor** tensor)
51+{
52+ auto size = GetShapeSize(shape) * sizeof(T);
53+ // 调用aclrtMalloc申请device侧内存
54+ auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
55+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
56+ // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上
57+ ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
58+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);
59+ 
60+ // 计算连续tensor的strides
61+ std::vector<int64_t> strides(shape.size(), 1);
62+ for (int64_t i = shape.size() - 2; i >= 0; i--) {
63+ strides[i] = shape[i + 1] * strides[i + 1];
64+ }
65+ 
66+ // 调用aclCreateTensor接口创建aclTensor
67+ *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND,
68+ shape.data(), shape.size(), *deviceAddr);
69+ return 0;
70+}
71+ 
72+int main()
73+{
74+ // 1. (固定写法)device/stream初始化,参考acl API手册
75+ // 根据自己的实际device填写deviceId
76+ int32_t deviceId = 0;
77+ aclrtStream stream;
78+ auto ret = Init(deviceId, &stream);
79+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
80+ 
81+ // 2. 构造输入与输出,需要根据API的接口自定义构造
82+ std::vector<int64_t> selfShape = {4, 2};
83+ std::vector<int64_t> otherShape = {4, 2};
84+ std::vector<int64_t> outShape = {4, 2};
85+ void* selfDeviceAddr = nullptr;
86+ void* otherDeviceAddr = nullptr;
87+ void* outDeviceAddr = nullptr;
88+ aclTensor* self = nullptr;
89+ aclTensor* other = nullptr;
90+ aclScalar* alpha = nullptr;
91+ aclTensor* out = nullptr;
92+ std::vector<float> selfHostData = {0, 1, 2, 3, 4, 5, 6, 7};
93+ std::vector<float> otherHostData = {1, 1, 1, 2, 2, 2, 3, 3};
94+ std::vector<float> outHostData(8, 0);
95+ float alphaValue = 1.2f;
96+ // 创建self aclTensor
97+ ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_FLOAT, &self);
98+ CHECK_RET(ret == ACL_SUCCESS, return ret);
99+ // 创建other aclTensor
100+ ret = CreateAclTensor(otherHostData, otherShape, &otherDeviceAddr, aclDataType::ACL_FLOAT, &other);
101+ CHECK_RET(ret == ACL_SUCCESS, return ret);
102+ // 创建alpha aclScalar
103+ alpha = aclCreateScalar(&alphaValue, aclDataType::ACL_FLOAT);
104+ CHECK_RET(alpha != nullptr, return ret);
105+ // 创建out aclTensor
106+ ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT, &out);
107+ CHECK_RET(ret == ACL_SUCCESS, return ret);
108+ 
109+ uint64_t workspaceSize = 0;
110+ aclOpExecutor* executor;
111+ 
112+ // aclnnInplaceAddRelu接口调用示例
113+ // 3. 调用CANN算子库API
114+ LOG_PRINT("\ntest aclnnInplaceAddRelu\n");
115+ // 调用aclnnInplaceAddRelu第一段接口
116+ ret = aclnnInplaceAddReluGetWorkspaceSize(self, other, alpha, &workspaceSize, &executor);
117+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceAddReluGetWorkspaceSize failed. ERROR: %d\n", ret);
118+ return ret);
119+ // 根据第一段接口计算出的workspaceSize申请device内存
120+ void* workspaceAddr = nullptr;
121+ if (workspaceSize > 0) {
122+ ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
123+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
124+ }
125+ // 调用aclnnInplaceAddRelu第二段接口
126+ ret = aclnnInplaceAddRelu(workspaceAddr, workspaceSize, executor, stream);
127+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceAddRelu failed. ERROR: %d\n", ret); return ret);
128+ 
129+ // 4. (固定写法)同步等待任务执行结束
130+ ret = aclrtSynchronizeStream(stream);
131+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
132+ 
133+ // 5. 获取输出的值,将device侧内存上的结果拷贝至host侧,需要根据具体API的接口定义修改
134+ auto size = GetShapeSize(outShape);
135+ std::vector<float> resultData(size, 0);
136+ ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), selfDeviceAddr,
137+ size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST);
138+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);
139+ for (int64_t i = 0; i < size; i++) {
140+ LOG_PRINT("result[%ld] is: %f\n", i, resultData[i]);
141+ }
142+ 
143+ // 6. 释放aclTensor和aclScalar,需要根据具体API的接口定义修改
144+ aclDestroyTensor(self);
145+ aclDestroyTensor(other);
146+ aclDestroyScalar(alpha);
147+ aclDestroyTensor(out);
148+ 
149+ // 7. 释放Device资源,需要根据具体API的接口定义修改
150+ aclrtFree(selfDeviceAddr);
151+ aclrtFree(otherDeviceAddr);
152+ aclrtFree(outDeviceAddr);
153+ if (workspaceSize > 0) {
154+ aclrtFree(workspaceAddr);
155+ }
156+ aclrtDestroyStream(stream);
157+ aclrtResetDevice(deviceId);
158+ aclFinalize();
159+ 
160+ return 0;
161+}
@@ -0,0 +1,134 @@
1+/**
2+ * Copyright (c) 2025 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+#include <iostream>
11+#include <vector>
12+#include "acl/acl.h"
13+#include "aclnnop/aclnn_relu.h"
14+ 
15+#define CHECK_RET(cond, return_expr) \
16+ do { \
17+ if (!(cond)) { \
18+ return_expr; \
19+ } \
20+ } while (0)
21+#define LOG_PRINT(message, ...) \
22+ do { \
23+ printf(message, ##__VA_ARGS__); \
24+ } while (0)
25+int64_t GetShapeSize(const std::vector<int64_t>& shape)
26+{
27+ int64_t shapeSize = 1;
28+ for (auto i : shape) {
29+ shapeSize *= i;
30+ }
31+ return shapeSize;
32+}
33+int Init(int32_t deviceId, aclrtStream* stream)
34+{
35+ // 固定写法,资源初始化
36+ auto ret = aclInit(nullptr);
37+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
38+ ret = aclrtSetDevice(deviceId);
39+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
40+ ret = aclrtCreateStream(stream);
41+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
42+ return 0;
43+}
44+ 
45+template <typename T>
46+int CreateAclTensor(const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr,
47+ aclDataType dataType, aclTensor** tensor)
48+{
49+ auto size = GetShapeSize(shape) * sizeof(T);
50+ // 调用aclrtMalloc申请device侧内存
51+ auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
52+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
53+ // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上
54+ ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
55+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);
56+ // 计算连续tensor的strides
57+ std::vector<int64_t> strides(shape.size(), 1);
58+ for (int64_t i = shape.size() - 2; i >= 0; i--) {
59+ strides[i] = shape[i + 1] * strides[i + 1];
60+ }
61+ // 调用aclCreateTensor接口创建aclTensor
62+ *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND,
63+ shape.data(), shape.size(), *deviceAddr);
64+ return 0;
65+}
66+int main()
67+{
68+ // 1. (固定写法)device/stream初始化,参考acl API手册
69+ // 根据自己的实际device填写deviceId
70+ int32_t deviceId = 0;
71+ aclrtStream stream;
72+ auto ret = Init(deviceId, &stream);
73+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
74+ // 2. 构造输入与输出,需要根据API的接口自定义构造
75+ std::vector<int64_t> selfShape = {2, 2};
76+ std::vector<int64_t> outShape = {2, 2};
77+ void* selfDeviceAddr = nullptr;
78+ void* outDeviceAddr = nullptr;
79+ aclTensor* self = nullptr;
80+ aclTensor* out = nullptr;
81+ std::vector<float> selfHostData = {0, 1, 2, 3};
82+ std::vector<float> outHostData = {0, 0, 0, 0};
83+ // 创建self aclTensor
84+ ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_FLOAT, &self);
85+ CHECK_RET(ret == ACL_SUCCESS, return ret);
86+ // 创建out aclTensor
87+ ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT, &out);
88+ CHECK_RET(ret == ACL_SUCCESS, return ret);
89+ // 3. 调用CANN算子库API,需要修改为具体的API名称
90+ // aclnnInplaceRelu接口调用示例
91+ uint64_t inplaceWorkspaceSize = 0;
92+ aclOpExecutor* inplaceExecutor;
93+ // 调用aclnnInplaceRelu第一段接口
94+ ret = aclnnInplaceReluGetWorkspaceSize(self, &inplaceWorkspaceSize, &inplaceExecutor);
95+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceReluGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
96+ // 根据第一段接口计算出的workspaceSize申请device内存
97+ void* inplaceWorkspaceAddr = nullptr;
98+ if (inplaceWorkspaceSize > 0) {
99+ ret = aclrtMalloc(&inplaceWorkspaceAddr, inplaceWorkspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
100+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
101+ }
102+ // 调用aclnnInplaceRelu第二段接口
103+ ret = aclnnInplaceRelu(inplaceWorkspaceAddr, inplaceWorkspaceSize, inplaceExecutor, stream);
104+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceRelu failed. ERROR: %d\n", ret); return ret);
105+ 
106+ // 4. (固定写法)同步等待任务执行结束
107+ ret = aclrtSynchronizeStream(stream);
108+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
109+ 
110+ // 5. 获取输出的值,将device侧内存上的结果拷贝至host侧,需要根据具体API的接口定义修改
111+ auto inplaceSize = GetShapeSize(selfShape);
112+ std::vector<float> inplaceResultData(inplaceSize, 0);
113+ ret = aclrtMemcpy(inplaceResultData.data(), inplaceResultData.size() * sizeof(inplaceResultData[0]), selfDeviceAddr,
114+ inplaceSize * sizeof(inplaceResultData[0]), ACL_MEMCPY_DEVICE_TO_HOST);
115+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);
116+ for (int64_t i = 0; i < inplaceSize; i++) {
117+ LOG_PRINT("aclnnInplaceRelu result[%ld] is: %f\n", i, inplaceResultData[i]);
118+ }
119+ 
120+ // 6. 释放aclTensor和aclScalar,需要根据具体API的接口定义修改
121+ aclDestroyTensor(self);
122+ aclDestroyTensor(out);
123+ 
124+ // 7. 释放device资源,需要根据具体API的接口定义修改
125+ aclrtFree(selfDeviceAddr);
126+ aclrtFree(outDeviceAddr);
127+ if (inplaceWorkspaceSize > 0) {
128+ aclrtFree(inplaceWorkspaceAddr);
129+ }
130+ aclrtDestroyStream(stream);
131+ aclrtResetDevice(deviceId);
132+ aclFinalize();
133+ return 0;
134+}
@@ -0,0 +1,134 @@
1+/**
2+ * Copyright (c) 2025 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+#include <iostream>
11+#include <vector>
12+#include "acl/acl.h"
13+#include "aclnnop/aclnn_relu.h"
14+ 
15+#define CHECK_RET(cond, return_expr) \
16+ do { \
17+ if (!(cond)) { \
18+ return_expr; \
19+ } \
20+ } while (0)
21+#define LOG_PRINT(message, ...) \
22+ do { \
23+ printf(message, ##__VA_ARGS__); \
24+ } while (0)
25+int64_t GetShapeSize(const std::vector<int64_t>& shape)
26+{
27+ int64_t shapeSize = 1;
28+ for (auto i : shape) {
29+ shapeSize *= i;
30+ }
31+ return shapeSize;
32+}
33+int Init(int32_t deviceId, aclrtStream* stream)
34+{
35+ // 固定写法,资源初始化
36+ auto ret = aclInit(nullptr);
37+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
38+ ret = aclrtSetDevice(deviceId);
39+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
40+ ret = aclrtCreateStream(stream);
41+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
42+ return 0;
43+}
44+ 
45+template <typename T>
46+int CreateAclTensor(const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr,
47+ aclDataType dataType, aclTensor** tensor)
48+{
49+ auto size = GetShapeSize(shape) * sizeof(T);
50+ // 调用aclrtMalloc申请device侧内存
51+ auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
52+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
53+ // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上
54+ ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
55+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);
56+ // 计算连续tensor的strides
57+ std::vector<int64_t> strides(shape.size(), 1);
58+ for (int64_t i = shape.size() - 2; i >= 0; i--) {
59+ strides[i] = shape[i + 1] * strides[i + 1];
60+ }
61+ // 调用aclCreateTensor接口创建aclTensor
62+ *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND,
63+ shape.data(), shape.size(), *deviceAddr);
64+ return 0;
65+}
66+int main()
67+{
68+ // 1. (固定写法)device/stream初始化,参考acl API手册
69+ // 根据自己的实际device填写deviceId
70+ int32_t deviceId = 0;
71+ aclrtStream stream;
72+ auto ret = Init(deviceId, &stream);
73+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
74+ // 2. 构造输入与输出,需要根据API的接口自定义构造
75+ std::vector<int64_t> selfShape = {2, 2};
76+ std::vector<int64_t> outShape = {2, 2};
77+ void* selfDeviceAddr = nullptr;
78+ void* outDeviceAddr = nullptr;
79+ aclTensor* self = nullptr;
80+ aclTensor* out = nullptr;
81+ std::vector<float> selfHostData = {0, 1, 2, 3};
82+ std::vector<float> outHostData = {0, 0, 0, 0};
83+ // 创建self aclTensor
84+ ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_FLOAT, &self);
85+ CHECK_RET(ret == ACL_SUCCESS, return ret);
86+ // 创建out aclTensor
87+ ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT, &out);
88+ CHECK_RET(ret == ACL_SUCCESS, return ret);
89+ // 3. 调用CANN算子库API,需要修改为具体的API名称
90+ // aclnnRelu接口调用示例
91+ uint64_t workspaceSize = 0;
92+ aclOpExecutor* executor;
93+ // 调用aclnnRelu第一段接口
94+ ret = aclnnReluGetWorkspaceSize(self, out, &workspaceSize, &executor);
95+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnReluGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
96+ // 根据第一段接口计算出的workspaceSize申请device内存
97+ void* workspaceAddr = nullptr;
98+ if (workspaceSize > 0) {
99+ ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
100+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
101+ }
102+ // 调用aclnnRelu第二段接口
103+ ret = aclnnRelu(workspaceAddr, workspaceSize, executor, stream);
104+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnRelu failed. ERROR: %d\n", ret); return ret);
105+ 
106+ // 4. (固定写法)同步等待任务执行结束
107+ ret = aclrtSynchronizeStream(stream);
108+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
109+ 
110+ // 5. 获取输出的值,将device侧内存上的结果拷贝至host侧,需要根据具体API的接口定义修改
111+ auto size = GetShapeSize(outShape);
112+ std::vector<float> resultData(size, 0);
113+ ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr,
114+ size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST);
115+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);
116+ for (int64_t i = 0; i < size; i++) {
117+ LOG_PRINT("aclnnRelu result[%ld] is: %f\n", i, resultData[i]);
118+ }
119+ 
120+ // 6. 释放aclTensor和aclScalar,需要根据具体API的接口定义修改
121+ aclDestroyTensor(self);
122+ aclDestroyTensor(out);
123+ 
124+ // 7. 释放device资源,需要根据具体API的接口定义修改
125+ aclrtFree(selfDeviceAddr);
126+ aclrtFree(outDeviceAddr);
127+ if (workspaceSize > 0) {
128+ aclrtFree(workspaceAddr);
129+ }
130+ aclrtDestroyStream(stream);
131+ aclrtResetDevice(deviceId);
132+ aclFinalize();
133+ return 0;
134+}
@@ -1,326 +0,0 @@
1-/**
2- * This file is part of the OpenBOAT project at Harbin Institute of Technology (HIT)
3- * and is contributed to the CANN Open Software.
4- *
5- * Copyright (c) 2025 AISS Group, Harbin Institute of Technology (HIT).
6- * All Rights Reserved.
7- *
8- * Authors (accounts):
9- 
10- * - Tu Yuanhang <@TuYHAAAAAA>
11- * - Su Tonghua <@sutonghua>
12- *
13- * This program is free software: you can redistribute it and/or modify it.
14- * Licensed under the CANN Open Software License Agreement Version 2.0 (the "License").
15- * You may not use this file except in compliance with the License.
16- * See the LICENSE file at the root of the repository for the full text of the License.
17- *
18- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED,
19- * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
20- */
21-#include <iostream>
22-#include <fstream>
23-#include <string.h>
24-#include <stdint.h>
25-#include <vector>
26-#include <string>
27-#include <map>
28-#include "assert.h"
29- 
30-#include "graph.h"
31-#include "types.h"
32-#include "tensor.h"
33-#include "ge_error_codes.h"
34-#include "ge_api_types.h"
35-#include "ge_api.h"
36-#include "array_ops.h"
37-#include "ge_ir_build.h"
38- 
39-#include "experiment_ops.h"
40-#include "nn_other.h"
41-#include "../op_graph/relu_v2_proto.h"
42- 
43-#define FAILED -1
44-#define SUCCESS 0
45- 
46-using namespace ge;
47-using std::map;
48-using std::string;
49-using std::vector;
50- 
51-#define ADD_INPUT(intputIndex, intputName, intputDtype, inputShape, value) \
52- vector<int64_t> placeholder##intputIndex##_shape = inputShape; \
53- auto placeholder##intputIndex = op::Data("placeholder" + intputIndex).set_attr_index(0); \
54- TensorDesc placeholder##intputIndex##_desc = TensorDesc(ge::Shape(placeholder##intputIndex##_shape), FORMAT_ND, \
55- intputDtype); \
56- placeholder##intputIndex##_desc.SetPlacement(ge::kPlacementHost); \
57- placeholder##intputIndex##_desc.SetFormat(FORMAT_ND); \
58- Tensor tensor_placeholder##intputIndex; \
59- if (intputDtype == DT_FLOAT || intputDtype == DT_FLOAT16) { \
60- ret = GenOnesDataFloat32(placeholder##intputIndex##_shape, tensor_placeholder##intputIndex, \
61- placeholder##intputIndex##_desc, static_cast<float>(value)); \
62- } else if (intputDtype == DT_INT32 || intputDtype == DT_INT16) { \
63- ret = GenOnesData(placeholder##intputIndex##_shape, tensor_placeholder##intputIndex, \
64- placeholder##intputIndex##_desc, intputDtype, static_cast<int>(value)); \
65- } \
66- if (ret != SUCCESS) { \
67- printf("%s - ERROR - [XIR]: Generate input data failed\n", GetTime().c_str()); \
68- return FAILED; \
69- } \
70- placeholder##intputIndex.update_input_desc_x(placeholder##intputIndex##_desc); \
71- input.push_back(tensor_placeholder##intputIndex); \
72- graph.AddOp(placeholder##intputIndex); \
73- add1.set_input_##intputName(placeholder##intputIndex); \
74- inputs.push_back(placeholder##intputIndex);
75- 
76-#define ADD_CONST_INPUT(intputIndex, intputName, intputDtype, inputShape, value) \
77- vector<int64_t> placeholder##intputIndex##_shape = inputShape; \
78- auto placeholder##intputIndex = op::Const("placeholder" + intputIndex); \
79- TensorDesc placeholder##intputIndex##_desc = TensorDesc(ge::Shape(placeholder##intputIndex##_shape), FORMAT_ND, \
80- intputDtype); \
81- placeholder##intputIndex##_desc.SetPlacement(ge::kPlacementHost); \
82- placeholder##intputIndex##_desc.SetFormat(FORMAT_ND); \
83- Tensor tensor_placeholder##intputIndex; \
84- if (intputDtype == DT_FLOAT || intputDtype == DT_FLOAT16) { \
85- ret = GenOnesDataFloat32(placeholder##intputIndex##_shape, tensor_placeholder##intputIndex, \
86- placeholder##intputIndex##_desc, static_cast<float>(value)); \
87- } else if (intputDtype == DT_INT32 || intputDtype == DT_INT16) { \
88- ret = GenOnesData(placeholder##intputIndex##_shape, tensor_placeholder##intputIndex, \
89- placeholder##intputIndex##_desc, intputDtype, static_cast<int>(value)); \
90- } \
91- if (ret != SUCCESS) { \
92- printf("%s - ERROR - [XIR]: Generate input data failed\n", GetTime().c_str()); \
93- return FAILED; \
94- } \
95- placeholder##intputIndex.SetAttr("value", tensor_placeholder##intputIndex); \
96- placeholder##intputIndex.update_output_desc_y(placeholder##intputIndex##_desc); \
97- graph.AddOp(placeholder##intputIndex); \
98- add1.set_input_##intputName(placeholder##intputIndex); \
99- add1.update_input_desc_##intputName(placeholder##intputIndex##_desc); \
100- inputs.push_back(placeholder##intputIndex);
101- 
102-#define ADD_OUTPUT(outputIndex, outputName, outputDtype, outputShape) \
103- TensorDesc outputName##outputIndex##_desc = TensorDesc(ge::Shape(outputShape), FORMAT_ND, outputDtype); \
104- add1.update_output_desc_##outputName(outputName##outputIndex##_desc);
105-//新加
106-#define LOG_PRINT(message, ...) \
107- do { \
108- printf(message, ##__VA_ARGS__); \
109- } while (0)
110- 
111-string GetTime()
112-{
113- time_t timep;
114- time(&timep);
115- char tmp[64];
116- strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S,000", localtime(&timep));
117- return tmp;
118-}
119- 
120-uint32_t GetDataTypeSize(DataType dt)
121-{
122- uint32_t dilation = 1;
123- uint32_t oneByte = 1;
124- uint32_t twoByte = 2;
125- uint32_t fourByte = 4;
126- uint32_t eightByte = 8;
127- 
128- if (dt == ge::DT_FLOAT) {
129- dilation = fourByte;
130- } else if (dt == ge::DT_FLOAT16) {
131- dilation = twoByte;
132- } else if (dt == ge::DT_BF16) {
133- dilation = twoByte;
134- } else if (dt == ge::DT_INT16) {
135- dilation = twoByte;
136- } else if (dt == ge::DT_UINT16) {
137- dilation = twoByte;
138- } else if (dt == ge::DT_INT32) {
139- dilation = fourByte;
140- } else if (dt == ge::DT_UINT32) {
141- dilation = fourByte;
142- } else if (dt == ge::DT_INT64) {
143- dilation = eightByte;
144- } else if (dt == ge::DT_UINT64) {
145- dilation = eightByte;
146- } else if (dt == ge::DT_INT8) {
147- dilation = oneByte;
148- }
149- return dilation;
150-}
151- 
152-int32_t GenOnesDataFloat32(vector<int64_t> shapes, Tensor& input_tensor, TensorDesc& input_tensor_desc, float value)
153-{
154- input_tensor_desc.SetRealDimCnt(shapes.size());
155- size_t size = 1;
156- for (uint32_t i = 0; i < shapes.size(); i++) {
157- size *= shapes[i];
158- }
159- uint32_t byteSizeFloat32 = 4;
160- uint32_t data_len = size * byteSizeFloat32;
161- float* pData = new (std::nothrow) float[size];
162- 
163- for (size_t i = 0; i < size; ++i) {
164- *(pData + i) = value;
165- }
166- input_tensor = Tensor(input_tensor_desc, (uint8_t*)pData, data_len);
167- return SUCCESS;
168-}
169- 
170-int32_t GenOnesData(vector<int64_t> shapes, Tensor& input_tensor, TensorDesc& input_tensor_desc, DataType data_type,
171- int value)
172-{
173- input_tensor_desc.SetRealDimCnt(shapes.size());
174- size_t size = 1;
175- for (uint32_t i = 0; i < shapes.size(); i++) {
176- size *= shapes[i];
177- }
178- uint32_t data_len = size * GetDataTypeSize(data_type);
179- int32_t* pData = new (std::nothrow) int32_t[size];
180- for (uint32_t i = 0; i < size; ++i) {
181- *(pData + i) = value;
182- }
183- input_tensor = Tensor(input_tensor_desc, reinterpret_cast<uint8_t*>(pData), data_len);
184- return SUCCESS;
185-}
186- 
187-int32_t WriteDataToFile(string bin_file, uint64_t data_size, uint8_t* inputData)
188-{
189- FILE* fp;
190- fp = fopen(bin_file.c_str(), "w");
191- fwrite(inputData, sizeof(uint8_t), data_size, fp);
192- fclose(fp);
193- return SUCCESS;
194-}
195- 
196-int CreateOppInGraph(DataType inDtype, std::vector<ge::Tensor>& input, std::vector<Operator>& inputs,
197- std::vector<Operator>& outputs, Graph& graph)
198-{
199- Status ret = SUCCESS;
200- // 自定义代码:添加单算子定义到图中
201- auto add1 = op::ReluV2("add1");
202- std::vector<int64_t> xShape = {32, 4, 4, 4};
203- ADD_INPUT(1, x, inDtype, xShape, 2);
204- 
205- ADD_OUTPUT(1, y, inDtype, xShape);
206- 
207- outputs.push_back(add1);
208- // 添加完毕
209- return SUCCESS;
210-}
211- 
212-int main(int argc, char* argv[])
213-{
214- const char* graph_name = "tc_ge_irrun_test";
215- Graph graph(graph_name);
216- std::vector<ge::Tensor> input;
217- 
218- printf("%s - INFO - [XIR]: Start to initialize ge using ge global options\n", GetTime().c_str());
219- std::map<AscendString, AscendString> global_options = {{"ge.exec.deviceId", "0"}, {"ge.graphRunMode", "1"}};
220- Status ret = ge::GEInitialize(global_options);
221- if (ret != SUCCESS) {
222- printf("%s - INFO - [XIR]: Initialize ge using ge global options failed\n", GetTime().c_str());
223- return FAILED;
224- }
225- printf("%s - INFO - [XIR]: Initialize ge using ge global options success\n", GetTime().c_str());
226- 
227- std::vector<Operator> inputs{};
228- std::vector<Operator> outputs{};
229- 
230- std::cout << argv[1] << std::endl;
231- char* endptr;
232- //修改类型
233- DataType inDtype = DT_FLOAT;
234- 
235- std::cout << inDtype << std::endl;
236- 
237- ret = CreateOppInGraph(inDtype, input, inputs, outputs, graph);
238- if (ret != SUCCESS) {
239- printf("%s - ERROR - [XIR]: Create ir session using build options failed\n", GetTime().c_str());
240- return FAILED;
241- }
242- 
243- if (!inputs.empty() && !outputs.empty()) {
244- graph.SetInputs(inputs).SetOutputs(outputs);
245- }
246- 
247- std::map<AscendString, AscendString> build_options = {
248- 
249- };
250- printf("%s - INFO - [XIR]: Start to create ir session using build options\n", GetTime().c_str());
251- ge::Session* session = new Session(build_options);
252- 
253- if (session == nullptr) {
254- printf("%s - ERROR - [XIR]: Create ir session using build options failed\n", GetTime().c_str());
255- return FAILED;
256- }
257- printf("%s - INFO - [XIR]: Create ir session using build options success\n", GetTime().c_str());
258- printf("%s - INFO - [XIR]: Start to add compute graph to ir session\n", GetTime().c_str());
259- 
260- std::map<AscendString, AscendString> graph_options = {
261- 
262- };
263- uint32_t graph_id = 0;
264- ret = session->AddGraph(graph_id, graph, graph_options);
265- 
266- printf("%s - INFO - [XIR]: Session add ir compute graph to ir session success\n", GetTime().c_str());
267- printf("%s - INFO - [XIR]: dump graph to txt\n", GetTime().c_str());
268- std::string file_path = "./dump";
269- aclgrphDumpGraph(graph, file_path.c_str(), file_path.length());
270- printf("%s - INFO - [XIR]: Start to run ir compute graph\n", GetTime().c_str());
271- std::vector<ge::Tensor> output;
272- ret = session->RunGraph(graph_id, input, output);
273- if (ret != SUCCESS) {
274- printf("%s - INFO - [XIR]: Run graph failed\n", GetTime().c_str());
275- delete session;
276- GEFinalize();
277- return FAILED;
278- }
279- printf("%s - INFO - [XIR]: Session run ir compute graph success\n", GetTime().c_str());
280- 
281- int input_num = input.size();
282- for (int i = 0; i < input_num; i++) {
283- std::cout << "input " << i << " dtype : " << input[i].GetTensorDesc().GetDataType() << std::endl;
284- string input_file = "./tc_ge_irrun_test_0008_npu_input_" + std::to_string(i) + ".bin";
285- uint8_t* input_data_i = input[i].GetData();
286- int64_t input_shape = input[i].GetTensorDesc().GetShape().GetShapeSize();
287- std::cout << "this is " << i << "th input, input shape size =" << input_shape << std::endl;
288- uint32_t data_size = input_shape * GetDataTypeSize(input[i].GetTensorDesc().GetDataType());
289- WriteDataToFile((const char*)input_file.c_str(), data_size, input_data_i);
290- }
291- 
292- int output_num = output.size();
293- for (int i = 0; i < output_num; i++) {
294- std::cout << "output " << i << " dtype : " << output[i].GetTensorDesc().GetDataType() << std::endl;
295- string output_file = "./tc_ge_irrun_test_0008_npu_output_" + std::to_string(i) + ".bin";
296- uint8_t* output_data_i = output[i].GetData();
297- int64_t output_shape = output[i].GetTensorDesc().GetShape().GetShapeSize();
298- std::cout << "this is " << i << "th output, output shape size =" << output_shape << std::endl;
299- uint32_t data_size = output_shape * GetDataTypeSize(output[i].GetTensorDesc().GetDataType());
300- WriteDataToFile((const char*)output_file.c_str(), data_size, output_data_i);
301- //新加3行打印
302- float* result = (float*)output_data_i;
303- // int32_t* result = (int32_t*)output_data_i;
304- for (int64_t j = 0; j < 8; j++) {
305- LOG_PRINT("result[%ld] is: %f\n", j, result[j]);
306- // LOG_PRINT("result[%ld] is: %d\n", j, result[j]);
307- }
308- }
309- 
310- ge::AscendString error_msg = ge::GEGetErrorMsgV2();
311- std::string error_str(error_msg.GetString());
312- std::cout << "Error message: " << error_str << std::endl;
313- ge::AscendString warning_msg = ge::GEGetWarningMsgV2();
314- std::string warning_str(warning_msg.GetString());
315- std::cout << "Warning message: " << warning_str << std::endl;
316- printf("%s - INFO - [XIR]: Precision is ok\n", GetTime().c_str());
317- printf("%s - INFO - [XIR]: Start to finalize ir graph session\n", GetTime().c_str());
318- delete session;
319- ret = ge::GEFinalize();
320- if (ret != SUCCESS) {
321- printf("%s - INFO - [XIR]: Finalize ir graph session failed\n", GetTime().c_str());
322- return FAILED;
323- }
324- printf("%s - INFO - [XIR]: Finalize ir graph session success\n", GetTime().c_str());
325- return SUCCESS;
326-}
@@ -0,0 +1,326 @@
1+/**
2+ * Copyright (c) 2025 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+#include "aclnn_add_relu.h"
12+#include "level0/add.h"
13+#include "level0/axpy.h"
14+#include "relu_v2.h"
15+#include "aclnn_kernels/common/op_error_check.h"
16+#include "aclnn_kernels/cast.h"
17+#include "aclnn_kernels/contiguous.h"
18+#include "level0/mul.h"
19+#include "level0/logical_and.h"
20+#include "level0/logical_or.h"
21+#include "aclnn_kernels/common/op_error_check.h"
22+#include "opdev/common_types.h"
23+#include "aclnn/aclnn_base.h"
24+#include "opdev/data_type_utils.h"
25+#include "opdev/format_utils.h"
26+#include "opdev/op_dfx.h"
27+#include "opdev/op_executor.h"
28+#include "opdev/op_log.h"
29+#include "opdev/shape_utils.h"
30+#include "opdev/tensor_view_utils.h"
31+#include "opdev/platform.h"
32+ 
33+using namespace op;
34+#ifdef __cplusplus
35+extern "C" {
36+#endif
37+ 
38+/* AddRelu 算子的完整计算流程如下:
39+ * self other
40+ * | |
41+ * \ /
42+ * Contiguous(workspace_0) Contiguous(workspace_2)
43+ * \ /
44+ * Cast(workspace_1) Cast(workspace_3)
45+ * \ /
46+ * Add(workspace_4)
47+ * |
48+ * Cast(workspace_5)
49+ * |
50+ * Relu(workspace_6)
51+ * |
52+ * ViewCopy
53+ * |
54+ * result
55+ */
56+ 
57+static constexpr size_t MAX_DIM_LEN = 8;
58+ 
59+// 根据API定义,需要列出所能支持的所有dtype
60+static const std::initializer_list<op::DataType> AXPY_DTYPE_SUPPORT_LIST = {
61+ op::DataType::DT_FLOAT, op::DataType::DT_INT32, op::DataType::DT_FLOAT16};
62+ 
63+static const std::initializer_list<op::DataType> DTYPE_SUPPORT_LIST_WITHOUT_BF16 = {
64+ op::DataType::DT_FLOAT, op::DataType::DT_FLOAT16, op::DataType::DT_INT8, op::DataType::DT_UINT8,
65+ op::DataType::DT_INT16, op::DataType::DT_INT32, op::DataType::DT_INT64};
66+ 
67+static const std::initializer_list<op::DataType> DTYPE_SUPPORT_LIST_WITH_BF16 = {
68+ op::DataType::DT_FLOAT, op::DataType::DT_FLOAT16, op::DataType::DT_BF16, op::DataType::DT_INT8,
69+ op::DataType::DT_UINT8, op::DataType::DT_INT16, op::DataType::DT_INT32, op::DataType::DT_INT64};
70+ 
71+static const std::initializer_list<op::DataType> FP_DTYPE_SUPPORT_LIST = {op::DataType::DT_FLOAT, op::DataType::DT_BF16,
72+ op::DataType::DT_FLOAT16};
73+ 
74+static bool CheckNotNull(const aclTensor* self, const aclTensor* other, const aclScalar* alpha, const aclTensor* out)
75+{
76+ OP_CHECK_NULL(self, return false);
77+ OP_CHECK_NULL(other, return false);
78+ OP_CHECK_NULL(alpha, return false);
79+ OP_CHECK_NULL(out, return false);
80+ return true;
81+}
82+ 
83+static inline const std::initializer_list<op::DataType>& GetDtypeSupportListBySocVersion()
84+{
85+ auto curArch = GetCurrentPlatformInfo().GetCurNpuArch();
86+ if (curArch == NpuArch::DAV_2201 || curArch == NpuArch::DAV_3510) {
87+ return DTYPE_SUPPORT_LIST_WITH_BF16;
88+ } else {
89+ return DTYPE_SUPPORT_LIST_WITHOUT_BF16;
90+ }
91+}
92+ 
93+static void CheckFormat(const aclTensor* self, const aclTensor* other, const aclTensor* out)
94+{
95+ OP_CHECK_NO_RETURN(self->GetStorageFormat() != Format::FORMAT_FRACTAL_NZ,
96+ OP_LOGW("Format of self gets [%s], this format mat lead to precision failure",
97+ op::ToString(Format::FORMAT_FRACTAL_NZ).GetString()));
98+ OP_CHECK_NO_RETURN(other->GetStorageFormat() != Format::FORMAT_FRACTAL_NZ,
99+ OP_LOGW("Format of other gets [%s], this format mat lead to precision failure",
100+ op::ToString(Format::FORMAT_FRACTAL_NZ).GetString()));
101+ OP_CHECK_NO_RETURN(out->GetStorageFormat() != Format::FORMAT_FRACTAL_NZ,
102+ OP_LOGW("Format of out gets [%s], this format mat lead to precision failure",
103+ op::ToString(Format::FORMAT_FRACTAL_NZ).GetString()));
104+}
105+ 
106+static bool CheckPromoteType(const op::DataType selfDtype, const op::DataType otherDtype, const aclScalar* alpha,
107+ const op::DataType outDtype, op::DataType promoteType)
108+{
109+ // 检查self和other能否做数据类型推导
110+ if (promoteType == DataType::DT_UNDEFINED) {
111+ OP_LOGE(ACLNN_ERR_PARAM_INVALID, "Self dtype %s and other dtype %s can not promote dtype.",
112+ op::ToString(selfDtype).GetString(), op::ToString(otherDtype).GetString());
113+ return false;
114+ }
115+ 
116+ // 检查alpha能否转换为推导后的数据类型
117+ if (promoteType == op::DataType::DT_BOOL) {
118+ OP_CHECK(
119+ IsIntegralType(DataType(alpha->GetDataType()), true),
120+ OP_LOGE(ACLNN_ERR_PARAM_INVALID, "Alpha dtype %s can't be cast to the promote dtype %s.",
121+ op::ToString(DataType(alpha->GetDataType())).GetString(), op::ToString(promoteType).GetString()),
122+ return false);
123+ } else if (!CanCast(DataType(alpha->GetDataType()), promoteType)) {
124+ OP_LOGE(ACLNN_ERR_PARAM_INVALID, "Alpha dtype %s can't be cast to the promote dtype %s.",
125+ op::ToString(DataType(alpha->GetDataType())).GetString(), op::ToString(promoteType).GetString());
126+ return false;
127+ }
128+ 
129+ // 检查推导后的数据类型能否转换为输出的数据类型
130+ if (!CanCast(promoteType, outDtype)) {
131+ OP_LOGE(ACLNN_ERR_PARAM_INVALID, "Promote dtype %s can't be cast to the desired output type %s.",
132+ op::ToString(promoteType).GetString(), op::ToString(outDtype).GetString());
133+ return false;
134+ }
135+ return true;
136+}
137+ 
138+static bool CheckShape(const aclTensor* self, const aclTensor* other, const aclTensor* y)
139+{
140+ OP_CHECK_MAX_DIM(self, MAX_DIM_LEN, return false);
141+ OP_CHECK_MAX_DIM(other, MAX_DIM_LEN, return false);
142+ 
143+ op::Shape broadcastShape;
144+ OP_CHECK_BROADCAST_AND_INFER_SHAPE(self, other, broadcastShape, return false);
145+ 
146+ if (broadcastShape != y->GetViewShape()) {
147+ OP_LOGE(ACLNN_ERR_PARAM_INVALID, "Shape of out should be %s, but current is %s.",
148+ op::ToString(broadcastShape).GetString(), op::ToString(y->GetViewShape()).GetString());
149+ return false;
150+ }
151+ return true;
152+}
153+ 
154+static aclnnStatus CheckParams(const aclTensor* self, const aclTensor* other, const aclScalar* alpha,
155+ const aclTensor* y)
156+{
157+ // 1. 检查参数是否为空指针
158+ CHECK_RET(CheckNotNull(self, other, alpha, y), ACLNN_ERR_PARAM_NULLPTR);
159+ 
160+ // 2. 检查输入的数据类型是否在API支持的数据类型范围之内,需要根据api定义校验
161+ const std::initializer_list<op::DataType> dtypeSupportList = GetDtypeSupportListBySocVersion();
162+ // 检查self的数据类型是否在add算子的支持列表内
163+ OP_CHECK_DTYPE_NOT_SUPPORT(self, dtypeSupportList, return ACLNN_ERR_PARAM_INVALID);
164+ // 检查other的数据类型是否在add算子的支持列表内
165+ OP_CHECK_DTYPE_NOT_SUPPORT(other, dtypeSupportList, return ACLNN_ERR_PARAM_INVALID);
166+ 
167+ // 3. 检查self和other能否做数据类型推导以及推导的数据类型能否转换为输出数据类型
168+ op::DataType promoteType = op::PromoteType(self->GetDataType(), other->GetDataType());
169+ CHECK_RET(CheckPromoteType(self->GetDataType(), other->GetDataType(), alpha, y->GetDataType(), promoteType),
170+ ACLNN_ERR_PARAM_INVALID);
171+ 
172+ // 4. 检查双输入是否能broadcast
173+ CHECK_RET(CheckShape(self, other, y), ACLNN_ERR_PARAM_INVALID);
174+ 
175+ // 5. 检查format
176+ CheckFormat(self, other, y);
177+ 
178+ return ACLNN_SUCCESS;
179+}
180+ 
181+static bool IsSupportAxpy(const DataType promoteType) { return CheckType(promoteType, AXPY_DTYPE_SUPPORT_LIST); }
182+ 
183+static bool IsFpDtype(const DataType promoteType) { return CheckType(promoteType, FP_DTYPE_SUPPORT_LIST); }
184+ 
185+inline static bool isAddMixDtypeSupport(const aclTensor* self, const aclTensor* other)
186+{
187+ return (self->GetDataType() == DataType::DT_FLOAT16 && other->GetDataType() == DataType::DT_FLOAT) ||
188+ (self->GetDataType() == DataType::DT_FLOAT && other->GetDataType() == DataType::DT_FLOAT16) ||
189+ (self->GetDataType() == DataType::DT_BF16 && other->GetDataType() == DataType::DT_FLOAT) ||
190+ (self->GetDataType() == DataType::DT_FLOAT && other->GetDataType() == DataType::DT_BF16);
191+}
192+ 
193+aclnnStatus aclnnAddReluGetWorkspaceSize(const aclTensor* self, const aclTensor* other, aclScalar* alpha,
194+ aclTensor* out, uint64_t* workspaceSize, aclOpExecutor** executor)
195+{
196+ L2_DFX_PHASE_1(aclnnAddRelu, DFX_IN(self, other, alpha), DFX_OUT(out));
197+ // 固定写法,创建OpExecutor
198+ auto uniqueExecutor = CREATE_EXECUTOR();
199+ CHECK_RET(uniqueExecutor.get() != nullptr, ACLNN_ERR_INNER_CREATE_EXECUTOR);
200+ 
201+ // 固定写法,参数检查
202+ auto ret = CheckParams(self, other, alpha, out);
203+ CHECK_RET(ret == ACLNN_SUCCESS, ret);
204+ 
205+ // add算子的空tensor在kernel中支持,对标竞品根据算子实际情况补充
206+ if (self->IsEmpty() || other->IsEmpty()) {
207+ // 根据实际支持情况补充
208+ *workspaceSize = 0;
209+ uniqueExecutor.ReleaseTo(executor);
210+ return ACLNN_SUCCESS;
211+ }
212+ 
213+ // 固定写法,将输入self转换成连续的tensor
214+ auto selfContiguous = l0op::Contiguous(self, uniqueExecutor.get());
215+ CHECK_RET(selfContiguous != nullptr, ACLNN_ERR_INNER_NULLPTR);
216+ 
217+ // 固定写法,将输入other转换成连续的tensor
218+ auto otherContiguous = l0op::Contiguous(other, uniqueExecutor.get());
219+ CHECK_RET(otherContiguous != nullptr, ACLNN_ERR_INNER_NULLPTR);
220+ 
221+ // 申请add的输出tensor
222+ const aclTensor* addOpOut = nullptr;
223+ // 判断输入是否符合kernel支持的混合输入类型
224+ bool isMixDataType = isAddMixDtypeSupport(self, other);
225+ if (isMixDataType && !(alpha->ToFloat() > 1 || alpha->ToFloat() < 1)) {
226+ // 无需调用Cast,直接调用L0带混合数据类型的kernel
227+ addOpOut = l0op::Add(selfContiguous, otherContiguous, uniqueExecutor.get());
228+ } else {
229+ // 对self和other两个输入做隐式数据类型转换,根据具体算子语义按需调用
230+ auto promoteType = op::PromoteType(self->GetDataType(), other->GetDataType());
231+ promoteType = IsFpDtype(promoteType) ? DataType::DT_FLOAT : promoteType;
232+ 
233+ // 将输入self的数据类型转换成隐式数据类型,根据具体算子语义按需调用
234+ auto selfCasted = l0op::Cast(selfContiguous, promoteType, uniqueExecutor.get());
235+ CHECK_RET(selfCasted != nullptr, ACLNN_ERR_INNER_NULLPTR);
236+ 
237+ // 将输入other的数据类型转换成隐式数据类型,根据具体算子语义按需调用
238+ auto otherCasted = l0op::Cast(otherContiguous, promoteType, uniqueExecutor.get());
239+ CHECK_RET(otherCasted != nullptr, ACLNN_ERR_INNER_NULLPTR);
240+ 
241+ // 进行非混合输入类型的Add计算分支判断
242+ if (!(alpha->ToFloat() > 1 || alpha->ToFloat() < 1)) {
243+ addOpOut = l0op::Add(selfCasted, otherCasted, uniqueExecutor.get());
244+ } else if (IsSupportAxpy(promoteType)) {
245+ addOpOut = l0op::Axpy(selfCasted, otherCasted, alpha->ToFloat(), uniqueExecutor.get());
246+ } else {
247+ const auto alphaTensor = uniqueExecutor.get()->ConvertToTensor(alpha, promoteType);
248+ const auto otherRes = l0op::Mul(otherCasted, alphaTensor, uniqueExecutor.get());
249+ addOpOut = l0op::Add(selfCasted, otherRes, uniqueExecutor.get());
250+ }
251+ }
252+ CHECK_RET(addOpOut != nullptr, ACLNN_ERR_INNER_NULLPTR);
253+ 
254+ // 如果out的数据类型是int16,则转成int32进行relu计算
255+ const aclTensor* addOutCasted = nullptr;
256+ if (addOpOut->GetDataType() == DataType::DT_INT16) {
257+ addOutCasted = l0op::Cast(addOpOut, DataType::DT_INT32, uniqueExecutor.get());
258+ } else {
259+ addOutCasted = addOpOut;
260+ }
261+ CHECK_RET(addOutCasted != nullptr, ACLNN_ERR_INNER_NULLPTR);
262+ 
263+ // 如果addOutCasted不是UINT8类型,需要调用relu计算
264+ const aclTensor* reluRes = nullptr;
265+ if (addOutCasted->GetDataType() != op::DataType::DT_UINT8) {
266+ // 调用relu进行计算
267+ reluRes = l0op::ReluV2(addOutCasted, uniqueExecutor.get());
268+ } else {
269+ reluRes = addOutCasted;
270+ }
271+ CHECK_RET(reluRes != nullptr, ACLNN_ERR_INNER_NULLPTR);
272+ 
273+ // reluRes数据类型cast回out
274+ auto outCasted = l0op::Cast(reluRes, out->GetDataType(), uniqueExecutor.get());
275+ 
276+ // 固定写法,将计算结果outCasted拷贝到输出out上,out可能是非连续的tensor
277+ auto viewCopyResult = l0op::ViewCopy(outCasted, out, uniqueExecutor.get());
278+ CHECK_RET(viewCopyResult != nullptr, ACLNN_ERR_INNER_NULLPTR);
279+ 
280+ // 固定写法,获取计算过程中需要使用的workspace大小
281+ *workspaceSize = uniqueExecutor->GetWorkspaceSize();
282+ uniqueExecutor.ReleaseTo(executor); // 需要把 uniqueExecutor持有executor转移给executor
283+ return ACLNN_SUCCESS;
284+}
285+ 
286+aclnnStatus aclnnAddRelu(void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream)
287+{
288+ L2_DFX_PHASE_2(aclnnAddRelu);
289+ // 固定写法,调用框架能力,完成计算
290+ return CommonOpExecutorRun(workspace, workspaceSize, executor, stream);
291+}
292+ 
293+static inline aclnnStatus CheckInplace(const aclTensor* selfRef, const aclTensor* other)
294+{
295+ OP_CHECK_NULL(selfRef, return ACLNN_ERR_PARAM_NULLPTR);
296+ OP_CHECK_NULL(other, return ACLNN_ERR_PARAM_NULLPTR);
297+ 
298+ op::Shape broadcastShape;
299+ OP_CHECK_BROADCAST_AND_INFER_SHAPE(selfRef, other, broadcastShape, return ACLNN_ERR_PARAM_INVALID);
300+ 
301+ OP_CHECK(selfRef->GetViewShape() == broadcastShape,
302+ OP_LOGE(ACLNN_ERR_PARAM_NULLPTR, "Expected shape of selfRef should be %s, but got %s.",
303+ op::ToString(broadcastShape).GetString(), op::ToString(selfRef->GetViewShape()).GetString()),
304+ return ACLNN_ERR_PARAM_INVALID);
305+ return ACLNN_SUCCESS;
306+}
307+ 
308+aclnnStatus aclnnInplaceAddReluGetWorkspaceSize(aclTensor* selfRef, const aclTensor* other, aclScalar* alpha,
309+ uint64_t* workspaceSize, aclOpExecutor** executor)
310+{
311+ auto ret = CheckInplace(selfRef, other);
312+ CHECK_RET(ret == ACLNN_SUCCESS, ret);
313+ 
314+ return aclnnAddReluGetWorkspaceSize(selfRef, other, alpha, selfRef, workspaceSize, executor);
315+}
316+ 
317+aclnnStatus aclnnInplaceAddRelu(void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream)
318+{
319+ L2_DFX_PHASE_2(aclnnInplaceAddRelu);
320+ // 固定写法,调用框架能力,完成计算
321+ return CommonOpExecutorRun(workspace, workspaceSize, executor, stream);
322+}
323+ 
324+#ifdef __cplusplus
325+}
326+#endif
@@ -0,0 +1,93 @@
1+/**
2+ * Copyright (c) 2025 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+#ifndef OP_API_INC_ADD_RELU_H_
11+#define OP_API_INC_ADD_RELU_H_
12+ 
13+#include "aclnn/aclnn_base.h"
14+#include "aclnn_util.h"
15+ 
16+#ifdef __cplusplus
17+extern "C" {
18+#endif
19+ 
20+/**
21+ * @brief aclnnAddRelu的第一段接口,根据具体的计算流程,计算workspace大小。
22+ * @domain aclnn_math
23+ *
24+ * 算子功能:完成加法计算后,对张量结果进行处理:大于0则不变,小于0则修改为0。
25+ *
26+ * @param [in] self: npu
27+ * device侧的aclTensor,数据类型支持整型,浮点类型,且数据类型需要与other构成互相推导关系,shape需要与other满足broadcast关系。
28+ * 支持非连续的Tensor,数据格式支持ND,且数据格式需要与other一致。
29+ * @param [in] other: npu
30+ * device侧的aclTensor,数据类型支持整型,浮点类型,且数据类型需要与self构成互相推导关系,shape需要与self满足broadcast关系。
31+ * 支持非连续的Tensor,数据格式支持ND,且数据格式需要与self一致。
32+ * @param [in] alpha: host侧的aclScalar,数据类型需要可转换成self与other推导后的数据类型。
33+ * @param [in] out: npu
34+ * device侧的aclTensor,数据类型支持整型,浮点类型,且数据类型需要是self与other推导之后可转换的数据类型,shape需要是self与other
35+ * broadcast之后的shape,数据格式支持ND,且数据格式需要与self一致。
36+ * @param [out] workspaceSize: 返回用户需要在npu device侧申请的workspace大小。
37+ * @param [out] executor: 返回op执行器,包含算子计算流程。
38+ * @return aclnnStatus: 返回状态码。
39+ */
40+ACLNN_API aclnnStatus aclnnAddReluGetWorkspaceSize(const aclTensor* self, const aclTensor* other, aclScalar* alpha,
41+ aclTensor* out, uint64_t* workspaceSize, aclOpExecutor** executor);
42+ 
43+/**
44+ * @brief aclnnAdd的第二段接口,用于执行计算。
45+ *
46+ * @param [in] workspace: 在npu device侧申请的workspace内存起址。
47+ * @param [in] workspace_size: 在npu device侧申请的workspace大小,由第一段接口aclnnAddGetWorkspaceSize获取。
48+ * @param [in] executor: op执行器,包含了算子计算流程。
49+ * @param [in] stream: acl stream流。
50+ * @return aclnnStatus: 返回状态码。
51+ */
52+ACLNN_API aclnnStatus aclnnAddRelu(void* workspace, uint64_t workspaceSize, aclOpExecutor* executor,
53+ aclrtStream stream);
54+ 
55+/**
56+ * @brief aclnnInplaceAddRelu的第一段接口,根据具体的计算流程,计算workspace大小。
57+ * @domain aclnn_math
58+ *
59+ * 算子功能:完成加法计算后,对张量结果进行处理:大于0则不变,小于0则修改为0。
60+ *
61+ * @param [in] selfRef: npu
62+ * device侧的aclTensor,数据类型支持整型,浮点类型,且数据类型需要与other构成互相推导关系,shape需要与other满足broadcast关系。
63+ * 支持非连续的Tensor,数据格式支持ND,且数据格式需要与other一致。
64+ * @param [in] other: npu
65+ * device侧的aclTensor,数据类型支持整型,浮点类型,且数据类型需要与self构成互相推导关系,shape需要与self满足broadcast关系。
66+ * 支持非连续的Tensor,数据格式支持ND,且数据格式需要与self一致。
67+ * @param [in] alpha: host侧的aclScalar,数据类型需要可转换成self与other推导后的数据类型。
68+ * device侧的aclTensor,数据类型支持整型,浮点类型,且数据类型需要是self与other推导之后可转换的数据类型,shape需要是self与other
69+ * broadcast之后的shape,数据格式支持ND,且数据格式需要与self一致。
70+ * @param [out] workspaceSize: 返回用户需要在npu device侧申请的workspace大小。
71+ * @param [out] executor: 返回op执行器,包含算子计算流程。
72+ * @return aclnnStatus: 返回状态码。
73+ */
74+ACLNN_API aclnnStatus aclnnInplaceAddReluGetWorkspaceSize(aclTensor* selfRef, const aclTensor* other, aclScalar* alpha,
75+ uint64_t* workspaceSize, aclOpExecutor** executor);
76+ 
77+/**
78+ * @brief aclnnAdd的第二段接口,用于执行计算。
79+ *
80+ * @param [in] workspace: 在npu device侧申请的workspace内存起址。
81+ * @param [in] workspace_size: 在npu device侧申请的workspace大小,由第一段接口aclnnAddGetWorkspaceSize获取。
82+ * @param [in] executor: op执行器,包含了算子计算流程。
83+ * @param [in] stream: acl stream流。
84+ * @return aclnnStatus: 返回状态码。
85+ */
86+ACLNN_API aclnnStatus aclnnInplaceAddRelu(void* workspace, uint64_t workspaceSize, aclOpExecutor* executor,
87+ aclrtStream stream);
88+ 
89+#ifdef __cplusplus
90+}
91+#endif
92+ 
93+#endif // OP_API_INC_ADD_RELU_H_
@@ -0,0 +1,157 @@
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+#include "aclnn_relu.h"
12+#include "aclnn_kernels/common/op_error_check.h"
13+#include "aclnn_kernels/contiguous.h"
14+#include "op_api/aclnn_util.h"
15+#include "opdev/make_op_executor.h"
16+#include "opdev/op_dfx.h"
17+#include "opdev/op_executor.h"
18+#include "relu_v2.h"
19+ 
20+using namespace op;
21+ 
22+#ifdef __cplusplus
23+extern "C" {
24+#endif
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, op::DataType::DT_FLOAT16, op::DataType::DT_INT8, op::DataType::DT_UINT8,
30+ op::DataType::DT_INT16, op::DataType::DT_INT32, op::DataType::DT_INT64};
31+static const std::initializer_list<op::DataType> ASCEND910B_DTYPE_SUPPORT_LIST = {
32+ op::DataType::DT_FLOAT, op::DataType::DT_FLOAT16, op::DataType::DT_BF16, op::DataType::DT_INT8,
33+ op::DataType::DT_UINT8, op::DataType::DT_INT16, op::DataType::DT_INT32, op::DataType::DT_INT64};
34+ 
35+static inline const std::initializer_list<op::DataType>& GetDtypeSupportList()
36+{
37+ if (GetCurrentPlatformInfo().GetSocVersion() >= SocVersion::ASCEND910B &&
38+ GetCurrentPlatformInfo().GetSocVersion() <= SocVersion::ASCEND910E) {
39+ return ASCEND910B_DTYPE_SUPPORT_LIST;
40+ }
41+ if (Ops::NN::AclnnUtil::IsRegbase()) {
42+ return ASCEND910B_DTYPE_SUPPORT_LIST;
43+ }
44+ return ASCEND910_DTYPE_SUPPORT_LIST;
45+}
46+ 
47+static bool CheckNotNull(const aclTensor* self, const aclTensor* out)
48+{
49+ OP_CHECK_NULL(self, return false);
50+ OP_CHECK_NULL(out, return false);
51+ return true;
52+}
53+ 
54+static bool CheckDtypeValid(const aclTensor* self, const aclTensor* out)
55+{
56+ auto supportList = GetDtypeSupportList();
57+ OP_CHECK_DTYPE_NOT_SUPPORT(self, supportList, return false);
58+ OP_CHECK_DTYPE_NOT_SUPPORT(out, supportList, return false);
59+ OP_CHECK_DTYPE_NOT_MATCH(self, out->GetDataType(), return false);
60+ return true;
61+}
62+ 
63+static bool CheckShape(const aclTensor* self, const aclTensor* out)
64+{
65+ OP_CHECK_SHAPE_NOT_EQUAL(self, out, return false);
66+ OP_CHECK_MAX_DIM(self, MAX_DIM_LEN, return false);
67+ OP_CHECK_MAX_DIM(out, MAX_DIM_LEN, return false);
68+ return true;
69+}
70+ 
71+static aclnnStatus CheckParams(const aclTensor* self, const aclTensor* out)
72+{
73+ CHECK_RET(CheckNotNull(self, out), ACLNN_ERR_PARAM_NULLPTR);
74+ CHECK_RET(CheckDtypeValid(self, out), ACLNN_ERR_PARAM_INVALID);
75+ CHECK_RET(CheckShape(self, out), ACLNN_ERR_PARAM_INVALID);
76+ return ACLNN_SUCCESS;
77+}
78+ 
79+static aclnnStatus CheckInplaceParams(const aclTensor* self)
80+{
81+ OP_CHECK_NULL(self, return ACLNN_ERR_PARAM_NULLPTR);
82+ auto supportList = GetDtypeSupportList();
83+ OP_CHECK_DTYPE_NOT_SUPPORT(self, supportList, return ACLNN_ERR_PARAM_INVALID);
84+ OP_CHECK_MAX_DIM(self, MAX_DIM_LEN, return ACLNN_ERR_PARAM_INVALID);
85+ return ACLNN_SUCCESS;
86+}
87+ 
88+static aclnnStatus ExecReluGetWorkspaceSize(const aclTensor* self, const aclTensor* out, uint64_t* workspaceSize,
89+ aclOpExecutor** executor)
90+{
91+ auto uniqueExecutor = CREATE_EXECUTOR();
92+ CHECK_RET(uniqueExecutor.get() != nullptr, ACLNN_ERR_INNER_CREATE_EXECUTOR);
93+ 
94+ auto ret = CheckParams(self, out);
95+ CHECK_RET(ret == ACLNN_SUCCESS, ret);
96+ 
97+ if (self->IsEmpty()) {
98+ *workspaceSize = 0;
99+ uniqueExecutor.ReleaseTo(executor);
100+ return ACLNN_SUCCESS;
101+ }
102+ 
103+ auto selfContiguous = l0op::Contiguous(self, uniqueExecutor.get());
104+ CHECK_RET(selfContiguous != nullptr, ACLNN_ERR_INNER_NULLPTR);
105+ 
106+ // UINT8 全为非负数,ReLU 等价于恒等变换,不需走 relu 计算(kernel 也不支持 uint8),
107+ // 直接 ViewCopy 输出即可。
108+ const aclTensor* reluOpOut = nullptr;
109+ if (selfContiguous->GetDataType() == op::DataType::DT_UINT8) {
110+ reluOpOut = selfContiguous;
111+ } else {
112+ reluOpOut = l0op::ReluV2(selfContiguous, uniqueExecutor.get());
113+ }
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(const aclTensor* self, const aclTensor* out, uint64_t* workspaceSize,
125+ 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(aclTensor* selfRef, uint64_t* workspaceSize, aclOpExecutor** executor)
133+{
134+ OP_CHECK_COMM_INPUT(workspaceSize, executor);
135+ L2_DFX_PHASE_1(aclnnInplaceRelu, DFX_IN(selfRef), DFX_OUT(selfRef));
136+ 
137+ auto ret = CheckInplaceParams(selfRef);
138+ CHECK_RET(ret == ACLNN_SUCCESS, ret);
139+ 
140+ return ExecReluGetWorkspaceSize(selfRef, selfRef, workspaceSize, executor);
141+}
142+ 
143+aclnnStatus aclnnRelu(void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, const aclrtStream stream)
144+{
145+ L2_DFX_PHASE_2(aclnnRelu);
146+ return CommonOpExecutorRun(workspace, workspaceSize, executor, stream);
147+}
148+ 
149+aclnnStatus aclnnInplaceRelu(void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, const aclrtStream stream)
150+{
151+ L2_DFX_PHASE_2(aclnnInplaceRelu);
152+ return CommonOpExecutorRun(workspace, workspaceSize, executor, stream);
153+}
154+ 
155+#ifdef __cplusplus
156+}
157+#endif
@@ -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+#ifndef ACLNN_RELU_H_
12+#define ACLNN_RELU_H_
13+ 
14+#include "aclnn/aclnn_base.h"
15+#include "aclnn_util.h"
16+ 
17+#ifdef __cplusplus
18+extern "C" {
19+#endif
20+ 
21+ACLNN_API aclnnStatus aclnnReluGetWorkspaceSize(const aclTensor* self, const aclTensor* out, uint64_t* workspaceSize,
22+ aclOpExecutor** executor);
23+ 
24+ACLNN_API aclnnStatus aclnnRelu(void* workspace, uint64_t workspaceSize, aclOpExecutor* executor,
25+ const aclrtStream stream);
26+ 
27+ACLNN_API aclnnStatus aclnnInplaceReluGetWorkspaceSize(aclTensor* selfRef, uint64_t* workspaceSize,
28+ aclOpExecutor** executor);
29+ 
30+ACLNN_API aclnnStatus aclnnInplaceRelu(void* workspace, uint64_t workspaceSize, aclOpExecutor* executor,
31+ const aclrtStream stream);
32+ 
33+#ifdef __cplusplus
34+}
35+#endif
36+ 
37+#endif
@@ -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+#include "relu_v2.h"
12+#include "aclnn_kernels/common/op_error_check.h"
13+#include "opdev/make_op_executor.h"
14+#include "opdev/op_def.h"
15+#include "opdev/op_dfx.h"
16+#include "opdev/op_executor.h"
17+#include "opdev/op_log.h"
18+ 
19+using namespace op;
20+ 
21+namespace l0op {
22+ 
23+OP_TYPE_REGISTER(ReluV2);
24+ 
25+const aclTensor* ReluV2(const aclTensor* self, aclOpExecutor* executor)
26+{
27+ L0_DFX(ReluV2, 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(ReluV2, OP_INPUT(self), OP_OUTPUT(reluOut));
32+ OP_CHECK(ret == ACLNN_SUCCESS, OP_LOGE(ACLNN_ERR_INNER_NULLPTR, "ReluV2 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+#ifndef OP_API_OP_API_COMMON_INC_LEVEL0_OP_RELU_V2_H_
12+#define OP_API_OP_API_COMMON_INC_LEVEL0_OP_RELU_V2_H_
13+ 
14+#include "opdev/make_op_executor.h"
15+#include "opdev/op_executor.h"
16+ 
17+namespace l0op {
18+const aclTensor* ReluV2(const aclTensor* self, aclOpExecutor* executor);
19+}
20+ 
21+#endif
@@ -8,4 +8,5 @@
8# See LICENSE in the root of the software repository for the full text of the License.8# See LICENSE in the root of the software repository for the full text of the License.
9# ----------------------------------------------------------------------------9# ----------------------------------------------------------------------------
10 10 
11-add_graph_plugin_sources()11+message(STATUS "=== Debug: start ops.activation.relu.graph_plugin.CMakeLists.txt ")
12+add_graph_plugin_sources()
@@ -1,48 +0,0 @@
1-/**
2- * This file is part of the OpenBOAT project at Harbin Institute of Technology (HIT)
3- * and is contributed to the CANN Open Software.
4- *
5- * Copyright (c) 2025 AISS Group, Harbin Institute of Technology (HIT).
6- * All Rights Reserved.
7- *
8- * Authors (accounts):
9- 
10- * - Tu Yuanhang <@TuYHAAAAAA>
11- * - Su Tonghua <@sutonghua>
12- *
13- * This program is free software: you can redistribute it and/or modify it.
14- * Licensed under the CANN Open Software License Agreement Version 2.0 (the "License").
15- * You may not use this file except in compliance with the License.
16- * See the LICENSE file at the root of the repository for the full text of the License.
17- *
18- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED,
19- * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
20- */
21- 
22-/*!
23- * \file relu_v2_graph_infer.cpp
24- * \brief relu_v2 operater graph infer resource
25- */
26-#include "register/op_impl_registry.h"
27-#include "log/log.h"
28- 
29-namespace ops {
30-using namespace ge;
31- 
32-static constexpr int64_t IDX_0 = 0;
33- 
34-static ge::graphStatus InferDataTypeReluV2(gert::InferDataTypeContext* context)
35-{
36- OP_LOGD(context->GetNodeName(), "Begin to do InferDataTypeReluV2");
37- 
38- // 设置输出的dtype Relu算子逻辑是一个数和0求最大值
39- ge::DataType sizeDtype = context->GetInputDataType(IDX_0);
40- context->SetOutputDataType(IDX_0, sizeDtype);
41- 
42- OP_LOGD(context->GetNodeName(), "End to do InferDataTypeReluV2");
43- return GRAPH_SUCCESS;
44-}
45- 
46-IMPL_OP(ReluV2).InferDataType(InferDataTypeReluV2);
47- 
48-}; // namespace ops
Rexperimental/activation/relu_v2/op_graph/fusion_pass/CMakeLists.txt→experimental/activation/relu_v2/tests/CMakeLists.txt+13-6
Rexperimental/activation/relu_v2/op_host/CMakeLists.txt→experimental/activation/relu_v2/tests/ut/op_api/CMakeLists.txt+10-8