Ascend C sqrt算子实战
摘要:编写一个Ascend C的sqrt算子,并通过内核调用方式在cpu和npu模式下进行验证。
本文分享自华为云社区《【2023 · CANN训练营第一季】——Ascend C sqrt算子实战》,作者:dayao。
前言
编写一个Ascend C的sqrt算子,并通过内核调用方式在cpu和npu模式下进行验证。在训练营沙箱环境下,cpu模式工作正常结果正确。
一、概述
先简单回顾下TIK C++算子矢量编程的流程和实现。
矢量算子开发流程如下:
主要工作内容有:
1、算子分析:确定输入输出,确定数学表达式以及底层实现接口,确定核函数定义。
2、算子类的实现:实现init()和process()。init()完成内存初始化,实质上体现的是多核运行,和单核数据切分以及是否开启double buffer优化;Process()实现的是CopyIn,Compute、CopyOut三个流水任务。
3、算子验证:通过核函数的内核调用符的方式调用算子,计算出结果,并于使用相同输入用numpy计算结果进行比对,误差在一定范围内即可。实际应用中,需要使用原有框架的算子进行计算精度比对。
二、算子分析
算子定义如下:假定仍是8个逻辑核。
查询TIK C++的API可知,可以使用(TIK C++ API/矢量计算/单目/Sqrt,采用2级接口)完成运算,得到最终结果。
三、代码分析
直接在训练营课程提供的add_tik2算子工程上修改。代码地址:https://gitee.com/zgx950813/samples/tree/master/tik2_demo/kernel_samples/kernel_add_sample
修改代码目录结构如下:CMakeLists.txt和data_utils.h未作修改,编译和执行脚本run.sh只改了计算结果与真值比对部分。
一)、核函数定义
与例程相比,输入参数只有x。
- extern "C" __global__ __aicore__ void sqrt_tik2(__gm__ uint8_t* x, __gm__ uint8_t* z)
- {
- KernelSqrt op;
- op.Init(x, z);
- op.Process();
- }
二)、算子类
实现方式与add例程类似。init()函数里初始化内存:x,y的Global Memory ;流水线任务通讯内存;Process()实现流水线任务;按范式编写CopyIn、Compute、CopyOut。与add例程最大差异是,在compute函数中,调用sqrt的2类接口API实现计算。
- class KernelSqrt {
- public:
- __aicore__ inline KernelSqrt() {}
- __aicore__ inline void Init(__gm__ uint8_t* x, __gm__ uint8_t* z)
- {
- // get start index for current core, core parallel
- xGm.SetGlobalBuffer((__gm__ half*)x + block_idx * BLOCK_LENGTH, BLOCK_LENGTH);
- zGm.SetGlobalBuffer((__gm__ half*)z + block_idx * BLOCK_LENGTH, BLOCK_LENGTH);
- // pipe alloc memory to queue, the unit is Bytes
- pipe.InitBuffer(inQueueX, BUFFER_NUM, TILE_LENGTH * sizeof(half));
- pipe.InitBuffer(outQueueZ, BUFFER_NUM, TILE_LENGTH * sizeof(half));
- }
- __aicore__ inline void Process()
- {
- // loop count need to be doubled, due to double buffer
- constexpr int32_t loopCount = TILE_NUM * BUFFER_NUM;
- // tiling strategy, pipeline parallel
- for (int32_t i = 0; i < loopCount; i++) {
- CopyIn(i);
- Compute(i);
- CopyOut(i);
- }
- }
- private:
- __aicore__ inline void CopyIn(int32_t progress)
- {
- // alloc tensor from queue memory
- LocalTensor<half> xLocal = inQueueX.AllocTensor<half>();
- // copy progress_th tile from global tensor to local tensor
- DataCopy(xLocal, xGm[progress * TILE_LENGTH], TILE_LENGTH);
- // enque input tensors to VECIN queue
- inQueueX.EnQue(xLocal);
- }
- __aicore__ inline void Compute(int32_t progress)
- {
- // deque input tensors from VECIN queue
- LocalTensor<half> xLocal = inQueueX.DeQue<half>();
- LocalTensor<half> zLocal = outQueueZ.AllocTensor<half>();
- // call Sqrt instr for computation
- Sqrt(zLocal, xLocal, TILE_LENGTH);
- // enque the output tensor to VECOUT queue
- outQueueZ.EnQue<half>(zLocal);
- // free input tensors for reuse
- inQueueX.FreeTensor(xLocal);
- }
- __aicore__ inline void CopyOut(int32_t progress)
- {
- // deque output tensor from VECOUT queue
- LocalTensor<half> zLocal = outQueueZ.DeQue<half>();
- // copy progress_th tile from local tensor to global tensor
- DataCopy(zGm[progress * TILE_LENGTH], zLocal, TILE_LENGTH);
- // free output tensor for reuse
- outQueueZ.FreeTensor(zLocal);
- }
- private:
- TPipe pipe;
- // create queues for input, in this case depth is equal to buffer num
- TQue<QuePosition::VECIN, BUFFER_NUM> inQueueX;
- // create queue for output, in this case depth is equal to buffer num
- TQue<QuePosition::VECOUT, BUFFER_NUM> outQueueZ;
- GlobalTensor<half> xGm, zGm;
- };
三)、核函数调用
1、在CPU模式下,通过ICPU_RUN_KF调用
- ICPU_RUN_KF(sqrt_tik2, blockDim, x, z); // use this macro for cpu debug
2、在NPU模式下,通过<<<>>>调用
- #ifndef __CCE_KT_TEST__
- // call of kernel function
- void sqrt_tik2_do(uint32_t blockDim, void* l2ctrl, void* stream, uint8_t* x, uint8_t* z)
- {
- sqrt_tik2<<<blockDim, l2ctrl, stream>>>(x, z);
- }
- #endif
由于<<<>>>,只能在NPU模式下调用,所以需要用条件编译,不在CPU调试模式下有效。在调用sqrt_tik2_do,需要按ascendcl应用编程的要求进行。
3、调用代码
通过“__CCE_KT_TEST__”宏区分CPU和NPU模式。
- int32_t main(int32_t argc, char* argv[])
- {
- size_t inputByteSize = 8 * 2048 * sizeof(uint16_t); // uint16_t represent half
- size_t outputByteSize = 8 * 2048 * sizeof(uint16_t); // uint16_t represent half
- uint32_t blockDim = 8;
- #ifdef __CCE_KT_TEST__
- uint8_t* x = (uint8_t*)tik2::GmAlloc(inputByteSize);
- uint8_t* z = (uint8_t*)tik2::GmAlloc(outputByteSize);
- ReadFile("./input/input_x.bin", inputByteSize, x, inputByteSize);
- // PrintData(x, 16, printDataType::HALF);
- ICPU_RUN_KF(sqrt_tik2, blockDim, x, z); // use this macro for cpu debug
- // PrintData(z, 16, printDataType::HALF);
- WriteFile("./output/output_z.bin", z, outputByteSize);
- tik2::GmFree((void *)x);
- tik2::GmFree((void *)z);
- #else
- aclInit(nullptr);
- aclrtContext context;
- aclError error;
- int32_t deviceId = 0;
- aclrtCreateContext(&context, deviceId);
- aclrtStream stream = nullptr;
- aclrtCreateStream(&stream);
- uint8_t *xHost, *zHost;
- uint8_t *xDevice, *zDevice;
- aclrtMallocHost((void**)(&xHost), inputByteSize);
- aclrtMallocHost((void**)(&zHost), outputByteSize);
- aclrtMalloc((void**)&xDevice, inputByteSize, ACL_MEM_MALLOC_HUGE_FIRST);
- aclrtMalloc((void**)&zDevice, outputByteSize, ACL_MEM_MALLOC_HUGE_FIRST);
- ReadFile("./input/input_x.bin", inputByteSize, xHost, inputByteSize);
- // PrintData(xHost, 16, printDataType::HALF);
- aclrtMemcpy(xDevice, inputByteSize, xHost, inputByteSize, ACL_MEMCPY_HOST_TO_DEVICE);
- sqrt_tik2_do(blockDim, nullptr, stream, xDevice, zDevice); // call kernel in this function
- aclrtSynchronizeStream(stream);
- aclrtMemcpy(zHost, outputByteSize, zDevice, outputByteSize, ACL_MEMCPY_DEVICE_TO_HOST);
- // PrintData(zHost, 16, printDataType::HALF);
- WriteFile("./output/output_z.bin", zHost, outputByteSize);
- aclrtFree(xDevice);
- aclrtFree(zDevice);
- aclrtFreeHost(xHost);
- aclrtFreeHost(zHost);
- aclrtDestroyStream(stream);
- aclrtResetDevice(deviceId);
- aclFinalize();
- #endif
- return 0;
- }
四)、基准数据生成——sqrt_tik2.py
使用numpy生成input_x和基准结果golden。
- import numpy as np
- def gen_golden_data_simple():
- input_x = np.random.uniform(0, 100, [8, 2048]).astype(np.float16)
- golden = np.sqrt(input_x).astype(np.float16)
- input_x.tofile("./input/input_x.bin")
- golden.tofile("./output/golden.bin")
- if __name__ == "__main__":
- gen_golden_data_simple()
五)、计算结果比较
使用numpy的allclose()函数比较算子计算与基准数据的结果。实际上由于npu模式编译出错,实际未执行改函数进行比较。CPU模式下,算子计算出的结果与基准golden数据完全一致,两者的md5相同。
四、编译运行
本次课程提供了沙箱运行环境,想个办法把代码搞进去。
一)、配置环境变量
二)、CPU模式
cpu模式顺利编译运行,结果与对比组完全一致。
三)、NPU模式
npu模式下编译报错,因为沙箱时间有限,以后有机会再研究。
Ascend C sqrt算子实战的更多相关文章
- Spark算子---实战应用
Spark算子实战应用 数据集 :http://grouplens.org/datasets/movielens/ MovieLens 1M Datase 相关数据文件 : users.dat --- ...
- Spark GraphX图计算核心算子实战【AggreagteMessage】
一.简介 参考博客:https://www.cnblogs.com/yszd/p/10186556.html 二.代码实现 package graphx import org.apache.log4j ...
- 6.RDD算子实战
from pyspark import SparkContext,SparkConf import sys if __name__ == '__main__': if len(sys.argv) != ...
- Ascend Pytorch算子功能验证
Ascend Pytorch算子功能验证 编写测试用例 以add算子为例,测试脚本文件命名为:add_testcase.py.以下示例仅为一个简单的用例实现,具体算子的实现,需要根据算子定义进行完整的 ...
- Ascend Pytorch算子适配层开发
Ascend Pytorch算子适配层开发 适配方法 找到和PyTorch算子功能对应的NPU TBE算子,根据算子功能计算出输出Tensor的size,再根据TBE算子原型构造对应的input/ou ...
- 数学之路-python计算实战(20)-机器视觉-拉普拉斯算子卷积滤波
拉普拉斯算子进行二维卷积计算,线性锐化滤波 # -*- coding: utf-8 -*- #线性锐化滤波-拉普拉斯算子进行二维卷积计算 #code:myhaspl@myhaspl.com impor ...
- Ceres Solver: 高效的非线性优化库(二)实战篇
Ceres Solver: 高效的非线性优化库(二)实战篇 接上篇: Ceres Solver: 高效的非线性优化库(一) 如何求导 Ceres Solver提供了一种自动求导的方案,上一篇我们已经看 ...
- 《TensorFlow实战》中AlexNet卷积神经网络的训练中
TensorFlow实战中AlexNet卷积神经网络的训练 01 出错 TypeError: as_default() missing 1 required positional argument: ...
- flink实时数仓从入门到实战
第一章.flink实时数仓入门 一.依赖 <!--Licensed to the Apache Software Foundation (ASF) under oneor more contri ...
- AI实战分享 | 基于CANN的辅助驾驶应用案例
摘要:什么是辅助驾驶?简而言之,就是借助汽车对周围环境的自动感知和分析,让驾驶员预先察觉可能发生的危险,有效增加汽车驾驶的舒适性和安全性. 导读:基于昇腾AI异构计算架构CANN的辅助驾驶AI应用实战 ...
随机推荐
- RPC 与 Restful 的区别
PRC 是一种技术的代名词,HTTP 是一种协议,RPC 可以通过 HTTP 来实现,也可以通过 Socket 自己实现一套协议来实现.所以谈论为什么用 RPC 不用 HTTP 是无意义的.但我们习惯 ...
- better-scroll横向滚动、纵向滚动
<div ref="tab" class="tab"> <ul ref="tabWrapper" class=" ...
- grub 命令使用
命令列表 ubuntu 的 iso 盘内一般有 command.lst 这个文件,里面是 grub 支持的命令 加载字体 ( 方便中文显示 ) grub> loadfont $prefix/fo ...
- Mybatis 获取自增主键 useGeneratedKeys与keyProperty 解答
Mybatis 获取自增主键 今天开发的时候遇到一个疑惑,业务场景是这样的, 但是百度好久没有找到合适的解答,于是自己向同事了解,感觉还不错,因此写上了这个文章 有一个表A和一个表B A就是一个主表, ...
- [Linux]常用命令之【hostname】
1: 个人的片面理解: hostname是主机名(的"昵称"),而非域名.一般设置hostname,来标识当前机器的主要用途.以区别与其它机器 2: hostname的严格定义: ...
- JSON.parse 函数 (JavaScript)
将 JavaScript 对象表示法 (JSON) 字符串转换为对象. 语法 参数 返回值 异常 以下示例使用 JSON.parse 将 JSON 字符串转换成对象. var jsontext = ' ...
- Python代码相似度计算(基于AST和SW算法)
代码相似度计算将基于AST和Smith-Waterman算法 AST (抽象语法树) AST即Abstract Syntax Trees,是源代码的抽象语法结构的树状表示,树上的每个节点都表示源代码中 ...
- 从 1 秒到 10 毫秒!在 APISIX 中减少 Prometheus 请求阻塞
本文介绍了 Prometheus 插件造成长尾请求现象的原因,以及如何解决这个问题. 作者屠正松,Apache APISIX PMC Member. 原文链接 现象 在 APISIX 社区中,曾有部分 ...
- 理解Java程序的执行
main 方法 public class Solution { public static void main(String[] args) { Person person = new Person( ...
- 04-webpack初体验
/** * index.js: webpack入口起点文件 * * 1.运行指令: * 开发环境:webpack ./src/index.js -o ./build --mode=developmen ...