已合并
【代码侦探Challenge05】: complete Challenge05 MulCustom #3456
【代码侦探Challenge05】: complete Challenge05 MulCustom #3456
已合并
jczhaang创建于 9月6日
共 3 个文件变更+319-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,300 @@
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+ // 请补充……
37+ // 1. 计算单核处理长度 blockLength = totalLength / GetBlockNum()
38+ // 2. 计算 tileLength = blockLength / tileNum / BUFFER_NUM
39+ // 3. 使用 SetGlobalBuffer 设置 xGm, yGm, zGm 的起始地址和长度
40+ // (注意用 GetBlockIdx() 计算每个核的偏移)
41+ // 4. 使用 pipe.InitBuffer 为 inQueueX, inQueueY, outQueueZ 分配内存
42+ // 每个队列分配 BUFFER_NUM 个块,每块大小 tileLength * sizeof(float)
43+ this->blockLength = totalLength / AscendC::GetBlockNum();
44+ this->tileNum = tileNum;
45+ this->tileLength = this->blockLength / this->tileNum / BUFFER_NUM;
46+ 
47+ xGm.SetGlobalBuffer(
48+ (__gm__ float *)x + this->blockLength * AscendC::GetBlockIdx(),
49+ this->blockLength);
50+ yGm.SetGlobalBuffer(
51+ (__gm__ float *)y + this->blockLength * AscendC::GetBlockIdx(),
52+ this->blockLength);
53+ zGm.SetGlobalBuffer(
54+ (__gm__ float *)z + this->blockLength * AscendC::GetBlockIdx(),
55+ this->blockLength);
56+ 
57+ pipe.InitBuffer(inQueueX, BUFFER_NUM, this->tileLength * sizeof(float));
58+ pipe.InitBuffer(inQueueY, BUFFER_NUM, this->tileLength * sizeof(float));
59+ pipe.InitBuffer(outQueueZ, BUFFER_NUM, this->tileLength * sizeof(float));
60+ }
61+ __aicore__ inline void Process()
62+ {
63+ // 请补充……
64+ // 循环次数 = tileNum * BUFFER_NUM
65+ // 每次迭代调用 CopyIn(i) → Compute(i) → CopyOut(i)
66+ int32_t loopCount = this->tileNum * BUFFER_NUM;
67+ 
68+ for (int32_t i = 0; i < loopCount; i++) {
69+ CopyIn(i);
70+ Compute(i);
71+ CopyOut(i);
72+ }
73+ }
74+ 
75+private:
76+ __aicore__ inline void CopyIn(int32_t progress)
77+ {
78+ // 请补充……
79+ // 1. 从 inQueueX 中 AllocTensor<float> 获取 xLocal
80+ // 2. 从 inQueueY 中 AllocTensor<float> 获取 yLocal
81+ // 3. 使用 DataCopy 从 xGm[progress * tileLength] 搬入 tileLength 个元素到 xLocal
82+ // 4. 使用 DataCopy 从 yGm[progress * tileLength] 搬入 tileLength 个元素到 yLocal
83+ // 5. 将 xLocal, yLocal 入队 (EnQue)
84+ AscendC::LocalTensor<float> xLocal = inQueueX.AllocTensor<float>();
85+ AscendC::LocalTensor<float> yLocal = inQueueY.AllocTensor<float>();
86+ 
87+ AscendC::DataCopy(
88+ xLocal,
89+ xGm[progress * this->tileLength],
90+ this->tileLength);
91+ 
92+ AscendC::DataCopy(
93+ yLocal,
94+ yGm[progress * this->tileLength],
95+ this->tileLength);
96+ 
97+ inQueueX.EnQue(xLocal);
98+ inQueueY.EnQue(yLocal);
99+ }
100+ __aicore__ inline void Compute(int32_t progress)
101+ {
102+ // 请补充……
103+ // 1. 从 inQueueX 中 DeQue<float> 获取 xLocal
104+ // 2. 从 inQueueY 中 DeQue<float> 获取 yLocal
105+ // 3. 从 outQueueZ 中 AllocTensor<float> 获取 zLocal
106+ // 4. 使用 Mul(zLocal, xLocal, yLocal, tileLength) 计算逐元素乘法
107+ // 5. 将 zLocal 入队 (EnQue)
108+ // 6. 释放 xLocal, yLocal (FreeTensor)
109+ AscendC::LocalTensor<float> xLocal = inQueueX.DeQue<float>();
110+ AscendC::LocalTensor<float> yLocal = inQueueY.DeQue<float>();
111+ AscendC::LocalTensor<float> zLocal = outQueueZ.AllocTensor<float>();
112+ 
113+ AscendC::Mul(zLocal, xLocal, yLocal, this->tileLength);
114+ 
115+ outQueueZ.EnQue(zLocal);
116+ 
117+ inQueueX.FreeTensor(xLocal);
118+ inQueueY.FreeTensor(yLocal);
119+ }
120+ __aicore__ inline void CopyOut(int32_t progress)
121+ {
122+ // 请补充……
123+ // 1. 从 outQueueZ 中 DeQue<float> 获取 zLocal
124+ // 2. 使用 DataCopy 将 zLocal 搬出到 zGm[progress * tileLength]
125+ // 3. 释放 zLocal (FreeTensor)
126+ AscendC::LocalTensor<float> zLocal = outQueueZ.DeQue<float>();
127+ 
128+ AscendC::DataCopy(
129+ zGm[progress * this->tileLength],
130+ zLocal,
131+ this->tileLength);
132+ 
133+ outQueueZ.FreeTensor(zLocal);
134+ }
135+ 
136+private:
137+ // 请补充……
138+ // 声明以下成员变量:
139+ // TPipe pipe;
140+ // TQue<TPosition::VECIN, BUFFER_NUM> inQueueX, inQueueY;
141+ // TQue<TPosition::VECOUT, BUFFER_NUM> outQueueZ;
142+ // GlobalTensor<float> xGm, yGm, zGm;
143+ // uint32_t blockLength, tileNum, tileLength;
144+ 
145+ AscendC::TPipe pipe;
146+ 
147+ AscendC::TQue<AscendC::TPosition::VECIN, BUFFER_NUM> inQueueX;
148+ AscendC::TQue<AscendC::TPosition::VECIN, BUFFER_NUM> inQueueY;
149+ AscendC::TQue<AscendC::TPosition::VECOUT, BUFFER_NUM> outQueueZ;
150+ 
151+ AscendC::GlobalTensor<float> xGm;
152+ AscendC::GlobalTensor<float> yGm;
153+ AscendC::GlobalTensor<float> zGm;
154+ 
155+ uint32_t blockLength;
156+ uint32_t tileNum;
157+ uint32_t tileLength;
158+};
159+ 
160+// Kernel 入口函数
161+__global__ __aicore__ void mul_custom(GM_ADDR x, GM_ADDR y, GM_ADDR z, MulCustomTilingData tiling)
162+{
163+ // 请补充……
164+ // 1. 添加 KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIV_ONLY)
165+ // 2. 实例化 KernelMul op
166+ // 3. 调用 op.Init(x, y, z, tiling.totalLength, tiling.tileNum)
167+ // 4. 调用 op.Process()
168+ KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIV_ONLY);
169+ 
170+ KernelMul op;
171+ op.Init(x, y, z, tiling.totalLength, tiling.tileNum);
172+ op.Process();
173+}
174+ 
175+// ----- Host 侧:kernel 直调封装 -----
176+ 
177+std::vector<float> kernel_mul(std::vector<float> &x, std::vector<float> &y)
178+{
179+ // 请补充……
180+ // 1. 定义 blockDim = 8, totalLength = x.size()
181+ // 2. 定义 tiling: totalLength, tileNum = 8
182+ // 3. 初始化 ACL: aclInit, aclrtSetDevice, aclrtCreateStream
183+ // 4. 申请 Host/Device 内存 (aclrtMallocHost, aclrtMalloc)
184+ // 5. 将输入数据拷贝到 Device (aclrtMemcpy H2D)
185+ // 6. 调用 kernel: mul_custom<<<blockDim, nullptr, stream>>>(xDevice, yDevice, zDevice, tiling)
186+ // 7. 同步流 (aclrtSynchronizeStream)
187+ // 8. 将结果从 Device 拷贝回 Host (aclrtMemcpy D2H)
188+ // 9. 构造 vector<float> 返回
189+ // 10. 释放内存,销毁流,重置设备,aclFinalize
190+ constexpr uint32_t blockDim = 8;
191+ uint32_t totalLength = x.size();
192+ size_t totalByteSize = totalLength * sizeof(float);
193+ int32_t deviceId = 0;
194+ aclrtStream stream = nullptr;
195+ 
196+ MulCustomTilingData tiling = {/*totalLength:*/totalLength, /*tileNum:*/8};
197+ 
198+ uint8_t *xHost = reinterpret_cast<uint8_t *>(x.data());
199+ uint8_t *yHost = reinterpret_cast<uint8_t *>(y.data());
200+ uint8_t *zHost = nullptr;
201+ 
202+ uint8_t *xDevice = nullptr;
203+ uint8_t *yDevice = nullptr;
204+ uint8_t *zDevice = nullptr;
205+ 
206+ aclInit(nullptr);
207+ aclrtSetDevice(deviceId);
208+ aclrtCreateStream(&stream);
209+ 
210+ aclrtMallocHost((void **)(&zHost), totalByteSize);
211+ 
212+ aclrtMalloc((void **)&xDevice, totalByteSize, ACL_MEM_MALLOC_HUGE_FIRST);
213+ aclrtMalloc((void **)&yDevice, totalByteSize, ACL_MEM_MALLOC_HUGE_FIRST);
214+ aclrtMalloc((void **)&zDevice, totalByteSize, ACL_MEM_MALLOC_HUGE_FIRST);
215+ 
216+ aclrtMemcpy(
217+ xDevice,
218+ totalByteSize,
219+ xHost,
220+ totalByteSize,
221+ ACL_MEMCPY_HOST_TO_DEVICE);
222+ 
223+ aclrtMemcpy(
224+ yDevice,
225+ totalByteSize,
226+ yHost,
227+ totalByteSize,
228+ ACL_MEMCPY_HOST_TO_DEVICE);
229+ 
230+ mul_custom<<<blockDim, nullptr, stream>>>(
231+ xDevice,
232+ yDevice,
233+ zDevice,
234+ tiling);
235+ 
236+ aclrtSynchronizeStream(stream);
237+ 
238+ aclrtMemcpy(
239+ zHost,
240+ totalByteSize,
241+ zDevice,
242+ totalByteSize,
243+ ACL_MEMCPY_DEVICE_TO_HOST);
244+ 
245+ std::vector<float> z(
246+ (float *)zHost,
247+ (float *)(zHost + totalByteSize));
248+ 
249+ aclrtFree(xDevice);
250+ aclrtFree(yDevice);
251+ aclrtFree(zDevice);
252+ aclrtFreeHost(zHost);
253+ 
254+ aclrtDestroyStream(stream);
255+ aclrtResetDevice(deviceId);
256+ aclFinalize();
257+ 
258+ return z;
259+}
260+ 
261+// ----- 验证与主函数 -----
262+ 
263+uint32_t VerifyResult(std::vector<float> &output, std::vector<float> &golden)
264+{
265+ auto printTensor = [](std::vector<float> &tensor, const char *name) {
266+ constexpr size_t maxPrintSize = 20;
267+ std::cout << name << ": ";
268+ std::copy(tensor.begin(), tensor.begin() + std::min(tensor.size(), maxPrintSize),
269+ std::ostream_iterator<float>(std::cout, " "));
270+ if (tensor.size() > maxPrintSize) {
271+ std::cout << "...";
272+ }
273+ std::cout << std::endl;
274+ };
275+ printTensor(output, "Output");
276+ printTensor(golden, "Golden");
277+ if (std::equal(golden.begin(), golden.end(), output.begin())) {
278+ std::cout << "[Success] Case accuracy is verification passed." << std::endl;
279+ return 0;
280+ } else {
281+ std::cout << "[Failed] Case accuracy is verification failed!" << std::endl;
282+ return 1;
283+ }
284+}
285+ 
286+int32_t main(int32_t argc, char *argv[])
287+{
288+ constexpr uint32_t totalLength = 8 * 2048;
289+ constexpr float valueX = 1.2f;
290+ constexpr float valueY = 2.3f;
291+ std::vector<float> x(totalLength, valueX);
292+ std::vector<float> y(totalLength, valueY);
293+ 
294+ // 请补充……
295+ // 调用 kernel_mul(x, y) 获取 output
296+ std::vector<float> output = kernel_mul(x, y);
297+ 
298+ std::vector<float> golden(totalLength, valueX * valueY);
299+ return VerifyResult(output, golden);
300+}
@@ -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