已开启
【代码侦探Challenge05】实现逐元素乘法算子 MulCustom #2431
aodebiao创建于 25 天前
【代码侦探Challenge05】实现逐元素乘法算子 MulCustom #2431
已开启
共 3 个文件变更+207-0
| @@ -0,0 +1,10 @@ | |||
| 1 | +cmake_minimum_required(VERSION 3.16) | ||
| 2 | +find_package(ASC REQUIRED) | ||
| 3 | +project(kernel_samples LANGUAGES ASC CXX) | ||
| 4 | + | ||
| 5 | +add_executable(mul_test | ||
| 6 | + mul_custom.asc | ||
| 7 | +) | ||
| 8 | +target_compile_options(mul_test PRIVATE | ||
| 9 | + $<$<COMPILE_LANGUAGE:ASC>:--npu-arch=dav-2201> | ||
| 10 | +) | ||
| @@ -0,0 +1,188 @@ | |||
| 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 | + this->blockLength = totalLength / AscendC::GetBlockNum(); | ||
| 37 | + this->tileNum = tileNum; | ||
| 38 | + this->tileLength = this->blockLength / tileNum / BUFFER_NUM; | ||
| 39 | + uint32_t blockOffset = this->blockLength * AscendC::GetBlockIdx(); | ||
| 40 | + xGm.SetGlobalBuffer((__gm__ float *)x + blockOffset, this->blockLength); | ||
| 41 | + yGm.SetGlobalBuffer((__gm__ float *)y + blockOffset, this->blockLength); | ||
| 42 | + zGm.SetGlobalBuffer((__gm__ float *)z + blockOffset, this->blockLength); | ||
| 43 | + pipe.InitBuffer(inQueueX, BUFFER_NUM, this->tileLength * sizeof(float)); | ||
| 44 | + pipe.InitBuffer(inQueueY, BUFFER_NUM, this->tileLength * sizeof(float)); | ||
| 45 | + pipe.InitBuffer(outQueueZ, BUFFER_NUM, this->tileLength * sizeof(float)); | ||
| 46 | + } | ||
| 47 | + __aicore__ inline void Process() | ||
| 48 | + { | ||
| 49 | + int32_t loopCount = this->tileNum * BUFFER_NUM; | ||
| 50 | + for (int32_t i = 0; i < loopCount; ++i) { | ||
| 51 | + CopyIn(i); | ||
| 52 | + Compute(i); | ||
| 53 | + CopyOut(i); | ||
| 54 | + } | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | +private: | ||
| 58 | + __aicore__ inline void CopyIn(int32_t progress) | ||
| 59 | + { | ||
| 60 | + AscendC::LocalTensor<float> xLocal = inQueueX.AllocTensor<float>(); | ||
| 61 | + AscendC::LocalTensor<float> yLocal = inQueueY.AllocTensor<float>(); | ||
| 62 | + uint32_t tileOffset = progress * this->tileLength; | ||
| 63 | + AscendC::DataCopy(xLocal, xGm[tileOffset], this->tileLength); | ||
| 64 | + AscendC::DataCopy(yLocal, yGm[tileOffset], this->tileLength); | ||
| 65 | + inQueueX.EnQue(xLocal); | ||
| 66 | + inQueueY.EnQue(yLocal); | ||
| 67 | + } | ||
| 68 | + __aicore__ inline void Compute(int32_t) | ||
| 69 | + { | ||
| 70 | + AscendC::LocalTensor<float> xLocal = inQueueX.DeQue<float>(); | ||
| 71 | + AscendC::LocalTensor<float> yLocal = inQueueY.DeQue<float>(); | ||
| 72 | + AscendC::LocalTensor<float> zLocal = outQueueZ.AllocTensor<float>(); | ||
| 73 | + AscendC::Mul(zLocal, xLocal, yLocal, this->tileLength); | ||
| 74 | + outQueueZ.EnQue(zLocal); | ||
| 75 | + inQueueX.FreeTensor(xLocal); | ||
| 76 | + inQueueY.FreeTensor(yLocal); | ||
| 77 | + } | ||
| 78 | + __aicore__ inline void CopyOut(int32_t progress) | ||
| 79 | + { | ||
| 80 | + AscendC::LocalTensor<float> zLocal = outQueueZ.DeQue<float>(); | ||
| 81 | + AscendC::DataCopy(zGm[progress * this->tileLength], zLocal, this->tileLength); | ||
| 82 | + outQueueZ.FreeTensor(zLocal); | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | +private: | ||
| 86 | + AscendC::TPipe pipe; | ||
| 87 | + AscendC::TQue<AscendC::TPosition::VECIN, BUFFER_NUM> inQueueX, inQueueY; | ||
| 88 | + AscendC::TQue<AscendC::TPosition::VECOUT, BUFFER_NUM> outQueueZ; | ||
| 89 | + AscendC::GlobalTensor<float> xGm, yGm, zGm; | ||
| 90 | + uint32_t blockLength; | ||
| 91 | + uint32_t tileNum; | ||
| 92 | + uint32_t tileLength; | ||
| 93 | +}; | ||
| 94 | + | ||
| 95 | +// Kernel 入口函数 | ||
| 96 | +__global__ __aicore__ void mul_custom(GM_ADDR x, GM_ADDR y, GM_ADDR z, MulCustomTilingData tiling) | ||
| 97 | +{ | ||
| 98 | + KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIV_ONLY); | ||
| 99 | + KernelMul op; | ||
| 100 | + op.Init(x, y, z, tiling.totalLength, tiling.tileNum); | ||
| 101 | + op.Process(); | ||
| 102 | +} | ||
| 103 | + | ||
| 104 | +// ----- Host 侧:kernel 直调封装 ----- | ||
| 105 | + | ||
| 106 | +std::vector<float> kernel_mul(std::vector<float> &x, std::vector<float> &y) | ||
| 107 | +{ | ||
| 108 | + constexpr uint32_t blockDim = 8; | ||
| 109 | + uint32_t totalLength = x.size(); | ||
| 110 | + size_t totalByteSize = totalLength * sizeof(float); | ||
| 111 | + constexpr int32_t deviceId = 0; | ||
| 112 | + aclrtStream stream = nullptr; | ||
| 113 | + MulCustomTilingData tiling = {totalLength, 8}; | ||
| 114 | + uint8_t *xHost = reinterpret_cast<uint8_t *>(x.data()); | ||
| 115 | + uint8_t *yHost = reinterpret_cast<uint8_t *>(y.data()); | ||
| 116 | + uint8_t *zHost = nullptr; | ||
| 117 | + uint8_t *xDevice = nullptr; | ||
| 118 | + uint8_t *yDevice = nullptr; | ||
| 119 | + uint8_t *zDevice = nullptr; | ||
| 120 | + | ||
| 121 | + aclInit(nullptr); | ||
| 122 | + aclrtSetDevice(deviceId); | ||
| 123 | + aclrtCreateStream(&stream); | ||
| 124 | + | ||
| 125 | + aclrtMallocHost(reinterpret_cast<void **>(&zHost), totalByteSize); | ||
| 126 | + aclrtMalloc(reinterpret_cast<void **>(&xDevice), totalByteSize, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 127 | + aclrtMalloc(reinterpret_cast<void **>(&yDevice), totalByteSize, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 128 | + aclrtMalloc(reinterpret_cast<void **>(&zDevice), totalByteSize, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 129 | + | ||
| 130 | + aclrtMemcpy(xDevice, totalByteSize, xHost, totalByteSize, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 131 | + aclrtMemcpy(yDevice, totalByteSize, yHost, totalByteSize, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 132 | + | ||
| 133 | + mul_custom<<<blockDim, nullptr, stream>>>(xDevice, yDevice, zDevice, tiling); | ||
| 134 | + aclrtSynchronizeStream(stream); | ||
| 135 | + | ||
| 136 | + aclrtMemcpy(zHost, totalByteSize, zDevice, totalByteSize, ACL_MEMCPY_DEVICE_TO_HOST); | ||
| 137 | + std::vector<float> z(reinterpret_cast<float *>(zHost), | ||
| 138 | + reinterpret_cast<float *>(zHost + totalByteSize)); | ||
| 139 | + | ||
| 140 | + aclrtFree(xDevice); | ||
| 141 | + aclrtFree(yDevice); | ||
| 142 | + aclrtFree(zDevice); | ||
| 143 | + aclrtFreeHost(zHost); | ||
| 144 | + aclrtDestroyStream(stream); | ||
| 145 | + aclrtResetDevice(deviceId); | ||
| 146 | + aclFinalize(); | ||
| 147 | + | ||
| 148 | + return z; | ||
| 149 | +} | ||
| 150 | + | ||
| 151 | +// ----- 验证与主函数 ----- | ||
| 152 | + | ||
| 153 | +uint32_t VerifyResult(std::vector<float> &output, std::vector<float> &golden) | ||
| 154 | +{ | ||
| 155 | + auto printTensor = [](std::vector<float> &tensor, const char *name) { | ||
| 156 | + constexpr size_t maxPrintSize = 20; | ||
| 157 | + std::cout << name << ": "; | ||
| 158 | + std::copy(tensor.begin(), tensor.begin() + std::min(tensor.size(), maxPrintSize), | ||
| 159 | + std::ostream_iterator<float>(std::cout, " ")); | ||
| 160 | + if (tensor.size() > maxPrintSize) { | ||
| 161 | + std::cout << "..."; | ||
| 162 | + } | ||
| 163 | + std::cout << std::endl; | ||
| 164 | + }; | ||
| 165 | + printTensor(output, "Output"); | ||
| 166 | + printTensor(golden, "Golden"); | ||
| 167 | + if (std::equal(golden.begin(), golden.end(), output.begin())) { | ||
| 168 | + std::cout << "[Success] Case accuracy is verification passed." << std::endl; | ||
| 169 | + return 0; | ||
| 170 | + } else { | ||
| 171 | + std::cout << "[Failed] Case accuracy is verification failed!" << std::endl; | ||
| 172 | + return 1; | ||
| 173 | + } | ||
| 174 | +} | ||
| 175 | + | ||
| 176 | +int32_t main(int32_t argc, char *argv[]) | ||
| 177 | +{ | ||
| 178 | + constexpr uint32_t totalLength = 8 * 2048; | ||
| 179 | + constexpr float valueX = 1.2f; | ||
| 180 | + constexpr float valueY = 2.3f; | ||
| 181 | + std::vector<float> x(totalLength, valueX); | ||
| 182 | + std::vector<float> y(totalLength, valueY); | ||
| 183 | + | ||
| 184 | + std::vector<float> output = kernel_mul(x, y); | ||
| 185 | + | ||
| 186 | + std::vector<float> golden(totalLength, valueX * valueY); | ||
| 187 | + return VerifyResult(output, golden); | ||
| 188 | +} | ||
| @@ -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 | ||