已合并
aclnnAddN删除broadcast操作并支持标量tensor #3655
fitZepHYr创建于 6月30日
aclnnAddN删除broadcast操作并支持标量tensor #3655
已合并
共 4 个文件变更+39-78
| @@ -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>INT32,INT64,FLOAT16,BFLOAT16,FLOAT32</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 | + - 数据类型:支持INT32,INT64,FLOAT16,BFLOAT16,FLOAT32。 |
| 193 | - 数据格式:仅支持ND格式。 | 190 | - 数据格式:仅支持ND格式。 |
| 194 | - - 参数Shape:输入Tensor维度范围为1~8,不支持标量Tensor。 | 191 | + - 参数Shape:输入Tensor维度范围为1~8,需要满足shape一致。 |
| 195 | - - Broadcast:输入TensorList中的Tensor需要满足broadcast关系。 | ||
| 196 | 192 | ||
| 197 | </details> | 193 | </details> |
| 198 | 194 | ||
| @@ -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检查 |
| 93 | static bool CheckShape(const aclTensorList* tensors, const aclTensor* out) | 81 | static 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 | } |
| 115 | - // 输出shape应该等于输入tensors经过broadcast后的shape | 105 | + |
| 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进行broadcast | 164 | + 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); |
| @@ -26,12 +26,12 @@ extern "C" { | |||
| 26 | * @domain aclnn_math | 26 | * @domain aclnn_math |
| 27 | * 参数描述: | 27 | * 参数描述: |
| 28 | * @param [in] tensors | 28 | * @param [in] tensors |
| 29 | - * 输入TensorList,数据类型支持 INT32, INT64, FLOAT16, BFLOAT16, FLOAT32。 | 29 | + * 输入TensorList,数据类型支持 INT32,INT64,FLOAT16,BFLOAT16,FLOAT32。 |
| 30 | - * tensors中的tensor需要满足broadcast关系。 | 30 | + * tensors中的tensor需要满足shape一致。 |
| 31 | * 支持非连续的Tensor,数据格式支持ND。 | 31 | * 支持非连续的Tensor,数据格式支持ND。 |
| 32 | * @param [out] out | 32 | * @param [out] out |
| 33 | - * 输出Tensor,数据类型支持 INT32, INT64, FLOAT16, BFLOAT16, FLOAT32。 | 33 | + * 输出Tensor,数据类型支持 INT32,INT64,FLOAT16,BFLOAT16,FLOAT32。 |
| 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 | ```mermaid | 51 | ```mermaid |
| 52 | flowchart LR | 52 | flowchart 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获取。 |
| @@ -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 | } |
🟠 High Priority
变更删除了 aclnn_add_n.cpp 中的 broadcast 逻辑(移除 GetTensorBroadcastShape 函数、移除 BroadcastTo 调用),但头文件 aclnn_add_n.h 未同步更新:
tensors中的tensor需要满足broadcast关系。应改为 shape 一致。shape需要与tensors中的tensor做broadcast后的shape一致。应改为与 tensors shape 一致。后果:头文件是 API 的对外契约文档,残留的 broadcast 描述会误导调用方,使其错误地认为 broadcast 仍然生效。
建议:同步更新 aclnn_add_n.h 中第 30、34 行的参数描述和第 51-55 行的 mermaid 流程图,移除 broadcast 相关描述,改为 shape 一致性要求。