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

相关文章

SpringBoot开发中十大常见陷阱深度解析与避坑指南

《SpringBoot开发中十大常见陷阱深度解析与避坑指南》在SpringBoot的开发过程中,即使是经验丰富的开发者也难免会遇到各种棘手的问题,本文将针对SpringBoot开发中十大常见的“坑... 目录引言一、配置总出错?是不是同时用了.properties和.yml?二、换个位置配置就失效?搞清楚加

Python中对FFmpeg封装开发库FFmpy详解

《Python中对FFmpeg封装开发库FFmpy详解》:本文主要介绍Python中对FFmpeg封装开发库FFmpy,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、FFmpy简介与安装1.1 FFmpy概述1.2 安装方法二、FFmpy核心类与方法2.1 FF

基于Python开发Windows屏幕控制工具

《基于Python开发Windows屏幕控制工具》在数字化办公时代,屏幕管理已成为提升工作效率和保护眼睛健康的重要环节,本文将分享一个基于Python和PySide6开发的Windows屏幕控制工具,... 目录概述功能亮点界面展示实现步骤详解1. 环境准备2. 亮度控制模块3. 息屏功能实现4. 息屏时间

Python实例题之pygame开发打飞机游戏实例代码

《Python实例题之pygame开发打飞机游戏实例代码》对于python的学习者,能够写出一个飞机大战的程序代码,是不是感觉到非常的开心,:本文主要介绍Python实例题之pygame开发打飞机... 目录题目pygame-aircraft-game使用 Pygame 开发的打飞机游戏脚本代码解释初始化部

使用Python开发一个现代化屏幕取色器

《使用Python开发一个现代化屏幕取色器》在UI设计、网页开发等场景中,颜色拾取是高频需求,:本文主要介绍如何使用Python开发一个现代化屏幕取色器,有需要的小伙伴可以参考一下... 目录一、项目概述二、核心功能解析2.1 实时颜色追踪2.2 智能颜色显示三、效果展示四、实现步骤详解4.1 环境配置4.

Python使用smtplib库开发一个邮件自动发送工具

《Python使用smtplib库开发一个邮件自动发送工具》在现代软件开发中,自动化邮件发送是一个非常实用的功能,无论是系统通知、营销邮件、还是日常工作报告,Python的smtplib库都能帮助我们... 目录代码实现与知识点解析1. 导入必要的库2. 配置邮件服务器参数3. 创建邮件发送类4. 实现邮件

基于Python开发一个有趣的工作时长计算器

《基于Python开发一个有趣的工作时长计算器》随着远程办公和弹性工作制的兴起,个人及团队对于工作时长的准确统计需求日益增长,本文将使用Python和PyQt5打造一个工作时长计算器,感兴趣的小伙伴可... 目录概述功能介绍界面展示php软件使用步骤说明代码详解1.窗口初始化与布局2.工作时长计算核心逻辑3

python web 开发之Flask中间件与请求处理钩子的最佳实践

《pythonweb开发之Flask中间件与请求处理钩子的最佳实践》Flask作为轻量级Web框架,提供了灵活的请求处理机制,中间件和请求钩子允许开发者在请求处理的不同阶段插入自定义逻辑,实现诸如... 目录Flask中间件与请求处理钩子完全指南1. 引言2. 请求处理生命周期概述3. 请求钩子详解3.1

如何基于Python开发一个微信自动化工具

《如何基于Python开发一个微信自动化工具》在当今数字化办公场景中,自动化工具已成为提升工作效率的利器,本文将深入剖析一个基于Python的微信自动化工具开发全过程,有需要的小伙伴可以了解下... 目录概述功能全景1. 核心功能模块2. 特色功能效果展示1. 主界面概览2. 定时任务配置3. 操作日志演示

JavaScript实战:智能密码生成器开发指南

本文通过JavaScript实战开发智能密码生成器,详解如何运用crypto.getRandomValues实现加密级随机密码生成,包含多字符组合、安全强度可视化、易混淆字符排除等企业级功能。学习密码强度检测算法与信息熵计算原理,获取可直接嵌入项目的完整代码,提升Web应用的安全开发能力 目录