【Ascend】AscendC编程接口,基础API和高阶API,tensor高维切分计算怎么理解?

本文主要是对官网中一些难以理解和混淆的内容做一些分析和记录。

1 AscendC编程接口

Ascend C提供一组类库API,开发者使用标准C++语法和类库API进行编程。Ascend C编程类库API示意图如下所示,分为:

  • Kernel API:用于实现算子核函数的API接口。包括:
    • 基本数据结构:kernel API中使用到的基本数据结构,比如GlobalTensor和LocalTensor。
    • 基础API:实现对硬件能力的抽象,开放芯片的能力,保证完备性和兼容性。标注为ISASI(Instruction Set Architecture Special Interface,硬件体系结构相关的接口)类别的API,不能保证跨硬件版本兼容。
    • 高阶API:实现一些常用的计算算法,用于提高编程开发效率,通常会调用多种基础API实现。高阶API包括数学库、Matmul、Softmax等API。高阶API可以保证兼容性。
  • Host API:
    • 高阶API配套的Tiling API:kernel侧高阶API配套的Tiling API,方便开发者获取kernel计算时所需的Tiling参数。
    • Ascend C算子原型注册与管理API:用于Ascend C算子原型定义和注册的API。
    • Tiling数据结构注册API:用于Ascend C算子TilingData数据结构定义和注册的API。
    • 平台信息获取API:在实现Host侧的Tiling函数时,可能需要获取一些硬件平台的信息,来支撑Tiling的计算,比如获取硬件平台的核数等信息。平台信息获取API提供获取这些平台信息的功能。
  • 算子调测API:用于算子调测的API,包括孪生调试,性能调测等。

进行Ascend C算子Host侧编程时,需要使用基础数据结构和API,请参考基础数据结构与接口;完成算子开发后,需要使用Runtime API完成算子的调用,请参考“AscendCL API(C)”。

放大

说明
Ascend C API所在头文件目录为:

  • 基础API:${INSTALL_DIR}/include/ascendc/basic_api/interface

  • 高阶API:(注意,如下目录头文件中包含的接口如果未在资料中声明,属于间接调用接口,开发者无需关注)

    • ${INSTALL_DIR}/include/ascendc/highlevel_api/lib
    • ${INSTALL_DIR}/include/tiling

    ${INSTALL_DIR}请替换为CANN软件安装后文件存储路径。若安装的Ascend-cann-toolkit软件包,以root安装举例,则安装后文件存储路径为:/usr/local/Ascend/ascend-toolkit/latest。

全量的API接口,可以参考:Ascend C API列表

2 基础API概述

对于基础API,主要分为以下几类:

  • 标量计算API,实现调用Scalar计算单元执行计算的功能。
  • 矢量计算API,实现调用Vector计算单元执行计算的功能。
  • 矩阵计算API,实现调用Cube计算单元执行计算的功能。
  • 数据搬运API,计算API基于Local Memory数据进行计算,所以数据需要先从Global Memory搬运至Local Memory,再使用计算API完成计算,最后从Local Memory搬出至Global Memory。执行搬运过程的接口称之为数据搬运API,比如DataCopy接口。
  • 内存管理与同步控制API
    • 内存管理API,用于分配管理内存,比如AllocTensor、FreeTensor接口;
    • 同步控制API,完成任务间的通信和同步,比如EnQue、DeQue接口。不同的API指令间有可能存在依赖关系,从抽象硬件架构可知,不同的指令异步并行执行,为了保证不同指令队列间的指令按照正确的逻辑关系执行,需要向不同的组件发送同步指令。同步控制API内部即完成这个发送同步指令的过程,开发者无需关注内部实现逻辑,使用简单的API接口即可完成。

对于基础API中的计算API,根据对数据操作方法的不同,分为以下几种计算方式

  • 整个tensor参与计算:通过运算符重载的方式实现,支持“+, -, *, /, |, &, <, >, <=, >=, ==, !=”,实现计算的简化表达。例如:
    dst=src1+src2
  • tensor前n个数据计算:针对源操作数的连续n个数据进行计算并连续写入目的操作数,解决一维tensor的连续计算问题。例如:
    Add(dst, src1, src2, n);
  • tensor高维切分计算:功能灵活的计算API,充分发挥硬件优势,支持对每个操作数的DataBlock Stride,Repeat Stride,Mask等参数的操作。
    下图以矢量加法为例,展示了几种计算方式的特点。
    计算API几种计算方式的特点
    针对tensor高维切分计算进行打开理解。

2.1 tensor高维切分计算

如何使用Tensor高维切分计算API

使用tensor高维切分计算API可充分发挥硬件优势,支持开发者控制指令的迭代执行和操作数的地址间隔,功能更加灵活。

矢量计算通过Vector计算单元完成,矢量计算的源操作数和目的操作数均通过Unified Buffer(UB)来进行存储。Vector计算单元每个迭代会从UB中取出8个datablock(每个datablock数据块内部地址连续,长度32Byte,总共256Byte),进行计算,并写入对应的8个datablock中

本文主要对mask参数进行理解说明

mask参数
mask用于控制每次迭代内参与计算的元素。可通过连续模式逐bit模式两种方式进行设置。

  • 连续模式:表示前面连续的多少个元素参与计算。数据类型为uint64_t。取值范围和源操作数的数据类型有关,数据类型不同,每次迭代内能够处理的元素个数最大值不同(当前数据类型单次迭代时能处理的元素个数最大值为:256 / sizeof(数据类型))。当操作数的数据类型占bit位16位时(如half/uint16_t),mask∈[1, 128];当操作数为32位时(如float/int32_t),mask∈[1, 64]。

具体样例如下:

// int16_t数据类型单次迭代能处理的元素个数最大值为256/sizeof(int16_t) = 128,mask = 64,mask∈[1, 128],所以是合法输入
// repeatTimes = 1, 共128个元素,单次迭代能处理128个元素,故repeatTimes = 1
// dstBlkStride, src0BlkStride, src1BlkStride = 1, 单次迭代内连续读取和写入数据
// dstRepStride, src0RepStride, src1RepStride = 8, 迭代间的数据连续读取和写入
uint64_t mask = 64;
AscendC::Add(dstLocal, src0Local, src1Local, mask, 1, { 1, 1, 1, 8, 8, 8 });

结果示例如下:

输入数据(src0Local): [1 2 3 ... 64 ...128]
输入数据(src1Local): [1 2 3 ... 64 ...128]
输出数据(dstLocal): [2 4 6 ... 128 undefined...undefined]

上述示例中,只有64个元素参与计算,65~128的元素是没有参与计算的,所以是undefined。

// int32_t数据类型单次迭代能处理的元素个数最大值为256/sizeof(int32_t) = 64,mask = 64,mask∈[1, 64],所以是合法输入
// repeatTimes = 1, 共64个元素,单次迭代能处理64个元素,故repeatTimes = 1
// dstBlkStride, src0BlkStride, src1BlkStride = 1, 单次迭代内连续读取和写入数据
// dstRepStride, src0RepStride, src1RepStride = 8, 迭代间的数据连续读取和写入(8个表示连续,是根据UB每次取8个dataBlock)
uint64_t mask = 64;
AscendC::Add(dstLocal, src0Local, src1Local, mask, 1, { 1, 1, 1, 8, 8, 8 });

结果示例如下:

输入数据(src0Local): [1 2 3 ... 64]
输入数据(src1Local): [1 2 3 ... 64]
输出数据(dstLocal): [2 4 6 ... 128]
  • 逐bit模式:可以按位控制哪些元素参与计算,bit位的值为1表示参与计算,0表示不参与
    mask为数组形式,数组长度和数组元素的取值范围和操作数的数据类型有关。当操作数为16位时,数组长度为2,mask[0]、mask[1]∈[0, 2^64-1]并且不同时为0;当操作数为32位时,数组长度为1,mask[0]∈(0, 2^64-1];当操作数为64位时,数组长度为1,mask[0]∈(0, 2^32-1]。

具体样例如下:

// 数据类型为int16_t
uint64_t mask[2] = {6148914691236517205, 6148914691236517205};
// repeatTimes = 1, 共128个元素,单次迭代能处理128个元素,故repeatTimes = 1。
// dstBlkStride, src0BlkStride, src1BlkStride = 1, 单次迭代内连续读取和写入数据。
// dstRepStride, src0RepStride, src1RepStride = 8, 迭代间的数据连续读取和写入。
AscendC::Add(dstLocal, src0Local, src1Local, mask, 1, { 1, 1, 1, 8, 8, 8 });

结果示例如下:

输入数据(src0Local): [1 2 3 ... 64 ...127 128]
输入数据(src1Local): [1 2 3 ... 64 ...127 128]
输出数据(dstLocal): [2 undefined 6 ... undefined ...254 undefined]

mask过程如下:

mask={6148914691236517205, 6148914691236517205}(注:6148914691236517205表示64位二进制数0b010101…01,mask按照低位到高位的顺序排布)
在这里插入图片描述
这里有没想过,为什么16位时,数组长度为2?
分析:当数据是16位,也就是2Byte,ub一次处理256Byte,也就是能处理128个数;那么掩码mask也就需要128个数,一个mask用64位表示,那么128个数,总共就需要2个这样的mask。

// 数据类型为int32_t
uint64_t mask[1] = {6148914691236517205};
// repeatTimes = 1, 共64个元素,单次迭代能处理64个元素,故repeatTimes = 1。
// dstBlkStride, src0BlkStride, src1BlkStride = 1, 单次迭代内连续读取和写入数据。
// dstRepStride, src0RepStride, src1RepStride = 8, 迭代间的数据连续读取和写入。
AscendC::Add(dstLocal, src0Local, src1Local, mask, 1, { 1, 1, 1, 8, 8, 8 });

结果示例如下:

输入数据(src0Local): [1 2 3 … 63 64]
输入数据(src1Local): [1 2 3 … 63 64]
输出数据(dstLocal): [2 undefined 6 … 126 undefined]
mask过程如下:

mask={6148914691236517205, 0}(注:6148914691236517205表示64位二进制数0b010101…01)

在这里插入图片描述
分析:当数据是32位,也就是4Byte,ub一次处理256Byte,也就是能处理64个数;那么掩码mask也就需要64个数,那么只需要一个mask用64位表示即可。

那么当操作数为64位时,数组长度为1,mask[0]∈(0, 2^32-1],这又如何分析呢?
分析见文章底部。

3 高阶API概述

高阶API一般是基于单核对常见算法的抽象和封装,用于提高编程开发效率,通常会调用多种基础API实现。高阶API包括数学库、Matmul、Softmax等API。

如下图所示,实现一个矩阵乘操作,使用基础API需要的步骤较多,需要关注格式转换、数据切分等逻辑;使用高阶API则无需关注这些逻辑,可以快速实现功能。
在这里插入图片描述

由于AI处理器的Scalar计算单元执行能力有限,为减少算子Kernel侧的Scalar计算,将部分计算在Host端执行,这需要编写Host端Tiling代码。注意,在程序中调用高阶API的Tiling接口或者使用高阶API的Tiling结构体参数时,需要引入依赖的头文件,示例如下:

#include "tiling/tiling_api.h"

问题:当操作数为64位时,数组长度为1,mask[0]∈(0, 2^32-1]?
分析:当数据是64位,也就是8Byte,ub一次处理256Byte,也就是能处理32个数;那么掩码mask也就需要32个数,那么只需要一个mask用32位表示即可。

Java是一种具备卓越性能与广泛平台适应性的高级程序设计语言,最初由Sun Microsystems(现属Oracle公司)的James Gosling及其团队于1995年正式发布。该语言在设计上追求简洁性、稳定性、可移植性以及并发处理能力,同时具备动态执行特性。其核心特征与显著优点可归纳如下: **平台无关性**:遵循“一次编写,随处运行”的理念,Java编写的程序能够在多种操作系统与硬件环境中执行,无需针对不同平台进行修改。这一特性主要依赖于Java虚拟机(JVM)的实现,JVM作为程序与底层系统之间的中间层,负责解释并执行编译后的字节码。 **面向对象范式**:Java全面贯彻面向对象的设计原则,提供对封装、继承、多态等机制的完整支持。这种设计方式有助于构建结构清晰、模块独立的代码,提升软件的可维护性与扩展性。 **并发编程支持**:语言层面集成了多线程处理能力,允许开发者构建能够同时执行多项任务的应用程序。这一特性尤其适用于需要高并发处理的场景,例如服务器端软件、网络服务及大规模分布式系统。 **自动内存管理**:通过内置的垃圾回收机制,Java运行时环境能够自动识别并释放不再使用的对象所占用的内存空间。这不仅降低了开发者在内存管理方面的工作负担,也有效减少了因手动管理内存可能引发的内存泄漏问题。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值