基础知识【免费下载链接】asc-devkit本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言原生支持C和C标准规范主要由类库和语言扩展层构成提供多层级API满足多维场景算子开发诉求。项目地址: https://gitcode.com/cann/asc-devkit[!NOTE]说明 本节内容为使用高阶API进行矩阵乘法的编程指导。使用高阶API进行实际的矩阵编程时需要通过API参考确认接口支持的产品型号。矩阵乘法概述Matmul的计算公式为C A * B bias其示意图如下。A、B为源操作数A为左矩阵形状为[M, K]B为右矩阵形状为[K, N]。C为目的操作数存放矩阵乘结果的矩阵形状为[M, N]。bias为矩阵乘偏置形状为[1, N]。对A*B结果矩阵的每一行都采用该bias进行偏置。图1Matmul矩阵乘示意图![](https://raw.gitcode.com/cann/asc-devkit/raw/258f0725972f41f1a23d1fcda692fd94f2d443ce/docs/zh/guide/figures/Matmul矩阵乘示意图.png Matmul矩阵乘示意图?utm_sourcegitcode_repo_files)矩阵乘法数据流在了解矩阵乘法数据流之前需要先回顾一下几个重要的存储逻辑位置的概念逻辑位置和物理存储的映射关系请参考逻辑位置和物理存储的映射关系搬入数据的存放位置A1用于存放整块A矩阵可类比CPU多级缓存中的二级缓存搬入数据的存放位置B1用于存放整块B矩阵可类比CPU多级缓存中的二级缓存搬入数据的存放位置C1用于存放整块的矩阵乘偏置Bias矩阵可类比CPU多级缓存中的二级缓存搬入数据的存放位置A2用于存放切分后的小块A矩阵可类比CPU多级缓存中的一级缓存搬入数据的存放位置B2用于存放切分后的小块B矩阵可类比CPU多级缓存中的一级缓存搬入数据的存放位置C2用于存放切分后的小块矩阵乘偏置Bias矩阵可类比CPU多级缓存中的一级缓存结果数据的存放位置CO1用于存放小块结果C矩阵可理解为Cube Out结果数据的存放位置CO2用于存放整块结果C矩阵可理解为Cube Out搬入数据的存放位置VECCALC一般在计算需要临时变量时使用此位置。矩阵乘法数据流指矩阵乘的输入输出在各存储位置间的流向。逻辑位置的数据流向如下图所示为了简化描述没有列出biasA矩阵从输入位置到A2的数据流如下输入位置可以是GM或者VECOUTGM-A2GM-A1-A2VECOUT-A1-A2。由于A1比A2的空间更大数据从GM或VECOUT可以先搬入A1进行缓存待该数据执行Cube计算前数据直接从A1搬入A2这样在搬运大量数据时可以减少计算前的等待时间提升性能只有在搬入数据较少的场景才可能使用GM-A2的数据流。B矩阵从输入位置到B2的数据流如下输入位置可以是GM或者VECOUTGM-B2GM-B1-B2VECOUT-B1-B2。由于B1比B2的空间更大数据从GM或VECOUT可以先搬入B1进行缓存待该数据执行Cube计算前数据直接从B1搬入B2这样在搬运大量数据时可以减少计算前的等待时间提升性能只有在搬入数据较少的场景才可能使用GM-B2的数据流。完成A2*B2CO1计算。CO1数据汇聚到CO2CO1-CO2。从CO2到输出位置输出位置可以是GM或者VECINCO2-GM/CO2-VECIN。数据格式在完成Matmul矩阵乘法时主要涉及到两种分形格式ND和NZ。其它的数据格式请参考数据排布格式。ND普通格式N维张量。NZ为满足AI Core中Cube计算单元高性能计算的需要引入该特殊格式。ND – NZ的变换过程为(..., N, H, W )-pad-(..., N, H1*H0, W1*W0)-reshape-(..., N, H1, H0, W1, W0)-transpose-(..., N, W1, H1, H0, W0)如下图所示WH大小的矩阵被分为H1*W1个分形按照列优先排布形状如N字形每个分形内部有H0*W0个元素按照行优先排布形状如z字形。所以这种数据格式称为NZ大N小Z格式。下面我们再通过一个具体的例子来深入理解ND和NZ格式的数据排布区别。假设分形格式为2*2如下图所示4*4的矩阵ND144和NZ12222格式存储的情况下数据在内存中的排布格式分别为ND: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15NZ: 0, 1, 4, 5, 8, 9, 12, 13, 2, 3, 6, 7, 10, 11, 14, 15数据分块Tiling多核切分为了实现多核并行需要将矩阵数据进行切分分配到不同的核上进行处理。切分策略如下图所示对于A矩阵沿着M轴进行切分切分成多份的singleCoreM单核上处理SingleCoreM * K大小的数据。对于B矩阵沿着N轴进行切分切分成多份的singleCoreN单核上处理K * SingleCoreN大小的数据。对于C矩阵SingleCoreM * K大小的A矩阵和K * SingleCoreN大小的B矩阵相乘得到SingleCoreM * SingleCoreN大小的C矩阵即为单核上输出的C矩阵大小。比如下图中共有8个核参与计算将A矩阵沿着M轴划分为4块将B矩阵沿着N轴切分为两块单核上仅处理某一分块比如图中绿色部分为core3上参与计算的数据SingleCoreM * K大小的A矩阵分块和SingleCoreN* K大小的B矩阵分块相乘得到SingleCoreM * SingleCoreN大小的C矩阵分块。另外单核上处理的K轴长度为SingleCoreK对于K轴较大的场景可以沿着K轴进行切分切分成多份的singleCoreK详细案例介绍请参考Matmul高阶API开启多核切K。核内切分大多数情况下Local Memory的存储无法完整的容纳算子的输入与输出需要每次搬运一部分输入进行计算然后搬出再搬运下一部分输入进行计算直到得到完整的最终结果也就是需要做核内的输入切分。切分的策略如下所示对于A矩阵沿M轴进行切分将singleCoreM切分成多份的baseM切分成的份数对应图示的mIter沿K轴进行切分切分成多份的baseK。对于B矩阵沿N轴进行切分将singleCoreN切分成多份的baseN切分成的份数对应图示的nIter沿K轴进行切分切分成多份的baseK。对于C矩阵A矩阵中baseM*baseK大小的分块和B矩阵中baseK*baseN大小的分块相乘并累加得到C矩阵中对应位置baseM*baseN大小的分块。比如图中结果矩阵中的绿色矩阵块5是通过如下的累加过程得到的a*ab*bc*cd*de*ef*f。除了基本块形状baseM, baseN, baseK外还有一些常用的tiling参数其含义如下iterateOrder一次Iterate迭代计算出[baseM, baseN]大小的C矩阵分片。Iterate完成后Matmul会自动偏移下一次Iterate输出的C矩阵位置iterateOrder表示自动偏移的顺序。0代表先往M轴方向偏移再往N轴方向偏移1代表先往N轴方向偏移再往M轴方向偏移。在上图的示例中iterateOrder取值为0。depthA1depthB1L1 BufferA1、L1 BufferB1中存储的矩阵片分别全载到L0A BufferA2、L0B BufferB2的份数L0A BufferA2、L0B BufferB2的存储大小分别是baseM * baseK、baseN * baseK即depthA1是A1矩阵切片含有baseM * baseK块的个数depthB1是B1矩阵切片含有baseN * baseK块的个数。stepMstepNstepM为左矩阵在L1 BufferA1中缓存的buffer M方向上baseM的倍数stepN为右矩阵在L1 BufferB1中缓存的buffer N方向上baseN的倍数。stepKastepKbstepKa为左矩阵在L1 BufferA1中缓存的buffer K方向上baseK的倍数stepKb为右矩阵在L1 BufferB1中缓存的buffer K方向上baseK的倍数。【免费下载链接】asc-devkit本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言原生支持C和C标准规范主要由类库和语言扩展层构成提供多层级API满足多维场景算子开发诉求。项目地址: https://gitcode.com/cann/asc-devkit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考