已合并
【代码侦探Challenge05】补全gcw_93rlw6ed的MulCustom逐元素乘法算子 #2433
gcw_93rlw6ed创建于 9月1日
【代码侦探Challenge05】补全gcw_93rlw6ed的MulCustom逐元素乘法算子 #2433
已合并
共 3 个文件变更+395-0
| @@ -0,0 +1,11 @@ | |||
| 1 | +# mul_custom.asc 逐元素乘法算子构建脚本(与模板一致,无需修改) | ||
| 2 | +cmake_minimum_required(VERSION 3.16) | ||
| 3 | +find_package(ASC REQUIRED) | ||
| 4 | +project(kernel_samples LANGUAGES ASC CXX) | ||
| 5 | + | ||
| 6 | +add_executable(mul_test | ||
| 7 | + mul_custom.asc | ||
| 8 | +) | ||
| 9 | +target_compile_options(mul_test PRIVATE | ||
| 10 | + $<$<COMPILE_LANGUAGE:ASC>:--npu-arch=dav-2201> | ||
| 11 | +) | ||
| @@ -0,0 +1,375 @@ | |||
| 1 | +// mul_custom.asc | ||
| 2 | +// 逐元素乘法算子:z[i] = x[i] * y[i] | ||
| 3 | +// | ||
| 4 | +// 学习自第2章 Add 算子示例,实现包含以下部分: | ||
| 5 | +// 1. KernelMul 类成员变量声明 | ||
| 6 | +// 2. Init —— 初始化 TPipe、TQue、GlobalTensor | ||
| 7 | +// 3. Process —— 主循环调用 CopyIn → Compute → CopyOut | ||
| 8 | +// 4. CopyIn —— 从 GM 搬入数据到 UB | ||
| 9 | +// 5. Compute —— 执行 Mul 计算 | ||
| 10 | +// 6. CopyOut —— 将结果从 UB 搬回 GM | ||
| 11 | +// 7. mul_custom kernel 入口函数 | ||
| 12 | +// 8. kernel_mul host 侧函数 | ||
| 13 | +// 9. main 函数中调用 kernel_mul | ||
| 14 | + | ||
| 15 | +#include <cstdint> | ||
| 16 | +#include <iostream> | ||
| 17 | +#include <vector> | ||
| 18 | +#include <algorithm> | ||
| 19 | +#include <iterator> | ||
| 20 | +#include "acl/acl.h" | ||
| 21 | +#include "kernel_operator.h" | ||
| 22 | + | ||
| 23 | +constexpr uint32_t BUFFER_NUM = 2; // Double Buffer 队列深度 | ||
| 24 | + | ||
| 25 | +struct MulCustomTilingData | ||
| 26 | +{ | ||
| 27 | + uint32_t totalLength; | ||
| 28 | + uint32_t tileNum; | ||
| 29 | +}; | ||
| 30 | + | ||
| 31 | +class KernelMul { | ||
| 32 | +public: | ||
| 33 | + __aicore__ inline KernelMul() {} | ||
| 34 | + __aicore__ inline void Init(GM_ADDR x, GM_ADDR y, GM_ADDR z, uint32_t totalLength, uint32_t tileNum) | ||
| 35 | + { | ||
| 36 | + // 1. 单核处理长度:totalLength 均分到每个 AI Core | ||
| 37 | + this->blockLength = totalLength / AscendC::GetBlockNum(); | ||
| 38 | + this->tileNum = tileNum; | ||
| 39 | + // 2. 分片长度:单核数据按 tileNum 切分,每份再拆成 BUFFER_NUM 块构成双缓冲 | ||
| 40 | + this->tileLength = this->blockLength / tileNum / BUFFER_NUM; | ||
| 41 | + | ||
| 42 | + // 3. 每个核通过 GetBlockIdx() 偏移,只处理自己的那段 Global Memory | ||
| 43 | + uint32_t blockOffset = this->blockLength * AscendC::GetBlockIdx(); | ||
| 44 | + xGm.SetGlobalBuffer((__gm__ float *)x + blockOffset, this->blockLength); | ||
| 45 | + yGm.SetGlobalBuffer((__gm__ float *)y + blockOffset, this->blockLength); | ||
| 46 | + zGm.SetGlobalBuffer((__gm__ float *)z + blockOffset, this->blockLength); | ||
| 47 | + | ||
| 48 | + // 4. 为三个队列各分配 BUFFER_NUM 块 UB 缓冲,每块 tileLength 个 float | ||
| 49 | + pipe.InitBuffer(inQueueX, BUFFER_NUM, this->tileLength * sizeof(float)); | ||
| 50 | + pipe.InitBuffer(inQueueY, BUFFER_NUM, this->tileLength * sizeof(float)); | ||
| 51 | + pipe.InitBuffer(outQueueZ, BUFFER_NUM, this->tileLength * sizeof(float)); | ||
| 52 | + } | ||
| 53 | + __aicore__ inline void Process() | ||
| 54 | + { | ||
| 55 | + // 主循环:tileNum * BUFFER_NUM 次迭代,三段流水通过队列解耦 | ||
| 56 | + int32_t loopCount = this->tileNum * BUFFER_NUM; | ||
| 57 | + for (int32_t i = 0; i < loopCount; ++i) { | ||
| 58 | + CopyIn(i); | ||
| 59 | + Compute(i); | ||
| 60 | + CopyOut(i); | ||
| 61 | + } | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | +private: | ||
| 65 | + __aicore__ inline void CopyIn(int32_t progress) | ||
| 66 | + { | ||
| 67 | + // 从 GM 搬入第 progress 个分片到 UB,并入队 | ||
| 68 | + AscendC::LocalTensor<float> xLocal = inQueueX.AllocTensor<float>(); | ||
| 69 | + AscendC::LocalTensor<float> yLocal = inQueueY.AllocTensor<float>(); | ||
| 70 | + | ||
| 71 | + AscendC::DataCopy(xLocal, xGm[progress * this->tileLength], this->tileLength); | ||
| 72 | + AscendC::DataCopy(yLocal, yGm[progress * this->tileLength], this->tileLength); | ||
| 73 | + | ||
| 74 | + inQueueX.EnQue(xLocal); | ||
| 75 | + inQueueY.EnQue(yLocal); | ||
| 76 | + } | ||
| 77 | + __aicore__ inline void Compute(int32_t progress) | ||
| 78 | + { | ||
| 79 | + // 出队输入,执行逐元素乘法 zLocal = xLocal * yLocal,结果入队 | ||
| 80 | + AscendC::LocalTensor<float> xLocal = inQueueX.DeQue<float>(); | ||
| 81 | + AscendC::LocalTensor<float> yLocal = inQueueY.DeQue<float>(); | ||
| 82 | + AscendC::LocalTensor<float> zLocal = outQueueZ.AllocTensor<float>(); | ||
| 83 | + | ||
| 84 | + AscendC::Mul(zLocal, xLocal, yLocal, this->tileLength); | ||
| 85 | + | ||
| 86 | + outQueueZ.EnQue<float>(zLocal); | ||
| 87 | + | ||
| 88 | + inQueueX.FreeTensor(xLocal); | ||
| 89 | + inQueueY.FreeTensor(yLocal); | ||
| 90 | + } | ||
| 91 | + __aicore__ inline void CopyOut(int32_t progress) | ||
| 92 | + { | ||
| 93 | + // 出队结果,从 UB 搬回 GM | ||
| 94 | + AscendC::LocalTensor<float> zLocal = outQueueZ.DeQue<float>(); | ||
| 95 | + | ||
| 96 | + AscendC::DataCopy(zGm[progress * this->tileLength], zLocal, this->tileLength); | ||
| 97 | + | ||
| 98 | + outQueueZ.FreeTensor(zLocal); | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | +private: | ||
| 102 | + AscendC::TPipe pipe; | ||
| 103 | + | ||
| 104 | + AscendC::TQue<AscendC::TPosition::VECIN, BUFFER_NUM> inQueueX; | ||
| 105 | + AscendC::TQue<AscendC::TPosition::VECIN, BUFFER_NUM> inQueueY; | ||
| 106 | + AscendC::TQue<AscendC::TPosition::VECOUT, BUFFER_NUM> outQueueZ; | ||
| 107 | + | ||
| 108 | + AscendC::GlobalTensor<float> xGm; | ||
| 109 | + AscendC::GlobalTensor<float> yGm; | ||
| 110 | + AscendC::GlobalTensor<float> zGm; | ||
| 111 | + | ||
| 112 | + uint32_t blockLength; | ||
| 113 | + uint32_t tileNum; | ||
| 114 | + uint32_t tileLength; | ||
| 115 | +}; | ||
| 116 | + | ||
| 117 | +// Kernel 入口函数 | ||
| 118 | +__global__ __aicore__ void mul_custom(GM_ADDR x, GM_ADDR y, GM_ADDR z, MulCustomTilingData tiling) | ||
| 119 | +{ | ||
| 120 | + // 显式指定 Kernel 任务类型为 AIV-only(Vector 核) | ||
| 121 | + KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIV_ONLY); | ||
| 122 | + KernelMul op; | ||
| 123 | + op.Init(x, y, z, tiling.totalLength, tiling.tileNum); | ||
| 124 | + op.Process(); | ||
| 125 | +} | ||
| 126 | + | ||
| 127 | +// ----- Host 侧:kernel 直调封装 ----- | ||
| 128 | + | ||
| 129 | +std::vector<float> kernel_mul(std::vector<float> &x, std::vector<float> &y) | ||
| 130 | +{ | ||
| 131 | + if (x.size() != y.size()) { | ||
| 132 | + std::cerr << "Error: input vectors must have the same size." << std::endl; | ||
| 133 | + return {}; | ||
| 134 | + } | ||
| 135 | + if (x.empty()) { | ||
| 136 | + std::cerr << "Error: input vectors must not be empty." << std::endl; | ||
| 137 | + return {}; | ||
| 138 | + } | ||
| 139 | + | ||
| 140 | + // 1~2. 多核与分片参数:8 核并行,每核数据再分 8 个 tile(×BUFFER_NUM 双缓冲) | ||
| 141 | + const uint32_t totalLength = static_cast<uint32_t>(x.size()); | ||
| 142 | + constexpr uint32_t blockDim = 8; | ||
| 143 | + constexpr uint32_t tileNum = 8; | ||
| 144 | + const size_t byteSize = totalLength * sizeof(float); | ||
| 145 | + | ||
| 146 | + std::vector<float> output(totalLength, 0.0f); | ||
| 147 | + | ||
| 148 | + // 3. 初始化 ACL 运行环境 | ||
| 149 | + auto ret = aclInit(nullptr); | ||
| 150 | + if (ret != ACL_SUCCESS) { | ||
| 151 | + std::cerr << "aclInit failed. ERROR: " << ret << std::endl; | ||
| 152 | + return output; | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | + int32_t deviceId = 0; | ||
| 156 | + ret = aclrtSetDevice(deviceId); | ||
| 157 | + if (ret != ACL_SUCCESS) { | ||
| 158 | + std::cerr << "aclrtSetDevice failed. ERROR: " << ret << std::endl; | ||
| 159 | + aclFinalize(); | ||
| 160 | + return output; | ||
| 161 | + } | ||
| 162 | + | ||
| 163 | + aclrtStream stream = nullptr; | ||
| 164 | + ret = aclrtCreateStream(&stream); | ||
| 165 | + if (ret != ACL_SUCCESS) { | ||
| 166 | + std::cerr << "aclrtCreateStream failed. ERROR: " << ret << std::endl; | ||
| 167 | + aclrtResetDevice(deviceId); | ||
| 168 | + aclFinalize(); | ||
| 169 | + return output; | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + float *xHost = nullptr; | ||
| 173 | + float *yHost = nullptr; | ||
| 174 | + float *zHost = nullptr; | ||
| 175 | + | ||
| 176 | + uint8_t *xDevice = nullptr; | ||
| 177 | + uint8_t *yDevice = nullptr; | ||
| 178 | + uint8_t *zDevice = nullptr; | ||
| 179 | + | ||
| 180 | + // 4. 申请 Host/Device 内存 | ||
| 181 | + ret = aclrtMallocHost(reinterpret_cast<void **>(&xHost), byteSize); | ||
| 182 | + if (ret != ACL_SUCCESS) { | ||
| 183 | + std::cerr << "aclrtMallocHost for xHost failed. ERROR: " << ret << std::endl; | ||
| 184 | + aclrtDestroyStream(stream); | ||
| 185 | + aclrtResetDevice(deviceId); | ||
| 186 | + aclFinalize(); | ||
| 187 | + return output; | ||
| 188 | + } | ||
| 189 | + ret = aclrtMallocHost(reinterpret_cast<void **>(&yHost), byteSize); | ||
| 190 | + if (ret != ACL_SUCCESS) { | ||
| 191 | + std::cerr << "aclrtMallocHost for yHost failed. ERROR: " << ret << std::endl; | ||
| 192 | + aclrtFreeHost(xHost); | ||
| 193 | + aclrtDestroyStream(stream); | ||
| 194 | + aclrtResetDevice(deviceId); | ||
| 195 | + aclFinalize(); | ||
| 196 | + return output; | ||
| 197 | + } | ||
| 198 | + ret = aclrtMallocHost(reinterpret_cast<void **>(&zHost), byteSize); | ||
| 199 | + if (ret != ACL_SUCCESS) { | ||
| 200 | + std::cerr << "aclrtMallocHost for zHost failed. ERROR: " << ret << std::endl; | ||
| 201 | + aclrtFreeHost(xHost); | ||
| 202 | + aclrtFreeHost(yHost); | ||
| 203 | + aclrtDestroyStream(stream); | ||
| 204 | + aclrtResetDevice(deviceId); | ||
| 205 | + aclFinalize(); | ||
| 206 | + return output; | ||
| 207 | + } | ||
| 208 | + | ||
| 209 | + std::copy(x.begin(), x.end(), xHost); | ||
| 210 | + std::copy(y.begin(), y.end(), yHost); | ||
| 211 | + | ||
| 212 | + ret = aclrtMalloc(reinterpret_cast<void **>(&xDevice), byteSize, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 213 | + if (ret != ACL_SUCCESS) { | ||
| 214 | + std::cerr << "aclrtMalloc for xDevice failed. ERROR: " << ret << std::endl; | ||
| 215 | + aclrtFreeHost(xHost); | ||
| 216 | + aclrtFreeHost(yHost); | ||
| 217 | + aclrtFreeHost(zHost); | ||
| 218 | + aclrtDestroyStream(stream); | ||
| 219 | + aclrtResetDevice(deviceId); | ||
| 220 | + aclFinalize(); | ||
| 221 | + return output; | ||
| 222 | + } | ||
| 223 | + ret = aclrtMalloc(reinterpret_cast<void **>(&yDevice), byteSize, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 224 | + if (ret != ACL_SUCCESS) { | ||
| 225 | + std::cerr << "aclrtMalloc for yDevice failed. ERROR: " << ret << std::endl; | ||
| 226 | + aclrtFree(xDevice); | ||
| 227 | + aclrtFreeHost(xHost); | ||
| 228 | + aclrtFreeHost(yHost); | ||
| 229 | + aclrtFreeHost(zHost); | ||
| 230 | + aclrtDestroyStream(stream); | ||
| 231 | + aclrtResetDevice(deviceId); | ||
| 232 | + aclFinalize(); | ||
| 233 | + return output; | ||
| 234 | + } | ||
| 235 | + ret = aclrtMalloc(reinterpret_cast<void **>(&zDevice), byteSize, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 236 | + if (ret != ACL_SUCCESS) { | ||
| 237 | + std::cerr << "aclrtMalloc for zDevice failed. ERROR: " << ret << std::endl; | ||
| 238 | + aclrtFree(xDevice); | ||
| 239 | + aclrtFree(yDevice); | ||
| 240 | + aclrtFreeHost(xHost); | ||
| 241 | + aclrtFreeHost(yHost); | ||
| 242 | + aclrtFreeHost(zHost); | ||
| 243 | + aclrtDestroyStream(stream); | ||
| 244 | + aclrtResetDevice(deviceId); | ||
| 245 | + aclFinalize(); | ||
| 246 | + return output; | ||
| 247 | + } | ||
| 248 | + | ||
| 249 | + // 5. 输入数据 Host -> Device | ||
| 250 | + ret = aclrtMemcpy(xDevice, byteSize, xHost, byteSize, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 251 | + if (ret != ACL_SUCCESS) { | ||
| 252 | + std::cerr << "aclrtMemcpy to xDevice failed. ERROR: " << ret << std::endl; | ||
| 253 | + aclrtFree(xDevice); | ||
| 254 | + aclrtFree(yDevice); | ||
| 255 | + aclrtFree(zDevice); | ||
| 256 | + aclrtFreeHost(xHost); | ||
| 257 | + aclrtFreeHost(yHost); | ||
| 258 | + aclrtFreeHost(zHost); | ||
| 259 | + aclrtDestroyStream(stream); | ||
| 260 | + aclrtResetDevice(deviceId); | ||
| 261 | + aclFinalize(); | ||
| 262 | + return output; | ||
| 263 | + } | ||
| 264 | + ret = aclrtMemcpy(yDevice, byteSize, yHost, byteSize, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 265 | + if (ret != ACL_SUCCESS) { | ||
| 266 | + std::cerr << "aclrtMemcpy to yDevice failed. ERROR: " << ret << std::endl; | ||
| 267 | + aclrtFree(xDevice); | ||
| 268 | + aclrtFree(yDevice); | ||
| 269 | + aclrtFree(zDevice); | ||
| 270 | + aclrtFreeHost(xHost); | ||
| 271 | + aclrtFreeHost(yHost); | ||
| 272 | + aclrtFreeHost(zHost); | ||
| 273 | + aclrtDestroyStream(stream); | ||
| 274 | + aclrtResetDevice(deviceId); | ||
| 275 | + aclFinalize(); | ||
| 276 | + return output; | ||
| 277 | + } | ||
| 278 | + | ||
| 279 | + // 6. 组装 tiling 并启动 kernel:8 核 × tileNum 8 × BUFFER_NUM 2 | ||
| 280 | + MulCustomTilingData tiling; | ||
| 281 | + tiling.totalLength = totalLength; | ||
| 282 | + tiling.tileNum = tileNum; | ||
| 283 | + | ||
| 284 | + mul_custom<<<blockDim, nullptr, stream>>>(xDevice, yDevice, zDevice, tiling); | ||
| 285 | + | ||
| 286 | + // 7. 同步流,等待 kernel 执行完成 | ||
| 287 | + ret = aclrtSynchronizeStream(stream); | ||
| 288 | + if (ret != ACL_SUCCESS) { | ||
| 289 | + std::cerr << "aclrtSynchronizeStream failed. ERROR: " << ret << std::endl; | ||
| 290 | + aclrtFree(xDevice); | ||
| 291 | + aclrtFree(yDevice); | ||
| 292 | + aclrtFree(zDevice); | ||
| 293 | + aclrtFreeHost(xHost); | ||
| 294 | + aclrtFreeHost(yHost); | ||
| 295 | + aclrtFreeHost(zHost); | ||
| 296 | + aclrtDestroyStream(stream); | ||
| 297 | + aclrtResetDevice(deviceId); | ||
| 298 | + aclFinalize(); | ||
| 299 | + return output; | ||
| 300 | + } | ||
| 301 | + | ||
| 302 | + // 8. 结果 Device -> Host | ||
| 303 | + ret = aclrtMemcpy(zHost, byteSize, zDevice, byteSize, ACL_MEMCPY_DEVICE_TO_HOST); | ||
| 304 | + if (ret != ACL_SUCCESS) { | ||
| 305 | + std::cerr << "aclrtMemcpy to zHost failed. ERROR: " << ret << std::endl; | ||
| 306 | + aclrtFree(xDevice); | ||
| 307 | + aclrtFree(yDevice); | ||
| 308 | + aclrtFree(zDevice); | ||
| 309 | + aclrtFreeHost(xHost); | ||
| 310 | + aclrtFreeHost(yHost); | ||
| 311 | + aclrtFreeHost(zHost); | ||
| 312 | + aclrtDestroyStream(stream); | ||
| 313 | + aclrtResetDevice(deviceId); | ||
| 314 | + aclFinalize(); | ||
| 315 | + return output; | ||
| 316 | + } | ||
| 317 | + | ||
| 318 | + // 9. 构造返回值 | ||
| 319 | + std::copy(zHost, zHost + totalLength, output.begin()); | ||
| 320 | + | ||
| 321 | + // 10. 释放资源,去初始化 | ||
| 322 | + aclrtFree(xDevice); | ||
| 323 | + aclrtFree(yDevice); | ||
| 324 | + aclrtFree(zDevice); | ||
| 325 | + | ||
| 326 | + aclrtFreeHost(xHost); | ||
| 327 | + aclrtFreeHost(yHost); | ||
| 328 | + aclrtFreeHost(zHost); | ||
| 329 | + | ||
| 330 | + aclrtDestroyStream(stream); | ||
| 331 | + aclrtResetDevice(deviceId); | ||
| 332 | + aclFinalize(); | ||
| 333 | + | ||
| 334 | + return output; | ||
| 335 | +} | ||
| 336 | + | ||
| 337 | +// ----- 验证与主函数 ----- | ||
| 338 | + | ||
| 339 | +uint32_t VerifyResult(std::vector<float> &output, std::vector<float> &golden) | ||
| 340 | +{ | ||
| 341 | + auto printTensor = [](std::vector<float> &tensor, const char *name) { | ||
| 342 | + constexpr size_t maxPrintSize = 20; | ||
| 343 | + std::cout << name << ": "; | ||
| 344 | + std::copy(tensor.begin(), tensor.begin() + std::min(tensor.size(), maxPrintSize), | ||
| 345 | + std::ostream_iterator<float>(std::cout, " ")); | ||
| 346 | + if (tensor.size() > maxPrintSize) { | ||
| 347 | + std::cout << "..."; | ||
| 348 | + } | ||
| 349 | + std::cout << std::endl; | ||
| 350 | + }; | ||
| 351 | + printTensor(output, "Output"); | ||
| 352 | + printTensor(golden, "Golden"); | ||
| 353 | + if (std::equal(golden.begin(), golden.end(), output.begin())) { | ||
| 354 | + std::cout << "[Success] Case accuracy is verification passed." << std::endl; | ||
| 355 | + return 0; | ||
| 356 | + } else { | ||
| 357 | + std::cout << "[Failed] Case accuracy is verification failed!" << std::endl; | ||
| 358 | + return 1; | ||
| 359 | + } | ||
| 360 | +} | ||
| 361 | + | ||
| 362 | +int32_t main(int32_t argc, char *argv[]) | ||
| 363 | +{ | ||
| 364 | + constexpr uint32_t totalLength = 8 * 2048; | ||
| 365 | + constexpr float valueX = 1.2f; | ||
| 366 | + constexpr float valueY = 2.3f; | ||
| 367 | + std::vector<float> x(totalLength, valueX); | ||
| 368 | + std::vector<float> y(totalLength, valueY); | ||
| 369 | + | ||
| 370 | + // 调用 kernel_mul 执行逐元素乘法 | ||
| 371 | + std::vector<float> output = kernel_mul(x, y); | ||
| 372 | + | ||
| 373 | + std::vector<float> golden(totalLength, valueX * valueY); | ||
| 374 | + return VerifyResult(output, golden); | ||
| 375 | +} | ||
| @@ -0,0 +1,9 @@ | |||
| 1 | +#!/bin/bash | ||
| 2 | +# 激活cann环境,可根据实际情况修改,在线环境一般不用修改 | ||
| 3 | +source "$ASCEND_TOOLKIT_HOME/set_env.sh" | ||
| 4 | +mkdir -p build | ||
| 5 | +export ASC_DIR="$ASCEND_HOME_PATH/aarch64-linux/tikcpp/ascendc_kernel_cmake/" | ||
| 6 | +cd build/ && \ | ||
| 7 | +cmake .. && \ | ||
| 8 | +make && \ | ||
| 9 | +./mul_test | ||