上文介绍了Matmul矩阵乘的数据切分方案和数据流。Ascend C提供一组Matmul高阶API,封装了这些常用的切分和数据搬运、计算的算法逻辑,方便用户快速实现Matmul矩阵乘法的运算操作。开发者在host侧通过调用API自动获取Tiling参数,该参数传递到kernel侧后,在初始化操作时传入,通过几个简单的API即可完成矩阵乘操作。
host侧自动获取Tiling参数的关键步骤介绍如下:
auto ascendcPlatform = platform_ascendc::PlatformAscendC(context->GetPlatformInfo()); MatmulApiTiling cubeTiling(ascendcPlatform);
创建对象时需要传入需要传入硬件平台信息,硬件平台信息可以通过GetPlatformInfo获取。
cubeTiling.SetAType(TPosition::GM, CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT16); cubeTiling.SetBType(TPosition::GM, CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT16); cubeTiling.SetCType(TPosition::LCM, CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT); cubeTiling.SetBiasType(TPosition::GM, CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT);
cubeTiling.SetShape(M, N, K); cubeTiling.SetOrgShape(M, N, K);
cubeTiling.SetBufferSpace(-1, -1, -1);
cubeTiling.SetBias(true);
MatmulCustomTilingData tiling; if (cubeTiling.GetTiling(tiling.cubeTilingData) == -1){ return ge::GRAPH_FAILED; }
kernel侧使用Matmul API矩阵乘运算的具体步骤如下:
创建Matmul对象的示例如下:
// 纯cube模式(只有矩阵计算)场景下,需要设置该代码宏,并且必须在#include "lib/matmul_intf.h"之前设置 #define ASCENDC_CUBE_ONLY #include "lib/matmul_intf.h" typedef MatmulType<TPosition::GM, CubeFormat::ND, half> aType; typedef MatmulType<TPosition::GM, CubeFormat::ND, half> bType; typedef MatmulType<TPosition::GM, CubeFormat::ND, float> cType; typedef MatmulType<TPosition::GM, CubeFormat::ND, float> biasType; Matmul<aType, bType, cType, biasType> mm;
创建对象时需要传入A、B、C、Bias的参数类型信息, 类型信息通过MatmulType来定义,包括:内存逻辑位置、数据格式、数据类型。
REGIST_MATMUL_OBJ(&pipe, GetSysWorkSpacePtr(), mm, &tiling); // 初始化
mm.SetTensorA(gm_a); // 设置左矩阵A mm.SetTensorB(gm_b); // 设置右矩阵B mm.SetBias(gm_bias); // 设置Bias
while (mm.Iterate()) { mm.GetTensorC(gm_c); }
mm.IterateAll(gm_c);
mm.End();
Matmul高阶API内部实现时需要使用系统workspace,开发者需要:
size_t userWorkspaceSize = 0; size_t systemWorkspaceSize = ascendcPlatform.GetLibApiWorkSpaceSize(); size_t *currentWorkspace = context->GetWorkspaceSizes(1); currentWorkspace[0] = userWorkspaceSize + systemWorkspaceSize;
// 使用Matmul时必须设置workspace空间 SetSysWorkspace(workspace); if (GetSysWorkSpacePtr() == nullptr) { return; }
Host Tiling时可以设置Shape信息,用于Tiling计算;kernel侧运行时也可以修改部分shape信息,用于尾块设置、Matmul复用(多个Matmul计算复用一个Matmul对象)等场景。本节对涉及到的Shape概念进行介绍,并给出host侧和kernel侧设置Tiling信息的指导。
通过数据分块(Tiling)的介绍我们已经了解了orgShape(M、N、K),singleCoreShape(singleCoreM、singleCoreN、singleCoreK),baseShape(baseM、baseN、baseK)的概念,如下图所示:
除此之外,单核的Matmul Tiling时,实际参与Matmul计算的shape可以是原始shape中的一部分,singleM, singleN, singleK用于表达实际参与Matmul计算的shape,如下图所示。在单核的情况下,singleM, singleN, singleK会透传给singleCoreM, singleCoreN, singleCoreK。
创建Matmul对象时需要传入A、B、C、Bias的参数类型信息, 类型信息通过MatmulType来定义,包括:内存逻辑位置、数据格式、数据类型。示例如下:
typedef MatmulType<TPosition::GM, CubeFormat::ND, half> aType; typedef MatmulType<TPosition::GM, CubeFormat::ND, half> bType; typedef MatmulType<TPosition::GM, CubeFormat::ND, float> cType; typedef MatmulType<TPosition::GM, CubeFormat::ND, float> biasType; Matmul<aType, bType, cType, biasType> mm;
针对数据格式,包括CubeFormat::ND,CubeFormat::NZ, CubeFormat::ND_ALIGN三种,ND和NZ格式在数据格式章节已经介绍。
ND_ALIGN用于配置输出矩阵时按照一定的补齐规则进行输出。ND–>ND_ALIGN变换过程下图所示,矩阵数据类型为uint32_t,假设输出矩阵输出到UB,原矩阵N方向没有32字节对齐,设置ND_ALIGN则在其后补0,将其对齐到32字节。