已合并
aclnnAddN删除broadcast操作并支持标量tensor #3655
fitZepHYr创建于 6月30日
aclnnAddN删除broadcast操作并支持标量tensor #3655
已合并
fitZepHYr创建于 6月30日
4 个文件变更+39-78
Mmath/add_n/docs/aclnnAddN.md+9-13
@@ -70,8 +70,8 @@ aclnnStatus aclnnAddN(
70 <td>tensors(const aclTensorList*)</td>70 <td>tensors(const aclTensorList*)</td>
71 <td>输入</td>71 <td>输入</td>
72 <td>输入TensorList,对输入tensors进行主元素相加求和操作。</td>72 <td>输入TensorList,对输入tensors进行主元素相加求和操作。</td>
73- <td><li>支持空TensorList,此时输出为空Tensor。</li><li>tensors中的Tensor需要满足<a href="../../../docs/zh/context/broadcast关系.md" class="md-link">broadcast关系</a>。</li><li>不支持标量Tensor(0维度)。</li></td>73+ <td><li>支持空TensorList,此时输出为空Tensor。</li><li>tensors中的Tensor需要满足shape一致。</li></td>
74- <td>INT32, INT64, FLOAT16, BFLOAT16, FLOAT32</td>74+ <td>INT32INT64FLOAT16BFLOAT16FLOAT32</td>
75 <td>ND</td>75 <td>ND</td>
76 <td>1~8</td>76 <td>1~8</td>
77 <td>√</td>77 <td>√</td>
@@ -80,7 +80,7 @@ aclnnStatus aclnnAddN(
80 <td>out(aclTensor*)</td>80 <td>out(aclTensor*)</td>
81 <td>输出</td>81 <td>输出</td>
82 <td>输出Tensor,存储求和结果。</td>82 <td>输出Tensor,存储求和结果。</td>
83- <td><li>不支持空Tensor。</li><li>数据类型需与tensors中的Tensor保持一致。</li><li>shape需要与tensors中的Tensor做broadcast后的shape一致。</li></td>83+ <td><li>不支持空Tensor。</li><li>数据类型需与tensors中的Tensor保持一致。</li><li>shape需要与tensors中的Tensor的shape一致。</li></td>
84 <td>数据类型与tensors保持一致。</td>84 <td>数据类型与tensors保持一致。</td>
85 <td>ND</td>85 <td>ND</td>
86 <td>1~8</td>86 <td>1~8</td>
@@ -133,21 +133,18 @@ aclnnStatus aclnnAddN(
133 <td>tensors是空指针,或tensors中的某个Tensor是空指针,或out是空指针。</td>133 <td>tensors是空指针,或tensors中的某个Tensor是空指针,或out是空指针。</td>
134 </tr>134 </tr>
135 <tr>135 <tr>
136- <td rowspan="6">ACLNN_ERR_PARAM_INVALID</td>136+ <td rowspan="5">ACLNN_ERR_PARAM_INVALID</td>
137- <td rowspan="6">161002</td>137+ <td rowspan="5">161002</td>
138 <td>tensors或out的数据类型不在支持的范围之内。</td>138 <td>tensors或out的数据类型不在支持的范围之内。</td>
139 </tr>139 </tr>
140 <tr>140 <tr>
141- <td>tensors或out的shape维度超过8,或为0维度(标量)。</td>141+ <td>tensors或out的shape维度超过8。</td>
142 </tr>142 </tr>
143 <tr>143 <tr>
144 <td>tensors中的Tensor数据类型不一致。</td>144 <td>tensors中的Tensor数据类型不一致。</td>
145 </tr>145 </tr>
146 <tr>146 <tr>
147- <td>tensors中的Tensor无法进行broadcast。</td>147+ <td>tensors中的Tensor shape不一致。</td>
148- </tr>
149- <tr>
150- <td>out的shape与tensors broadcast后的shape不一致。</td>
151 </tr>148 </tr>
152 <tr>149 <tr>
153 <td>当前NPU架构不支持此算子,仅支持ASCEND910B(A2)和ASCEND910_93(A3)系列。</td>150 <td>当前NPU架构不支持此算子,仅支持ASCEND910B(A2)和ASCEND910_93(A3)系列。</td>
@@ -189,10 +186,9 @@ aclnnStatus aclnnAddN(
189 <details>186 <details>
190 <summary>主场景说明:</summary>187 <summary>主场景说明:</summary>
191 188 
192- - 数据类型:支持INT32, INT64, FLOAT16, BFLOAT16, FLOAT32。189+ - 数据类型:支持INT32INT64FLOAT16BFLOAT16FLOAT32。
193 - 数据格式:仅支持ND格式。190 - 数据格式:仅支持ND格式。
194- - 参数Shape:输入Tensor维度范围为1~8,不支持标量Tensor191+ - 参数Shape:输入Tensor维度范围为1~8,需要满足shape一致
195- - Broadcast:输入TensorList中的Tensor需要满足broadcast关系。
196 192 
197 </details>193 </details>
198 194 
Mmath/add_n/op_api/aclnn_add_n.cpp+24-47
@@ -77,18 +77,6 @@ static bool CheckArch()
77 return true;77 return true;
78}78}
79 79 
80-// 获取BroadcastShape
81-static bool GetTensorBroadcastShape(const aclTensorList* tensors, op::Shape &broadcastShape)
82-{
83- broadcastShape = (*tensors)[0]->GetViewShape();
84- for (uint64_t i = 1; i < tensors->Size(); i++) {
85- if (!BroadcastInferShape((*tensors)[i]->GetViewShape(), broadcastShape, broadcastShape)) {
86- return false;
87- }
88- }
89- return true;
90-}
91- 
92// 进行Shape检查80// 进行Shape检查
93static bool CheckShape(const aclTensorList* tensors, const aclTensor* out)81static bool CheckShape(const aclTensorList* tensors, const aclTensor* out)
94{82{
@@ -99,21 +87,22 @@ static bool CheckShape(const aclTensorList* tensors, const aclTensor* out)
99 MAX_SUPPORT_DIMS_NUMS);87 MAX_SUPPORT_DIMS_NUMS);
100 return false;88 return false;
101 }89 }
102- if (dimNum == 0) {
103- OP_LOGE(ACLNN_ERR_PARAM_INVALID, "Input tensor %lu has 0 dimension (scalar), which is not supported in this operator.", i);
104- return false;
105- }
106 }90 }
107 OP_CHECK_MAX_DIM(out, MAX_SUPPORT_DIMS_NUMS, return false);91 OP_CHECK_MAX_DIM(out, MAX_SUPPORT_DIMS_NUMS, return false);
108 92
109- op::Shape broadcastShape;93+ auto shape = (*tensors)[0]->GetViewShape();
110- if (!GetTensorBroadcastShape(tensors, broadcastShape)) {94+ for (uint64_t i = 1; i < tensors->Size(); i++) {
111- // 检查输入tensors中的tensor是否都满足broadcast规则95+ if ((*tensors)[i]->GetViewShape() != shape) {
112- OP_LOGE(ACLNN_ERR_PARAM_INVALID, "Input tensors can't broadcast.");96+ OP_LOGE(ACLNN_ERR_PARAM_INVALID, "Input tensors should have same shape.");
97+ return false;
98+ }
99+ }
100+ 
101+ if (shape != out->GetViewShape()) {
102+ OP_LOGE(ACLNN_ERR_PARAM_INVALID, "Input tensors should have same shape with output.");
113 return false;103 return false;
114 }104 }
atomgit-bot
atomgit-botatomgit-bot6月30日

🟠 High Priority

变更删除了 aclnn_add_n.cpp 中的 broadcast 逻辑(移除 GetTensorBroadcastShape 函数、移除 BroadcastTo 调用),但头文件 aclnn_add_n.h 未同步更新:

  • 第 30 行注释:tensors中的tensor需要满足broadcast关系。 应改为 shape 一致。
  • 第 34 行注释:shape需要与tensors中的tensor做broadcast后的shape一致。 应改为与 tensors shape 一致。

后果:头文件是 API 的对外契约文档,残留的 broadcast 描述会误导调用方,使其错误地认为 broadcast 仍然生效。

建议:同步更新 aclnn_add_n.h 中第 30、34 行的参数描述和第 51-55 行的 mermaid 流程图,移除 broadcast 相关描述,改为 shape 一致性要求。

likedislike
115- // 输出shape应该等于输入tensors经过broadcast后的shape105+ 
116- OP_CHECK_SHAPE_NOT_EQUAL_WITH_EXPECTED_SIZE(out, broadcastShape, return false);
117 return true;106 return true;
118}107}
119 108 
@@ -172,32 +161,20 @@ aclnnStatus aclnnAddNGetWorkspaceSize(const aclTensorList* tensors, aclTensor *o
172 }161 }
173 }162 }
174 163
175- // 对输入tensors进行broadcast164+ aclTensor *addnOut = nullptr;
176- op::Shape broadcastShape = (*tensors)[0]->GetViewShape();165+ if (tensors->Size() == 1) {
177- for (uint64_t i = 1; i < tensors->Size(); i++) {166+ addnOut = const_cast<aclTensor*>((*tensors)[0]);
178- BroadcastInferShape((*tensors)[i]->GetViewShape(), broadcastShape, broadcastShape);
179- }
180- 
181- op::FVector<int64_t, op::MAX_DIM_NUM> broadcastDims = op::ToShapeVector(broadcastShape);
182- auto broadcastShapeArray = uniqueExecutor.get()->AllocIntArray(broadcastDims.data(), broadcastDims.size());
183- CHECK_RET(broadcastShapeArray != nullptr, ACLNN_ERR_INNER_NULLPTR);
184-
185- op::FVector<const aclTensor *> tensorList;
186- for (uint64_t i = 0; i < tensors->Size(); i++) {
187- auto tensorsContiguous = l0op::Contiguous((*tensors)[i], uniqueExecutor.get());
188- CHECK_RET(tensorsContiguous != nullptr, ACLNN_ERR_INNER_NULLPTR);
189- auto tensorsBroadcast = l0op::BroadcastTo(tensorsContiguous, broadcastShapeArray, uniqueExecutor.get());
190- CHECK_RET(tensorsBroadcast != nullptr, ACLNN_ERR_INNER_NULLPTR);
191- tensorList.push_back(tensorsBroadcast);
192- }
193- 
194- // 调用AddN算子计算
195- const aclTensorList *inputList = uniqueExecutor.get()->AllocTensorList(tensorList.data(), tensorList.size());
196- const aclTensor *addnOut;
197- if (tensors->Size() == 1 || broadcastDims.size() == 0) {
198- addnOut = (*tensors)[0];
199 } else {167 } else {
200- addnOut = l0op::AddN(inputList, uniqueExecutor.get());168+ op::FVector<const aclTensor *> tensorList;
169+ for (uint64_t i = 0; i < tensors->Size(); i++) {
170+ auto tensorsContiguous = l0op::Contiguous((*tensors)[i], uniqueExecutor.get());
171+ CHECK_RET(tensorsContiguous != nullptr, ACLNN_ERR_INNER_NULLPTR);
172+ tensorList.push_back(tensorsContiguous);
173+ }
174+
175+ // 调用AddN算子计算
176+ const aclTensorList *inputList = uniqueExecutor.get()->AllocTensorList(tensorList.data(), tensorList.size());
177+ addnOut = const_cast<aclTensor*>(l0op::AddN(inputList, uniqueExecutor.get()));
201 }178 }
202 179 
203 CHECK_RET(addnOut != nullptr, ACLNN_ERR_INNER_NULLPTR);180 CHECK_RET(addnOut != nullptr, ACLNN_ERR_INNER_NULLPTR);
Mmath/add_n/op_api/aclnn_add_n.h+6-6
@@ -26,12 +26,12 @@ extern "C" {
26 * @domain aclnn_math26 * @domain aclnn_math
27 * 参数描述:27 * 参数描述:
28 * @param [in] tensors28 * @param [in] tensors
29- * 输入TensorList,数据类型支持 INT32, INT64, FLOAT16, BFLOAT16, FLOAT32。29+ * 输入TensorList,数据类型支持 INT32INT64FLOAT16BFLOAT16FLOAT32。
30- * tensors中的tensor需要满足broadcast关系30+ * tensors中的tensor需要满足shape一致
31 * 支持非连续的Tensor,数据格式支持ND。31 * 支持非连续的Tensor,数据格式支持ND。
32 * @param [out] out32 * @param [out] out
33- * 输出Tensor,数据类型支持 INT32, INT64, FLOAT16, BFLOAT16, FLOAT32。33+ * 输出Tensor,数据类型支持 INT32INT64FLOAT16BFLOAT16FLOAT32。
34- * shape需要与tensors中的tensor做broadcast后的shape一致。34+ * shape需要与tensors中的tensor的shape一致。
35 * 支持非连续的Tensor,数据格式支持ND。35 * 支持非连续的Tensor,数据格式支持ND。
36 * @param [out] workspaceSize 返回用户需要在npu device侧申请的workspace大小。36 * @param [out] workspaceSize 返回用户需要在npu device侧申请的workspace大小。
37 * @param [out] executor 返回op执行器,包含了算子计算流程。37 * @param [out] executor 返回op执行器,包含了算子计算流程。
@@ -50,8 +50,8 @@ ACLNN_API aclnnStatus aclnnAddNGetWorkspaceSize(const aclTensorList *tensors, ac
50 * api计算的基本路径:50 * api计算的基本路径:
51```mermaid51```mermaid
52flowchart LR52flowchart LR
53- A[(tensors)]-->B([l0op::Contiguous])-->C[(l0op::BroadcastTo)]-->D([l0op::AddN])53+ A[(tensors)]-->B([l0op::Contiguous])-->C([l0op::AddN])
54- -->E([l0op::ViewCopy])-->F[(out)]54+ -->D([l0op::ViewCopy])-->E[(out)]
55```55```
56 * @param [in] workspace: 在npu device侧申请的workspace内存起址。56 * @param [in] workspace: 在npu device侧申请的workspace内存起址。
57 * @param [in] workspaceSize: 在npu device侧申请的workspace大小,由第一段接口aclnnAddNGetWorkspaceSize获取。57 * @param [in] workspaceSize: 在npu device侧申请的workspace大小,由第一段接口aclnnAddNGetWorkspaceSize获取。
Mmath/add_n/tests/ut/op_api/test_aclnn_add_n.cpp+0-12
@@ -149,16 +149,4 @@ TEST_F(l2_addn_test, l2_addn_test_10dims) {
149 uint64_t workspace_size = 0;149 uint64_t workspace_size = 0;
150 aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspace_size);150 aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspace_size);
151 EXPECT_EQ(aclRet, ACLNN_ERR_PARAM_INVALID);151 EXPECT_EQ(aclRet, ACLNN_ERR_PARAM_INVALID);
152-}
153- 
154-// broadcast
155-TEST_F(l2_addn_test, l2_addn_test_broadcast) {
156- auto tensor_1_desc = TensorDesc({2, 3}, ACL_FLOAT, ACL_FORMAT_ND).Value(vector<float>{1, 2, 3, 4, 5, 6});
157- auto tensor_2_desc = TensorDesc({2, 3}, ACL_FLOAT, ACL_FORMAT_ND).Value(vector<float>{1, 2, 3, 4, 5, 6});
158- auto out_tensor_desc = TensorDesc({1, 3}, ACL_FLOAT, ACL_FORMAT_ND);
159- auto tensor_list_desc = TensorListDesc({tensor_1_desc, tensor_2_desc});
160- auto ut = OP_API_UT(aclnnAddN, INPUT(tensor_list_desc), OUTPUT(out_tensor_desc));
161- uint64_t workspace_size = 0;
162- aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspace_size);
163- EXPECT_EQ(aclRet, ACLNN_ERR_PARAM_INVALID);
164}152}