已关闭
【社区任务】floor_div算子设计文档 #197
leozxw创建于 2025年11月11日关闭于 3月20日
【社区任务】floor_div算子设计文档 #197
已关闭
leozxw创建于 2025年11月11日关闭于 3月20日
1 个文件变更+269-0
@@ -0,0 +1,269 @@
1+# aclnnFloorDiv
2+ 
3+## 产品支持情况
4+ 
5+| 产品 | 是否支持 |
6+| --- | --- |
7+| Atlas A3 训练系列产品 / Atlas A3 推理系列产品 | √ |
8+| Atlas A2 训练系列产品 / Atlas 800I A2 推理产品 / A200I A2 Box 异构组件 | √ |
9+ 
10+## 功能说明
11+ 
12+- **算子功能**
13+ 对输入张量 `x1` 和输入张量 `x2` 相除得到`y`,并对`y`中每一个元素执行向下取整(不大于该值的最大整数)操作,并输出结果张量 `out`
14+
15+- **计算公式**
16+
17+ $$
18+ out=⌊\frac{x1}{x2}⌋
19+ $$
20+ 
21+## 函数原型
22+ 
23+每个算子分为[两段式接口](../../../docs/context/%E4%B8%A4%E6%AE%B5%E5%BC%8F%E6%8E%A5%E5%8F%A3.md),必须先调用“aclnnAbsGetWorkspaceSize”接口获取计算所需workspace大小以及包含了算子计算流程的执行器,再调用“aclnnAbs”接口执行计算。
24+ 
25+```Cpp
26+aclnnStatus aclnnFloorDivGetWorkspaceSize(
27+ const aclTensor *self,
28+ aclTensor *out,
29+ uint64_t *workspaceSize,
30+ aclOpExecutor **executor)
31+```
32+ 
33+```Cpp
34+aclnnStatus aclnnFloorDiv(
35+ void *workspace,
36+ uint64_t workspaceSize,
37+ aclOpExecutor *executor,
38+ const aclrtStream stream)
39+```
40+ 
41+## aclnnFloorDivGetWorkspaceSize
42+ 
43+### 参数说明
44+ 
45+| 参数名 | 输入/输出 | 描述 | 使用说明 | 数据类型 | 数据格式 | 维度(shape) | 非连续Tensor |
46+| --- | --- | --- | --- | --- | --- | --- | --- |
47+| x1 | 输入 | 待进行 `floor_div` 计算的输入张量。公式中的 `x1`。 | 无 | FLOAT32、FLOAT16、INT32、INT8、UINT8、BFLOAT16 | ND | 0-8 | √ |
48+| x2 | 输入 | 待进行 `floor_div` 计算的输入张量。公式中的 `x2`。 | shape与`x1`相同 | FLOAT32、FLOAT16、INT32、INT8、UINT8、BFLOAT16 | ND | 0-8 | √ |
49+| out | 输出 | `floor_div` 计算的输出张量,公式中的 `out`。 | shape 与 `x1` 相同 | FLOAT32、FLOAT16、INT32、INT8、UINT8、BFLOAT16 | ND | 0-8 | √ |
50+| workspaceSize | 输出 | 返回 device 侧执行该算子时所需的 workspace 大小。 | - | - | - | - | - |
51+| executor | 输出 | 返回 op 执行器,包含算子计算流程。 | - | - | - | - | - |
52+ 
53+---
54+ 
55+### 返回值
56+ 
57+`aclnnStatus`:返回状态码,具体参见 [aclnn返回码](https://poe.com/docs/context/aclnn%E8%BF%94%E5%9B%9E%E7%A0%81.md)。
58+ 
59+**第一段接口** 会完成入参检查,如以下场景时报错:
60+ 
61+| 返回码 | 错误码 | 描述 |
62+| --- | --- | --- |
63+| ACLNN_ERR_PARAM_NULLPTR | 161001 | 传入的 tensor 是空指针。 |
64+| ACLNN_ERR_PARAM_INVALID | 161002 | self 数据类型或格式不在支持范围内。 |
65+| | | self 数据维度超过 8 维。 |
66+| | | self 与 out 的数据形状不一致。 |
67+ 
68+## aclnnFloorDiv
69+ 
70+### 参数说明
71+ 
72+| 参数名 | 输入/输出 | 描述 |
73+| --- | --- | --- |
74+| workspace | 输入 | 在 Device 侧申请的 workspace 内存地址。 |
75+| workspaceSize | 输入 | workspace 的大小,由第一段接口 `aclnnFloorDivGetWorkspaceSize` 获取。 |
76+| executor | 输入 | op 执行器,包含算子计算流程。 |
77+| stream | 输入 | 指定执行任务的 Stream。 |
78+ 
79+### 返回值
80+ 
81+`aclnnStatus`:返回状态码,具体参见[aclnn返回码](../../../docs/context/aclnn%E8%BF%94%E5%9B%9E%E7%A0%81.md)。
82+ 
83+## 约束说明
84+ 
85+- 当前支持 **FLOAT32、FLOAT16、INT32、INT8、UINT8、BFLOAT16** 三种数据类型
86+ 
87+## 调用示例
88+ 
89+示例代码如下,仅供参考,具体编译和执行过程请参考[编译与运行样例](../../../docs/context/%E7%BC%96%E8%AF%91%E4%B8%8E%E8%BF%90%E8%A1%8C%E6%A0%B7%E4%BE%8B.md)。
90+ 
91+```Cpp
92+/**
93+ * This program is free software, you can redistribute it and/or modify.
94+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
95+ * This file is a part of the CANN Open Software.
96+ * Licensed under CANN Open Software License Agreement Version 2.0 (the "License").
97+ * Please refer to the License for details. You may not use this file except in compliance with the License.
98+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
99+ * BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
100+ * See LICENSE in the root of the software repository for the full text of the License.
101+ */
102+ 
103+#include <iostream>
104+#include <vector>
105+#include "acl/acl.h"
106+#include "aclnn_floor_div.h"
107+// 修改测试数据类型
108+using DataType = int32_t;
109+#define ACL_TYPE aclDataType::ACL_INT32
110+#define CHECK_RET(cond, return_expr) \
111+ do { \
112+ if (!(cond)) { \
113+ return_expr; \
114+ } \
115+ } while (0)
116+ 
117+#define LOG_PRINT(message, ...) \
118+ do { \
119+ printf(message, ##__VA_ARGS__); \
120+ } while (0)
121+ 
122+int64_t GetShapeSize(const std::vector<int64_t>& shape)
123+{
124+ int64_t shapeSize = 1;
125+ for (auto i : shape) {
126+ shapeSize *= i;
127+ }
128+ return shapeSize;
129+}
130+ 
131+void PrintOutResult(std::vector<int64_t>& shape, void** deviceAddr)
132+{
133+ auto size = GetShapeSize(shape);
134+ std::vector<DataType> resultData(size, 0);
135+ auto ret = aclrtMemcpy(
136+ resultData.data(), resultData.size() * sizeof(resultData[0]), *deviceAddr, size * sizeof(resultData[0]),
137+ 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);
139+ for (int64_t i = 0; i < size; i++) {
140+ LOG_PRINT("mean result[%ld] is: ", i); // float
141+ std::cout << resultData[i] << std::endl;
142+ //LOG_PRINT("mean result[%ld] is: %d\n", i, resultData[i]); // int
143+ }
144+}
145+ 
146+int Init(int32_t deviceId, aclrtStream* stream)
147+{
148+ // 固定写法,初始化
149+ auto ret = aclInit(nullptr);
150+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
151+ ret = aclrtSetDevice(deviceId);
152+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
153+ ret = aclrtCreateStream(stream);
154+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
155+ return 0;
156+}
157+ 
158+template <typename T>
159+int CreateAclTensor(
160+ const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr, aclDataType dataType,
161+ aclTensor** tensor)
162+{
163+ auto size = GetShapeSize(shape) * sizeof(T);
164+ // 2. 申请device侧内存
165+ auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
166+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
167+ // 3. 调用aclrtMemcpy将host侧数据拷贝到device侧内存上
168+ ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
169+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);
170+ 
171+ // 计算连续tensor的strides
172+ std::vector<int64_t> strides(shape.size(), 1);
173+ for (int64_t i = shape.size() - 2; i >= 0; i--) {
174+ strides[i] = shape[i + 1] * strides[i + 1];
175+ }
176+ 
177+ // 调用aclCreateTensor接口创建aclTensor
178+ *tensor = aclCreateTensor(
179+ shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(),
180+ *deviceAddr);
181+ return 0;
182+}
183+ 
184+int main()
185+{
186+ // 1. 调用acl进行device/stream初始化
187+ int32_t deviceId = 0;
188+ aclrtStream stream;
189+ auto ret = Init(deviceId, &stream);
190+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
191+ 
192+ // 2. 构造输入与输出,需要根据API的接口自定义构造
193+ aclTensor* selfX = nullptr;
194+ void* selfXDeviceAddr = nullptr;
195+ std::vector<int64_t> selfXShape = {1, 1, 3, 4};
196+ std::vector<DataType> selfXHostData(12);
197+ for(int i = 0; i < selfXHostData.size(); i++) {
198+ selfXHostData[i] = (DataType)(i - (int)selfXHostData.size() / 2);
199+ }
200+ ret = CreateAclTensor(selfXHostData, selfXShape, &selfXDeviceAddr, ACL_TYPE, &selfX);
201+ CHECK_RET(ret == ACL_SUCCESS, return ret);
202+ 
203+ aclTensor* selfY = nullptr;
204+ void* selfYDeviceAddr = nullptr;
205+ std::vector<int64_t> selfYShape = {1, 1, 3, 4};
206+ std::vector<DataType> selfYHostData(12, 2.0);
207+ ret = CreateAclTensor(selfYHostData, selfYShape, &selfYDeviceAddr, ACL_TYPE, &selfY);
208+ CHECK_RET(ret == ACL_SUCCESS, return ret);
209+ 
210+ aclTensor* out = nullptr;
211+ void* outDeviceAddr = nullptr;
212+ std::vector<int64_t> outShape = {1, 1, 3, 4};
213+ std::vector<DataType> outHostData(12, 300.0);
214+ ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, ACL_TYPE, &out);
215+ CHECK_RET(ret == ACL_SUCCESS, return ret);
216+ 
217+ // 3. 调用CANN算子库API,需要修改为具体的Api名称
218+ uint64_t workspaceSize = 0;
219+ aclOpExecutor* executor;
220+ 
221+ LOG_PRINT("Before GetWorkspaceSize: selfX=%p, selfY=%p, out=%p\n", (void*)selfX, (void*)selfY, (void*)out);
222+ LOG_PRINT("Before GetWorkspaceSize: selfXDeviceAddr=%p, selfYDeviceAddr=%p, outDeviceAddr=%p\n",
223+ selfXDeviceAddr, selfYDeviceAddr, outDeviceAddr);
224+ // 4. 调用aclnnAddExample第一段接口
225+ ret = aclnnFloorDivGetWorkspaceSize(selfX, selfY, out, &workspaceSize, &executor);
226+ LOG_PRINT("aclnnFloorDivGetWorkspaceSize returned %d, workspaceSize=%llu, executor=%p\n",
227+ ret, (unsigned long long)workspaceSize, (void*)executor);
228+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnFloorDivExampleGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
229+ 
230+ // 根据第一段接口计算出的workspaceSize申请device内存
231+ void* workspaceAddr = nullptr;
232+ if (workspaceSize > static_cast<uint64_t>(0)) {
233+ ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
234+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
235+ }
236+ 
237+ // 5. 调用aclnnAddExample第二段接口
238+ ret = aclnnFloorDiv(workspaceAddr, workspaceSize, executor, stream);
239+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnMulExample failed. ERROR: %d\n", ret); return ret);
240+ 
241+ // 6. (固定写法)同步等待任务执行结束
242+ ret = aclrtSynchronizeStream(stream);
243+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
244+ 
245+ // 5. 获取输出的值,将device侧内存上的结果拷贝至host侧,需要根据具体API的接口定义修改
246+ std::vector<int64_t> outShape1 = {12};
247+ PrintOutResult(outShape1, &outDeviceAddr);
248+ 
249+ // 7. 释放aclTensor,需要根据具体API的接口定义修改
250+ aclDestroyTensor(selfX);
251+ aclDestroyTensor(selfY);
252+ aclDestroyTensor(out);
253+ 
254+ // 8. 释放device资源
255+ aclrtFree(selfXDeviceAddr);
256+ aclrtFree(selfYDeviceAddr);
257+ aclrtFree(outDeviceAddr);
258+ if (workspaceSize > static_cast<uint64_t>(0)) {
259+ aclrtFree(workspaceAddr);
260+ }
261+ aclrtDestroyStream(stream);
262+ aclrtResetDevice(deviceId);
263+ 
264+ // 9. acl去初始化
265+ aclFinalize();
266+ 
267+ return 0;
268+}
269+```