☰
asc-devkit 矩阵编程高阶 API 实战指南:从 Matmul 数据流、Tiling 到特性场景全解析
2026/10/17 1:40:21 网站建设 项目流程
  • 人工智能
  • 深度学习
  • 算子库
  • CANN
  • Ascend

【免费下载链接】asc-devkit

本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C++标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。

项目地址:https://gitcode.com/cann/asc-devkit
点击查看免费下载

本指南以 CANN asc-devkit 开源仓库中的「矩阵编程(高阶API)」系列文档为骨架,系统讲解基于 Ascend C 高阶 API 实现矩阵乘法(Matmul)算子的完整路径:先建立矩阵乘的计算模型、存储逻辑位置与数据流、ND/NZ 数据格式与 Tiling 切分等基础知识,再按 Host 侧 Tiling 生成与 Kernel 侧算子实现两大环节给出可直接运行的代码范式,最后逐项梳理多核切分、异步处理、量化/反量化、稀疏矩阵乘、MxMatmul、Batch Matmul 等特性场景的适用条件、约束与调用示例。读完本文,你将能够独立完成一个 Matmul 算子的 tiling 生成、核函数编写、编译运行与结果验证,并具备按业务需求选择并组合高阶 API 特性模板的能力。

一、矩阵乘法基础:公式、逻辑位置与数据流

1.1 计算模型

Matmul 的计算公式为:

C = A * B + bias
  • A、B 为源操作数:A 为左矩阵,形状为[M, K];B 为右矩阵,形状为[K, N];
  • C 为目的操作数,存放矩阵乘结果,形状为[M, N];
  • bias 为矩阵乘偏置,形状为[1, N],对 A*B 结果矩阵的每一行都采用同一 bias 进行偏置。

该公式及矩阵形状定义完整记录在基础知识中,是后续所有高阶 API 配置(SetOrgShape、SetShape)与地址偏移计算的出发点。

Matmul矩阵乘示意图

1.2 存储逻辑位置

矩阵乘数据流依赖一组存储“逻辑位置”,其与物理存储的映射关系详见逻辑位置和物理存储的映射关系。常用位置及语义如下:

逻辑位置用途类比
A1 / B1 / C1存放整块 A、B、Bias 矩阵CPU 多级缓存中的二级缓存
A2 / B2 / C2存放切分后的小块 A、B、BiasCPU 多级缓存中的一级缓存
CO1存放小块结果 C 矩阵Cube Out
CO2存放整块结果 C 矩阵Cube Out
VECCALC计算需要临时变量时使用—

1.3 矩阵乘法数据流

矩阵乘法数据流描述输入输出在各存储位置间的流向(为简化描述,通常不列出 bias):

  • A 矩阵:输入位置可以是 GM 或 VECOUT。数据流为GM->A2、GM->A1->A2、VECOUT->A1->A2。由于 A1 空间比 A2 更大,大批量数据先搬入 A1 缓存、执行 Cube 计算前再从 A1 搬入 A2,可减少计算前的等待、提升性能;仅在搬入数据量较少的场景才可能使用GM->A2直达流。
  • B 矩阵:同样支持GM->B2、GM->B1->B2、VECOUT->B1->B2,大批量数据优先走 B1 缓存路径。
  • 计算与输出:完成A2*B2=CO1计算后,CO1->CO2汇聚,再从 CO2 输出到 GM 或 VECIN(CO2->GM/CO2->VECIN)。

矩阵乘法数据流示意图

二、数据格式:ND 与 NZ

Matmul 主要涉及两种分形格式:

  • 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)

即(W, H)大小的矩阵被分为(H1*W1)个分形,按列优先排布(形如 N 字形);每个分形内部含(H0*W0)个元素,按行优先排布(形如 z 字形),因此称为 NZ(大 N 小 Z)格式。其余数据格式(如 ND_ALIGN)可参考数据排布格式。

以分形格式 2*2、4*4 矩阵为例,ND(1,4,4)与 NZ(1,2,2,2,2)在内存中的排布差异为:

ND: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 NZ: 0, 1, 4, 5, 8, 9, 12, 13, 2, 3, 6, 7, 10, 11, 14, 15

三、数据分块(Tiling):多核切分与核内切分

3.1 多核切分

为实现多核并行,需要将矩阵数据切分到不同核上处理:

  • A 矩阵沿 M 轴切分为多份singleCoreM,单核处理SingleCoreM * K大小数据;
  • B 矩阵沿 N 轴切分为多份singleCoreN,单核处理K * SingleCoreN大小数据;
  • C 矩阵由SingleCoreM * K的 A 分块与K * SingleCoreN的 B 分块相乘,得到SingleCoreM * SingleCoreN的单核输出。

例如 8 个核参与计算、A 沿 M 轴分 4 块、B 沿 N 轴分 2 块时,单核只处理一个分块。此外,当 K 轴较大时还可以沿 K 轴切分为多份singleCoreK,详细案例见Matmul高阶API开启多核切K。

3.2 核内切分

大多数情况下 Local Memory 无法完整容纳算子输入输出,需要“搬一部分、算一部分、搬出去”的核内切分:

  • A 矩阵沿 M 轴切分为多份baseM(份数即 mIter),沿 K 轴切分为多份baseK;
  • B 矩阵沿 N 轴切分为多份baseN(份数即 nIter),沿 K 轴切分为多份baseK;
  • C 矩阵中baseM*baseN分块由baseM*baseK与baseK*baseN分块乘积累加得到(例如 6 个基本块a*a+b*b+c*c+d*d+e*e+f*f的累加)。

3.3 常用 Tiling 参数

除基本块形状 baseM/baseN/baseK 外,还有几个关键参数:

  • iterateOrder:一次 Iterate 迭代计算[baseM, baseN]大小的 C 分片,Iterate 完成后 Matmul 自动偏移下一次输出位置。0表示先沿 M 轴偏移再沿 N 轴偏移;1表示先沿 N 轴再沿 M 轴偏移。
  • depthA1 / depthB1:L1 Buffer(A1/B1)中矩阵片分别全载到 L0A Buffer(A2)/L0B Buffer(B2)的份数,即 A1 切片含baseM*baseK块的个数、B1 切片含baseN*baseK块的个数。
  • stepM / stepN:左/右矩阵在 L1 Buffer 中缓存的 M/N 方向上 baseM/baseN 的倍数。
  • stepKa / stepKb:左/右矩阵在 L1 Buffer 中缓存的 K 方向上 baseK 的倍数。

四、算子实现:Host 侧 Tiling 与 Kernel 侧计算

Ascend C 将上述切分、搬运与计算算法封装为一组 Matmul 高阶 API。开发者 Host 侧调用 API 自动获取 Tiling 参数,Kernel 侧初始化时传入,通过几个简单 API 即可完成矩阵乘。完整样例见 matmul_advanced_api 样例(其中 matmul_advanced_api.asc 为完整实现)。

矩阵编程流程示意图

4.1 Host 侧自动获取 Tiling 参数

关键步骤共 6 步:

① 创建 Tiling 对象。传入硬件平台信息创建 PlatformAscendC 对象,再创建 Tiling 对象:

auto ascendcPlatform = platform_ascendc::PlatformAscendCManager::GetInstance(); matmul_tiling::MultiCoreMatmulTiling tilingApi(*ascendcPlatform);

② 设置核数及 A、B 的内存逻辑位置、格式和数据类型:

tilingApi.SetDim(ascendcPlatform->GetCoreNumAic()); tilingApi.SetAType(AscendC::TPosition::GM, CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT16, false); tilingApi.SetBType(AscendC::TPosition::GM, CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT16, false); tilingApi.SetCType(AscendC::TPosition::GM, CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT);

③ 设置矩阵 shape 信息:

tilingApi.SetOrgShape(M, N, K); // 设置原始完整的形状M、N、K tilingApi.SetShape(M, N, K);

④ 设置可用空间大小。设置 Matmul 可用的 L1 Buffer/L0C Buffer/UB 空间大小,-1表示使用 AI 处理器对应 Buffer 的默认大小:

tilingApi.SetBufferSpace(-1, -1, -1);

⑤ 获取 Tiling 参数:

int64_t res = tilingApi.GetTiling(tilingData); if (res == -1) { std::cout << "gen tiling failed" << std::endl; }

⑥ 序列化保存等操作:

uint32_t tcubeTilingSize = tilingData.GetDataSize(); tilingData.SaveToBuffer(tilingBuf, tcubeTilingSize);

以仓库样例为例(M=512, N=512, K=128,A/B 为 half、ND 格式,C 为 float、ND 格式),SetDim(2)后 GetTiling 会在 2 个 Cube 核约束内生成实际使用核数usedCoreNum,并将 M=512 均分给 2 个核,得到singleCoreM=256、singleCoreN=512、singleCoreK=128。

4.2 Kernel 侧矩阵乘运算

① 创建 Matmul 对象。纯 Cube 模式(只有矩阵计算)建议定义ASCENDC_CUBE_ONLY宏避免额外性能开销;默认为 MIX 模式(矩阵计算+矢量计算),使用ASCENDC_CUBE_ONLY时必须用ASCEND_IS_AIC/ASCEND_IS_AIV宏隔离 Cube 与 Vector 代码(详见融合算子编程):

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; AscendC::Matmul<aType, bType, cType> mm;

MatmulType定义 A、B、C 的类型信息(内存逻辑位置、数据格式、数据类型),需与 Host 侧 SetAType/SetBType/SetCType 保持一致。

② 初始化操作。Matmul 高阶 API 内部需要使用系统 workspace,开发者需自行申请:

REGIST_MATMUL_OBJ(&pipe, GetSysWorkSpacePtr(), mm, &tiling); // 初始化

workspace 空间由框架申请管理,系统 workspace 大小通过 GetLibApiWorkSpaceSize 获取:

size_t userWorkspaceSize = 0; size_t systemWorkspaceSize = static_cast<size_t>(ascendcPlatform.GetLibApiWorkSpaceSize()); size_t workspaceSize = userWorkspaceSize + systemWorkspaceSize;

③ 设置原始矩阵 shape 及当前核使用的 A、B。多核场景需根据切分方向为每个核设置矩阵起始地址。样例沿 M 轴切分 A 和 C:SetOrgShape需在SetTensorA/SetTensorB之前调用;A 按[M, Ka]连续存放,当前核偏移为GetBlockIdx() * tiling.singleCoreM * tiling.Ka;B 不切分,所有核从bGlobal[0]读取完整 B 矩阵参与 K 轴累加。第二个参数false表示矩阵不转置:

mm.SetOrgShape(tiling.M, tiling.N, tiling.Ka, tiling.Kb); mm.SetTensorA(aGlobal[GetBlockIdx() * tiling.singleCoreM * tiling.Ka], false); mm.SetTensorB(bGlobal[0], false);

④ 完成矩阵乘操作,两种方式:

  • Iterate + while 循环(可自行控制迭代次数,方式灵活):
while (mm.Iterate()) { mm.GetTensorC(cGlobal[GetBlockIdx() * tiling.singleCoreM * tiling.N]); }
  • IterateAll(无需循环迭代,使用简单):
mm.IterateAll(cGlobal[GetBlockIdx() * tiling.singleCoreM * tiling.N]);

C 矩阵按[M, N]连续存放,当前核写回偏移为GetBlockIdx() * tiling.singleCoreM * tiling.N。

⑤ 结束操作:

mm.End();

调用End可结束当前 Matmul 对象的使用并释放内部计算资源,避免多个 Matmul 对象之间的资源冲突。

4.3 编译运行与结果验证

在样例根目录执行:

source ${install_path}/cann/set_env.sh # 配置环境变量 mkdir -p build && cd build cmake -DCMAKE_ASC_ARCHITECTURES=dav-2201 ..; make -j; # 编译(默认npu模式) python3 ../scripts/gen_data.py # 生成测试输入数据 ./demo # 执行样例 python3 ../scripts/verify_result.py output/output.bin output/golden.bin # 验证结果

编译选项:CMAKE_ASC_RUN_MODE可选npu(默认)或sim(NPU 仿真);CMAKE_ASC_ARCHITECTURES可选dav-2201(Atlas A2/A3 系列)或dav-3510(Ascend 950PR&950DT 系列)。执行成功后输出test pass!。详细说明见样例 README.md。

五、Shape 信息与 Format 格式的设置

5.1 Shape 体系

Tiling 与运行时涉及四层 Shape 概念:

  • orgShape:M、N、K(原始完整形状);
  • singleCoreShape:singleCoreM、singleCoreN、singleCoreK(单核分配到的形状);
  • singleShape:singleM、singleN、singleK(实际参与 Matmul 计算的 shape,单核时透传给 singleCore 系列);
  • baseShape:baseM、baseN、baseK(核内基本块)。

Kernel 运行时修改 Shape 的方式:

  • SetTail、SetSingleShape 运行时修改 singleCoreM/N/K,其中处理尾块用 SetTail,Matmul 复用(多个 Matmul 计算复用一个对象)场景用 SetSingleShape;
  • SetOrgShape 运行时修改 M、N、K,用于 Matmul 复用场景。

单核 Tiling 设置:SetOrgShape(必选,设置 M/N/K)、SetShape(非必选,设置 singleM/N/K)、SetFixSplit(非必选,设置 baseM/N/K)。多核 Tiling 额外支持SetSingleShape(设置 singleCoreM/N/K)与SetSingleRange(设置 singleCoreM/N/K 的范围)。各接口详见 Matmul_Tiling。

5.2 Format 格式

创建 Matmul 对象时,A、B、C 支持三种 CubeFormat:

  • CubeFormat::ND、CubeFormat::NZ:见本文第二节;
  • CubeFormat::ND_ALIGN:用于 N 方向非 32 字节对齐的矩阵乘,详见数据排布格式。

六、Matmul 特性场景全景

除基础计算能力外,高阶 API 还提供一系列适用于不同场景的处理能力,完整特性表见 Matmul特性介绍。以下按场景类别展开。

6.1 多核对齐 / 非对齐切分

  • 多核对齐切分(文档):M 能被 singleCoreM 整除、N 能被 singleCoreN 整除、K 能被 singleCoreK 整除时,沿 M/N/K 轴切分实现多核并行。不切 K 轴时单核处理singleCoreM*K与K*singleCoreN分块;切 K 轴时单核处理singleCoreM*singleCoreK与singleCoreK*singleCoreN,C 分块由多个核上并行算得的乘积累加。多核 Tiling 需构造MultiCoreMatmulTiling并用SetDim设置 Matmul 可用的核数(仅多核场景设置,用于计算 tiling 参数);SetSimdNumBlocks设置整个算子实际加载的核数,是必须设置的。关键代码:
auto ascendcPlatform = platform_ascendc::PlatformAscendCManager::GetInstance(); matmul_tiling::MultiCoreMatmulTiling cubeTiling(*ascendcPlatform); cubeTiling.SetDim(ascendcPlatform.GetCoreNumAic()); cubeTiling.SetAType(matmul_tiling::TPosition::GM, matmul_tiling::CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT16); cubeTiling.SetBType(matmul_tiling::TPosition::GM, matmul_tiling::CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT16); cubeTiling.SetCType(matmul_tiling::TPosition::GM, matmul_tiling::CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT); cubeTiling.SetBiasType(matmul_tiling::TPosition::GM, matmul_tiling::CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT); cubeTiling.SetOrgShape(M, N, K); cubeTiling.SetShape(M, N, K); cubeTiling.EnableBias(isBias); optiling::TCubeTiling tilingData; int ret = cubeTiling.GetTiling(tilingData); // ret = -1 时表示生成失败

完整样例:多核切 M/N 样例、多核切 K 样例。

  • 多核非对齐切分(文档):M/N/K 无法整除 singleCoreM/N/K 时产生尾块。需在不改变原有 tiling 的前提下调用SetTail重新设置本次计算的 singleCoreM/N/K,并按 tailM/tailN/tailK 搬运计算。SetTail必须在Iterate/IterateAll之前调用:
int tailM = tiling.M - mCoreIndex * tiling.singleCoreM; tailM = tailM < tiling.singleCoreM ? tailM : tiling.singleCoreM; int tailN = tiling.N - nCoreIndex * tiling.singleCoreN; tailN = tailN < tiling.singleCoreN ? tailN : tiling.singleCoreN; if (tailM < tiling.singleCoreM || tailN < tiling.singleCoreN) { matmulObj.SetTail(tailM, tailN); }

完整样例见 matmul_multi_core_unaligned。

6.2 异步场景处理

异步场景 说明:Iterate 与 IterateAll 在 MIX 场景下支持同步/异步两种模式,纯 Cube 场景仅支持同步模式。

  • Iterate & GetTensorC 异步:通过模板参数Iterate<false>()开启。调用 Iterate 后无需立即 GetTensorC 等待搬运完成,可先执行其他操作,需要结果时再获取。异步需用SetWorkspace设置临时空间(物理地址 +singleCoreM*singleCoreN大小的 C 空间)缓存 Iterate 结果,且SetWorkspace必须在 Iterate 之前调用:
mm.SetWorkspace(workspace, size); // workspace为临时空间物理地址,size为singleCoreM*singleCoreN的C大小 mm.template Iterate<false>(); // …… 执行其他操作 auto mIter = Ceil(singleCoreM, baseM); auto nIter = Ceil(singleCoreN, baseN); for (int i = 0; i < mIter * nIter ; ++i) { mm.GetTensorC<false>(gm_c); }

约束:传入的 C 地址空间不小于baseM*baseN;支持只输出到 UB(VECIN)、只输出到 GM、同时输出到 GM 和 UB 三种方式,其中取到 UB 时数据格式仅支持 NZ,取到 GM 时支持 ND 或 NZ。

  • IterateAll 异步:IterateAll<false>(...)后可用 WaitIterateAll 等待完成;约束为传入 C 地址空间不小于singleCoreM*singleCoreN,且仅支持连续输出至 GM。完整样例见 matmul_fused、matmul_async_iterate、matmul_async_iterate_all。

6.3 矩阵乘输出的量化 / 反量化

量化/反量化场景:Matmul 支持将结果从 L0C Buffer(CO1)搬出到 GM 时执行量化或反量化。

  • 量化场景:A、B 为 half 或 bfloat16_t,输出 C 为 int8_t,搬出时量化;
  • 反量化场景:A、B 为 int8_t 或 int4b_t、输出 C 为 half,或 A、B、C 均为 int8_t 时,搬出时反量化。

两种模式(Tiling 侧用 SetDequantType 设置):

模式Tiling 侧接口Kernel 侧接口
同一系数(PER_TENSOR,shape 为 [1])SetDequantType(DequantType::SCALAR)SetQuantScalar(gmScalar)
向量(PER_CHANNEL,shape 为 [n])SetDequantType(DequantType::TENSOR)SetQuantVector(gmTensor)

支持的数据类型组合(含平台范围)及约束详见原文表 2。关键约束:SetQuantScalar/SetQuantVector 必须在 Iterate/IterateAll 之前调用;Kernel 侧与 Tiling 侧的量化模式必须一致。Kernel 侧同一系数模式示例:

REGIST_MATMUL_OBJ(&pipe, GetSysWorkSpacePtr(), mm, &tiling); float tmp = 0.1; // 输出gm时会乘以0.1 uint64_t ans = static_cast<uint64_t>(*reinterpret_cast<int32_t*>(&tmp)); // 浮点值量化系数转换为uint64_t类型设置 mm.SetQuantScalar(ans); mm.SetTensorA(gm_a); mm.SetTensorB(gm_b); mm.SetBias(gm_bias); mm.IterateAll(gm_c);

完整样例见 matmul_quant。

6.4 矩阵乘输出的 Channel 拆分

ChannelSplit 场景:当 C 矩阵为 NZ 格式、float 类型时,默认每个分形为 16*16;ChannelSplit 将每个分形切分为 16*8,使内轴 32 字节对齐(1 个 float 为 4 字节),内轴数据量与一条 NPU 矢量计算指令处理的数据单元一致,便于后续计算。默认不启用,需通过 MatmulConfig 的isEnableChannelSplit=true开启。开启需满足:C 为 CubeFormat::NZ、float 类型、内存逻辑位置为 GM,且 L0C Buffer(CO1)数据类型为 float:

constexpr static MatmulConfigMode configMode = MatmulConfigMode::CONFIG_NORM; constexpr static MatmulFuncParams funcParamsChannelSplit{ false, false, false, false, 0, IterateOrder::ORDER_M, ScheduleType::INNER_PRODUCT, true, false, false, false, true/*isEnableChannelSplit*/ }; constexpr static MatmulConfig MM_CFG = GetMMConfig<configMode>(funcParamsChannelSplit); Matmul<A_TYPE, B_TYPE, C_TYPE, BIAS_TYPE, MM_CFG> mm;

完整样例见 matmul_channelsplit_output。

6.5 矩阵向量乘(GEMV)

GEMV 场景:M=1、形状为 (1, K) 的左矩阵 A 与 (K, N) 的 B 相乘。通过将 A 数据格式配置为CubeFormat::VECTOR开启 GEMV 模式:A 从 A1 搬入 A2 时,1*K 向量被当作 16*16 矩阵一次完成分形搬运,搬运数据量更少、性能更好;未开启时 M 方向按非对齐场景处理(需对齐 32 字节、搬运 K/16 次)。

约束:原始输入形状 M 必须等于 1;左矩阵 A 不支持转置;GM 上的左矩阵数据需 16 字节对齐。Tiling 侧与 Kernel 侧关键代码:

// Tiling:设置A矩阵格式为CubeFormat::VECTOR tiling.SetAType(matmul_tiling::TPosition::GM, matmul_tiling::CubeFormat::VECTOR, matmul_tiling::DataType::DT_FLOAT16); // Kernel:MatmulType模板参数A_TYPE的格式为CubeFormat::VECTOR using A_TYPE = AscendC::MatmulType<AscendC::TPosition::GM, CubeFormat::VECTOR, half>; using B_TYPE = AscendC::MatmulType<AscendC::TPosition::GM, CubeFormat::ND, half>; using C_TYPE = AscendC::MatmulType<AscendC::TPosition::GM, CubeFormat::ND, float>; AscendC::Matmul<A_TYPE, B_TYPE, C_TYPE, BIAS_TYPE> mm;

完整样例见 matmul_format_gemv。

6.6 4:2 稀疏矩阵乘(Sparse Matmul)

4:2 稀疏矩阵乘:输入 A、B 为稀疏矩阵,B 中每 4 个元素至少 2 个为零。用户需先对 B 进行 4:2 稠密化(每 4 个元素过滤 2 个零元素,稠密算法见 MmadWithSparse),生成稠密化 B 矩阵和索引矩阵 index。索引矩阵数据类型为 int2,加载前需拼接为 int8,且在一个 int8 地址中按逆序排布(如索引矩阵1 2 0 1 0 2 1 0在地址中排布为1 0 2 1 0 1 2 0)。

实现流程:Host 侧通过SetSparse(true)设置场景;Kernel 侧用SparseMatmulType定义 B 矩阵参数类型(含 B 矩阵与索引矩阵的内存逻辑位置、格式、数据类型),用SetSparseIndex传入索引矩阵;Matmul API 内部根据索引矩阵从 A 矩阵每 4 个元素中选择 2 个对应位置元素参与计算:

using B_TYPE = AscendC::SparseMatmulType<AscendC::TPosition::GM, AscendC::TPosition::GM, CubeFormat::ND, BType, true>; AscendC::Matmul<A_TYPE, B_TYPE, C_TYPE, BIAS_TYPE, CFG_MDL> mm; mm.SetTensorA(gm_a); mm.SetTensorB(gm_b); mm.SetSparseIndex(gm_index); // 传入稠密化过程中生成的索引矩阵 mm.SetBias(gm_bias); while (mm.Iterate()) { mm.GetTensorC(gm_c); }

SparseMatmulType参数约束:POSITION(B 矩阵)仅支持 GM;INDEX_POSITION(索引矩阵)仅支持 GM;CubeFormat 支持 ND/NZ;TYPE 仅支持 int8_t;ISTRANS 仅支持 true(开启转置);LAYOUT 仅支持LAYOUT::NONE;IBSHARE 仅支持 false。该场景仅支持 MDL 模板下的纯 Cube 模式,索引矩阵只支持 int8 类型和 NZ 格式,M/K/N 任一值不能为 0。完整样例见 matmul_sparse。

6.7 TSCM 输入的矩阵乘

TSCM 输入场景:TSCM 表示 L1 Buffer 空间对应的逻辑内存,开发者可自行管理 TSCM,缓存一份数据并在不同场景中灵活配置为 A、B 或 Bias。该场景下 Matmul 直接使用传入的 TSCM 地址,不进行 GM 到 TSCM 的搬入,适合自定义非连续搬入、搬入数据预处理等需求,可通过自定义管理 TSCM 灵活配置 MTE2 流水、实现跨 Matmul 对象的全局 DoubleBuffer。约束:设置为 TSCM 输入的矩阵必须在 TSCM 中全载。调用示例:

TQue<TPosition::A1, 1> scm; // 队列逻辑位置A1,队列深度为1 pipe->InitBuffer(scm, 1, tiling.M * tiling.Ka * sizeof(A_T)); Matmul<A_TYPE, B_TYPE, C_TYPE, BIAS_TYPE> mm1; // A_TYPE的TPosition为TSCM,B_TYPE的TPosition为GM REGIST_MATMUL_OBJ(&pipe, GetSysWorkSpacePtr(), mm1); mm1.Init(&tiling); auto scmTensor = scm.AllocTensor<A_T>(); DataCopy(scmTensor, gm_a, tiling.M * tiling.Ka); scm.EnQue(scmTensor); LocalTensor<A_T> scmLocal = scm.DeQue<A_T>(); mm1.SetTensorA(scmLocal); mm1.SetTensorB(gm_b); mm1.SetBias(gm_bias); mm1.IterateAll(gm_c); scm.FreeTensor(scmLocal);

完整样例见 matmul_tscm。

6.8 矩阵乘输出的 N 方向对齐(ND_ALIGN)

ND_ALIGN 输出:当 N 方向非 32 字节对齐时(如 M=16, K=16, N=14 的 half 矩阵乘),配置 C 为 ND_ALIGN 格式后,Matmul API 会在 C 矩阵 N 方向通过添加无效数据填充,确保按 N 方向 32 字节对齐输出。约束:为 C 申请的 Buffer 空间为 N 向上 32 字节对齐后的空间大小。Tiling 侧SetCType设置格式为CubeFormat::ND_ALIGN,Kernel 侧模板参数 cType 同步设置:

tiling.SetCType(matmul_tiling::TPosition::GM, matmul_tiling::CubeFormat::ND_ALIGN, matmul_tiling::DataType::DT_FLOAT); typedef AscendC::MatmulType<AscendC::TPosition::GM, CubeFormat::ND_ALIGN, float> cType;

完整样例见 matmul_format_nd_align。

6.9 单次矩阵乘局部输出(Partial Output)

Partial Output:默认一次 Iterate 会在 K 方向做一次或多次基本块计算并累加后输出;开启 Partial Output 后,调用 Iterate 不做 K 轴累加、只做单次基本块计算,用户通过 GetTensorC 获取单片数据后自行累加。典型场景:先获取单次基本块数据进行反量化再累加。约束:仅支持 MDL 模板;获取结果仅支持 Iterate + GetTensorC 的连续写模式(不支持非连续写与 IterateAll);不支持带 Bias 的 Matmul。调用示例:

constexpr static MatmulConfigMode configMode = MatmulConfigMode::CONFIG_MDL; constexpr static MatmulFuncParams funcParams = { false, false, false, false, 0, IterateOrder::UNDEF, ScheduleType::INNER_PRODUCT, true, true, true /* isPartialOutput */ }; constexpr static MatmulConfig CFG_PARTIAL = GetMMConfig<configMode>(funcParams); Matmul<A_TYPE, B_TYPE, C_TYPE, BIAS_TYPE, CFG_PARTIAL> mm; mm.SetTensorA(gmA, isTransposeA); mm.SetTensorB(gmB, isTransposeB); while (mm.Iterate()) { mm.GetTensorC(tmpGmC[dstOffset], false, true); dstOffset += baseM * baseN; // 其他操作 }

完整样例见 matmul_partial_output。

6.10 AIC 和 AIV 独立运行机制(双主模式)

双主模式:在分离模式下,区别于 MIX 模式通过消息机制驱动 AIC 运行,双主模式下 AIC 与 AIV 独立运行代码、不依赖消息驱动,可提高 Matmul 性能。默认不开启,通过 MatmulConfig 的enableMixDualMaster参数开启。约束:仅支持 Norm 模板和 MDL 模板;Kernel 类型为 MIX,且 AIC 核数:AIV 核数为 1:1,或 1:2 且 A/B 同时开启 IBSHARE;同一算子内所有 Matmul 对象该参数取值必须一致;A/B/Bias 仅支持 GM 输入;结果仅支持 IterateAll 输出到 GlobalTensor。调用示例:

constexpr static MatmulConfig MM_CFG = GetNormalConfig(false, false, false, BatchMode::BATCH_LESS_THAN_L1, true, IterateOrder::ORDER_M, ScheduleType::OUTER_PRODUCT, false, true/*enableMixDualMaster*/); Matmul<A_TYPE, B_TYPE, C_TYPE, BIAS_TYPE, MM_CFG> mm;

完整样例见 matmul_mixdualmaster。

6.11 MxMatmul 场景(微缩放格式矩阵乘)

MxMatmul 场景:微缩放(Microscaling,MX)格式浮点数可在低位宽下保持稳健的 AI 训练和推理精度。MX 格式由共享缩放因子 X(位宽 w)、私有元素 Pi(位宽 d)、块大小 k 三部分构成,每个包含 k 个元素的块可用 (w+k*d) 位编码。当前支持 MXFP8(fp8_e5m2_t / fp8_e4m3fn_t,d=8,k=32,缩放因子 fp8_e8m0_t 位宽 8)与 MXFP4(fp4x2_e1m2_t / fp4x2_e2m1_t,d=4,k=32)等类型。

MxMatmul 计算带有量化系数:C = (scaleA ⊗ A) * (scaleB ⊗ B) + Bias,其中 ⊗ 为广播乘法,K 方向每 32 个元素共享一个量化因子。A 形状为 [M, K],scaleA 为 [M, K/32];B 为 [K, N],scaleB 为 [K/32, N]。Host 侧需调用SetScaleAType/SetScaleBType设置量化系数矩阵,调用SetMadType(MatrixMadType::MXMODE)设置 MxMatmul 场景;Kernel 侧用MatmulTypeWithScale定义 A/B 参数类型,并通过模板参数MatmulWithScalePolicy开启 MxMatmul 模板策略:

using A_TYPE = AscendC::MatmulTypeWithScale<AscendC::TPosition::GM, AscendC::TPosition::GM, CubeFormat::ND, fp8_e5m2_t, isTransposeA>; using B_TYPE = AscendC::MatmulTypeWithScale<AscendC::TPosition::GM, AscendC::TPosition::GM, CubeFormat::ND, fp8_e5m2_t, isTransposeB>; AscendC::Matmul<A_TYPE, B_TYPE, C_TYPE, BIAS_TYPE, CFG_MDL, MatmulCallBackFunc<nullptr, nullptr, nullptr>, AscendC::Impl::Detail::MatmulWithScalePolicy> mm; mm.SetTensorA(gm_a, isTransposeA); mm.SetTensorB(gm_b, isTransposeB); mm.SetTensorScaleA(gm_scaleA, isTransposeScaleA); mm.SetTensorScaleB(gm_scaleB, isTransposeScaleB); mm.SetBias(gm_bias); while (mm.Iterate()) { mm.GetTensorC(gm_c); }

MatmulTypeWithScale模板参数(POSITION、SCALE_POSITION、FORMAT、TYPE、ISTRANS、SRCPOS、SCALE_FORMAT、SCALE_ISTRANS、SCALE_SRCPOS)的取值与约束见原文表 3,要点包括:A/B 及 scale 矩阵位置可选 GM/VECOUT/TSCM,TSCM 时格式仅支持 NZ;A 的 FORMAT 可含 VECTOR(配合 GEMV);当 scaleA/scaleB 以 ND 格式输入时,API 内部格式转换需占用 UB 临时空间,需用SetLocalWorkspace配置(大小按原文公式计算)。完整样例见 matmul_mx、matmul_mx_ub_tscm_nz、matmul_mx_scale_cache。

七、Batch Matmul:批量处理与 Bias 复用

7.1 Batch Matmul 基础功能

Batch Matmul 通过 IterateBatch 一次计算出多个singleCoreM*singleCoreN大小的 C 矩阵。当单次 Matmul 输入 shape 较小时,搬运开销占整体耗时比例大,批量处理可有效提升带宽利用率。

支持 4 种 Layout:BSNGD、SBNGD、BNGS1S2、NORMAL(BMNK 排布)。NORMAL 排布下,一次 IterateBatch 可同时算出 mat_c1..mat_c4 = mat_ai*mat_bi。约束要点:仅支持 Norm 模板;BSNGD/SBNGD/BNGS1S2 格式要求多 Batch 数据总和小于 L1 Buffer(NORMAL 无此限制,但需通过 MatmulConfig 配置 batchMode);BSNGD/SBNGD 输入输出仅支持 ND,BNGS1S2/NORMAL 支持 ND/NZ;不支持量化/反量化;异步模式不支持 IterateBatch 搬移到 UB;开启双主模式时不支持 Batch Matmul;A/B 支持 half/float/bfloat16_t/int8_t,不支持 int4b_t。

NORMAL 格式调用示例(Tiling 侧用 SetBatchInfoForNormal 设置 M/N/K 轴信息与 BatchNum):

constexpr int32_t BATCH_NUM = 3; tiling->SetBatchInfoForNormal(BATCH_NUM, BATCH_NUM, M, N, K); // 设置矩阵排布 // Kernel侧 typedef AscendC::MatmulType <AscendC::TPosition::GM, CubeFormat::ND, half, false, LayoutMode::NORMAL> aType; typedef AscendC::MatmulType <AscendC::TPosition::GM, CubeFormat::ND, half, true, LayoutMode::NORMAL> bType; typedef AscendC::MatmulType <AscendC::TPosition::GM, CubeFormat::ND, float, false, LayoutMode::NORMAL> cType; constexpr MatmulConfig MM_CFG = GetNormalConfig(false, false, false, BatchMode::BATCH_LESS_THAN_L1); AscendC::Matmul<aType, bType, cType, biasType, MM_CFG> mm; mm.SetTensorA(gm_a); mm.SetTensorB(gm_b); mm.SetBias(gm_bias); mm.IterateBatch(gm_c, batchA, batchB, false); // 左矩阵每次计算batchA个MK数据,右矩阵每次计算batchB个KN数据 mm.End();

完整样例见 batch_matmul。

7.2 Batch Matmul 复用 Bias 矩阵

Batch Matmul 复用 Bias:Batch Matmul 默认 Bias 输入包含 Batch 轴(大小为 Batch*N);当每个 Batch 使用相同 Bias 时,将 MatmulConfig 的isBiasBatch参数置为 false,只需输入一个不带 Batch 轴的1*singleCoreNBias 矩阵。约束:A/B/C 均为 NORMAL Layout 时,batchMode 不能设为 SINGLE_LARGE_THAN_L1(单 Batch 的 A/B 数据总和不得超过 L1 Buffer 大小):

constexpr MatmulConfigMode configMode = MatmulConfigMode::CONFIG_NORM; constexpr MatmulBatchParams batchParams = { false, BatchMode::BATCH_LESS_THAN_L1, false /* isBiasBatch */ }; constexpr MatmulConfig CFG_MM = GetMMConfig<configMode>(batchParams); AscendC::Matmul<A_TYPE, B_TYPE, C_TYPE, BIAS_TYPE, CFG_MM> mm; mm.SetTensorA(gm_a); mm.SetTensorB(gm_b); mm.SetBias(gm_bias); // 矩阵大小为1 * singleCoreN mm.IterateBatch(gm_c, batchA, batchB, false); mm.End();

完整样例见 batch_matmul_bias_reuse。

八、特性组合与模板选择速览

特性场景核心开启方式模板/模式限制
多核对齐/非对齐切分MultiCoreMatmulTiling + SetDim / SetTail纯 Cube 或 MIX
异步处理Iterate<false> / IterateAll<false> + SetWorkspace / WaitIterateAll仅 MIX 支持异步
量化/反量化SetDequantType + SetQuantScalar / SetQuantVector数据类型组合受限
ChannelSplitMatmulConfig.isEnableChannelSplit=trueC 为 NZ、float、GM
GEMVA 格式设为 CubeFormat::VECTORM 必须为 1,A 不转置
4:2 稀疏SetSparse(true) + SparseMatmulType + SetSparseIndex仅 MDL 模板纯 Cube
TSCM 输入MatmulType 位置设为 TSCMTSCM 矩阵需全载
ND_ALIGN 输出C 格式设为 CubeFormat::ND_ALIGNBuffer 空间需按 32 字节对齐
Partial OutputMatmulConfig.isPartialOutput=true仅 MDL 模板,不支持 Bias
双主模式MatmulConfig.enableMixDualMaster=trueNorm/MDL,MIX 核数比例 1:1 或 1:2
MxMatmulSetMadType(MXMODE) + MatmulTypeWithScale + MatmulWithScalePolicyNorm/MDL,类型组合受限
Batch MatmulIterateBatch + SetBatchInfoForNormal仅 Norm 模板,4 种 Layout
Batch 复用 BiasMatmulConfig.isBiasBatch=falseNORMAL 下 batchMode 受限

各场景的详细约束与完整样例链接均可在对应特性文档及Matmul特性介绍中继续查阅。高阶 API 的接口级定义(SetTensorA/SetTensorB/SetBias/Iterate/IterateAll/SetTail 等)可在 SIMD-API 的 cube_compute 模块中按名称检索;其底层实现位于仓库 impl/adv_api/detail/matmul 目录(如 matmul_impl.h、matmul_config_impl.h),Tiling 侧实现位于 impl/adv_api/tiling/matmul,有兴趣的读者可结合源码进一步印证各特性的内部调度逻辑。

  • 人工智能
  • 深度学习
  • 算子库
  • CANN
  • Ascend

【免费下载链接】asc-devkit

本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C++标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。

项目地址:https://gitcode.com/cann/asc-devkit
点击查看免费下载

相关推荐

上一篇:终极配置指南:如何深度调优mpv播放器性能
下一篇:探索React Image Gallery:构建高效图片画廊的利器

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询