使用说明
Ascend C提供一组Matmul高阶API,方便用户快速实现Matmul矩阵乘法的运算操作。
Matmul的计算公式为:C = A * B + Bias,其示意图如下。
- A、B为源操作数,A为左矩阵,形状为[M, K];B为右矩阵,形状为[K, N]。
- C为目的操作数,存放矩阵乘结果的矩阵,形状为[M, N]。
- Bias为矩阵乘偏置,形状为[1, N]。对A*B结果矩阵的每一行都采用该Bias进行偏置。

除Matmul的基本功能外,对Matmul的特性场景说明如下。您可以根据实际应用场景,选择性了解Matmul特性场景的内容。
- Matmul Tiling常量化
Matmul Tiling常量化为在编译期期间获取常量化的Matmul Tiling参数并进行算子编译,从而减少Scalar计算开销,提升算子整体性能。具体为,在获取Matmul模板时,可以确定MatmulConfig的singleCore Shape(MatmulConfig中的singleCoreM/singleCoreN/singleCoreK)和Base Shape(MatmulConfig中的basicM/basicN/basicK)参数,或者只确定Base Shape参数;通过指定获取模板的接口中的singleCore Shape和Base Shape参数,或者只指定Base Shape参数,获取自定义模板;然后通过调用GetMatmulApiTiling接口,得到常量化的Matmul Tiling参数。
- Sparse Matmul场景
Sparse Matmul场景为稀疏矩阵计算场景,该场景支持B矩阵为4:2的稠密输入并进行矩阵乘计算,从而减少矩阵乘计算时的内存占用,提升性能。在计算执行前的数据准备阶段,自行完成B矩阵的稠密化(稠密过程参考稠密算法说明)。实现Matmul计算时,在Kernel侧将B矩阵的参数类型设置为SparseMatmulType类型,稠密化过程中生成的索引矩阵通过SetSparseIndex接口传入,另外在获取Tiling前需要通过SetSparse接口设置使能Sparse Matmul场景。

下文中提及的M轴方向,即为A矩阵纵向;K轴方向,即为A矩阵横向或B矩阵纵向;N轴方向,即为B矩阵横向;尾轴,即为矩阵最后一个维度。
实现Matmul矩阵乘运算的具体步骤如下:
- 创建Matmul对象。
- 初始化操作。
- 设置左矩阵A、右矩阵B、Bias。
- 完成矩阵乘操作。
- 结束矩阵乘操作。
- 创建Matmul对象
创建Matmul对象的示例如下:
- 默认为MIX模式(包含矩阵计算和矢量计算),该场景下,不能定义ASCENDC_CUBE_ONLY宏。
- 纯Cube模式(只有矩阵计算)场景下,需要在代码中定义ASCENDC_CUBE_ONLY宏。
1 2 3 4 5 6 7 8 9
// 纯cube模式(只有矩阵计算)场景下,需要设置该代码宏,并且必须在#include "lib/matmul_intf.h"之前设置 // #define ASCENDC_CUBE_ONLY #include "lib/matmul_intf.h" typedef AscendC::MatmulType<AscendC::TPosition::GM, CubeFormat::ND, half> aType; typedef AscendC::MatmulType<AscendC::TPosition::GM, CubeFormat::ND, half> bType; typedef AscendC::MatmulType<AscendC::TPosition::GM, CubeFormat::ND, float> cType; typedef AscendC::MatmulType<AscendC::TPosition::GM, CubeFormat::ND, float> biasType; AscendC::Matmul<aType, bType, cType, biasType> mm;
创建对象时需要传入A、B、C、Bias的参数类型信息, 类型信息通过MatmulType来定义,包括:内存逻辑位置、数据格式、数据类型。
1 2 3 4 5 6 7 8 9
template <AscendC::TPosition POSITION, CubeFormat FORMAT, typename TYPE, bool ISTRANS = false, LayoutMode LAYOUT = LayoutMode::NONE, bool IBSHARE = false> struct MatmulType { constexpr static AscendC::TPosition pos = POSITION; constexpr static CubeFormat format = FORMAT; using T = TYPE; constexpr static bool isTrans = ISTRANS; constexpr static LayoutMode layout = LAYOUT; constexpr static bool ibShare = IBSHARE; };
Sparse Matmul场景与上述不同的是,通过SparseMatmulType类型定义矩阵B的参数类型,参数类型信息包括:矩阵B的内存逻辑位置、索引矩阵的内存逻辑位置、数据格式、数据类型等。
1 2
// 通过SparseMatmulType定义矩阵B的参数类型信息 typedef AscendC::SparseMatmulType<AscendC::TPosition::GM, AscendC::TPosition::GM, CubeFormat::ND, half> bType;
1 2 3 4
template <TPosition POSITION, TPosition INDEX_POSITION, CubeFormat FORMAT, typename TYPE, bool ISTRANS = false, LayoutMode LAYOUT = LayoutMode::NONE, bool IBSHARE = false> struct SparseMatmulType : public MatmulType<POSITION, FORMAT, TYPE, ISTRANS, LAYOUT, IBSHARE> { constexpr static TPosition indexPosition = INDEX_POSITION; };
表1 MatmulType参数说明 参数
说明
POSITION
内存逻辑位置。
针对
Atlas A2 训练系列产品/Atlas 800I A2 推理产品/A200I A2 Box 异构组件 :- A矩阵可设置为TPosition::GM,TPosition::VECOUT,TPosition::TSCM
- B矩阵可设置为TPosition::GM,TPosition::VECOUT,TPosition::TSCM
- Bias可设置为TPosition::GM,TPosition::VECOUT,TPosition::TSCM
- C矩阵可设置为TPosition::GM,TPosition::VECIN, TPosition::CO1
注意,C矩阵设置为TPosition::CO1时,C矩阵的数据排布格式仅支持CubeFormat::NZ,C矩阵的数据类型仅支持float、int32_t。
针对
Atlas 推理系列产品 AI Core:- A矩阵可设置为TPosition::GM,TPosition::VECOUT
- B矩阵可设置为TPosition::GM,TPosition::VECOUT
- Bias可设置为TPosition::GM,TPosition::VECOUT
- C矩阵可设置为TPosition::GM,TPosition::VECIN
针对
Atlas 200I/500 A2 推理产品 :- A矩阵可设置为TPosition::GM,TPosition::VECOUT,TPosition::TSCM
- B矩阵可设置为TPosition::GM,TPosition::VECOUT,TPosition::TSCM
- Bias可设置为TPosition::GM,TPosition::VECOUT
- C矩阵可设置为TPosition::GM,TPosition::VECIN, TPosition::TSCM
FORMAT
针对
Atlas A2 训练系列产品/Atlas 800I A2 推理产品/A200I A2 Box 异构组件 :- A矩阵可设置为CubeFormat::ND,CubeFormat::NZ
- B矩阵可设置为CubeFormat::ND,CubeFormat::NZ
- Bias可设置为CubeFormat::ND
- C矩阵可设置为CubeFormat::ND,CubeFormat::NZ,CubeFormat::ND_ALIGN
针对
Atlas 推理系列产品 AI Core:- A矩阵可设置为CubeFormat::ND,CubeFormat::NZ
- B矩阵可设置为CubeFormat::ND,CubeFormat::NZ
- Bias可设置为CubeFormat::ND
- C矩阵可设置为CubeFormat::ND,CubeFormat::NZ,CubeFormat::ND_ALIGN
注意:针对
Atlas 推理系列产品 AI Core,C矩阵设置为CubeFormat::ND时,要求尾轴32字节对齐,比如数据类型是half的情况下,N要求是16的倍数。针对
Atlas 200I/500 A2 推理产品 :- A矩阵可设置为CubeFormat::ND,CubeFormat::NZ
- B矩阵可设置为CubeFormat::ND,CubeFormat::NZ
- Bias可设置为CubeFormat::ND
- C矩阵可设置为CubeFormat::ND,CubeFormat::NZ
注意: 针对
Atlas 200I/500 A2 推理产品 ,C矩阵设置为TPosition::VECIN或者TPosition::TSCM,CubeFormat::ND时,要求尾轴32字节对齐,比如数据类型是half的情况下,N要求是16的倍数;C矩阵设置为TPosition::VECIN或者TPosition::TSCM,CubeFormat::NZ时,N要求是16的倍数。关于CubeFormat::NZ格式的A矩阵、B矩阵、C矩阵的对齐约束,请参考表4。
TYPE
数据类型。
针对Atlas A2 训练系列产品/Atlas 800I A2 推理产品/A200I A2 Box 异构组件 :- A矩阵可设置为half、float、bfloat16_t 、int8_t、int4b_t
- B矩阵可设置为half、float、bfloat16_t 、int8_t、int4b_t
- Bias可设置为half、float、int32_t
- C矩阵可设置为half、float、bfloat16_t、int32_t、int8_t
针对
Atlas 推理系列产品 AI Core:- A矩阵可设置为half、int8_t
- B矩阵可设置为half、int8_t
- Bias可设置为float、int32_t
- C矩阵可设置为half、float、int8_t、int32_t
针对
Atlas 200I/500 A2 推理产品 :- A矩阵可设置为half、float、bfloat16_t 、int8_t
- B矩阵可设置为half、float、bfloat16_t 、int8_t
- Bias矩阵可设置为half、float、int32_t
- C矩阵可设置为half、float、bfloat16_t、int32_t
注意:除B矩阵为int8_t数据类型外,A矩阵和B矩阵数据类型需要一致,具体数据类型组合关系请参考表2。
是否开启使能矩阵转置的功能。
- true为开启使能矩阵转置的功能,开启后,分别通过SetTensorA和SetTensorB中的isTransposeA、isTransposeB参数设置A、B矩阵是否转置。若设置A、B矩阵转置,Matmul会认为A矩阵形状为[K, M],B矩阵形状为[N, K]。
- false为不开启使能矩阵转置的功能,通过SetTensorA和SetTensorB不能设置A、B矩阵的转置情况。Matmul会认为A矩阵形状为[M, K],B矩阵形状为[K, N]。
默认为false不使能转置。
LAYOUT
表征数据的排布。
NONE:默认值,表示不使用BatchMatmul;其他选项表示使用BatchMatmul。
NORMAL:BMNK的数据排布格式,具体可参考IterateBatch中对该数据排布的介绍。
BSNGD:原始BSH shape做reshape后的数据排布,具体可参考IterateBatch中对该数据排布的介绍。
SBNGD:原始SBH shape做reshape后的数据排布,具体可参考IterateBatch中对该数据排布的介绍。
BNGS1S2:一般为前两种数据排布进行矩阵乘的输出,S1S2数据连续存放,一个S1S2为一个batch的计算数据,具体可参考IterateBatch中对该数据排布的介绍。
IBSHARE
是否使能IBShare(IntraBlock Share)。IBShare的功能是能够复用L1 Buffer上相同的A矩阵或B矩阵数据。当A矩阵和B矩阵同时使能IBShare时,表示L1 Buffer上的A矩阵和B矩阵同时复用,此时只支持Norm模板(该场景的参数使用样例请参考matmulABshare样例、使能IBShare样例)。
注意,A矩阵和B矩阵同时使能IBShare的场景,需要满足:
- 同一算子中其它Matmul对象的A矩阵和B矩阵也必须同时使能IBShare;
- 获取矩阵计算结果时,只支持调用IterateAll接口输出到GlobalTensor,即计算结果放置于Global Memory的地址,不能调用GetTensorC等接口。
除A、B矩阵同时复用的场景外,与IBShare模板配合使用,使用IBShare模板的要求是复用的矩阵必须在L1 Buffer上全载,具体参数设置详见表2。
表2 Matmul输入输出数据类型的支持列表 A矩阵
B矩阵
Bias
C矩阵
支持平台
float
float
float/half
float
Atlas A2 训练系列产品/Atlas 800I A2 推理产品/A200I A2 Box 异构组件 Atlas 200I/500 A2 推理产品
half
half
float
float
Atlas A2 训练系列产品/Atlas 800I A2 推理产品/A200I A2 Box 异构组件 Atlas 推理系列产品 AI CoreAtlas 200I/500 A2 推理产品
half
half
half
float
Atlas A2 训练系列产品/Atlas 800I A2 推理产品/A200I A2 Box 异构组件 Atlas 200I/500 A2 推理产品
int8_t
int8_t
int32_t
int32_t/half
Atlas A2 训练系列产品/Atlas 800I A2 推理产品/A200I A2 Box 异构组件 Atlas 推理系列产品 AI CoreAtlas 200I/500 A2 推理产品
int4b_t
int4b_t
int32_t
int32_t/half
Atlas A2 训练系列产品/Atlas 800I A2 推理产品/A200I A2 Box 异构组件
bfloat16_t
bfloat16_t
float
float
Atlas A2 训练系列产品/Atlas 800I A2 推理产品/A200I A2 Box 异构组件 Atlas 200I/500 A2 推理产品
bfloat16_t
bfloat16_t
half
float
Atlas A2 训练系列产品/Atlas 800I A2 推理产品/A200I A2 Box 异构组件
half
half
float
int8_t
Atlas A2 训练系列产品/Atlas 800I A2 推理产品/A200I A2 Box 异构组件
bfloat16_t
bfloat16_t
float
int8_t
Atlas A2 训练系列产品/Atlas 800I A2 推理产品/A200I A2 Box 异构组件
int8_t
int8_t
int32_t
int8_t
Atlas A2 训练系列产品/Atlas 800I A2 推理产品/A200I A2 Box 异构组件 Atlas 推理系列产品 AI Core
half
half
float
half
Atlas A2 训练系列产品/Atlas 800I A2 推理产品/A200I A2 Box 异构组件 Atlas 推理系列产品 AI CoreAtlas 200I/500 A2 推理产品
half
half
half
half
Atlas A2 训练系列产品/Atlas 800I A2 推理产品/A200I A2 Box 异构组件 Atlas 200I/500 A2 推理产品
bfloat16_t
bfloat16_t
float
bfloat16_t
Atlas A2 训练系列产品/Atlas 800I A2 推理产品/A200I A2 Box 异构组件 Atlas 200I/500 A2 推理产品
half
int8_t
float
float
Atlas 推理系列产品 AI Core
- 初始化操作。
1
REGIST_MATMUL_OBJ(&pipe, GetSysWorkSpacePtr(), mm, &tiling); // 初始化matmul对象,参数含义请参考REGIST_MATMUL_OBJ章节
- 设置左矩阵A、右矩阵B、Bias。
1 2 3 4 5 6
mm.SetTensorA(gm_a); // 设置左矩阵A mm.SetTensorB(gm_b); // 设置右矩阵B mm.SetBias(gm_bias); // 设置Bias // Atlas 推理系列产品AI Core上需要额外调用SetLocalWorkspace接口设置计算所需的UB空间 mm.SetLocalWorkspace(usedUbBufLen);
- 完成矩阵乘操作。用户可以选择以下三种调用方式之一。
- 调用Iterate完成单次迭代计算,叠加while循环完成单核全量数据的计算。Iterate方式,可以自行控制迭代次数,完成所需数据量的计算,方式比较灵活。
1 2 3 4
// API接口内部会进行循环结束条件判断处理 while (mm.Iterate()) { mm.GetTensorC(gm_c); }
- 调用IterateAll完成单核上所有数据的计算。IterateAll方式,无需循环迭代,使用比较简单。
1
mm.IterateAll(gm_c);
- 用户申请用于存放矩阵乘结果的逻辑位置CO1内存,调用一次或多次Iterate完成单次或多次迭代计算,在需要搬出计算结果时,调用Fixpipe接口完成CO1上计算结果的搬运,然后释放申请的CO1内存。该方式下,用户可以灵活控制计算和搬运的节奏,根据实际需要,一次计算对应一次结果的搬出,或者将多次计算结果缓存在CO1内存中,再一次性搬出计算结果。
在此种调用方式下,创建Matmul对象时,必须定义C矩阵的内存逻辑位置为TPosition::CO1、数据排布格式为CubeFormat::NZ、数据类型为float或int32_t。
Atlas 推理系列产品 AI Core暂不支持该方式。Atlas 200I/500 A2 推理产品 暂不支持该方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
// 定义C矩阵的类型信息 typedef AscendC::MatmulType<AscendC::TPosition::CO1, CubeFormat::NZ, float> cType; // 创建Matmul对象 AscendC::Matmul<aType, bType, cType, biasType> mm; // 用户提前申请CO1的内存l0cTensor TQue<TPosition::CO1, 1> CO1_; // 128 * 1024为申请的CO1内存大小 GetTPipePtr()->InitBuffer(CO1_, 1, 128 * 1024); LocalTensor<L0cT> l0cTensor = CO1_.template AllocTensor<L0cT>(); // 将l0cTensor作为入参传入Iterate,矩阵乘结果输出到用户申请的l0cTensor上 mm.Iterate(false, l0cTensor); // 调用Fixpipe接口将CO1上的计算结果搬运到GM FixpipeParamsV220 params; params.nSize = nSize; params.mSize = mSize; params.srcStride = srcStride; params.dstStride = dstStride; CO1_.EnQue(l0cTensor); CO1_.template DeQue<L0cT>(); Fixpipe<cType, L0cT, CFG_ROW_MAJOR>(gm[dstOffset], l0cTensor, params); //释放CO1内存 CO1_.FreeTensor(l0cTensor);
- 调用Iterate完成单次迭代计算,叠加while循环完成单核全量数据的计算。Iterate方式,可以自行控制迭代次数,完成所需数据量的计算,方式比较灵活。
- 结束矩阵乘操作。
1
mm.End();
源/目的操作数 |
外轴 |
内轴 |
---|---|---|
A矩阵/B矩阵 |
16的倍数 |
C0_size的倍数 |
C矩阵 |
16的倍数 |
16的倍数 |
C矩阵(使能channel_split功能) |
16的倍数 |
C0_size的倍数 |
C矩阵(不使能channel_split功能) |
16的倍数 |
16的倍数 |
注1:half/bfloat16_t数据类型的C0_size为16,float数据类型的C0_size为8,int8_t数据类型的C0_size为32,int4b_t数据类型的C0_size为64。 注2:channel_split功能通过MatmulConfig中的isEnableChannelSplit参数配置,具体内容请参考MatmulConfig。 |
实现原理

计算过程分为如下几步:
- 数据从GM搬到A1:DataCopy每次从矩阵A,搬出一个stepM*baseM*stepKa*baseK的矩阵块a1,循环多次完成矩阵A的搬运;数据从GM搬到B1:DataCopy每次从矩阵B,搬出一个stepKb*baseK*stepN*baseN的矩阵块b1,循环多次完成矩阵B的搬运;
- 数据从A1搬到A2:LoadData每次从矩阵块a1,搬出一个baseM * baseK的矩阵块a0;数据从B1搬到B2,并完成转置:LoadData每次从矩阵块b1,搬出一个baseK * baseN的矩阵块,并将其转置为baseN * baseK的矩阵块b0;
- 矩阵乘:每次完成一个矩阵块a0 * b0的计算,得到baseM * baseN的矩阵块co1;
- 数据从矩阵块co1搬到矩阵块co2: DataCopy每次搬运一块baseM * baseN的矩阵块co1到singleCoreM * singleCoreN的矩阵块co2中;
- 重复2-4步骤,完成矩阵块a1 * b1的计算;
- 数据从矩阵块co2搬到矩阵块C:DataCopy每次搬运一块singleCoreM * singleCoreN的矩阵块co2到矩阵块C中;
- 重复1-6步骤,完成矩阵A * B = C的计算。