OrangePi AIpro 香橙派 昇腾 Ascend C算子开发 - HelloWorld

2024-08-31 06:52

本文主要是介绍OrangePi AIpro 香橙派 昇腾 Ascend C算子开发 - HelloWorld,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

OrangePi AIpro 香橙派 昇腾 Ascend C算子开发 - HelloWorld

flyfish

Ascend C算子编程SPMD(Single-Program Multiple-Data)编程
假设,从输入数据到输出数据需要经过3个阶段任务的处理(T1、T2、T3)。如下图所示,SPMD会启动一组进程,并行处理待处理的数据。对待处理数据切分,把切分后数据分片分发给不同进程处理,每个进程对自己的数据分片进行3个任务的处理。
在这里插入图片描述
具体到Ascend C编程模型中的应用,是将需要处理的数据被拆分并同时在多个计算核心(类比于上文介绍中的多个进程)上运行,从而获取更高的性能。多个AI Core共享相同的指令代码,每个核上的运行实例唯一的区别是block_idx不同,每个核通过不同的block_idx来识别自己的身份。block的概念类似于上文中进程的概念,block_idx就是标识进程唯一性的进程ID。并行计算过程的示意图如下图所示。
在这里插入图片描述
核函数(Kernel Function)Ascend C算子设备侧实现的入口。在核函数中,需要为在一个核上执行的代码规定要进行的数据访问和计算操作,当核函数被调用时,多个核都执行相同的核函数代码,具有相同的参数,并行执行。

Ascend C允许用户使用核函数这种C/C++函数的语法扩展来管理设备端的运行代码,用户在核函数中进行算子类对象的创建和其成员函数的调用,由此实现该算子的所有功能。核函数是主机端和设备端连接的桥梁。

一个Hello World例子展示Ascend C核函数(设备侧实现的入口函数)的基本写法和如何被调用的流程。

hello_world.cpp

#include "kernel_operator.h"extern "C" __global__ __aicore__ void hello_world()
{AscendC::printf("Hello World!!!\n");
}void hello_world_do(uint32_t blockDim, void *stream)
{hello_world<<<blockDim, nullptr, stream>>>();
}

main.cpp

#include "acl/acl.h"
extern void hello_world_do(uint32_t coreDim, void *stream);int32_t main(int argc, char const *argv[])
{aclInit(nullptr);int32_t deviceId = 0;aclrtSetDevice(deviceId);aclrtStream stream = nullptr;aclrtCreateStream(&stream);constexpr uint32_t blockDim = 8;hello_world_do(blockDim, stream);aclrtSynchronizeStream(stream);aclrtDestroyStream(stream);aclrtResetDevice(deviceId);aclFinalize();return 0;
}

HelloWorldSample例子

下载地址
进入源码目录执行

 bash run.sh -v Ascend310B4

结果

opType=hello_world, DumpHead: AIV-0, CoreType=, block dim=8, total_block_num=8, block_remain_len=1048424, block_initial_space=1048576, rsv=0, magic=5aa5bccd
CANN Version: 901005402, TimeStamp: 20240821
Hello World!!!
opType=hello_world, DumpHead: AIV-1, CoreType=, block dim=8, total_block_num=8, block_remain_len=1048424, block_initial_space=1048576, rsv=0, magic=5aa5bccd
CANN Version: 901005402, TimeStamp: 20240821
Hello World!!!
opType=hello_world, DumpHead: AIV-2, CoreType=, block dim=8, total_block_num=8, block_remain_len=1048424, block_initial_space=1048576, rsv=0, magic=5aa5bccd
CANN Version: 901005402, TimeStamp: 20240821
Hello World!!!
opType=hello_world, DumpHead: AIV-3, CoreType=, block dim=8, total_block_num=8, block_remain_len=1048424, block_initial_space=1048576, rsv=0, magic=5aa5bccd
CANN Version: 901005402, TimeStamp: 20240821
Hello World!!!
opType=hello_world, DumpHead: AIV-4, CoreType=, block dim=8, total_block_num=8, block_remain_len=1048424, block_initial_space=1048576, rsv=0, magic=5aa5bccd
CANN Version: 901005402, TimeStamp: 20240821
Hello World!!!
opType=hello_world, DumpHead: AIV-5, CoreType=, block dim=8, total_block_num=8, block_remain_len=1048424, block_initial_space=1048576, rsv=0, magic=5aa5bccd
CANN Version: 901005402, TimeStamp: 20240821
Hello World!!!
opType=hello_world, DumpHead: AIV-6, CoreType=, block dim=8, total_block_num=8, block_remain_len=1048424, block_initial_space=1048576, rsv=0, magic=5aa5bccd
CANN Version: 901005402, TimeStamp: 20240821
Hello World!!!
opType=hello_world, DumpHead: AIV-7, CoreType=, block dim=8, total_block_num=8, block_remain_len=1048424, block_initial_space=1048576, rsv=0, magic=5aa5bccd
CANN Version: 901005402, TimeStamp: 20240821
Hello World!!!

在这里插入图片描述

extern "C" __global__ __aicore__ void hello_world()

核函数时需要遵循以下规则

使用函数类型限定符

除了需要按照C/C++函数声明的方式定义核函数之外,还要为核函数加上额外的函数类型限定符,包含__global__和__aicore__。

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

__global__ __aicore__ void kernel_name(argument list);

编程中使用到的函数可以分为三类:核函数(device侧执行)host侧执行函数device侧执行函数(除核函数之外的)。三者的调用关系如下图所示:

host侧执行函数可以调用同类的host执行函数,也就是通用C/C++编程中的函数调用;也可以通过<<<>>>调用核函数。
device侧执行函数(除核函数之外的)可以调用同类的device侧执行函数。
核函数可以调用device侧执行函数(除核函数之外的)。
核函数(device侧执行)、host侧执行函数、device侧执行函数(除核函数之外的)调用关系图
在这里插入图片描述

使用变量类型限定符

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

规则:核函数必须具有void返回类型。
规则:仅支持入参为指针或C/C++内置数据类型(Primitive data types),如:half* s0、float* s1、int32_t c
建议:为了统一表达,建议使用GM_ADDR宏来修饰入参,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)

这里统一使用uint8_t类型的指针,在后续的使用中需要将其转化为实际的指针类型。

代码解释 extern "C"

extern "C" 告诉编译器,不要对这些函数名进行C++的名称修饰,这样C语言的代码就可以正确地调用这些函数。
如果有一部分代码是用C编写的,另一部分代码是用C++编写的,extern "C" 会确保 my_c_function 以C语言的方式进行链接,C++编写的代码能够调用C语言编写的函数。
名称修饰是编译器生成唯一符号名的一种机制,目的是支持C++的高级功能,如函数重载。通过名称修饰,编译器确保每个函数或变量在链接阶段具有唯一性,避免命名冲突。

举个例子

考虑以下两个函数:

int add(int a, int b);
double add(double a, double b);

在C语言中,由于函数名称不能重载,这两个函数将会引起命名冲突。但是在C++中,编译器会将这两个函数分别转换为不同的符号名,例如(符号名称是编译器生成的,具体表示可能会不同):

_add_int_int
_add_double_double

这些修饰后的名称在编译后的二进制文件中存储,使得它们可以在链接时区分开来。
使用 extern "C" 避免名称修饰如果希望C++函数能够被C代码调用,或者希望C++代码调用C语言的函数,需要使用extern "C"来告诉编译器不要对这些函数进行名称修饰。例如:

extern "C" void myFunction(int a);

在这种情况下,myFunction 的名字在编译后的二进制文件中将保持为 myFunction,而不会被修饰。

代码解释 extern void hello_world_do(uint32_t coreDim, void *stream);中的extern

在C++中,extern 关键字用于声明一个变量或函数是由其他文件定义的,而不是在当前文件中定义的。它告诉编译器这个函数的定义在另一个编译单元(例如另一个源文件)中,而不是在当前文件中。在提供的 main.cpp 文件中,extern void hello_world_do(uint32_t coreDim, void *stream); 这一行的作用是声明 hello_world_do 函数的存在,使得 main.cpp 文件可以调用这个函数,而不需要在 main.cpp 中定义它。

原理:

  1. 函数定义在另一个文件中
    hello_world_do 函数实际上是在 hello_world.cpp 文件中定义的。为了在 main.cpp 中使用这个函数,编译器需要知道这个函数的签名(返回类型、参数类型等)。通过使用 extern,告诉编译器“这个函数在别的地方定义了,只需要知道它的签名就可以了”。

  2. 链接阶段的作用
    编译器在编译 main.cpp 时,不需要知道 hello_world_do 函数的具体实现,只需要知道它的签名。而在链接阶段,链接器会把 hello_world.cpp 中的 hello_world_do 函数实现与 main.cpp 中的调用关联起来。

执行配置由3个参数决定:

blockDim,规定了核函数将会在几个核上执行。每个执行该核函数的核会被分配一个逻辑ID,即block_idx,可以在核函数的实现中调用GetBlockIdx来获取block_idx;
l2ctrl,保留参数,暂时设置为固定值nullptr,开发者无需关注;
stream,类型为aclrtStream,stream用于维护一些异步操作的执行顺序,确保按照应用程序中的代码调用顺序在device上执行。

在这里插入图片描述计算单元包括了三种基础计算资源:Cube计算单元、Vector计算单元和Scalar计算单元。
存储单元包括内部存储和外部存储:

AI Core的内部存储,统称为Local Memory,对应的数据类型为LocalTensor。由于不同芯片间硬件资源不固定,可以为UB、L1、L0A、L0B等。
AI Core能够访问的外部存储称之为Global Memory,对应的数据类型为GlobalTensor。

DMA(Direct Memory Access)搬运单元:负责在Global Memory和Local Memory之间搬运数据

这篇关于OrangePi AIpro 香橙派 昇腾 Ascend C算子开发 - HelloWorld的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1123199

相关文章

Spring Shell 命令行实现交互式Shell应用开发

《SpringShell命令行实现交互式Shell应用开发》本文主要介绍了SpringShell命令行实现交互式Shell应用开发,能够帮助开发者快速构建功能丰富的命令行应用程序,具有一定的参考价... 目录引言一、Spring Shell概述二、创建命令类三、命令参数处理四、命令分组与帮助系统五、自定义S

Python通过模块化开发优化代码的技巧分享

《Python通过模块化开发优化代码的技巧分享》模块化开发就是把代码拆成一个个“零件”,该封装封装,该拆分拆分,下面小编就来和大家简单聊聊python如何用模块化开发进行代码优化吧... 目录什么是模块化开发如何拆分代码改进版:拆分成模块让模块更强大:使用 __init__.py你一定会遇到的问题模www.

Spring Security基于数据库的ABAC属性权限模型实战开发教程

《SpringSecurity基于数据库的ABAC属性权限模型实战开发教程》:本文主要介绍SpringSecurity基于数据库的ABAC属性权限模型实战开发教程,本文给大家介绍的非常详细,对大... 目录1. 前言2. 权限决策依据RBACABAC综合对比3. 数据库表结构说明4. 实战开始5. MyBA

使用Python开发一个简单的本地图片服务器

《使用Python开发一个简单的本地图片服务器》本文介绍了如何结合wxPython构建的图形用户界面GUI和Python内建的Web服务器功能,在本地网络中搭建一个私人的,即开即用的网页相册,文中的示... 目录项目目标核心技术栈代码深度解析完整代码工作流程主要功能与优势潜在改进与思考运行结果总结你是否曾经

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

Python基于wxPython和FFmpeg开发一个视频标签工具

《Python基于wxPython和FFmpeg开发一个视频标签工具》在当今数字媒体时代,视频内容的管理和标记变得越来越重要,无论是研究人员需要对实验视频进行时间点标记,还是个人用户希望对家庭视频进行... 目录引言1. 应用概述2. 技术栈分析2.1 核心库和模块2.2 wxpython作为GUI选择的优

利用Python开发Markdown表格结构转换为Excel工具

《利用Python开发Markdown表格结构转换为Excel工具》在数据管理和文档编写过程中,我们经常使用Markdown来记录表格数据,但它没有Excel使用方便,所以本文将使用Python编写一... 目录1.完整代码2. 项目概述3. 代码解析3.1 依赖库3.2 GUI 设计3.3 解析 Mark

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

基于Python开发批量提取Excel图片的小工具

《基于Python开发批量提取Excel图片的小工具》这篇文章主要为大家详细介绍了如何使用Python中的openpyxl库开发一个小工具,可以实现批量提取Excel图片,有需要的小伙伴可以参考一下... 目前有一个需求,就是批量读取当前目录下所有文件夹里的Excel文件,去获取出Excel文件中的图片,并

基于Python开发PDF转PNG的可视化工具

《基于Python开发PDF转PNG的可视化工具》在数字文档处理领域,PDF到图像格式的转换是常见需求,本文介绍如何利用Python的PyMuPDF库和Tkinter框架开发一个带图形界面的PDF转P... 目录一、引言二、功能特性三、技术架构1. 技术栈组成2. 系统架构javascript设计3.效果图