【昇腾CANN训练营】理解AscendC编程中的基本概念
学好AscendC算子开发需要系统掌握昇腾NPU的体系架构和编程范式。在这个过程中,准确理解基础概念尤为重要,因为我们常常被学习课程中同一称谓的说法感到迷茫。一些名词在不同上下文环境中具有不同含义,这给初学者带来了不小的认知挑战。以"Block"这一概念为例,其具体含义会随着应用场景的变化而变化:
- 在并行计算语境下,它指代AI Core的逻辑计算单元
- 在数据切分时,它又代表单核计算所需的数据块大小
- 在指令层面,它表示数据处理的最小基本单位(32Byte)
这种术语的多义性也许并不多见,但确实容易造成学习者的困惑。在实际开发中,许多问题的根源往往可以追溯到对基础概念的掌握不够深入,所以我们需要在进阶过程中不时的回顾这些基础知识,澄清理解上的模糊点,并建立更加系统化的知识体系。
从AI Core的计算单元到内存层次结构,从并行计算模型到数据组织形式,这些都是理解昇腾NPU编程框架的基础概念。
我们先来看一下昇腾NPU的基本架构,耦合架构如下:
分离架构如下:
仔细查看上面的架构图,我们就能理解在课程中谭老师讲AddCustom算子所说的,AI Core它能摸到的数据,是UB里的数据。所以AI Core在计算之前,需要先将输入数据从GM搬运到UB中,而L2 Cache,则对于我们来说透明化了。
存储层次维度呈现为金字塔结构。位于底层的是Global Memory(GM),作为设备主存存储大规模数据;上一级是L2 Cache,用于缓存频繁访问的数据;最上层是Local Memory,包括UB(Unified Buffer)等高速存储单元。这种层次化设计通过数据局部性提升访存效率。并且我们可以看到GM和L2 Cache是所有的计算单元共享,而UB则是每个计算单元独享的。
在编程模型中,GlobalTensor和LocalTensor分别对应不同存储层次的数据容器。我们可以简单理解为GlobalTensor放在Global Memory,而LocalTensor放在UB(Unified Buffer)。
#昇腾NPU某型号的相关配置
coreNum: 40
CoreNumAic: 20
CoreNumAiv: 40
l0_a_size: 65536 bytes
l0_b_size: 65536
l0_c_size: 131072
l1_size: 524032
l2_size: 100,663,296
ub_size: 196352
hbm_size: 34,359,738,368
l0_a_bw: 0 //预留 不支持 单位是Byte/cycle,cycle代表时钟周期。
l0_b_bw: 0
l0_c_bw: 0
l1_bw: 0
l2_bw: 110
ub_bw: 0
hbm_bw: 32
AI Core是昇腾处理器的计算单元。每个AI Core包含三种计算部件:Cube执行矩阵运算,Vector处理矢量计算,Scalar负责标量操作。这些计算部件通过Core ID进行物理标识,而在编程模型中则使用Block概念进行逻辑抽象。BlockDim表示参与计算的逻辑核数量,BlockID则标识具体的逻辑核实例。这种核间的区分,是编程模型提供并行计算能力的基础。
但是一会是Core,一会是Block,是不是很容易混淆?我理解这里的关键区别在于:Core是硬件层面的物理计算单元,而Block是编程层面的逻辑抽象概念。在AscendC编程中,开发者通过Block来组织并行计算,而硬件会将这些逻辑Block映射到实际的物理Core上执行。这种抽象让编程模型可以独立于具体硬件实现。但现在的情况是,作为初学者,这二者我就是一一对应的。这种抽象的好处,我暂时还没有体会到。
并行执行的关键软件概念是Kernel。作为设备端执行的基本单元,Kernel通过__global__修饰符定义,其执行过程涉及三个重要阶段:首先通过Kernel Launch将计算任务提交到硬件;然后根据BlockDim进行逻辑核的实例化;最后各实例通过BlockID区分执行上下文。
数据组织维度的核心是Tensor,这是算子计算的基本数据结构。对于大规模Tensor计算,需要采用Tiling技术进行数据分块,TilingData记录分块参数,TilingFunc提供分块算法。这种分块计算与AI Core的并行计算能力形成协同,实现了计算资源的充分利用。
在算子实现层面,算子(Operator,简称OP),是深度学习算法中执行特定数学运算或操作的基础单元,例如激活函数(如ReLU)、卷积(Conv)、池化(Pooling)以及归一化(如Softmax)。通过组合这些算子,可以构建神经网络模型。
OpType是算子类型,一类算子的统称。例如,在网络中可能会出现多个Add算子,名称分别为Add1、Add2,但这类算子的OpType均为Add。所以我们在msopgen创建自定义算子工程时,其原型定义里面是opType,而不是opName。这有点像编程里面的类和实例之间的关系。
理解这些概念的准确定义及其相互关系,是掌握AscendC编程的基础,并为后续的算子开发和优化奠定坚实基础。更多基础概念,可以参考CANN社区版 Ascend C算子开发>概念原理和术语>术语表
- 点赞
- 收藏
- 关注作者
评论(0)