已关闭
【社区任务】floor_div算子设计文档 #197
leozxw创建于 2025年11月11日关闭于 3月20日
【社区任务】floor_div算子设计文档 #197
已关闭
共 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 | +``` | ||