BMM算子优化分析_v0.3 #3

Closed
opened 2026-08-21 08:43:48 +00:00 by admin · 1 comment
Owner

Batch Matmul算子特性分析

算子功能与接口说明

算子功能:完成带batch的矩阵乘计算。

算子输入:

  • 左矩阵(也叫A矩阵):[BatchA,M,K]、数据类型dtype、layout(典型ND)
  • 右矩阵(也叫B矩阵):[BatchB,M,K]、数据类型dtype、layout(典型ND)
  • 偏置bias:维度固定[B,1,N]、数据类型dtype、layout(固定ND),可为空

算子输出:

输出矩阵(也叫C矩阵):[BatchC,M,N]、数据类型dtype、layout(典型ND)

计算公式:

C=A@B+bias

其中,A,B 输入维度典型为3维,最后两维做矩阵乘计算。例如,A,B 输入维度分别为(B,M,K)、(B,K,N) 时,C维度为 (B,M,N)。bias是维度为(B,1,N)的向量。其中,B为矩阵乘法的batch数,M为第一个张量A的行数和输出张量C的行数,N为第二个张量B的列数和输出张量C的列数,K为第一个张量A的列数和第二个张量B的行数。

BMM算子最优实现分析

最优软件实现设计的系统推导

如何理解性能最优

NPU 上 BMM 的执行是核内多级硬件流水的并行——Cube 计算(MMAD)、GM/L2→L1 搬移(MTE2)、L1→L0 搬移(MTE1)、L0C 写出(Fixpipe),多核并行时各流水级时延可被双缓冲(double buffer)等机制相互掩盖,最终:

T_total=max(T_MMAD, T_MTE2, T_MTE1, T_Fixpipe [, T_Reduce])

总时延 = 流水线最慢的一级

算子优化的关键就是对瓶颈流水级的优化。 由此直接得出一个重要的设计自由度——瓶颈交换:当 MTE2(搬移)是瓶颈、MMAD(计算)不是瓶颈时,可以牺牲一定 MMAD 时延(例如冗余计算)换取 MTE2 性能提升;反之,当 MMAD 是瓶颈时,可以牺牲一定 MTE2 时延(例如重复搬移)换取计算效率提升。只要瓶颈级时延下降,总时延就下降。

后文会看到:MergeBatch 就是"牺牲算力换搬移效率"的典型,ASW_Basic 切 M/N 就是"牺牲搬移(重复读)换并行度"的典型——它们的存在正当性都来自这个 max 模型。

实现本质逻辑

BMM 在 NPU 上实现的本质是:把参与计算的数据分块(tile),由全部AIC 核并行 + 串行地完成这些分块的计算,再组合成最终结果:

C[B,M,N]={C[Bu,Mi,Nj]=∑kA[Bu,Mi,Kk]@[Bu,Kk,Nj]}

分块有 4 个维度:B、M、N、K。核间怎么分这 4 个维度,就是分支划分的第一性问题(核内分块是第二性问题,属于各分支内部的 tiling)。

从"读入 / 计算 / 写出"三个视角考察每个维度的核间切分特征(这是后续一切推导的基石):

切分维度 读入特征 计算特征 写出特征
切B 每个数据块只被固定的 1 个核读取,核间零重复读 每个输出块由 1 个核独立完成,无核间依赖 只写最终结果,无中间结果
切M/切N 切 M 则同一右矩阵块被多核重复读;切 N 则同一左矩阵块被多核重复读 每个输出块由 1 个核独立完成,无核间依赖 只写最终结果,无中间结果
切K 每个数据块只被固定的 1 个核读取,零重复读 每个输出块由多核共同完成,存在核间依赖 有中间结果写出,需核间 Reduce 归约

不同切分差异的根本原因:

B 维在数学上独立(BMM 语义就是逐 batch 独立矩阵乘),所以切 B 天然零重复、零依赖;

K 维有 L0C 累加机制:核内切 K 时,切K后多轮 mmad 在 256KB 的 L0C 上原地累加(cmatrixInitVal=false),中间结果不出核。一旦把 K 切到核间,单核的 L0C 装不下"别的核算的 K 段",部分和必须写出到 GM/L2 workspace,再由 AIV 归约——切 K 是唯一同时破坏"累加不出核"和"输出独占"两条性质的切法。

4 个维度的任意非空子集都可作为一种核间切分方案,共 2^4−1=15 种:

{B},{M},{N},{K},{B,M},{B,K},{B,N},{M,K},{M,N},{K,N},{B,M,K},{B,M,N},{B,K,N},{M,K,N},{B,M,K,N}

任何分核实现方案必属于其中一种 ⇒ 这 15 种是完备的。按 2.1.2 的特征分组:

组合 共同特征 优化重心
只切B {B} 零重复读,读写数据量固定 访存 Bound:搬移效率高;计算 Bound:计算效率高
切B+ M\N切分,不切K {B,M},{B,N},{B,M,N} {M},{N},{M,N}, 可能有重复读 访存 Bound:重复读尽量少 + 搬移效率高;计算 Bound:计算效率高
含K切分 {B,K}, {B,M,K},{B,K,N}, {B,M,K,N}, {K},{M,K},{K,N},{M,K,N}, 可能有重复读; 有中间结果写出 + 归约 计算效率高,且归约引入的额外 MTE2/Fixpipe 时延不能成为新瓶颈

15 种组合的代价结构只由两个布尔特征决定——是否含 K、是否含 M/N。

三个维度的"核间切分价格"严格排序:cost(切B)=0<cost(切M/N)≪cost(切K)

  • 切 B 免费:零重复读、零依赖、零中间写出;
  • 切 M/N 廉价但有价:共享矩阵被重复读,但若共享部分能驻留 L2,以DV100为例,128MB L2,重复读以 5.2TB/s 命中 L2 而非 1.6TB/s 的 GM,则代价大部分被 L2 吸收;配合 swizzle(执行顺序编排)压缩同时活跃的数据集,代价进一步压低;
  • 切 K 昂贵:归约流量 TReduce ∝gridK×输出量,且引入核间同步——这是结构性代价,L2 吸收不掉。

"价格表"不是经验,是 BMM 语义 + L0C 累加机制 + L2/GM 带宽结构三条事实的推论。 整条分支决策树就是一句话:按价格从低到高购买并行度,买不够才加价。

BMM算子软件分支推导

1、问题规约(能降维就不在 BMM 本体里解决)

  • 若 BatchA = 1或 BatchB = 1,BMM 可通过维度折叠转化为普通 Matmul,直接复用 Matmul 的成熟优化体系(tiling、L2 切分、全载),不必在 BMM 框架内重新发明轮子 → 转Matmul;
  • 退化 case(K=0 无计算、K=1 无累加深度)让 Cube 完全或几乎无用,改走 AIV 向量通路——这属于"计算通路选择",是正交于切分的前置判断。可称为特殊分支

2、先买免费的B维度

  • 切B可满足多轮都尽量填满核
  • 如果单Batch的M*N够大,(能开出大 tile、L0C 利用率高、Cube 喂得饱)→ 每核逐个 batch 做完整 Matmul 就是最优,任何额外切分只增代价。可称为IterBatch分支
  • 如果单Batch的M*N很小,把多个 batch 在核内合并成大 tile 计算([bM,K]@[K,bN],取块对角线输出),用冗余算力换 Cube 利用率与搬移效率。可称为MergeBatch分支

3、B维度买不满核,加点价买廉价的M/N维度切分

  • 优先切B后再切 M/N(或混合切),切M、N引入的共享矩阵的重复读交给 L2 + swizzle 吸收;可称为ASW_Basic分支

4、免费和廉价维度B、M、N都买不满核,才考虑加价买昂贵的K维度切分

  • 用核间规约代价换并行度收益;可称为StreamK分支

由此推导出6大分支,“转Matmul”、“特殊分支”、“MergeBatch”、“IterBatch”、“ASW_Basic”、“StreamK”

可转Matmul分支

进入分支条件

BatchA==1或BatchB==1

实现方案

对于BatchB==1,左矩阵 [B, M, K] 的 batch 维与 M 维在 ND 布局下内存相邻&紧排,直接视图为 [B·M, K];输出 [B·M, N][B, M, N] 的内存布局逐元素一致;无输入重排,无输出Split,可直接转Matmul无任何代价

对于BatchA==1,需将右矩阵 [B, K, N] 折叠为 [K, B·N],但B 的 batch 维与 N 维在内存中不相邻(中间隔 K),折叠等价于一次 [B,K,N]→[K,B,N] 的转置重排(O(B·K·N) 读写),且输出 [M, B·N] 与目标 [B, M, N] 之间存在置换,需要 scatter。

此时当A矩阵比较小时,优先考虑将A矩阵加载到每个核的L1,然后每个核均匀读取B矩阵完成全部计算,此时无需输入\输出重排的额外开销;

但A矩阵较大时,可将BatchA==1扩展到BatchA==BatchB,然后看是否能在BMM其他分支预估时延,再与B折叠转Matmul的+重排的时延相对比,择优进行实现方案选型。

MergeBatch分支

每个核负责多个Batch的Matmul计算,核间无需同步或通信,假设单核每次要完成b个Batch的Matmul计算,单核计算时将[b,M,K]@[b,K,N]=[b,M,N]转换合并成[bM,K]@[K,bN]=BlockTrace([bM,bN])=[b,M,N],所谓BlockTrace指的是以[M,N]的Block粒度将结果矩阵的块对角线取出作为输出。

进入分支条件

具体来说进入MergeBatch分支需同时满足以下条件:

1、 BatchA=BatchB && BatchA/aicNum=b_per_core>= 2*b0

2、 MN*out_dtype<=L0C/(2*b0*b0)

3、 B/aicNum*(MK+KN)>=payload_per_core

4、 Max(MK,KN)>min_TileSize

5、 2*M*N/(M+N) < (芯片对应位宽算存(GM)比/b0)

b0>=2

实现方案

确定Tiling参数

重点是确定bL1,kL1

基于L0C容量+访存Bound约束,先确定每次计算的b应该多大

MN*out_dtype<=L0C/(2*b*b) => b^2 <= L0C/(MN*out_dtype) => b<bi

2*M*N/(M+N) < (芯片对应位宽算存(GM)比/b) => b<bii

b=min(bi,bii,b_per_core),bL0=b

基于bL0和L0AB确定kL0大小

2*(2M*kL0a)<=L0A,2*(2N*kL0b)<=L0B,kL0_max=min(kL0a, kL0b, C0Size/in_dtype)

kL1>=min( kL0_max, 128Byte)

基于kL1和L1容量大小确定bL1

2*bL1_max*(M*kL1+kL1*N)<=L1_Size

bL1=min(bL1_max, b_per_core)

IterBatch分支

进入分支条件

进入InterBatch分支需同时满足以下条件:

(1) BatchA=BatchB && BatchA/AiCoreNum=b_per_core>= 1

(2) BatchA%AiCoreNum =0 或 BatchA%AiCoreNum > minCoreNum

(3) 需满足以下L1约束之一

a) b_per_core =1 && (MK+KN)*dtype <=L1_size

b) b_per_core >1 && 2*(MK+KN)*dtype <=L1_size

c) b_per_core =1 && (MK+KN)*dtype>L1Size且(MK+KN/Step)*dtype<L1Size或(MK/Step+KN)*dtype<L1Size,其中Step为大于1的正整数

d) b_per_core >1 && (MK+KN)*dtype>L1Size/2且(MK+KN/Step)*dtype<L1Size/2或(MK/Step+KN)*dtype<L1Size/2,其中Step为大于1的正整数

e) (MK+KN)*dtype>L1Size且(M*(K/Step)+(K/Step)*N)*dtype<L1Size,其中Step为大于1的正整数

(4) c\d\e的step切分后需满足带宽效率要求

minCoreNum表示带宽能利用满的最小核数,来自芯片实测经验DV100可取0.8*aicNum

实现方案

对于(3).a则每核1batch直接搬入L1,

对于L1->L0,先考虑L0C是否能放下完整单Batch输出MN,如果能放下,再看L0AB能否放下完整的左右矩阵,如果放下则不用切分,如果放不下则切K,BaseK=max(L0A\B约束下的K, 16)

如果L0C放不下则按M或N切分,将M或N中较小的那个,对齐到16的倍数,另一个通过L0C的容量来确定;例如M=min(M,N),BaseM=Align(M,16), BaseN=L0C/BaseM

if(L0C_Size<=M*N*L0C_ElementSize):

    BaseM = M

    BaseN = N

    BaseK = min( align(L0A/BaseM,16), align(L0B/BaseN,16) )

else:

    if(M<N):

        BaseM = align(M,16)

        BaseN = floor(L0C/BaseM)

        BaseK = min( floor_align(L0A/BaseM,16), floor_align(L0B/BaseN,16) )

    else:

        BaseN = align(N,16)

        BaseM = floor(L0C/BaseN)

        BaseK = min( floor_align(L0A/BaseM,16), floor_align(L0B/BaseN,16) )

对于(3).b如果L1可以放下2Batch则按Batch进行double buffer直接GM->L1->Cube->L0C->GM/L2的核内计算

对于L1->L0,

if(L0C_Size<=2*M*N*L0C_ElementSize):

    BaseM = M

    BaseN = N

    BaseK = min( floor_align(L0A/BaseM,16), floor_align(L0B/BaseN,16) )

else:

    if(M<N):

        BaseM = align(M,16)

        BaseK = min(floor_align(L0A/2/BaseM,16),K)

        BaseN = max(floor_align(L0C/2/BaseM,16),floor_align(L0B/2/BaseK,16))

    else:

        BaseN = align(N,16)

        BaseK = min(floor_align(L0B/2/BaseN,16),K)

        BaseM = max(floor_align(L0C/2/BaseN,16),floor_align(L0A/2/BaseK,16))

对于(3).c每个1batch,且L1可放下单batch完整的左或右矩阵,以及另外右或左矩阵的一部分,假设L1放完整左矩阵和部分右矩阵,则关键需要确定右矩阵应搬入的kL1_b,则kL1_b = min((L1_Size-MatA_Size)/N, K),fixpipe应开unitflag

StreamK分支

进入分支条件

B*M*N*L0C_ElementsDtype/256KB<核数/2

K>k_thr

实现方案

ASW_Basic分支

进入分支条件

M*N*L0C_ElementsDtype>=256KB

B*M*N*L0C_ElementsDtype/256KB>=核数

实现方案

特殊分支

K = 0:无任何计算,C = bias 或 0,纯 AIV 写值;

K = 1:退化为逐元素乘 C = A ⊙ B,无累加深度,Cube 的 16×16×16 粒度浪费 15/16,走 AIV 向量通路(GM→UB→Mul→GM)优于 Cube 通路。

Batch Matmul算子特性分析 # 算子功能与接口说明 算子功能:完成带batch的矩阵乘计算。 算子输入: * 左矩阵(也叫A矩阵):[BatchA,M,K]、数据类型dtype、layout(典型ND) * 右矩阵(也叫B矩阵):[BatchB,M,K]、数据类型dtype、layout(典型ND) * 偏置bias:维度固定[B,1,N]、数据类型dtype、layout(固定ND),可为空 算子输出: 输出矩阵(也叫C矩阵):[BatchC,M,N]、数据类型dtype、layout(典型ND) 计算公式: C=A@B+bias 其中,A,B 输入维度典型为3维,最后两维做矩阵乘计算。例如,A,B 输入维度分别为(B,M,K)、(B,K,N) 时,C维度为 (B,M,N)。bias是维度为(B,1,N)的向量。其中,B为矩阵乘法的batch数,M为第一个张量A的行数和输出张量C的行数,N为第二个张量B的列数和输出张量C的列数,K为第一个张量A的列数和第二个张量B的行数。 # BMM算子最优实现分析 ## 最优软件实现设计的系统推导 ### 如何理解性能最优 NPU 上 BMM 的执行是核内多级硬件流水的并行——Cube 计算(MMAD)、GM/L2→L1 搬移(MTE2)、L1→L0 搬移(MTE1)、L0C 写出(Fixpipe),多核并行时各流水级时延可被双缓冲(double buffer)等机制相互掩盖,最终: **T\_total=max(T\_MMAD, T\_MTE2, T\_MTE1, T\_Fixpipe [, T\_Reduce])** **总时延 = 流水线最慢的一级** 算子优化的关键就是对瓶颈流水级的优化。 由此直接得出一个重要的设计自由度——瓶颈交换:当 MTE2(搬移)是瓶颈、MMAD(计算)不是瓶颈时,可以牺牲一定 MMAD 时延(例如冗余计算)换取 MTE2 性能提升;反之,当 MMAD 是瓶颈时,可以牺牲一定 MTE2 时延(例如重复搬移)换取计算效率提升。只要瓶颈级时延下降,总时延就下降。 后文会看到:MergeBatch 就是"牺牲算力换搬移效率"的典型,ASW\_Basic 切 M/N 就是"牺牲搬移(重复读)换并行度"的典型——它们的存在正当性都来自这个 max 模型。 ### 实现本质逻辑 BMM 在 NPU 上实现的本质是:把参与计算的数据分块(tile),由全部AIC 核并行 + 串行地完成这些分块的计算,再组合成最终结果: C[B,M,N]={C[Bu,Mi,Nj]=∑kA[Bu,Mi,Kk]@[Bu,Kk,Nj]} 分块有 4 个维度:B、M、N、K。核间怎么分这 4 个维度,就是分支划分的第一性问题(核内分块是第二性问题,属于各分支内部的 tiling)。 从"读入 / 计算 / 写出"三个视角考察每个维度的核间切分特征(这是后续一切推导的基石): | | | | | | --- | --- | --- | --- | | 切分维度 | 读入特征 | 计算特征 | 写出特征 | | 切B | 每个数据块只被固定的 1 个核读取,核间零重复读 | 每个输出块由 1 个核独立完成,无核间依赖 | 只写最终结果,无中间结果 | | 切M/切N | **切 M 则同一右矩阵块被多核重复读;切 N 则同一左矩阵块被多核重复读** | 每个输出块由 1 个核独立完成,无核间依赖 | 只写最终结果,无中间结果 | | 切K | 每个数据块只被固定的 1 个核读取,零重复读 | 每个输出块由多核共同完成,存在核间依赖 | **有中间结果写出,需核间 Reduce 归约** | 不同切分差异的根本原因: B 维在数学上独立(BMM 语义就是逐 batch 独立矩阵乘),所以切 B 天然零重复、零依赖; K 维有 L0C 累加机制:核内切 K 时,切K后多轮 mmad 在 256KB 的 L0C 上原地累加(cmatrixInitVal=false),中间结果不出核。一旦把 K 切到核间,单核的 L0C 装不下"别的核算的 K 段",部分和必须写出到 GM/L2 workspace,再由 AIV 归约——切 K 是唯一同时破坏"累加不出核"和"输出独占"两条性质的切法。 4 个维度的任意非空子集都可作为一种核间切分方案,共 2^4−1=15 种: {B},{M},{N},{K},{B,M},{B,K},{B,N},{M,K},{M,N},{K,N},{B,M,K},{B,M,N},{B,K,N},{M,K,N},{B,M,K,N} 任何分核实现方案必属于其中一种 ⇒ 这 15 种是完备的。按 2.1.2 的特征分组: | | | | | | --- | --- | --- | --- | | 组 | 组合 | 共同特征 | 优化重心 | | 只切B | {B} | 零重复读,读写数据量固定 | 访存 Bound:搬移效率高;计算 Bound:计算效率高 | | 切B+ M\N切分,不切K | {B,M},{B,N},{B,M,N} {M},{N},{M,N}, | 可能有重复读 | 访存 Bound:重复读尽量少 + 搬移效率高;计算 Bound:计算效率高 | | 含K切分 | {B,K}, {B,M,K},{B,K,N}, {B,M,K,N}, {K},{M,K},{K,N},{M,K,N}, | 可能有重复读; 有中间结果写出 + 归约 | 计算效率高,且归约引入的额外 MTE2/Fixpipe 时延不能成为新瓶颈 | 15 种组合的代价结构只由两个布尔特征决定——是否含 K、是否含 M/N。 三个维度的"核间切分价格"严格排序:cost(切B)=0<cost(切M/N)≪cost(切K) * 切 B 免费:零重复读、零依赖、零中间写出; * 切 M/N 廉价但有价:共享矩阵被重复读,但若共享部分能驻留 L2,以DV100为例,128MB L2,重复读以 5.2TB/s 命中 L2 而非 1.6TB/s 的 GM,则代价大部分被 L2 吸收;配合 swizzle(执行顺序编排)压缩同时活跃的数据集,代价进一步压低; * 切 K 昂贵:归约流量 TReduce ∝gridK×输出量,且引入核间同步——这是结构性代价,L2 吸收不掉。 "价格表"不是经验,是 BMM 语义 + L0C 累加机制 + L2/GM 带宽结构三条事实的推论。 整条分支决策树就是一句话:按价格从低到高购买并行度,买不够才加价。 ### BMM算子软件分支推导 1、问题规约(能降维就不在 BMM 本体里解决) * 若 BatchA = 1或 BatchB = 1,BMM 可通过维度折叠**转化为普通 Matmul**,直接复用 Matmul 的成熟优化体系(tiling、L2 切分、全载),不必在 BMM 框架内重新发明轮子 → 转Matmul; * 退化 case(K=0 无计算、K=1 无累加深度)让 Cube 完全或几乎无用,改走 AIV 向量通路——这属于"计算通路选择",是正交于切分的前置判断。可称为特殊分支 2、先买免费的B维度 * 切B可满足多轮都尽量填满核 * 如果单Batch的M\*N够大,(能开出大 tile、L0C 利用率高、Cube 喂得饱)→ 每核逐个 batch 做完整 Matmul 就是最优,任何额外切分只增代价。可称为IterBatch分支 * 如果单Batch的M\*N很小,把多个 batch 在核内合并成大 tile 计算([bM,K]@[K,bN],取块对角线输出),用冗余算力换 Cube 利用率与搬移效率。可称为MergeBatch分支 3、B维度买不满核,加点价买廉价的M/N维度切分 * 优先切B后再切 M/N(或混合切),切M、N引入的共享矩阵的重复读交给 L2 + swizzle 吸收;可称为ASW\_Basic分支 4、免费和廉价维度B、M、N都买不满核,才考虑加价买昂贵的K维度切分 * 用核间规约代价换并行度收益;可称为StreamK分支 由此推导出6大分支,“转Matmul”、“特殊分支”、“MergeBatch”、“IterBatch”、“ASW\_Basic”、“StreamK” ## 可转Matmul分支 ### 进入分支条件 BatchA==1或BatchB==1 ### 实现方案 对于BatchB==1,左矩阵 `[B, M, K]` 的 batch 维与 M 维在 ND 布局下内存相邻&紧排,直接视图为 `[B·M, K]`;输出 `[B·M, N]` 与 `[B, M, N]` 的内存布局逐元素一致;无输入重排,无输出Split,可直接转Matmul无任何代价 对于BatchA==1,需将右矩阵 `[B, K, N]` 折叠为 `[K, B·N]`,但B 的 batch 维与 N 维在内存中不相邻(中间隔 K),折叠等价于一次 `[B,K,N]→[K,B,N]` 的转置重排(O(B·K·N) 读写),且输出 `[M, B·N]` 与目标 `[B, M, N]` 之间存在置换,**需要 scatter。** 此时当A矩阵比较小时,优先考虑将A矩阵加载到每个核的L1,然后每个核均匀读取B矩阵完成全部计算,此时无需输入\输出重排的额外开销; 但A矩阵较大时,可将BatchA==1扩展到BatchA==BatchB,然后看是否能在BMM其他分支预估时延,再与B折叠转Matmul的+重排的时延相对比,择优进行实现方案选型。 ## MergeBatch分支 每个核负责多个Batch的Matmul计算,核间无需同步或通信,假设单核每次要完成b个Batch的Matmul计算,单核计算时将[b,M,K]@[b,K,N]=[b,M,N]转换合并成[bM,K]@[K,bN]=BlockTrace([bM,bN])=[b,M,N],所谓BlockTrace指的是以[M,N]的Block粒度将结果矩阵的块对角线取出作为输出。 ### 进入分支条件 具体来说进入MergeBatch分支需同时满足以下条件: 1、 BatchA=BatchB && BatchA/aicNum=b\_per\_core>= 2\*b0 2、 MN\*out\_dtype<=L0C/(2\*b0\*b0) 3、 B/aicNum\*(MK+KN)>=payload\_per\_core 4、 Max(MK,KN)>min\_TileSize 5、 2\*M\*N/(M+N) < (芯片对应位宽算存(GM)比/b0) b0>=2 ### 实现方案 确定Tiling参数 重点是确定bL1,kL1 基于L0C容量+访存Bound约束,先确定每次计算的b应该多大 MN\*out\_dtype<=L0C/(2\*b\*b) => b^2 <= L0C/(MN\*out\_dtype) => b<bi 2\*M\*N/(M+N) < (芯片对应位宽算存(GM)比/b) => b<bii b=min(bi,bii,b\_per\_core),bL0=b 基于bL0和L0AB确定kL0大小 2\*(2M\*kL0a)<=L0A,2\*(2N\*kL0b)<=L0B,kL0\_max=min(kL0a, kL0b, C0Size/in\_dtype) kL1>=min( kL0\_max, 128Byte) 基于kL1和L1容量大小确定bL1 2\*bL1\_max\*(M\*kL1+kL1\*N)<=L1\_Size bL1=min(bL1\_max, b\_per\_core) ## IterBatch分支 ### 进入分支条件 进入InterBatch分支需同时满足以下条件: (1) BatchA=BatchB && BatchA/AiCoreNum=b\_per\_core>= 1 (2) BatchA%AiCoreNum =0 或 BatchA%AiCoreNum > minCoreNum (3) 需满足以下L1约束之一 a) b\_per\_core =1 && (MK+KN)\*dtype <=L1\_size b) b\_per\_core >1 && 2\*(MK+KN)\*dtype <=L1\_size c) b\_per\_core =1 && (MK+KN)\*dtype>L1Size且(MK+KN/Step)\*dtype<L1Size或(MK/Step+KN)\*dtype<L1Size,其中Step为大于1的正整数 d) b\_per\_core >1 && (MK+KN)\*dtype>L1Size/2且(MK+KN/Step)\*dtype<L1Size/2或(MK/Step+KN)\*dtype<L1Size/2,其中Step为大于1的正整数 e) (MK+KN)\*dtype>L1Size且(M\*(K/Step)+(K/Step)\*N)\*dtype<L1Size,其中Step为大于1的正整数 (4) c\d\e的step切分后需满足带宽效率要求 minCoreNum表示带宽能利用满的最小核数,来自芯片实测经验DV100可取0.8\*aicNum ### 实现方案 对于(3).a则每核1batch直接搬入L1, 对于L1->L0,先考虑L0C是否能放下完整单Batch输出MN,如果能放下,再看L0AB能否放下完整的左右矩阵,如果放下则不用切分,如果放不下则切K,BaseK=max(L0A\B约束下的K, 16) 如果L0C放不下则按M或N切分,将M或N中较小的那个,对齐到16的倍数,另一个通过L0C的容量来确定;例如M=min(M,N),BaseM=Align(M,16), BaseN=L0C/BaseM if(L0C\_Size<=M\*N\*L0C\_ElementSize):     BaseM = M     BaseN = N     BaseK = min( align(L0A/BaseM,16), align(L0B/BaseN,16) ) else:     if(M<N):         BaseM = align(M,16)         BaseN = floor(L0C/BaseM)         BaseK = min( floor\_align(L0A/BaseM,16), floor\_align(L0B/BaseN,16) )     else:         BaseN = align(N,16)         BaseM = floor(L0C/BaseN)         BaseK = min( floor\_align(L0A/BaseM,16), floor\_align(L0B/BaseN,16) ) 对于(3).b如果L1可以放下2Batch则按Batch进行double buffer直接GM->L1->Cube->L0C->GM/L2的核内计算 对于L1->L0, if(L0C\_Size<=2\*M\*N\*L0C\_ElementSize):     BaseM = M     BaseN = N     BaseK = min( floor\_align(L0A/BaseM,16), floor\_align(L0B/BaseN,16) ) else:     if(M<N):         BaseM = align(M,16)         BaseK = min(floor\_align(L0A/2/BaseM,16),K)         BaseN = max(floor\_align(L0C/2/BaseM,16),floor\_align(L0B/2/BaseK,16))     else:         BaseN = align(N,16)         BaseK = min(floor\_align(L0B/2/BaseN,16),K)         BaseM = max(floor\_align(L0C/2/BaseN,16),floor\_align(L0A/2/BaseK,16)) 对于(3).c每个1batch,且L1可放下单batch完整的左或右矩阵,以及另外右或左矩阵的一部分,假设L1放完整左矩阵和部分右矩阵,则关键需要确定右矩阵应搬入的kL1\_b,则kL1\_b = min((L1\_Size-MatA\_Size)/N, K),fixpipe应开unitflag ## StreamK分支 ### 进入分支条件 B\*M\*N\*L0C\_ElementsDtype/256KB<核数/2 K>k\_thr ### 实现方案 ## ASW\_Basic分支 ### 进入分支条件 M\*N\*L0C\_ElementsDtype>=256KB B\*M\*N\*L0C\_ElementsDtype/256KB>=核数 ### 实现方案 ## 特殊分支 K = 0:无任何计算,C = bias 或 0,纯 AIV 写值; K = 1:退化为逐元素乘 `C = A ⊙ B`,无累加深度,Cube 的 16×16×16 粒度浪费 15/16,走 AIV 向量通路(GM→UB→Mul→GM)优于 Cube 通路。
Author
Owner

StreamK的实现方案,再切K前先切B,再以L0C定M\N的切分BaseM、BaseN,固定较小的一个(M或N)作为BaseM或BaseN切另一个(N或M),最后再切K

StreamK的实现方案,再切K前先切B,再以L0C定M\N的切分BaseM、BaseN,固定较小的一个(M或N)作为BaseM或BaseN切另一个(N或M),最后再切K
admin closed this issue 2026-09-03 10:58:12 +00:00
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: admin/matmul-analysis#3
No description provided.