Ascend C编程入门课:编程基础与Hello World

Ascend C编程入门课

一、基础概念

1.Ascend C:是昇腾异构计算架构CANN针对算子开发场景推出的编程语言,通过多层接口抽象、自动并行计算、孪生调试等关键技术,极大提高算子开发效率。

2.使用Ascend C自定义开发算子的优势:

(1)C/C++原语编程,最大化匹配用户的开发习惯

(2)编程模型屏蔽硬件差异,编程范式提高开发效率

(3)多层级API封装,从简单到灵活,兼顾易用与高效

(4)孪生调试,CPU侧模拟昇腾AI处理器(NPU)的行为,可优先在CPU侧调试

注:NPU不能独立运行,需要与CPU协同工作,可以看成是CPU的协处理器,NPU与CPU通过PCIe总线连接在一起来协同工作。

3.当前Ascend C支持的产品型号为:

Atlas 推理系列产品(Ascend 310P处理器)

Atlas 训练系列产品

Atlas A2训练系列产品

Atlas 200/500 A2推理产品

CANN:释放澎湃算力,提供开放易用的开发体系,是华为针对AI场景推出的异构计算架构,通过提供多层次的编程接口,支持用户快速构建基于昇腾平台的AI应用和业务。

4.昇腾AI处理器:有不同的形态,最核心的部件是AI Core,有多个,是神经网络加速的计算核心,使用Ascend C编程语言开发的算子就运行在AI Core上。

AI Core内部的并行计算架构抽象如图:

AI Core外面有一个Gobal Memory,是多个AI Core共享的,内部有一块本地内存Local Memory,因为靠近计算单元,所以它的带宽非常高,相对的容量就会很小。AI Core内部的核心组件有三个计算单元,标量计算单元、向量计算单元,矩阵计算单元。还有一个DMA搬运单元负责在Global Memory和Local Memory之间搬运数据。

5.SIMD(单指令多数据计算):Ascend C编程API主要是向量计算API和矩阵运算API,计算API都是SIMD 样式。

6.并行计算中两种常见方法:单程序多数据(SPMD)和流水线并行

二、Ascend C编程模型与范式

1.SPMD模型

Ascend C算子编程是SPMD的编程,将需要处理的数据拆分并分布在多个计算核心上运行,多个AI Core共享相同的指令代码,每个核上的运行实例唯一的区别是block_idx不同,每个核通过不同的block_idx来识别自己的身份,编程中使用函数GetBlockIdx()获取ID。

2.核函数:是Ascend C算子设备侧实现的入口,要为在一个核上执行的代码规定要进行的数据访问和计算操作,当核函数被调用时,多个核都执行相同的核函数代码,具有相同的参数,并行执行Ascend C允许用户使用核函数这种C/C++函数的语法扩展来管理设备端的运行代码,用户在核函数中进行算子类对象的创建和其成员函数的调用,由此实现该算子的所有功能。

3.核函数定义:

extern "C" __global__ __aicore__ void add_custom(__gm__ uint8_t* x, __gm__ uint8_t* y, __gm__ uint8_t* z);

__global__ __aicore__ void kernel_name(argument list);

使用__global__函数类型限定符来标识它是一个核函数,可以被<<<...>>>调用;使用__aicore__函数类型限定符来标识该核函数在设备端AI Core上执行。

指针入参变量需要增加变量类型限定符__gm__。表明该指针变量指向Global Memory上某处内存地址。

为了表达统一,使用GM_ADDR宏定义:

#define GM_ADDR __gm__ uint8_t*

使用GM_ADDR修饰入参的样例如下:

extern "C" __global__ __aicore__ void add_custom(GM_ADDR x, GM_ADDR y, GM_ADDR z)

在后续的使用中需要将其转化为实际的指针类型。

核函数必须具有void返回类型。

SPMD编程模型允许核函数调用时,多个核并行地执行同一个计算任务。

常见的函数调用形式:

function_name(argument list);

核函数使用内核调用符<<<...>>>这种语法形式,来规定核函数的执行配置:

kernel_name<<<blockDim, l2ctrl, stream>>>(argument list);

注:内核调用符仅可在NPU侧编译时调用,CPU侧编译无法识别该符号。

核函数的调用是异步的,核函数的调用结束后,控制权立刻返回给主机端,可以调用aclrtSynchronizeStream函数来强制主机端程序等待所有核函数执行完毕。

4.编程API:Ascend C算子采用标准C++语法和一组类库API进行编程,可以在核函数的实现中根据自己的需求选择合适的API。Ascend C API的计算操作数都是Tensor类型:GlobalTensor和LocalTensor。

5.类库API分类:

高阶API:提供Matmul、SoftMax等高阶API,封装常用算法逻辑,可减少重复开发,提高开发者开发效率。

基础API:提供基础功能API。

计算类API,包括标量计算API、向量计算API、矩阵计算API,分别实现调用Scalar计算单元、Vector计算单元、Cube计算单元执行计算的功能。

数据搬运API,计算API基于Local Memory数据进行计算,所以数据需要先从Global Memory搬运至Local Memory,再使用计算接口完成计算,最后从Local Memory搬出至Global Memory。执行搬运过程的接口称之为数据搬移接口,比如DataCopy接口。

内存管理API,用于分配管理内存,比如AllocTensor、FreeTensor接口。

任务同步API,完成任务间的通信和同步,比如EnQue、DeQue接口。

6.Ascend C流水编程范式:把算子核内的处理程序,分成多个流水任务(Stage),以张量(Tensor)为数据载体,以队列(Queue)进行任务之间的通信与同步,以内存管理模块(Pipe)管理任务间通信内存。

7.编程范式-抽象编程模型“TPIPE并行计算”:

8.任务的通信和同步:Ascend C中使用Queue队列完成任务之间的数据通信和同步,提供EnQue、DeQue等基础API。CopyIn任务中将输入数据从Global内存搬运至Local内存后,需要使用EnQue将LocalTensor放入VECIN的Queue中;Compute任务等待VECIN的Queue中LocalTensor出队之后才可以完成矢量计算,计算完成后使用EnQue将计算结果LocalTensor放入到VECOUT的Queue中;CopyOut任务等待VECOUT的Queue中LocalTensor出队,再将其拷贝到Global内存。

9.Ascend C使用GlobalTensor和LocalTensor作为数据的基本操作单元,它是各种指令API直接调用的对象,也是数据的载体。

10.编程范式-内存管理:任务间数据传递使用到的内存统一由内存管理模块Pipe进行管理。Pipe作为片上内存管理者,通过InitBuffer接口对外提供Queue内存初始化功能,开发者可以通过该接口为指定的Queue分配内存。Queue队列内存初始化完成后,需要使用内存时,通过调用AllocTensor来为LocalTensor分配内存,当创建的LocalTensor完成相关计算无需再使用时,再调用FreeTensor来回收LocalTensor的内存。编程过程中使用到的临时变量内存同样通过Pipe进行管理。临时变量可以使用TBuf数据结构来申请指定QuePosition上的存储空间。使用TBuf申请的内存空间只能参与计算,无法执行Queue队列的入队出队操作。

  • Ascend C矢量编程

1.使用内置宏__CCE_KT_TEST__来标识<<<...>>>仅在NPU模式下才会编译到,if defined则在CPU模式下编译,反之在NPU。

四、核函数运行验证


  

    

// AscendCL初始化

    CHECK_ACL(aclInit(nullptr));

    // 运行管理资源申请

    aclrtContext context;

    int32_t deviceId = 0;

    CHECK_ACL(aclrtSetDevice(deviceId));

    CHECK_ACL(aclrtCreateContext(&context, deviceId));

    aclrtStream stream = nullptr;

    CHECK_ACL(aclrtCreateStream(&stream));

    // 分配Host内存

    uint8_t *xHost, *yHost, *zHost;

    uint8_t *xDevice, *yDevice, *zDevice;

    CHECK_ACL(aclrtMallocHost((void**)(&xHost), inputByteSize));

    CHECK_ACL(aclrtMallocHost((void**)(&yHost), inputByteSize));

    CHECK_ACL(aclrtMallocHost((void**)(&zHost), outputByteSize));

    // 分配Device内存

    CHECK_ACL(aclrtMalloc((void**)&xDevice, inputByteSize, ACL_MEM_MALLOC_HUGE_FIRST));

    CHECK_ACL(aclrtMalloc((void**)&yDevice, inputByteSize, ACL_MEM_MALLOC_HUGE_FIRST));

    CHECK_ACL(aclrtMalloc((void**)&zDevice, outputByteSize, ACL_MEM_MALLOC_HUGE_FIRST));

    // Host内存初始化

    ReadFile("./input/input_x.bin", inputByteSize, xHost, inputByteSize);

    ReadFile("./input/input_y.bin", inputByteSize, yHost, inputByteSize);

    CHECK_ACL(aclrtMemcpy(xDevice, inputByteSize, xHost, inputByteSize, ACL_MEMCPY_HOST_TO_DEVICE));

    CHECK_ACL(aclrtMemcpy(yDevice, inputByteSize, yHost, inputByteSize, ACL_MEMCPY_HOST_TO_DEVICE));

    // 用内核调用符<<<>>>调用核函数完成指定的运算,add_custom_do中封装了<<<>>>调用

    add_custom_do(blockDim, nullptr, stream, xDevice, yDevice, zDevice);

    CHECK_ACL(aclrtSynchronizeStream(stream));

    // 将Device上的运算结果拷贝回Host

    CHECK_ACL(aclrtMemcpy(zHost, outputByteSize, zDevice, outputByteSize, ACL_MEMCPY_DEVICE_TO_HOST));

    WriteFile("./output/output_z.bin", zHost, outputByteSize);

    // 释放申请的资源

    CHECK_ACL(aclrtFree(xDevice));

    CHECK_ACL(aclrtFree(yDevice));

    CHECK_ACL(aclrtFree(zDevice));

    CHECK_ACL(aclrtFreeHost(xHost));

    CHECK_ACL(aclrtFreeHost(yHost));

    CHECK_ACL(aclrtFreeHost(zHost));

    // AscendCL去初始化

    CHECK_ACL(aclrtDestroyStream(stream));

    CHECK_ACL(aclrtDestroyContext(context));

    CHECK_ACL(aclrtResetDevice(deviceId));

    CHECK_ACL(aclFinalize());

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/462869.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Golang协程详解

一.协程的引入 1.通过案例文章引入并发,协程概念 见:[go学习笔记.第十四章.协程和管道] 1.协程的引入,调度模型&#xff0c;协程资源竞争问题 通过上面文章可以总结出Go并发编程原理: 在一个处理进程中通过关键字 go 启用多个协程&#xff0c;然后在不同的协程中完成不同的子任…

【网络编程基础(一)】网络基础和SOCKET

这里写目录标题 1、网络三要素2、IPV4和IPV6区别3、网络交互3.1、交互模型图3.2、基础通信协议3.3、OSI参考模型与TCP/IP参考模型对应关系 4、SOCKET网络套接字4.1、SOCKET分类4.2、基于流式套接字的编程流程4.3、网络通信雏形4.4、socket函数4.4.1、socket函数示例 4.5、bind函…

部署一个本地的ChatGPT(Ollama)

一 下载Ollama Ollama下载地址&#xff1a;https://ollama.com/download 下载完后 二 安装运行 双击下载好的OllamaSetup.exe开发 安装Ollama: 安装完成后&#xff0c;多了一个Ollama的菜单如下图 &#xff1a; Ollama安装好默认是配置开机运行&#xff0c;如果没有运行可以在…

高效使用 JMeter 生成随机数:探索 Random 和 UUID 算法

在压力测试中&#xff0c;经常需要生成随机值来模拟用户行为。JMeter 提供了多种方式来生成随机值&#xff0c;本文来具体介绍一下。 随机数函数 JMeter 提供了多个用于生成随机数的函数&#xff0c;其中最常用的是__Random函数。该函数可以生成一个指定范围内的随机整数或浮…

pytorch 实现线性回归(Pytorch 03)

一 线性回归框架 线性模型的四个模块&#xff1a;训练的数据集&#xff0c;线性模型&#xff0c;损失函数&#xff0c;优化算法。 1.1 数据集 使用房价预测数据集&#xff0c;我们希望根据房屋的面积和房龄等来估算房屋价格。 1.2 线性模型 预测公式&#xff0c; 价格 权重…

智慧公厕建设的重要性

智慧公厕建设一直被视为提升城市管理水平的重要举措。作为一个关键的城市基础设施&#xff0c;智慧公厕不仅可以改善公共卫生管理&#xff0c;还能提升城市居民的社会民生生活质量。此外&#xff0c;智慧公厕建设还能促进城市的可持续发展&#xff0c;降低能源消耗&#xff0c;…

rust学习笔记(1-7)

原文 8万字带你入门Rust 1.包管理工具Cargo 新建项目 1&#xff09;打开 cmd 输入命令查看 cargo 版本 cargo --version2&#xff09; 使用 cargo new 项目名 在文件夹&#xff0c;按 shift 鼠标右键 &#xff0c;打开命令行&#xff0c;运行如下命令&#xff0c;即可创建…

【SQL】1193. 每月交易 I 【年月日(日期)拼接相关函数】

前述 知识点学习&#xff1a; SQL 日期函数 day() 、month()、year() 各种使用方法mysql 两个字符年月拼接 题目描述 leetcode题目&#xff1a;1193. 每月交易 I 思路 先按照年月排&#xff0c;再按照country排列 日期拼接相关的函数 year(): 截取年份&#xff1b;month…

linuxOPS基础_linux命令合集

uname查看操作系统信息 命令&#xff1a;uname [参数] 作用&#xff1a;获取计算机操作系统相关信息 参数&#xff1a;-a&#xff0c;选项-a代表all&#xff0c;表示获取全部的系统信息&#xff08;类型、全部主机名、内核版本、发布时间、开源计划&#xff09; 用法一&…

将OpenCV与gcc和CMake结合使用

返回&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV4.9.0开源计算机视觉库在 Linux 中安装 下一篇&#xff1a; 引言&#xff1a; 近年来&#xff0c;计算机视觉技术在图像处理、目标检测和机器人等方面得到了广泛的应用…

基于FPGA的光纤通信系统设计

文章目录 光纤通信系统的组成发送端FPGA端口定义状态机设计代码示例 接收端功能模块端口定义状态机设计 光纤通信系统的组成 发送端FPGA 发送控制逻辑、数据编码、校验码生成、缓存控制、时钟控制 端口定义 状态机设计 代码示例 接收端功能模块 接收端控制逻辑、数据解码、…

前世档案(不用二叉树语法秒杀版c++)

网络世界中时常会遇到这类滑稽的算命小程序&#xff0c;实现原理很简单&#xff0c;随便设计几个问题&#xff0c;根据玩家对每个问题的回答选择一条判断树中的路径&#xff08;如下图所示&#xff09;&#xff0c;结论就是路径终点对应的那个结点。 现在我们把结论从左到右顺序…

综合知识篇06-软件架构设计考点(2024年软考高级系统架构设计师冲刺知识点总结系列文章)

专栏系列文章: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12593400.html案例分析篇00-【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例…

javaEE——线程的等待和结束

文章目录 Thread 类及常见方法启动一个线程中断一个线程变量型中断调用 interrupt() 方法来通知观察标志位是否被清除 等待一个线程获取当前线程引用休眠当前线程 线程的状态观察线程的所有状态观察 1: 关注 NEW 、 RUNNABLE 、 TERMINATED 状态的切换 多线程带来的风险为什么会…

java serlvet 高校学生画像平台系统Myeclipse开发mysql数据库web结构java编程计算机网页项目echarts图形展现

一、源码特点 java serlvet 高校学生画像平台系统是一套完善的java web信息管理系统 系统采用serlvetdaobean 模式开发本系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCA…

Selenium基础

1. selenium简介 用于实现自动化测试的 python 包&#xff0c;使用前需要安装对应浏览器驱动 from time import sleep from selenium import webdriver option webdriver.ChromeOptions() # 指定chrome存储路径的二进制形式 option.binary_locationD:\Chrome\Google\Chrome\Ap…

Android VINF

周末搞这玩意欲仙欲死&#xff0c;没办法只有看看。VINTF是供应商接口对象&#xff08;VINTF 对象&#xff09;&#xff0c;准确的说&#xff0c;这个是属于兼容性矩阵概念。。。有点想起了以前看过的一个电影&#xff0c;异次元杀阵。。。下面是谷歌官方的图。 本质上其实就是…

【分布式websocket 】前端vuex管理客户端消息crud!使用localStorage来存储【第19期】

前言 聊天系统客户端是要存储消息的&#xff0c;因为所有所有的历史消息都从服务器拉的话一方面服务器压力大&#xff0c;另一方面也耗费用户流量。所以客户端存储消息是势在必行的。如何存储呢上一篇文章也写了&#xff0c;大概就是浏览器的话是localStorage或者IndexedDB。然…

解决Linux中Eclipse启动时找不到Java环境的问题

按照报错的意思是没有在/usr/local/eclipse/jre/bin/java下找到java环境&#xff0c;我检查了一下eclipse的目录结构发现在/usr/local/eclipse没有jre/bin/java&#xff0c;我的想法是自己建对应文件夹然后软连接到我的java环境 cd /usr/local/eclipse sudo mkdir jre cd jre s…

基于Struts开发物流配送(快递)管理系统

开发工具&#xff1a;EclipseJdkTomcatMySQL数据库