From 63b039ac51836c3616849ead94f07a9a46382264 Mon Sep 17 00:00:00 2001 From: admin Date: Thu, 20 Aug 2026 14:40:49 +0000 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20BatchMatMulV3=E7=AE=97?= =?UTF-8?q?=E5=AD=90=E5=88=86=E6=94=AF=E5=AE=9E=E7=8E=B0=E5=88=86=E6=9E=90?= =?UTF-8?q?.html?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BatchMatMulV3算子分支实现分析.html | 923 +++++++++++++++++++++++++++++ 1 file changed, 923 insertions(+) create mode 100644 BatchMatMulV3算子分支实现分析.html diff --git a/BatchMatMulV3算子分支实现分析.html b/BatchMatMulV3算子分支实现分析.html new file mode 100644 index 0000000..629555e --- /dev/null +++ b/BatchMatMulV3算子分支实现分析.html @@ -0,0 +1,923 @@ + + + + + +BatchMatMulV3 (BMM v3) 算子分支实现深度分析 —— 昇腾 950 / DAV_3510 + + + +
+ +

BatchMatMulV3(BMM v3)算子分支实现深度分析

+
+ 分析对象:ops-nn/matmul/batch_mat_mul_v3(CANN 开源算子仓,GitCode cann/ops-nn) |  + 目标平台:昇腾 950PR(NPU 架构版本 351x,NpuArch::DAV_3510) |  + 资料来源:昇腾NPU知识库(950 架构白皮书、CANN 9.0.0 AscendC 文档)+ 算子源码逐文件分析  | 日期:2026-08-20 +
+ + + + +

1. 算子概览与代码地图

+

BatchMatMulV3 是 CANN ops-nn 仓中批量矩阵乘的主力算子,承载 aclnnBatchMatMul / aclnnBaddbmm / aclnnAddbmm / aclnnEinsum / aclnnBatchMatMulWeightNz 等全部 BMM 类 API。语义为 C[b, m, n] = A[b, m, k] × B[b, k, n](+ 可选 bias),batch 维最多 4 级(总维度 ≤ 6),A/B/C 的 batch 维支持广播。

+ + + + + + + +
目录内容关键文件
op_host/op_tiling/Host 侧 tiling:分支选择、tiling 参数计算batch_mat_mul_v3_tiling.cpp(入口)、batch_mat_mul_v3_base_tiling.cpp(67KB,老路径基类)、arch35/(950 高级 tiling,12 个策略文件)
op_kernel/Device 侧 kernelarch35/batch_mat_mul_v3.cpp(arch35 入口,编译期 if constexpr 分发树)、batch_mat_mul_v3*.h(老架构 kernel)、arch35/batch_mat_mul_v3_*_advanced.h(各策略 kernel/block scheduler)
op_host/op_api/aclnn API 层aclnnBatchMatMul / Baddbmm / Einsum 等
依赖:ops-nn matmul/mat_mul_v3matmul/common/cmct公共 tiling 基类、MatmulImpl 高层 API、Cmct/Blaze GEMM 框架matmul_v3_tiling_advanced、mat_mul_pingpong_basic、kernel_matmul_iterbatch 等
+ +

BMM v3 的软件实现遵循昇腾算子的标准两层结构:

+ + + +

2. 硬件基础:昇腾 950PR 微架构规格与 tiling 设计约束

+

BMM v3 全部分支的设计动机都可以追溯到 950PR(DAV_3510)的以下硬件事实(来源:昇腾950 NPU 架构白皮书表3-1/表4-2、CANN 9.0.0《NPU架构版本351x》文档):

+ +

2.1 关键规格表

+ + + + + + + + + + + + + + + + +
规格项昇腾 950PR对 BMM tiling 的意义
AI 子系统36 个(满配),每个 = 1 Cube Core + 2 Vector CoreAIC:AIV = 1:2 是 StreamK、fixpipe 1V2 等 MIX 分支的结构性前提(源码多处硬性要求 aivNum == 2*aicNum
AIC / AIV 核数32(降配 28)/ 64(降配 56),AIC/AIV 分离架构
Cube 算力BF16/FP16 432 TFLOPS(纯 Cube);一拍完成 FP16 16×16×16决定 cubeBound(计算访存比拐点),进而决定 baseM/baseN 寻优
Vector 算力FP16/BF16 54 TFLOPS;每拍 256BK=0/K=1 等低密度计算退到 AIV 更划算的依据
L1 Buffer512KB / AICL1 全载(AL1/BL1 full load)、iterBatchL1(L1 驻留 batch 数)的容量上界
L0A / L0B64KB / AIC(512B 对齐,FRACTAL_NZ/ZN)baseM×baseK、baseN×baseK 单块上界;mergeBatchL0 合并 batch 数的上界
L0C256KB / AIC(较上代增大,白皮书明言"提供更灵活的 Tiling 策略")fp32 累加:baseM×baseN×4B×DB ≤ 256KB;iterbatch 的 batchOutNum 由它决定
UB512KB / AIV(32B 对齐)AIV 分支(TO_MUL)单轮驻留 batch 数 = UB / 单 batch 字节数
L2 Cache128MB(降配 112MB)全局统一滑窗/对角错位 swizzle 的收益来源:让并发核访问的 A/B 块在 L2 命中
片上内存128GB / 1.6TB/s(PR 降配 1.4TB/s;DT 为 4TB/s)PR 算存比高 → 偏计算受限 → "减少 HBM 搬运"类分支(L1 全载、iterbatch)收益更大
数据通路(351x 变化)新增 L0C→UB、UB↔L1、AIV UB↔L1 硬通道、SSBuffer 核间通信;删除 GM→L0 直通与 L1→GM所有数据必须经 L1 中转;CV 协同(AIC 算 + AIV 后处理)有硬化通路支撑
FixpipeL0C→GM/UB 随路量化/转置(NZ2ND 等)、ReLU 融合tiling_key 的 L0C2OUT_MODEL 位段(ON_THE_FLY / ND_FIXPIPE_1_1 / 1_2)
NDDMAAI Core 内置 N 维 DMA,≤5 维重排随搬运完成TENSOR_LEVEL(张量级 API)分支的硬件基础
核间同步CrossCoreSetFlag/WaitFlag,支持 AIC:AIV = 1:1 / 1:2MIX_AIC_1_2 kernel 类型(StreamK、fixpipe 1V2 epilogue)的同步机制
+ +

2.2 规格 → 设计约束的映射

+ +
950PR vs 950DT 的差异动机:PR 片上内存带宽 1.6TB/s 显著低于 DT 的 4TB/s,而 BF16 算力接近(486 vs 547 TFLOPS),即 PR 算存比更高、更偏计算受限(其定位即 LLM Prefill/推荐等吞吐场景)。这解释了为什么 950 的 BMM 分支体系如此重视"数据驻留复用"(L1 全载、iterbatch、mergebatch)——在 PR 上减少一字节 HBM 搬运比在 DT 上更值钱。
+ + +

3. Tiling 总体框架:入口、平台分流与分支短路遍历

+ +

3.1 入口与平台分流

+
// op_host/op_tiling/batch_mat_mul_v3_tiling.cpp
+static ge::graphStatus BatchMatMulV3TilingFunc(gert::TilingContext* context) {
+    if (IsAdvancedSocVersion(context)) {   // DAV_3510(950) / DAV_RESV(s8s4保留平台)
+        return batch_matmul_v3_advanced::BatchMatMulV3Tiling(context).DoTiling();  // arch35 高级 tiling
+    }
+    return TilingRegistry::GetInstance().DoTilingImpl(context);  // 老路径 BatchMatmulV3BaseTiling
+}
+IMPL_OP_OPTILING(BatchMatMulV3)
+    .Tiling(BatchMatMulV3TilingFunc)
+    .TilingParse<MatmulV3CompileInfo>(TilingPrepareForBatchMatMulV3)  // 提取平台规格
+    .GenSimplifiedKey(GenSimplifiedKey);
+

TilingPrepareForBatchMatMulV3 在编译期从平台信息提取 MatmulV3CompileInfo:aicNum/aivNum、L1/L0A/L0B/L0C/L2/UB 容量、supportL0c2out(fixpipe)、supportL12BtBf16、btSize(1024/4096)、npuArch——所有分支的定量条件都建立在这组平台参数之上,同一套代码因此能适配 950 满配/降配及其他 351x 产品。

+ +

3.2 arch35 主调用链

+
BatchMatMulV3TilingFunc + └─ BatchMatMulV3Tiling::DoTiling() [batch_matmul_v3_tiling_advanced.*, 继承 MatMulV3Tiling] + ├─ GetShapeAttrsInfo / CheckArgs / GetArgs / Validate* (M/N/K、转置、dtype、format 校验) + ├─ ExtractMatrixBatchInfo() 提取 4 级 batch:batchA0~A3 / B0~B3 / C0~C3 + ├─ ValidateMatrixBatchInfo() 广播规则校验 + ★MergeBatchAndMAxis(): + │ 当 batchB==1 且 A 不转置 → mValue = batchA*mValue,batch 折叠进 M 轴(BMM→MM 降维) + ├─ Extract/ValidateOptionalBatchInfo() (bias batch) + └─ MMTilingRegistry::DoTilingImpl(opType="BatchMatMulV3", npuArch, priorities) + └─ 按优先级表逐个构造策略类 → DoTiling(): + GetShapeAttrsInfo → IsCapable() → DoOpTiling() → AdjustOpTiling() → PostTiling() + ★ IsCapable()==false → GRAPH_PARAM_INVALID → 试下一个分支 + ★ 任一分支 GRAPH_SUCCESS → 立即短路返回
+ +

3.3 分支优先级表(DAV_3510)

+

定义于 arch35/batch_matmul_v3_tiling_strategy.h

+ + + + + + + + + + + + + +
优先级strategy 常量策略类一句话定位核类型
0BATCH_MATMUL_INPUT_K_EQUAL_ZEROBatchMatMulV3KEqZeroTilingK=0:输出置零AIV_ONLY
1BATCH_MATMUL_TO_MULBatchMatMulV3Matmul2MulTilingK=1:退化为逐元素乘AIV_ONLY
2BATCH_STREAM_KBatchMatMulV3BasicStreamKTilingK 巨大且 MN 并行度 < 一半核数:切 KMIX_AIC_1_2
3MERGE_BATCH_BASICAPIBatchMatMulV3MergeBatchBasicApiTiling小 M/N、巨大 batch:多 batch 合并进 L0AIC_ONLY
4ITER_BATCH_BROADCAST_BASICAPIBatchMatMulV3IterbatchBroadcastBasicApiTiling单边单轴 batch 广播AIC_ONLY
5ITER_BATCH_BASICAPIBatchMatMulV3IterBatchBasicApiTilingbatch 相等且 > 核数:L1/L0 多 batch 流水AIC_ONLY/MIX_AIC_1_2
6ITER_BATCHBatchMatMulV3IterBatchTiling同上但高阶 API(IterateBatch)AIC_ONLY
7AL1_FULL_LOAD_BASICBatchMatMulV3AswAL1FullLoadBasicTilingA 无 batch 且 M≤256:A 全量常驻 L1AIC_ONLY
8BL1_FULL_LOAD_BASICBatchMatMulV3AswBL1FullLoadBasicTilingB 无 batch 且 N≤256:B 全量常驻 L1AIC_ONLY
9ASW_BASICBatchMatMulV3AswBasicTiling通用基础 API + cubeBound 模型寻优AIC_ONLY
999BASEBatchMatMulV3AswTiling最终兜底(高阶/张量级 API),IsCapable 恒 trueAIC_ONLY
+

DAV_RESV(s8s4 保留平台)仅有:ITER_BATCH_BASICAPI → AL1_FULL_LOAD_BASIC → BL1_FULL_LOAD_BASIC → ASW_BASIC → BASE。

+ +
遍历顺序的设计逻辑(短路语义下,顺序 = 特判优先级): +
    +
  1. 计算模式彻底改变的特判最先(K=0/K=1):它们根本不需要 Cube,若被后面的 cube 模板接住就是纯浪费,必须最先拦截;
  2. +
  3. 改变并行结构的分支次之(StreamK):它对"核怎么分"做全局性改变(沿 K 拆核 + workspace 归约),要在 batch 优化之前决策;
  4. +
  5. batch 维度优化居中:mergebatch(L0 复用最高,条件最苛刻)→ 单边广播(复用次高)→ 普通 iterbatch 基础 API → 高阶 API iterbatch;
  6. +
  7. 数据驻留优化靠后(AL1/BL1 全载):消除单边重复搬运,是通用路径上的加强;
  8. +
  9. 通用模板垫底(ASW_BASIC → BASE=999):保证任意合法输入必然 tiling 成功。999 的大数值确保永远最后尝试。
+ + +

4. 为什么是这些分支:从 case 空间到分支全集的系统性论证

+

BMM 的 case 空间可由 5 个近似正交的特征维度刻画。每个分支恰好占据其中一个特征区域,且这些区域合起来覆盖了全部合法输入——这就是"为什么是这些分支"的系统回答:

+ + + + + + + + + + + +
特征维度取值区域微架构矛盾对应分支
① K 的退化程度K=0无计算,Cube 阵列完全无用K_EQUAL_ZERO(AIV 清零)
K=1无累加深度,mmad 16×16×16 粒度浪费 15/16TO_MUL(AIV 向量乘)
② MN×batch 并行度 vs 核数batchC×mCnt×nCnt ≤ aicNum/2 且 K 巨大32 个 AIC 大半闲置,唯一剩余并行维度是 KSTREAM_K(切 K + 部分和归约)
③ batch 量级与 M/N 大小M,N 很小(M≤N)、K≥64、batchC ≥ 4×aicNum单 batch 基本块喂不饱 L0(如 16×16),mmad 效率极低MERGE_BATCH(多 batch 拼成大 L0 块)
batchC > aicNum,M/N/K 正常每核要算多个 batch,逐 batch 重复搬 A/B 浪费 MTEITER_BATCH_BASICAPI / ITER_BATCH(L1/L0 一次驻留多 batch 流水)
batch ≤ 核数量级,M/N 大batch 不构成主并行维度,走 M/N 基本块切分ASW_BASIC / BASE
④ batch 广播形态恰好单边、单轴广播被广播算子的单 batch 数据可被对端多 batch 复用ITER_BATCH_BROADCAST(广播侧 L1 驻留一份)
⑤ 单边无 batch 且该边很小batchA=1 且 M≤256(B 大)A 全量可常驻 L1,消除全部重复搬运AL1_FULL_LOAD
batchB=1 且 N≤256(A 大)对称(注:A 不转置时已被 MergeBatchAndMAxis 折叠成 MM)BL1_FULL_LOAD
+

完备性的结构性保证:优先级表尾部是无条件分支(BASE=999 的 IsCapable 恒 true,ASW_BASIC 仅校验 dtype/batch 一致性),因此任何通过 Validate 的输入必然命中某一分支——不存在"所有分支都不收"的 case。而前 10 个分支的关系是"特判 ⊂ 通用":每个特判分支覆盖的 case 落入通用分支也能跑,只是性能更差;优先级顺序保证收益最大的实现先被选中。这与"性能最优分支集必须是完备分支全集子集"的方法论一致:这里完备全集 = 通用 cube 模板(BASE),特判分支是它在各特征区域上的性能占优替代。

+
一个容易忽略的降维优化ValidateMatrixBatchInfo 阶段的 MergeBatchAndMAxis() 在 batchB==1 且 A 不转置时直接把 batchA 折叠进 M 轴(mValue = batchA * mValue,batch 置 1),把 BMM 变成单个大 M 的 MatMul——此时后续所有 batch 类分支都不会命中,case 直接落到 ASW/AL1 全载体系。这说明分支选择不是纯"分诊",前面还有一层问题归约
+ +

5. 逐分支详解

+

本章对每个分支按统一结构展开:目标 case → 进入条件逐条解析(IsCapable,含每个阈值的取值依据)→ tiling 实现步骤(DoOpTiling 编号步骤,含公式)→ 关键参数的"为什么"分析 → kernel 实现。所有条件表达式、常量、注释均直接引用源码(文件:op_host/op_tiling/arch35/batch_matmul_v3_*_tiling.cpp 及 mat_mul_v3 公共实现),微架构依据标注知识库文档篇目;官方文档没有依据、仅为源码经验值的参数会明确标注

+ +

5.0 总览:分支决策流程与两条数据通路

+

arch35 tiling 按优先级 0→9→999 短路遍历,每个分支的 IsCapable 是一组"与"条件。整体决策流如下(条件为简化表述,精确表达式见各分支小节):

+ +
+ + + + + + tiling 输入(shape/dtype/batch) + + K == 0 ? + K == 1 且 batch≥128 ? + b×mCnt×nCnt ≤ aic/2 且 K 巨大 ? + batch ≥ 4×aic 且 M≤N 小、K≥64 ? + 恰好单边单轴 batch 广播 ? + batch 相等 > aic 且 L1 可载 ? + batchA=1 且 M≤256 且 A 可全载 ? + batchB=1 且 N≤256 且 B 可全载 ? + batch 相等、AB转置连续态一致 ? + + + + + + + + + + + + + 分支0 K_EQUAL_ZERO(AIV)输出=零矩阵,AIV InitOutput 直写 GM + 分支1 TO_MUL(AIV)K=1 退化向量乘,UB 多 batch pingpong + 分支2 STREAM_K(AIC+AIV 1:2)沿 K 切给空闲核算部分和,workspace 归约 + 分支3 MERGE_BATCH多 batch 合并成大 L0 块,喂饱 cube + 分支4 ITER_BATCH_BROADCAST广播侧 L1 驻留一份,对端多 batch 流水 + 分支5/6 ITER_BATCH(基础API/高阶API)L1/L0 一次驻留多 batch,IterateBatch 流水 + 分支7 AL1_FULL_LOADA 全量常驻 L1,整个 kernel 只搬一次 + 分支8 BL1_FULL_LOADB 全量常驻 L1(与分支7 镜像) + 分支9 ASW_BASICcubeBound 模型寻优 baseM/baseN + 滑窗调度 + 分支999 BASE(IsCapable 恒 true,最终兜底)默认 256×256×(128B/dtype) 基块 + CalL1Tiling;TENSOR_LEVEL 时支持任意广播 + + + + + + + + + + + + +
图 5-1 arch35(DAV_3510)分支短路决策流程。菱形为各分支 IsCapable 的核心判据(简化);任一分支命中即返回,不再尝试后续分支
+
+ +

理解各分支前,先建立 950PR 上的两条数据通路模型——所有分支的本质都是在这两条通路上做不同的驻留/复用/并行安排:

+ +
+ + + + 通路 A:Cube 通路(AIC,分支 2~9/999) + GMHBM 1.6TB/s + L1 512KBMTE2 搬运,DB/4buffer + L0A/L0B 64KBMTE1,512B对齐fractal 16×16 + Cube 阵列一拍 16×16×16 + L0C 256KBfp32 累加,可驻留累加 + fixpipeNZ2ND/量化随路 + GM + + + + + + + + L0C 驻留累加(K 循环不回 GM,文档191篇) + 351x 已删除 GM→L0 直通,所有数据必须经 L1 中转(054篇) + + 通路 B:Vector 通路(AIV,分支 0/1;老架构 GEMV) + GM + UB 512KB多 batch 驻留 pingpong + Vector 单元每拍 256B:Mul / InitOutput + GM + + + + 不经过 L1/L0/Cube,64 个 AIV 纯向量流水 + +
图 5-2 昇腾 950PR 上 BMM 的两条数据通路。分支 0/1 走通路 B,其余走通路 A;各分支的差异在于"哪一级驻留什么、驻留多少、并行维度选谁"
+
+ +
+

5.1 K_EQUAL_ZERO(优先级 0)—— K=0,输出清零 AIV_ONLY

+

tiling:arch35/batch_matmul_v3_k_equal_zero_tiling.cpp(68 行);kernel:mat_mul_v3/arch35/mat_mul_input_k_eq_zero_clear_output.h

+

① 目标 case

+

K 轴长度为 0 的退化 BMM。语义上 C = 对空 K 维求和 = 全零矩阵,没有任何乘加计算

+

② 进入条件(IsCapable,cpp L27-41,逐条)

+ + + + + +
#条件(源码表达式)取值依据 / 源码注释
1aFormat == FORMAT_FRACTAL_NZ || bFormat == FORMAT_FRACTAL_NZ → 拒绝注释:"The NZ format is not supported in this strategy." 清零 kernel 是纯 AIV vector kernel,只吃 ND 连续布局,NZ 分形格式对它无意义
2hasBias → 拒绝有 bias 时输出应为 bias 广播值而非 0,清零语义不成立
3kValue != 0 → 拒绝本分支唯一触发条件:K==0
+

③ tiling 实现步骤(DoOpTiling,cpp L43-48)

+
    +
  1. totalDataAmount = m × n × batchC —— 输出总元素量,作为负载均分指标(本分支不再细分单核任务,kernel 侧按总量均分);
  2. +
  3. usedCoreNum = aivNum —— 用全部 64 个 AIV 而非 32 个 AIC。为什么:清零是纯 vector 写操作,Cube 阵列全程空转;950 的 AIC/AIV 是分离架构、各有独立指令流,用 AIV 做这件事 AIC 可以完全不上电执行;且 AIV 核数是 AIC 的 2 倍,memset 类带宽型任务并行度更高。GetNumBlocks() 同样返回 aivNum。
  4. +
+

④ kernel 实现

+

MatMulInputKEqZeroClearOutputif ASCEND_IS_AIC { return; }(AIC 直接退出);每个 AIV 对均分的输出区间调用 AscendC::InitOutput<DTYPE_Y>(outputGM[...], copyDataAmount, 0) 向 GM 写 0,尾区间用 tailDataCount 处理。数据通路即图 5-2 通路 B 的极简形态(连 UB 都不需要,InitOutput 直接铺 GM)。

+

为什么要单列这个分支:K=0 在 shape 校验中是合法输入(k∈[0, INT32_MAX]),若不放行会被通用 cube 模板接住——白白建立 GM→L1→L0 流水做"0 深度累加",既浪费 AIC 又拖慢端到端时延。作为计算模式彻底改变的特判,它必须排在最高优先级拦截。tilingKey:BASIC_LEVEL + BATCH_MODEL + Model=K_EQUAL_ZERO + NONE_FULL_LOAD + ON_THE_FLY。

+
+ +
+

5.2 TO_MUL / matmul2mul(优先级 1)—— K=1 退化为向量乘 AIV_ONLY

+

tiling:arch35/batch_matmul_v3_matmul2mul_tiling.cpp(133 行);kernel:batch_mat_mul_v3_matmul2mul_cmct.h + common/cmct/kernel/kernel_batch_matmul_to_mul.h

+

① 目标 case

+

K==1 的 BMM:C[m,n] = A[m,0] × B[0,n],矩阵乘退化为列向量⊗行向量的外积,逐元素乘即可,K 维无累加深度

+

② 进入条件(IsCapable,cpp L67-115,逐条)

+ + + + + + + + + + + + + +
#条件(源码表达式)取值依据(源码注释原文 / 分析)
1输入 0/1 为"非连续转置" → 拒绝注释:"Non-contiguous transpose does not support Matmul2Mul." mul kernel 按连续内存寻址,不支持 view 转置的 stride 访问
2aType != bType || aType != cType → 拒绝注释:"Inconsistent a/b/c data types are not supported" 向量路径没有类型提升逻辑,三端必须同型
3NZ 格式 → 拒绝同分支 0,纯 vector kernel 只吃 ND
4A/B 四级 batch 任一不等 → 拒绝mul 是逐 batch 对应元素相乘,不支持 broadcast(广播 case 留给通用分支的 % 映射)
5batchC < 128 → 拒绝注释原文:"batch数大于等于128(AIV CoreNum *2) 才能开pingpong"128 = aivNum(64) × 2:每核至少分到 2 个 batch,才能一份在算、一份在搬,构成 UB pingpong 流水
6hasBias → 拒绝bias 加法未在 mul kernel 实现
7kValue != 1 → 拒绝触发条件:K==1
8n > 32B/dtype && n <= 256B/dtype → 拒绝注释原文:"N>256/DtypeSize才能用满Vector的计算能力,小N走特殊优化"。Vector 一拍处理 256B(054篇):N>256B/dtype 时一次 Mul 指令打满吞吐;N≤32B/dtype(不足一个 32B block)走 DoOpTiling 的小 N 特殊优化公式;中间区 (32B, 256B]/dtype 两头不占,让给其它分支
9n == 1 → 拒绝注释原文:"N=1时部分case劣化"——实测劣化的经验排除项
10(alignM + alignN + alignM×alignN)×dtype > ubSize → 拒绝UB 容量校验:单 batch 要同时驻留 A 向量(alignM 元素)、B 行(alignN)、C 外积结果(alignM×alignN),三者放不下 UB(512KB)则该分支不可行。alignNum_=32B/dtype 是 vector 一次访存的最小对齐粒度
11n % (256B/dtype) == 0 → 拒绝N 恰为 256B/dtype 整数倍属规整形状,存在更优的规整路径,本策略主动让出(防重复覆盖)
+

③ tiling 实现步骤(DoOpTiling,cpp L29-65)

+
    +
  1. 对齐准备:alignM = CeilAlign(m, 32B/dtype)alignN = CeilAlign(n, 32B/dtype)usedCoreNum = aivNum
  2. +
  3. singleBatchSize = (m + alignN + m×alignN) × dtypeSize —— 单 batch 占 UB 字节数(A + B + C 三段);小 N 特殊优化:若 alignN ≤ 32B/dtype(不足一条向量指令),改用 (alignM + alignN + alignM×alignN) × dtypeSize,A/C 也按对齐块整块处理;
  4. +
  5. singleCoreBatch = CeilDiv(batchC, aivNum) —— 每核应分 batch 数(负载均衡基准);
  6. +
  7. ubLimitBatchNum = ubSize / singleBatchSize —— UB 容量允许的单轮驻留 batch 数;
  8. +
  9. batchNum = min(singleCoreBatch, ubLimitBatchNum) —— 每核每轮实际处理 batch 数 = 负载均分与 UB 容量的较小者。这个 min 是本分支的核心:batchNum 决定 pingpong 的深度;
  10. +
  11. 尾轮处理三连:batchNumLastRound = singleCoreBatch % batchNum(整除时为 batchNum)lastCoreNum = batchC % (batchNum×aivNum) / batchNumLastRound(最后一轮满载核数);batchNumLastRoundTail(最后一个不满载核的 batch 数,恰整除时归并为满载核)——把"总 batch 不能整除 batchNum×核数"的余数精确拆成若干满核 + 一个尾核,kernel 直接按这三参数索引,无需运行时再做除法。
  12. +
+

④ 为什么 K=1 必须离开 Cube?

+

Cube 阵列一拍完成 16×16×16(fp16)的 fractal 乘加(054篇)。K=1 时,每次 mmad 的 K 维只有 1/16 被利用——阵列 15/16 的 MAC 空转,还要为此支付 GM→L1→L0A/L0B 的分形搬运、L0C 累加与 fixpipe 写出全链路开销,计算访存比极差。而 AIV 每拍处理 256B 连续数据,外积逐元素乘 + UB 多 batch pingpong 能把 MTE 带宽吃满;64 个 AIV 的聚合向量算力(FP16 54 TFLOPS)对这个计算密度绰绰有余。这就是为什么触发条件里 batchC≥128(开 pingpong)与 N>256B/dtype(打满向量指令)都以"带宽/指令利用率"为判据——该分支的收益模型是纯带宽模型,不是算力模型。

+

⑤ kernel 实现

+

KernelBatchMatMulToMul(Cmct 框架,ProblemShape 的 k 直接写死 1):if ASCEND_IS_AIC { return; };AIV 按 BlockSchedulerBatchMatMulToMulBuiltIn 给出的 usedCoreNum/singleCoreBatch/batchNum/lastCoreNum 做 batch 组轮询(tileIdx += blockNum),每组由 BlockMmadBuilder<..., BatchMatmulToMul<>> 生成的向量乘 block 处理。通路:GM→UB→Mul→GM,完全绕过 L1/L0/Cube(图 5-2 通路 B)。tilingKey:BASIC_LEVEL + BatchModel=BATCH_MATMUL_TO_MUL(AIV_ONLY)。

+
+
+

5.3 STREAM_K(优先级 2)—— 超大 K + MN 并行度不足时切 K MIX_AIC_1_2

+

tiling:arch35/batch_matmul_v3_basic_streamk_tiling.cpp(165 行);kernel:mat_mul_v3/arch35/mat_mul_streamk.h(MatMulStreamKKernel / MatMulStreamKActKernel)

+

① 目标 case

+

K 极大而 batch×M×N 基本块总数填不满 AIC 的场景(典型:小 batch、小 M/N、超长 K)。此时若只沿 batch/M/N 切分,32 个 AIC 大半空转,唯一剩余的并行维度是 K。

+

② 进入条件(IsCapable cpp L70-98 + CheckStreamKSKTiling cpp L37-68,逐条)

+ + + + + + + + + + + +
#条件(源码表达式)取值依据(注释原文 / 分析)
1GetDeterministicLevel() > 1 → 拒绝注释原文:"batch一致性控制,当开关等级为2或3时,拒绝切k模板,达到强一致性和batch一致性"。切 K 后多核部分和的归约顺序不定,引入浮点累加非确定性;确定性等级 ≥2 的业务(如训练梯度对比)禁用
2A/B 四级 batch 不等 → 拒绝kernel 按等 batch 均摊核数(blocksPerBatch = aicNum/batchC),广播 batch 无法均摊
3aFormat != FORMAT_ND → 拒绝注释:"ND is the only supported format for tensor_a in basic api"
4batchBias > 1 → 拒绝bias 多 batch 未支持
5非连续转置 → 拒绝注释:"Non-contiguous transpose does not support StreamK."
6aivNum != aicNum × 2 → 拒绝注释:"streamk only support aivNum == aicNum * 2"。归约/fixpipe 后处理依赖 1 AIC : 2 AIV 的固定配比(351x 硬件特征,054篇);非此配比平台直接拒绝
7fp32 非 hf32 且 k > 2,000,000 → 拒绝注释原文:"如果dtype是fp32且k轴大于200万 则走基础模板来保证fp32的精度""Due to the requirement of binary accumulation..."。fp32 超长 K 需要二叉树累加保精度,StreamK 的串行归约不满足;200 万是精度劣化可接受的经验上限(源码经验值,文档无依据)
8CeilAlign(k,256) < max(8192, aicNum×256B/dtype) → 拒绝K 下界双保险:aicNum×256B/dtype 保证"即使全部 AIC 都参与切 K,每核也至少分到 256B(一个 K 内轴搬运块 BASIC_BLOCK_K_256_BYTE)",否则单核 K 段太短、搬运启动开销盖过收益;8192 是绝对收益门槛(元素数)。外层 CeilAlign(k,256) 先把 K 对齐到基本块再比较
9batchC × CeilDiv(m,alignV) × CeilDiv(n,alignV) > aicNum/2 → 拒绝★ 核心判据。alignV=256(fp32 非 hf32 时 32,注释"如果是Fp32 基本块判断要用32")。注释:"判断bmn是否需要已经能切16份及以上"。语义:若 batch×M×N 已能切出超过"核数一半"的块,MN 方向并行度已够(留一半核作均衡余量),无需切 K;反之才进 StreamK。此条件同时保证 DoOpTiling 中 mnCnt ≤ blocksPerBatch/2,使 kCnt ≥ 2(见步骤 4/7 推导)
+

③ tiling 实现步骤(DoOpTiling,cpp L100-138)

+
    +
  1. ResetBase(DAV_3510 特化:baseM=baseN=256,baseK=128B/dtype,"256 is better base");
  2. +
  3. mCnt = CeilDiv(m, baseM)nCnt = CeilDiv(n, baseN)
  4. +
  5. blocksPerBatch = aicNum / batchC —— 每个 batch 平均分到的 AIC 核数;
  6. +
  7. mCnt/nCnt 收拢if (mCnt > blocksPerBatch/3 && mCnt < blocksPerBatch/2) mCnt = blocksPerBatch/2(nCnt 同理)。推导:由条件 9 已知 mnCnt ≤ bpb/2;若 mCnt 落在 (bpb/3, bpb/2),则 nCnt ≤ (bpb/2)/mCnt < 1.5 即 nCnt 必为 1;收拢后 mnCnt = bpb/2,下一步 kCnt = bpb/mnCnt = 2,恰好把该 batch 的核预算在 M 向和 K 向各分一半,得到规则的 2 路切 K;不收拢则产生零散尾块、负载不均。这是"块数离散化取整到规则分点"的手法;
  8. +
  9. 反推实际基本块:baseM = CeilAlign(CeilDiv(m, mCnt), 16),baseN 同理(16 = cube fractal 粒度,054篇);
  10. +
  11. tailInfo.kCnt = blocksPerBatch / mnCnt —— kCnt 推导:每 batch 的核预算 ÷ MN 已占块数 = 每个输出块还能再切几份 K;由 mnCnt ≤ bpb/2 得 kCnt ≥ 2(至少 2 路切分);
  12. +
  13. singleCoreK = CeilDiv(k, kCnt) —— 每核承担的 K 长度;
  14. +
  15. baseK = min(singleCoreK, FloorAlign(L0A半区/dtype/max(baseM,baseN), alignV)) —— K 基本块取"均分 K"与"L0A 容量上限"较小者。L0A 半区 = 64KB/2(DB 两份,067篇);K 为内轴时(A 不转置或 B 转置)按 128B 对齐(056篇:内轴 128B/256B/512B 对齐发挥带宽),否则按 16 对齐;
  16. +
  17. CalL1Tiling 定 stepK/depthA1/depthB1;
  18. +
  19. 深度再平衡if (baseM==baseN && depthB1==2×depthA1) { depthA1×=2; depthB1÷=2; }(注释"depthb1 is less than deptha1")——M、N 块相等时 A/B 应对称,若 B 的 L1 深度恰为 A 两倍则互换,使 depthA1 ≥ depthB1;
  20. +
  21. apiLevel:仅原生 BatchMatMulV3 节点且不回避 tensor API 时 TENSOR_LEVEL,否则 BASIC_LEVEL(注释:"DAV_RESV及CV自动融合当前只支持基础API")。
  22. +
+

④ workspace 与 L0C2Out 的设计

+

GetWorkspaceSize() = aicNum × 256×256 × 4B + 20MB:前半是每个 AIC 一块 256×256 fp32 部分和缓冲(对应 baseM×baseN 上限的累加结果——各核的部分和先写 GM workspace,再由归约阶段累加);后半 20MB 是基类默认 RPC 区(注释 "20MB workspace for RPC",核间通信/同步标志位)。fixpipe 选择 GetL0C2OutFlag()n>64 且 n%16≠0 且 m>2 且 m×n≥256 → ND_FIXPIPE_1_2(N 不对齐且输出块足够大时,L0C→GM 直通会成为瓶颈,改走 1 AIC : 2 AIV 的 ND fixpipe 通路分担搬出)。

+

⑤ 为什么这么做:StreamK 方法学背景

+

StreamK 思想源于 GPU 端的 Stream-K 工作中心分解(Osama 等,arXiv:2301.03598,2023):传统 data-parallel GEMM 按输出块切核,当输出块数 < 核数时算力浪费;StreamK 把总工作量(输出块×K 迭代)按"工作量流"均分给所有核,核间通过部分和归约汇合。CANN 公开文档中没有 StreamK 的说明(知识库检索仅命中 catlass/ops-nn 源码),官方文档的切 K 建议是 EnableMultiCoreSplitK + GM 清零 + AtomicAdd(206篇,案例 K=1024 切 2 核后 19.60us→13.70us)。BMM 这里的实现与官方简易切 K 的差异在于:用 workspace 归约而非 AtomicAdd(fp32 部分和写 workspace 再规约,避免 GM 原子加的顺序随机性——这也解释了条件 1 的确定性等级管控),并用 aivNum==2×aicNum 的 MIX 模式让 AIV 承担归约/搬出。进入条件的两条 K 阈值(≥max(8192, aic×256B/dtype) 才切、fp32 ≤200 万)分别守住"切 K 收益下限"与"fp32 精度上限"。

+
+ + + StreamK:一个输出块的 K 维被切给 kCnt 个核 + + 输出块 C[m,n]K = singleCoreK × kCnt + + + + 核 i:K 段 0 部分和 + 核 i+1:K 段 1 部分和 + …核 i+kCnt−1 + + + + + GM workspace(fp32 部分和) + aicNum × 256×256 × 4B + 20MB RPC + + + 归约(AIV,1:2 配比)+ fixpipe + 确定性等级≥2 时此分支被禁用 + 进入前提:batchC×mCnt×nCnt ≤ aicNum/2(MN 并行度不足一半核数)且 K ≥ max(8192, aicNum×256B/dtype)(每核 K 段足够长) + kCnt = blocksPerBatch / (mCnt×nCnt),由判据保证 kCnt ≥ 2;fp32 且 K>200 万时因 binary accumulation 精度要求回退基础模板 + +
图 5-3 StreamK 数据流:K 向切分 → 各核部分和写 workspace → AIV 归约写出
+
+
+ +
+

5.4 MERGE_BATCH_BASICAPI(优先级 3)—— 小 M/N 大 batch 合并进 L0 AIC_ONLY

+

tiling:arch35/batch_matmul_v3_mergebatch_basicapi_tiling.cpp(145 行);kernel:batch_mat_mul_v3_mergebatch_basicapi_cmct.h + common/cmct/kernel/kernel_matmul_merge_batch.h

+

① 目标 case

+

M、N 很小(要求 M≤N)、K≥64、batch 巨大的 BMM(如小维度投影层的海量 batch 推理)。单 batch 的基本块太小喂不饱 Cube,把多个 batch 在 M/N 方向"拼接"成大块一次算。

+

② 进入条件(IsCapable,cpp L28-77,逐条)

+ + + + + + + + + +
#条件(源码表达式)取值依据(注释原文 / 分析)
1非连续转置 / NZ → 拒绝合并 batch 要求物理内存连续(注释:"Non-contiguous transpose does not support MergeBatch."
2hasBias || (fp32 且非 hf32) → 拒绝bias 未支持;fp32 不转 hf32 时 L0C 内 fp32 累加块容量减半、合并收益差,直接排除
3A/B 四级 batch 不等 → 拒绝合并要求 A/B batch 完全一致(无广播)
4batchC < 4 × aicNum → 拒绝常量 MIN_BATCH_L0=4(.h L46),注释原文:"each aic should process at least 4 batchs"为什么 4:合并的最小粒度是 4 个 batch(合并 4 个才有"L0 一块装下"的收益),且每个 AIC 至少要分到一份最小合并块 → 总 batch ≥ 4×aicNum
5alignK < 64 || m > n → 拒绝注释:"shape check, aligned k shape should be at least 64"。K≥64(fp16 下 128B)是一次 K 内轴搬运的最低收益门槛;m≤n 因为合并沿 N 方向展开(tempAlignN=4×alignN),N 是较大维才有合并收益
6预计算 tempAlignM / tempAlignNtempAlignM = CeilAlign(4m, 16)但 isATrans 且 m>1 时 tempAlignM = 4×CeilAlign(m,16)——A 不转置时 4 个 batch 的 M 行内存天然连续,先拼成 4m 再整体对齐(损耗 ≤15 元素);A 转置时每个 batch 的 fractal 独立、M 维须各自对齐 16 再拼接(损耗 ≤4×15),容量校验更保守。tempAlignN = 4×alignN(N 向总是逐 batch 对齐后堆叠)
7L0 三容量校验(最小合并粒度 4 下):tempAlignM×minBaseK×dtype×2 > l0ASizetempAlignN×minBaseK×dtype×2 > l0BSizetempAlignM×tempAlignN×4B×2 > l0CSize → 拒绝注释:"l0 buffer check"。L0A 须装下"4 合并 M × 最小 K"的双缓冲;L0B 对称;L0C 须装下"4M×4N"的 fp32 累加双缓冲(64KB/64KB/256KB,950 白皮书表4-2)。连最小合并(4 batch)都放不下,合并就无可行性。minBaseK:K 参与 fractal 对齐(A 转置或 B 不转置)时取 16 倍数,否则取 c0Size=32B/dtype
+

③ tiling 实现步骤(DoOpTiling,cpp L79-113)

+
    +
  1. batchNumPerCore = CeilDiv(batchC, aicNum)
  2. +
  3. 由 L0B 反推 baseK 上限:maxBaseK = l0BSize / 4 / alignN / dtype / 2(L0B 要同时容纳 4 个 batch 的 B 块、双缓冲),向下 16 对齐、保底 16;
  4. +
  5. baseK = min(maxBaseK, 64) —— 注释:"threshold of basek is 64"为什么 64:mergebatch 的收益来自 batch 合并而非 K 深度;baseK=64(fp16 下 128B)已满足 K 内轴最小搬运粒度(056篇),把 baseK 压小可腾出 L0B 容量给更多合并 batch(下一步 maxBatchL0 与 baseK 成反比)——64 是"搬运效率"与"合并度"折中的经验上限(源码经验值);
  6. +
  7. maxBatchL0 = max(l0BSize/baseK/dtype/2 / alignN, 1) —— 已定 baseK 下,L0B 双缓冲能容纳的 N 向总元素 ÷ 单 batch 对齐 N = L0B 容量允许的最大合并 batch 数
  8. +
  9. mergeBatchL0 = min(CalBatchL0WithPolynomial(l0cElem, m), maxBatchL0, batchNumPerCore) —— 最终合并数取三者最小:L0C 多项式解(容量最优)∩ L0B 上限 ∩ 每核实际 batch 数
  10. +
  11. 合并块:baseM = CeilAlign(mergeBatchL0×m, 16)(transA 时逐 batch 对齐再累加)、baseN = mergeBatchL0 × alignN
  12. +
  13. L1 步进(注释 "4 buffer for al1_db and bl1_db"):stepKaMax = min(CeilDiv(batchNumPerCore, mergeBatchL0), l1Size/4/(baseM×baseK×dtype))——L1 均分 4 份(A pingpong 2 份 + B pingpong 2 份),单份容量 l1Size/4 除以单步数据量得容量允许的最大步数,再与 batch 轮数、K 总步数取 min;
  14. +
  15. 对外写回 baseM = alignM, baseN = alignN(单 batch 对齐值)+ mergeBatchL0 字段 —— 注释:"M和N在L1和L0里不切分"切分全部发生在 batch 维,合并倍数由独立字段传给 kernel。
  16. +
+

④ CalBatchL0WithPolynomial:L0C 容量下最优合并数的多项式求根(cpp L115-124)

+

注释原文:"多项式求解最佳batchL0"。设合并 x 个 batch,合并后 L0C 输出块为 CeilAlign(x·m,16) × (x·alignN)(fp32)。L0C 单缓冲容量 S = l0CSize/2/4 元素,约束:

+
CeilAlign(x·m, 16) · x·alignN ≤ S
+令 a = m/16,取整余量 t = 15/16(向上取整的最大余量),则 CeilAlign(x·m,16) ≈ 16(ax + t)
+代入:16(ax + t) · x·alignN ≤ S  ⟹  ax² + tx ≤ S/(16·alignN)
+两边乘 a 构造完全平方:(ax)² + t(ax) ≤ aS/(16·alignN) ≜ p
+解正根:ax = √(p + t²/4) − t/2 ≜ y        // 与源码 L121 逐符一致
+x = floor( min( p/(ceil(y)·a), ceil(y)/a ) )   // L0C 容量约束 ∩ M 块数自洽,保底 1
+

为什么这个根是最优:y 是连续意义下"恰好装满 L0C 单缓冲"的合并块 M 向 16-块数上界——约束取等号时 L0C 利用率 100%:x 再大就溢出、再小则合并度不足(cube 有效算力占比随合并度单调上升)。取整时用 ceil(y) 作实际 M 块数 Y,再同时满足 L0C 容量(x ≤ p/(Y·a))与 M 块数自洽(x·a ≤ Y)两个约束取 min,floor 后保底 1,得到不超过 L0C 容量的最大合法整数合并数。

+

⑤ kernel 实现

+

KernelMatMulMergeBatch + BlockSchedulerMergeBatchBuiltIn:构造时做负载均衡——mainBatchNum = batchC/batchAL1mainBatchLoop = mainBatchNum/blockNum,余数均摊成"主块/主尾块/最终尾块"三种 tile(前几个核多算一个),GetIterBatchTuple = {batchAL1, batchBL1, batchL0, batchL0}(A/B 在 L1/L0 可载不同 batch 数)。支持 fixpipe 1V2 融合 add/mul epilogue(BlockEpilogueMergeBatch)。tilingKey:BASIC_LEVEL + BatchModel=MERGE_BATCH_MODEL;另有一个细节:transA = isATrans && m>1(注释 "trans_a=true && m==1 equals to trans_a=False",m=1 的转置是退化情形按非转置选 kernel)。

+
+ + + mergeBatchL0 个 batch 在 M/N 方向拼接,一次 mmad 等价连续算多个 batch + + + b0 + b1 + b2 + b3 + 单 batch M×N 很小 + + + + + + + L0A:合并后 baseM 块 + + + + + L0B:合并后 baseN 块(N 向堆叠) + + + Cube 一次计算 + fractal 利用率拉满 + + + L0C 256KB + mergeBatchL0 由多项式求根 + 恰好装满 L0C 单缓冲 + 前提:batchC ≥ 4×aicNum,K ≥ 64,M ≤ N + +
图 5-4 MERGE_BATCH:多 batch 合并进 L0 的基本块拼接
+
+
+
+

5.5 ITER_BATCH_BROADCAST_BASICAPI(优先级 4)—— 单边单轴 batch 广播 AIC_ONLY

+

tiling:arch35/batch_matmul_v3_iterbatch_broadcast_basicapi_tiling.cpp(333 行);kernel:batch_mat_mul_v3_iterbatch_broadcast.h(Blaze BatchMatMulIterBatchBroadcastKernel)

+

① 目标 case

+

A 或 B 的某一级 batch 维为 1 的单边广播场景(如一份共享权重对多 batch 激活)。广播语义下被广播算子的单 batch 数据可被对端多个 batch 复用——L1 只需驻留一份广播算子,其余空间全部用来流水加载非广播算子的多个 batch,MTE 搬运量按广播倍数减少

+

② 进入条件(IsCapable,cpp L232-261 + 子检查,逐条)

+ + + + + + + + + +
#条件取值依据(注释原文 / 分析)
1NZ → 拒绝;任一输入 stride 非连续 → 拒绝IsContiguousStride 自实现检查:收集 view 各维 (stride, size),跳过 stride=0(广播维)和 size=1 的维,按 stride 降序排序后验证期望 stride 逐维累乘——保证 L1 迭代搬运的地址连续
2batchBias > 1 → 拒绝bias 必须单 batch
3hasBroadcastAxis() 必须为真4 级 batch 逐一比对(cpp L92-124):A维==1 且 B维!=1 记 A 广播轴,反之记 B 广播轴。"单边单轴"= 广播轴只在 A 或 B 一侧恰好出现 1 次:双边广播("Dual-side broadcast is not supported.")、多轴广播("Multi-axis broadcast is not supported.")、无广播("No broadcast axis detected, should use ITER_BATCH.")均拒绝。为什么:只有"单边单轴"才能保证 L1 驻留一份广播数据后、迭代轴是单一连续维;双边/多轴广播的复用关系无法用单驻留+单迭代表达,复杂 case 让位给通用 ASW 的 % 广播映射
4CheckNonBroadcastAxisMatch()除广播轴外,A/B 其余 3 级 batch 维必须逐维相等(允许两侧都为 1)
5batchC ≤ aicNum → 拒绝日志:"batchC <= aicNum, no need for iterbatch." batch 不大于核数时 batch 维并行度不足,无 iterbatch 收益
6CheckL1IterBatch()(cpp L143-206)见下方③的 L1 容量推导
7CheckL0IterBatch()(cpp L208-230)见下方④
+

③ L1 容量推导与 iterBatchL1 的两种公式(CheckL1IterBatch)

+
    +
  1. 单 batch 字节数:sizeAOneBatch = alignM×alignK×dtypesizeBOneBatch = alignK×alignN×dtypesizeCOneBatch = alignM×alignN×4B(L0C 内 fp32);
  2. +
  3. L1 双缓冲门槛:(sizeA + sizeB + bias) × 2 ≤ l1Size(A、B、bias 各一份、开 DB 要放得下 512KB L1);
  4. +
  5. 单 batch 广播判定innerDimsProduct = 广播轴之后(更内层)所有 batch 维在广播侧的乘积broadcastSingleBatch = (innerDimsProduct == 1)——含义:广播轴之后的内层维在广播侧全是 1,即广播侧在整个迭代中只对应同一份单 batch 数据,可以永久驻留 L1,循环只在非广播侧上做;
  6. +
  7. iterBatchL1 两公式(l1Avail = l1Size/2,先扣 DB): +
      +
    • 广播侧单 batch 驻留(如 A 广播):iterBatchL1 = (l1Avail − sizeA − bias) / sizeB —— L1 半区先扣驻留的 A 单 batch 和 bias,余下全部装 B 的多个 batch
    • +
    • 成对驻留(广播轴内层仍有真实 batch 维):iterBatchL1 = (l1Avail − bias) / (sizeA + sizeB) —— A、B 必须按 batch 成对进入;
  8. +
  9. 下限与截断:iterBatchL1 ≥ 2(注释:"iterBatchL1 expected to be no less than 2"——广播收益的前提是至少 2 个 batch 复用驻留侧);再 min(iterBatchL1, CeilDiv(batchC, aicNum)) 不超过单核平均 batch 数;
  10. +
  11. 整除因子收缩:迭代轴长度 = 单batch广播时的非广播侧广播轴维度(如 A 广播则 B 的第 bcAxis 维),或成对驻留时的 innerDimsProduct;从 min(iterBatchL1, 迭代轴长) 向下找能整除迭代轴的最大因子——保证 L1 一次搬运的 batch 段在迭代轴上连续且整除,避免尾批破坏地址连续性(BMM 的 batch 段搬运是 NDDMA 多维整块搬,段边界不对齐迭代轴会产生碎地址)。
  12. +
+

④ L0 容量检查与 0.8 均衡率(CheckL0IterBatch)

+
iterBatchL0A = (l0ASize/2) / sizeAOneBatch;   // L0A 半区(DB)可驻留 batch 数,L0B/L0C 同理
+l0CanLoadBatch = min(L0A, L0B, L0C) ≥ 1                       // 标准情形:三级半区各放≥1 batch
+              || (L0A无DB ≥ 1 && L0B无DB ≥ 1 && L0C半区 > 1)   // 放宽:牺牲输入 DB,换 L0C 多攒 batch
+              // 源码注释:// try to reduce fixpipe instr
+              // —— L0C 多攒几个 batch 的结果再一次 fixpipe 搬出,摊薄 fixpipe 指令开销
+

0.8 均衡率(仅当 !l0CanLoadBatch 时执行):

+
avgIterBatch      = batchC / aicNum                                    // 每核理想平均 batch 数
+actualMaxIterBatch = CeilDiv(CeilDiv(batchC, iterBatchL1), aicNum) × iterBatchL1   // 最忙核实际负载
+balanceRate = avgIterBatch / actualMaxIterBatch < 0.8 → 拒绝
+

语义:batch 先按 iterBatchL1 分组、组再均分到核,最忙核要处理"ceil(组数/核数)×iterBatchL1"个 batch(尾组按满组放大)。若 L1 分组导致核间倾斜超过 20%,iterbatch 得不偿失,宁可落到通用分支。为什么只在 L0 载不了多 batch 时检查:L0 顺畅时 iterbatch 的收益足够覆盖一定的倾斜;L0 已打折时再叠加 20% 以上倾斜就不值了。0.8 为源码经验值(defaultBalanceOfBatch)。

+

⑤ DoOpTiling 步骤(cpp L265-325)

+
    +
  1. 写回 iterBatchL1iterBatchL0 = max(min(L0A, L0B, L0C, iterBatchL1), 1)(L0 侧迭代不超过 L1 侧);
  2. +
  3. 写广播轴 broadcastAxisA/B(无广播侧保持哨兵 4,kernel 据此识别方向);
  4. +
  5. 基本块决策:l0CanLoadBatch 时 baseM/N/K = alignM/N/K(L0 能整 batch 装,一个 batch 一砖,M/N/K 都不切);
  6. +
  7. 否则按 M/N/K 大小关系 4-case 收缩基本块(思想:选最小的对齐维固定为整块——保证该维单次装完、至少一个矩阵的一维不被切,再用对应 L0 半区字节数反推另两维): + + + + + + +
    case条件固定推导
    1M<N 且 M>K(K 最小)baseK=alignKbaseM=min(l0A/2/dtype/baseK, alignM);baseN=min(l0B/2/dtype/baseK, alignN)
    2M<N 且 M≤K(M 最小)baseM=alignMbaseK=min(l0A/2/dtype/baseM, alignK);baseN=min(l0B/2/dtype/baseK, alignN)
    3M≥N 且 N>K(K 最小)baseK=alignKbaseN=min(l0B/2/dtype/baseK, alignN);baseM=min(l0A/2/dtype/baseK, alignM)
    4M≥N 且 N≤K(N 最小)baseN=alignNbaseK=min(l0B/2/dtype/baseN, alignK);baseM=min(l0A/2/dtype/baseK, alignM)
  8. +
  9. bias 约束:baseN = min(baseN, btSize/2/4)(BT 表半区能容纳的 fp32 元素数);
  10. +
  11. L0C 约束:较大维收缩至 l0CSize/2/4B ÷ 另一维(保证 baseM×baseN 不超 L0C 半区);
  12. +
  13. 三维 FloorAlign(max(x,16), 16)(cube fractal 粒度保底)。
  14. +
+

⑥ kernel 实现

+

Blaze 栈 BatchMatMulIterBatchBroadcastKernel(GemmUniversal + BlockSchedulerIterBatchBroadcast + MatmulIterBatchBroadcast<A_BC,B_BC>),scheduler 参数携带 broadcastAxisA/B 与 A/B/C 各 4 维 batch;NZ 等不支持 Blaze 的场景回落通用 BatchMatMulAswKernel(其 CalcGMOffset 的 batchCiIndex % batchAi / % batchBi 天然支持广播映射,只是失去 L1 单份驻留的搬运优化)。tilingKey:TENSOR_LEVEL + ITER_BATCH_BROADCAST_A/B_MODEL + 固定 ON_THE_FLY。

+
+ +
+

5.6 ITER_BATCH_BASICAPI(优先级 5)—— 普通大 batch(基础 API)AIC_ONLY MIX_AIC_1_2

+

tiling:arch35/batch_matmul_v3_iterbatch_basicapi_tiling.cpp(241 行);kernel:batch_mat_mul_v3_iterbatch_basicapi_cmct.h + common/cmct/kernel/kernel_matmul_iterbatch.h

+

① 目标 case

+

A/B batch 完全相等、batchC > aicNum 的普通大 batch BMM——每核要顺序算多个 batch,让 L1/L0 一次驻留多个 batch 形成流水,摊薄每 batch 的搬运与启动开销。

+

② 进入条件(IsCapable,cpp L74-136)

+
    +
  • A 侧非连续转置拒绝(注意:B 侧的 3D 非连续转置在 DoOpTiling 有专门兼容逻辑,见步骤 3);NZ 拒绝;batchBias ≤ 1;四级 batch A/B 完全相等(广播已被分支 4 截胡);batchC > aicNum
  • +
  • L1 门槛:((alignM×alignK + alignK×alignN)×dtype + bias) × 2 ≤ l1Size(单 batch A+B+bias 开 DB 放得下)。对齐细节(源码注释):fp16 或 fp32 的 (m,k)/(n,k) 布局 M/N 按 16 对齐;fp32 的 (k,m)/(k,n) 布局按 8×2 对齐,"for frac combine in loadtol0a/l0b"(L0 装载时的分形合并要求);
  • +
  • l0CanLoadBatch = min(L0A半区/sizeA, L0B半区/sizeB, L0C半区/sizeC, L1半区/(sizeA+sizeB)) ≥ 1(带 bias 另需 (btSize/2)/(alignN×4) ≥ 1);不满足时执行与分支 4 相同的 0.8 均衡率检查。
  • +
+

③ DoOpTiling 步骤(cpp L140-233)与两个经验常数

+
    +
  1. 两个微架构经验常数(L142-143,注释原文): +
    constexpr uint64_t mmadCount = 8UL;            // cube count which will cause issuequene
    +constexpr uint64_t fullCopySize = 64 * 1024UL; // datasize moving once which can use full of bandwith
    +依据分析:056篇给出"MTE1 和 MMAD 指令队列深度为 32,队列满会阻塞其他指令下发、引起流水断流"——mmadCount=8 是"L1 一次驻留的 batch 数使连续 mmad 发射不撑爆 issue queue"的工程经验值(具体数字 8 文档无出处,属源码经验值);fullCopySize=64KB 是"单次搬运打满带宽"的粒度(168篇的官方经验值是 16KB,64KB 更保守,同为源码经验值)。
  2. +
  3. iterBatchL1 的 8 截断(L144-153):if (8 × (单batch A+B 字节数) > 64KB)iterBatchL1 = min(iterBatchL1, 8, CeilDiv(batchC, aicNum))、iterBatchL0 同步钳到 8。逻辑:若 8 个 batch 的搬运量已超 64KB,说明单 batch 足够大、8 次搬运即可打满带宽——再增加驻留 batch 数只增加 issue queue 压力而无带宽收益;反之(单 batch 很小,8 个也填不满 64KB)不钳制,让 L1 尽量多装以摊薄搬运启动开销;
  4. +
  5. B 侧 3D 非连续转置兼容(L155-174,注释 "特殊处理3D非连续场景"):非连续转置 view 下只有最内层 batch 维(倒数第 3 维 innerBatch)保证物理连续,L1 迭代只能沿 innerBatch 且必须整除它——从 min(innerBatch, iterBatchL1) 向下找 innerBatch 的最大因子作为新 iterBatchL1,并把 innerBatch 写进 runInfo 供 kernel 算地址(与分支 4 的整除因子逻辑同构);
  6. +
  7. 基本块:l0CanLoadBatch 时 baseM/N/K = 完整对齐 M/N/K;否则同分支 4 的 4-case 收缩(本分支 A 侧用 aDtypeSize、B 侧用 bDtypeSize,分开计算);
  8. +
  9. bias 约束 baseN ≤ btSize/2/4(注释 "baseN满足C2大小",C2 即 bias 的 BT 空间);L0C 约束较大维;三维 16 对齐;
  10. +
  11. fixpipe 优化开关 GetL0C2OutFlagDav3510(L31-39): +
    if (n×bDtype > 256B && n % (256B/bDtype) != 0 && aivNum == 2×aicNum)
    +    return ND_FIXPIPE_1_2;   else  return ON_THE_FLY;
    +三条件同时满足才启用:① N 单行字节 > 256B;② N 非 256B 对齐(直接 fixpipe 写 GM 会产生非对齐写,056篇:非对齐向上取整补零、效率损失);③ 1C:2V 配比(vector 侧有足够算力消化 fixpipe 输出)。ND_FIXPIPE_1_2 的语义是"1 个 AIC 配 2 个 AIV 做 ND 对齐 fixpipe 后处理"(AIC↔AIV 用 MODE_4 CrossCoreFlag 逐 AIV 握手,087篇/054篇);该枚举无任何 CANN 文档说明,语义以源码为准
  12. +
+

④ kernel 实现

+

Cmct 四层组装 KernelMatMulIterBatch<ProblemShape, BlockMmad, BlockEpilogue, BlockScheduler>BlockSchedulerIterBatchBuiltIn 的调度粒度是"一组 batch"——GetTileNum = ceil(b/iterBatchL1)GetBlockCoord = {0,0,0, tileIdx×iterBatchL1}GetIterBatchTuple = {iterBatchL1, iterBatchL0, 0, 0}(L1 一次装 iterBatchL1 个 batch、L0 一次装 iterBatchL0 个);核映射为 batch 组轮询。ND_FIXPIPE_1_2 变体(MIX_AIC_1_2)经 BlockEpilogueIterbatch 由 2 个 AIV 配合 1 个 AIC 做后处理(支持 add/mul/relu 融合)。tilingKey:BASIC_LEVEL + SINGLE_BIAS_MODEL + l0C2Out 动态位。

+
+ +
+

5.7 ITER_BATCH(优先级 6)—— 大 batch 高阶 API 版 AIC_ONLY

+

tiling:arch35/batch_matmul_v3_iterbatch_tiling.cpp(143 行);kernel:arch35/batch_mat_mul_v3_iterbatch_kernel_advanced.h(BatchMatMulMultiBatchKernel)

+

① 目标 case 与分支 5 的关系

+

目标 case 与分支 5 相同(大 batch、batch 相等),区别在于搬移抽象层:分支 5 用基础 API/Cmct(host 手工管理 L1/L0 双缓冲),本分支用高阶 MatmulImpl API 的 IterateBatch(batch 维打进 MM 流水线,DB 由 API 内部管理)。官方文档依据(141篇):"调用一次 IterateBatch,可以计算出多个 singleCoreM × singleCoreN 大小的 C 矩阵""当单次 Matmul 输入 shape 较小时,搬运开销占比大,IterateBatch 批量处理可有效提升带宽利用率"

+

② 进入条件的关键差异(IsCapable,cpp L71-126)

+
    +
  • A、B 任一侧非连续转置均拒绝(高阶版没有分支 5 的 3D 兼容逻辑,日志 "Non-contiguous transpose does not support high-level IterBatch.");
  • +
  • iterBatch = l1Size / inputSizeOneBatch > 1不除 DB_SIZE),再与每核 batch 数取 min。 +为什么不除 2(与分支 4/5 的 l1Size/2 对比):batch 维的双缓冲由高阶 API 内部实现(API 在 L1 内对 batch 段做 ping-pong),host 侧只需给出"理论可装多少 batch";DoOpTiling 里两处注释互证——"L1 mm fullLoad, batch 2 DB"(L1 深度 depthA1=stepKa×stepM 不含 batch 因子,batch 的 DB 独立处理)和 "need align to 2 for db in api"(iterBatch 向下对齐 2,保证 API 能拆两份做 DB)。而 basicapi 版 kernel 手工管理 L1 双缓冲,所以 host 必须先除 2 预留;
  • +
  • L0 载不了多 batch 时同样 0.8 均衡率检查(此处 min 不含 L1——L1 可装性已由 iterBatch>1 保证)。
  • +
+

③ DoOpTiling 步骤(cpp L27-69)

+
    +
  1. ResetBase + CalL1Tiling 出初始 baseM/baseN;singleCoreM/N/K = 完整 m/n/k(单核内 M/N 不切分,并行维度只有 batch);
  2. +
  3. 多 batch 使能判定:if (baseM ≥ m && baseN ≥ n) { baseM/N = 对齐整块; isEnableMultiBatch = true; }——L1 基本块能完整覆盖 MN 时才允许多 batch 驻留 L0C;
  4. +
  5. baseK = min(l0A/2/baseM/dtype, l0B/2/baseN/dtype) 向下 16 对齐;
  6. +
  7. singleCoreK 减半条件(L43-45): +
    singleCoreK = (baseK < singleCoreK || iterBatch ≤ 4) ? singleCoreK / 2 : singleCoreK;
    +// 注释:4 avoid issueque
    +两个触发条件:① L0 装不下整条 K(baseK<singleCoreK),K 本就要分多次,先把单核 K 减半配合 L1 DB 流水;② iterBatch ≤ 4:batch 迭代次数太少时,cube 在 batch 循环间的指令发射不足以填满 issue queue,减半 K 让 K 循环与 batch 循环重叠、保持 issue queue 有指令可发。阈值 4 与分支 5 的 mmadCount=8 同源(8÷DB2=4 的直觉),均为源码经验值(056篇仅有队列深度 32 的机制描述);
  8. +
  9. step/depth:stepKa = stepKb = CeilDiv(singleCoreK, baseK)depthA1 = stepKa×stepM(注释 "L1 mm fullLoad, batch 2 DB");
  10. +
  11. iterBatch = FloorAlign(iterBatch, 2)(API 内部 DB 要求偶数);
  12. +
  13. batchOutNum(L0C 一次驻留的输出 batch 数): +
    batchOutNum = isEnableMultiBatch ? min( l0CSize / (baseM×baseN×dbL0C×4B), iterBatch ) : 1;
    +if (batchOutNum == iterBatch) batchOutNum = iterBatch >> 1;   // 恰好相等时再减半
    +为什么相等要减半:若 L0C 刚好能把本轮全部 batch 存满,就没有余量做"一半写出、一半继续算"的乒乓;减半后一半容量在计算、一半容量在 fixpipe 搬出,实现输出与计算的流水重叠。这里直接吃到 950 L0C 增大到 256KB 的红利(白皮书 §4.1.1 明言"更大的 L0C Buffer 提供更灵活的 Tiling 策略"——256KB 让更多 batch 的输出能驻留 L0C)。
  14. +
+

④ kernel 实现

+

BatchMatMulMultiBatchKernel + BatchMatMulMultiBatchBaseBlock:主循环 LoopTimes = ceil(batchC / (iterBatch×useCoreNum)),每核每轮处理 iterBatch 个 batch;核心是一次 mm_.IterateBatch(cGlobal, 0,0,0, singleASize, singleBSize) 让 MatmulImpl 内部完成多个 batch 的连续 mmad——batch 维打进 MM 流水线,L0C 按 batchOutNum 攒多 batch 再一次写出(SetNBatchOutNum,该接口 CANN 9.0/9.2 文档均未收录,属未文档化高阶接口,语义以源码为准),相对普通分支"一块一 Iterate/GetTensorC"显著减少指令发射与同步开销。GetMultiBatchInfo(loopIndex) 处理尾轮均衡(前几个核多算一个 batch)。tilingKey 只设 trans + SINGLE_BIAS_MODEL(apiLevel 由 key 默认值决定,落 HIGH_LEVEL)。

+
+
+

5.8 AL1 / BL1_FULL_LOAD_BASIC(优先级 7/8)—— 单边无 batch 的 L1 全载 AIC_ONLY

+

tiling:arch35/batch_matmul_v3_asw_al1_full_load_basic_tiling.cpp / ..._bl1_...;公共实现:mat_mul_v3/arch35/matmul_v3_basic_aswt_tiling.cpp(DoAL1FullLoad/DoBL1FullLoad);kernel:arch35/batch_mat_mul_v3_asw_{al1,bl1}_full_load_kernel_advanced.h

+

① 目标 case

+

A 无 batch(batchA=1)且 M≤256、B 有 batch 且总量大(AL1);镜像地 B 无 batch 且 N≤256(BL1)。典型:小 M 激活 / 小 N 权重(MoE 投影层)被大 batch 对端乘。"全载(full load)"的含义:小的一侧矩阵完整常驻 L1,整个 kernel 只从 GM 搬一次,之后所有 batch、所有 N(或 M)块的计算都从 L1 复用,彻底消除该侧的重复 MTE 搬运。

+

② 进入条件(IsCapable,AL1 为例,cpp L31-72,逐条)

+ + + + + + + + +
#条件取值依据(注释原文 / 分析)
1非连续转置 → 拒绝全载要求 GM 一次性 Nd2Nz 大整块搬入,view 转置 stride 不支持
2dtype 限 fp16/bf16(A/B),C 可 fp16/bf16/fp32;非支持类型且 B 为 NZ → 报错基础 API 全载模板的类型约束(fp32 输入只接受 ND)
3batchA > 1 → 拒绝注释:"matrix A should not have batch when AL1FullLoad"——A 有 batch 时每核要驻留多份 A,"全载"语义不成立。注意:batchB==1 且 A 不转置的情形已在更早的 MergeBatchAndMAxis() 被折叠成 MM(见 §4),走到这里的是 B 有 batch 的真 BMM
4m > 256 → 拒绝(源码注释 "m should be larger than 256" 与实际逻辑相反,系注释笔误,实际要求 m≤256)。为什么 256:m≤256 时 A 的 M 向恰好一个 baseM(256) 覆盖,stepM=1,单核 M 向无循环——A 全载后每核只需加载一次 A 且各核间 A 完全共享,全载收益最大
5★ "值得全载"门槛:alignMatBSize < l1Size×aicNum && batchB×CeilDiv(n,256) < 4×aicNum → 拒绝注释:"each core needs to loop at least 4 batch for MatB""总数据量大于2轮 batch大于4轮"。两个子条件:
① B 总量 < 全核 L1 之和:B 小到可摊到所有核各存一份、整体只搬一遍,A 全载没有搬运量优势;
② 每核循环轮数 < 4:A 全载的收益是"A 加载一次、B 流水多轮复用",每核 <4 轮则复用收益抵不过 A 驻留占用的 L1 空间(B 的 stepK/buffer 会被压小)。
取反语义:拒绝条件是"①且②同时成立",即进入全载 ⟺ B 足够大(必然被反复搬运)或 轮数足够多(每核≥4 轮),二者满足其一即值得
6alignMatASize × 2 > l1Size → 拒绝全载可行性:A 全量 16 对齐数据 ×2(L1 上 A 区开 ping-pong 双 buffer 保持流水)≤ 512KB L1,否则物理装不下
+

③ DoAL1FullLoad() 完整步骤(matmul_v3_basic_aswt_tiling.cpp L138-210)

+
    +
  1. 重置全载负载均衡(tailSplitCnt=1 等,注释 "全载模板需重置负载均衡计算");
  2. +
  3. remainL1Size = l1Size − (aL1Size + biasSize) —— A 全载驻留后剩余的 L1;
  4. +
  5. maxBaseN 由两个上限卡出maxBaseNWithL1 = remainL1 / (baseK×dtype×2)(剩余 L1 给 B 开 DB,每份 baseN×baseK)与 maxBaseNWithL0cDb = l0CSize / (baseM×4B×2)(L0C 双缓冲下 fp32 输出块须装得下),取小并 16 对齐;
  6. +
  7. balanceBaseN = maxBaseN(BMM 特有):单 MatMul 下 balanceBaseN = CeilDiv(n, aicNum)(把 N 均分给各核保均衡);BMM 下负载均衡已由 batch 维提供(总任务 = batch×mCore×nCore,并行度充足),无需再均分 N,直接取硬件允许的最大 baseN 最大化单块计算/搬运效率;
  8. +
  9. baseN = min(原baseN, maxBaseN, balanceBaseN);N 为内轴且 baseN>128B/dtype 时向下 128B 对齐(注释 "N内轴时满足128B对齐",056篇内轴对齐粒度);
  10. +
  11. stepK 上限maxStepK = min(CeilDiv(k,baseK), remainL1/(baseK×baseN×2×dtype), 4)——注释 "stepK最大不超过4":限制 L1 上 B 的流水级数,级数过多收益递减且挤压 buffer(4 为源码经验值);
  12. +
  13. baseK 翻倍重试(L169-178):当 B 为 K 内轴(isBTrans)且 stepK 被 buffer 上限卡住、且 maxStepK×baseK 不满足 256B 对齐、且 baseK 翻倍后 L0A 装得下((baseK×2)×baseM×dtype×2 ≤ l0ASize)时:baseK <<= 1 重算。注释:"B矩阵K为内轴,k_bl1不满足256B对齐,尝试baseK放大一倍,提升B矩阵搬运效率"——K 内轴 256B 对齐才能发挥搬运带宽(056篇/950白皮书 §4.3.2 L2 128B Sector);
  14. +
  15. stepK 枚举(1→maxStepK),取第一个同时满足的:baseN×baseK×stepK×dtype ≥ 48KB(L1_SINGLE_SIZE_LIMIT,注释 "L1搬运量约束"——单次 L1 搬运量阈值;注意:168篇的官方经验值是"单次搬运 16KB 以上发挥带宽最佳",48KB 为源码更保守的经验值)且 K 内轴时 kL1 % (256B/dtype) == 0(注释 "K内轴时约束kL1 256B对齐,发挥带宽能力");都不满足则放大到 maxStepK 兜底;
  16. +
  17. depthdepthA1 = stepM × stepKa(A 全载:深度 = 整个 A 的 M×K 块数,即 A 的所有块都在 L1 有槽位)、depthB1 = stepKb × 2(B 流水 DB);
  18. +
  19. singleCoreM = m(A 全载单核 M 向全覆盖)、singleCoreN = baseN
  20. +
  21. l1BufferNum = 4 或 2bL1TensorSize×4 + aL1TensorSize ≤ l1Size 则 B 区开 4 buffer(注释 "l1开2db后依然只使用了一半的空间,则开启4 db。该字段仅在基础api场景生效")。4 buffer 依据:文档无"4 buffer"直接依据(高阶 API 框架 db 只取 1/2;类似思想见 208篇 NBuffer33 模板的 3 份错峰:MTE2 Bound 时错开搬运流水、减少单次搬运量);
  22. +
  23. dbL0C = baseM×baseN×4B×2 ≤ l0CSize ? 2 : 1
  24. +
  25. usedCoreNum = min(CeilDiv(n, baseN) × batchC, aicNum) —— 并行任务 = N 向条带数 × batchC,batch 维提供主要并行度;
  26. +
  27. CalcTailBasicBlockAL1Full 尾块切分(L105-120):一轮任务 = nCnt 个 N 条带,最后一轮只有 tailCnt = nCnt % aicNum 个核有活;把每个尾块沿 N 向再切成 tailInfo.nCnt 份,使尾轮工作核数 tailCnt × nCnt 逼近 aicNum(条件 (nCnt+1)×tailCnt ≤ aicNum);若 N 是 B 内轴还要求切分后每份内轴字节 > 128B(避免内轴过短搬运效率崩塌)。
  28. +
+

④ 为什么全载能省搬运:官方定量例证(192篇)

+

192篇(L1 长驻优化)给出精确核算:L1=512K,左矩阵 992K、右矩阵 16K(fp16),无法同时全载。反例(不长驻):A 分 2 块、B 分 2 块,加载顺序"A1→B1→B2→A2→B1→B2",总搬运 6 次(A×2 + B×4,B 被每个 A 块周期重复搬);正例(16K 的 B 全载长驻):循环内只搬 A1、A2,共 3 次(B×1 + A×2)。BMM 把这一收益再放大 batch 倍——被全载侧的复用次数 ×= batchB,这就是为什么条件 5 用"B 总量 vs 全核 L1 之和"和"每核循环 ≥4 轮"两个尺度衡量复用收益。对带宽相对弱的 950PR(1.6TB/s,DT 为 4TB/s),消除重复搬运的价值更高。

+

⑤ kernel 实现

+

BatchMatMulAswAL1FullLoadKernel:关键类型重定义 A_TYPE_NEW = MatmulL1GmType<TPosition::TSCM, ...>——告诉 MatmulImpl 该操作数源在 L1(TSCM) 而非 GM。Process 仅两步:① AswAL1FullLoadKernelCopyInA1() 一次性 Nd2Nz DataCopy 把本核 singleCoreM×Ka 的 A 切片搬入 L1(TQue 深度 1、无 DB——只搬一次无需双缓冲);② 主循环与 ASW 相同(复用同一套滑窗 swizzle),但 mm_.SetTensorA(al1Local, ...) 直接给 L1 地址,并 SetOrgShape(singleCoreM, N, Ka)。与普通 ASW 的本质差异:A 的 GM→L1 搬移次数从"每个基本块一次"降为"整个 kernel 一次"。BL1 完全镜像(B 全载、CalCopyBL1 Nd2Nz/Nz2Nz 区分 GM 格式)。tilingKey:Model=BASIC + FullLoad=A_FULL_LOAD / B_FULL_LOAD

+
+ + + AL1 全载:A 一次性搬入 L1 常驻,B 按块流水;A 的 GM 搬运次数 = 1 + + GM:A(小)batchA=1,M≤256 + + GM:B(大)batchB 大、N 大 + + L1 512KB + + A 全载区(×2 DB) + depthA1 = stepM×stepKa(整个 A) + + B 流水区(2/4 buffer) + stepKb ≤ 4,单次 ≥48KB,K内轴256B对齐 + + 仅 1 次 + + 逐块流水(N 向 × batchB 轮) + + L0A/L0B 64KB + 从 L1 重载(非 GM) + + + Cube + mmad 流水 + + + L0C 256KB + maxBaseN 受 + l0C/(baseM×4×2) 约束 + + usedCoreNum = min(N向条带数 × batchC, aicNum);尾轮 N 条带沿 N 再切分逼近满载(CalcTailBasicBlockAL1Full) + +
图 5-5 AL1 全载数据通路(BL1 镜像:B 常驻、A 流水)
+
+
+ +
+

5.9 ASW_BASIC(优先级 9)与 BASE(999)—— 通用路径:cubeBound 寻优 + 自适应滑窗 AIC_ONLY

+

tiling:arch35/batch_matmul_v3_asw_basic_tiling.cpp + batch_matmul_v3_asw_tiling.cpp;公共:mat_mul_v3/arch35/matmul_v3_tiling_helper.cpp(ResetBase/GetRebalanceBlock/CalL1TilingDefault/GetAswWindowLen);kernel:batch_mat_mul_v3_asw_{kernel,block}_advanced.h

+

① 目标 case

+

前面 9 个特判都不命中的通用 case:batch 量适中、M/N 够大、无广播或复杂广播。ASW_BASIC 是带约束的通用路径(基础/张量 API + cubeBound 模型寻优),BASE(999) 是无条件兜底(高阶 API 默认参数)。ASW = Adaptive Sliding Window(自适应滑窗),命名佐证见 ops-nn 仓 dual_level_quant_batch_matmul 的 DualLevelQuantBatchMatmulTilingASW(adaptive_sliding_window);滑窗指 kernel 侧块到核映射的"滑窗+蛇形"swizzle(详见第 6 章)。

+

② ASW_BASIC 进入条件(cpp L31-57)

+
    +
  • A、B 的非连续转置状态必须一致(都连续或都非连续;混合则拒绝——注释 "ASW Basic only supports AB non-contiguous transpose",混合场景只留给 BASE 的高阶 API);
  • +
  • batch 完全相等(不等即广播——广播由 BASE 的 BROADCAST_BATCH_MODEL 或分支 4 承接);batchBias ≤ 1;dtype 同分支 7。
  • +
+

③ DoOpTiling 三步走:ResetBase → GetRebalanceBlock → CalL1Tiling

+

第 1 步 ResetBase(DAV_3510 默认值及依据):baseM=256、baseN=256(注释 "256 is better base"——DAV_3510 cube 以 256×256×(128B/dtype) 为最优基本块)、baseK=128B/dtype(L0A/L0B 单次载入内轴的最小高效粒度,056篇 128B)、stepM=stepN=1、iterateOrder=列优先、dbL0C=1、singleCoreK=全 K。

+

第 2 步 GetRebalanceBlock:cubeBound 解析模型寻优 baseM/baseN(helper.cpp L387-492)——这是通用路径的"基本块寻优器":

+
    +
  1. 平台指标:hbmBW/l2BW 由频率×核数×平台速率算出;computePower = 单核算力 × aicNum(fp32 时 ÷16);
  2. +
  3. cubeBoundEdge 公式(L418-419): +
    cubeBoundEdge = l2BW/computePower                                   // ① 理论阈值:L2 供数速率 ÷ cube 耗数速率
    +              + l2CacheUsage × (1 − l2BW/hbmBW) × cmr               // ② L2 装不下工作集时的访存惩罚
    +              − (1 + l2BW/hbmBW) / kValue                           // ③ K 向复用修正
    +cmr = (m+n)/(m×n)   l2CacheUsage = max(batch×(m+n)×k×dtype / l2Size, 1.0)
    +逐项含义:cube bound 的判据是"每输出块的相对搬运量 1/baseM+1/baseN ≤ edge";①是纯 L2 供数能力下的理论 edge;②当工作集超 L2(l2CacheUsage>1)时按 HBM 与 L2 带宽差抬高 edge(更容易判成 memory bound,倾向更大基本块);③K 越大 A/B 复用越充分,edge 越小(更容易 compute bound)。进入枚举前 edge 先乘 CUBE_BOUND_RATIO=0.85 预留 15% 余量(防实测达不到理论带宽);
  4. +
  5. 候选空间与对齐单位:候选上界由 L0 buffer(l0A/2/minKL0 等)、bias table、K 内轴对齐(BMM 固定内轴 512B 对齐,注释 "目前batchmatmul固定内轴512B对齐";memory bound 且非 batch 为 256B)卡出;fixpBoundEdge = m×n×hbmBW / ((m+n)×l2BW),k < fixpBoundEdge(fixpipe bound)时 baseN 强制 256B 对齐(注释 "fixpipe bound场景下,要求baseN是256B对齐,发挥搬出带宽");
  6. +
  7. 枚举与剪枝:curBaseM 从上界按对齐单位递减、内层 curBaseN 递减;剪枝① balanceRate ≥ 0.9(balanceRateEdge,注释 "用于判断是否取得最优解,进行减枝")且候选计算访存比既差于当前最优又大于 edge → 跳过;剪枝② fp32 cubeBound 且多轮时 baseM/N < 64(FP32_MIN_BASE_BLOCK)跳过(fp32 小块多轮效率差);
  8. +
  9. 评分:cubeBoundCond(满足 cubebound 且更均衡)优先;否则 balanceCond 以 cubeBoundParam/balanceRate(单位负载均衡率的搬运代价)为综合分,比值差 < EPSILON(1e-9) 时取更均衡者;命中 cubeBoundCond 后 edge 收紧为当前 param,形成单调收敛;
  10. +
  11. 尾块感知的 balanceRate(GetBalanceRateWithTail,L223-246):totalRound = batch×mCnt×nCnt,主轮数 mainRound = ⌈totalRound/核数⌉−1,尾轮按二维 √ 拆分估计(totalTailSplit 按 √ 开方分两维);尾块拆分后 baseM×baseN/split < 4096(MIN_TATL_BLOCK_SIZE)则无拆分收益;BMM(batchInfo≠)直接用均值/最忙核的简单比率——batch 维已摊薄尾块效应;
  12. +
  13. 收尾 GetBaseK:k ≤ maxBaseK(l0A/2/dtype/max(baseM,baseN))则 K 全载;否则按 256B/128B/64B/32B/16 递减选最大可行对齐;usedCoreNum = min(batch×mCore×nCore, aicNum);dbL0C/ubDB 按容量置 2/1。
  14. +
+

第 3 步 CalL1Tiling(CalL1TilingDefault,L45-80)maxStepK = min(CeilDiv(k,baseK), 8)(注释 "Shape约束 && issue queue约束"——stepK≤8 又是 issue queue 经验值);K 内轴对齐单位 512B/dtype(BMM 固定);stepK 从 1 枚举,容量约束 (aL1+bL1)×2 ≤ totalL1max(aL1,bL1)×4 ≤ l1Size(单边开 DB 不得超 L1 一半);更新条件优先级:首个可行解 > 256B 对齐 > 单次搬运 ≥48KB("优先对齐,对齐不了就把单次搬运量做大");得 stepKa=stepKb、depthA1=depthB1=stepK×2。

+

④ 收尾:l1BufferNum 与 apiLevel

+
    +
  • l1BufferNum = abL1TensorSize×4 ≤ l1Size ? 4 : 2(注释 "l1开2db后依然只使用了一半的空间,则开启4 db");
  • +
  • CheckTensorApiSupport:fp32 非 hf32、双 ND、连续、非全载且 k > 阈值(K>2^28 时阈值 8192,否则 1024;FP32_K_SWITCH_BASE/FP32_SPLIT_K_BASE1/2)判为 splitK 走 BASIC_LEVEL;纯 BatchMatMulV3 节点、连续、非 splitK、未禁用 tensor api → TENSOR_LEVEL(注释 "非切K且连续场景下才允许切换tensor api实现")。fp32 大 K 单核切 K 的依据:fp32 累加对 K 长度敏感,单核内把 K 分段累加(splitKRound)可降低长 K 的累加误差与 L0C 压力。
  • +
+

⑤ BASE(999) 兜底

+

IsCapable 恒 true;DoOpTiling 仅 ResetBase + CalL1Tiling + ubDB 判定——不做 GetRebalanceBlock(用默认 256/256/128B 基块,最保守);apiLevel 兜底为 HIGH_LEVEL(非 BASIC);batchModel = (apiLevel==TENSOR_LEVEL) ? BROADCAST_BATCH_MODEL : BATCH_MODEL——TENSOR_LEVEL 时高阶 API 内建任意 batch 广播支持(枚举注释 "只用于BatchMatMul广播场景"),这保证任意广播形态的 case 都不会漏接(分支 4 只接"单边单轴",其余广播全部汇到这里)。

+

⑥ ASW 滑窗窗长:GetAswWindowLen(matmul_v3_base_tiling_advanced.h L316-325)

+
sqrtNum = sqrt(aicNum);
+for (factor = sqrtNum; factor ≥ 1; --factor)
+    if (aicNum % factor == 0) return factor;   // 不超过 √aicNum 的最大因子
+

为什么这样取:滑窗是 M 向的分组宽度(kernel 侧 mainWindow = min(aswWindowLen, mCnt),窗内 N 向蛇形扫描,见 §6.1)。窗口越接近正方形,同窗口内 A 行块与 B 列块的 L2 足迹越小、并发核访问局部性越好——√aicNum 是"方形"的直觉;取 aicNum 的因子保证整窗被核数均分、窗口边界不碎。例:aicNum=32 → 窗长 4;25 → 5;24 → 4;质数核数退化为 1(退化为普通行优先)。窗长写入 tilingData.aswWindowLen 供 kernel 使用。

+
+ +

5.10 参数取值依据总表(含依据来源分级)

+ + + + + + + + + + + + + + + + + + + + + + + + +
参数 / 阈值出现位置取值依据来源
fractal 16 对齐(baseM/N/K)全部分支16文档:054篇 cube 一拍 16×16×16;064篇 一个分形恰好 512B
L0A/L0B 512B 对齐全部分支512B文档:054篇对齐要求;064篇 分形=512B
内轴 128B / 256B / 512B 对齐baseK、stepK、fixpipe 判定128/256/512B文档:056篇"搬运拆成 128B/256B/512B,非对齐向上取整";950白皮书 §4.3.2 L2 512B CacheLine/128B Sector;源码注释"BMM 固定内轴 512B 对齐"
DB ×2所有容量公式2文档:067篇 InitBuffer 块数=2 的乒乓机制;624篇 db 只取 1/2
batchC ≥ 128(TO_MUL)分支12×aivNum源码注释:"batch数大于等于128(AIV CoreNum *2) 才能开pingpong"
N>256B/dtype(TO_MUL)分支1256B源码注释 + 054篇 Vector 每拍 256B
K ≥ max(8192, aic×256B/dtype)(StreamK)分支28192 / 256B逻辑推断:每核 K 段至少一个 256B 内轴搬运块 + 绝对收益门槛
fp32 K ≤ 200 万(StreamK)分支22×10⁶源码注释(binary accumulation 精度),具体数值无文档依据
batchC×mCnt×nCnt ≤ aicNum/2(StreamK)分支21/2逻辑推断:MN 并行度不足一半核数才切 K;同时保证 kCnt≥2
MIN_BATCH_L0 = 4(mergebatch)分支34源码注释:"each aic should process at least 4 batchs"
baseK ≤ 64(mergebatch)分支364源码注释("threshold of basek is 64")+ 推断:压 K 深度换 L0B 合并度
iterBatchL1 ≥ 2(iterbatch 系)分支4/5/62源码注释:广播/驻留收益的前提是至少 2 batch 复用
mmadCount = 8 / iterBatch ≤ 4分支5/68 / 4源码经验值:注释 "cube count which will cause issuequene";文档仅有 056篇"MTE1/MMAD 队列深度 32"的机制描述,8/4 无官方出处
fullCopySize = 64KB / L1_SINGLE_SIZE_LIMIT = 48KB分支5、全载、CalL1Tiling64KB / 48KB源码经验值:注释 "moving once which can use full of bandwith";168篇官方经验值为 16KB(更宽松)
均衡率 0.8 / balanceRateEdge 0.9 / CUBE_BOUND_RATIO 0.85分支4/5/6、GetRebalanceBlock0.8 / 0.9 / 0.85源码经验值(注释仅说明用途,无推导)
m/n ≤ 256(全载)分支7/8256 = baseM/baseN逻辑:单 base 块覆盖全载轴,stepM/stepN=1
"值得全载":总量 ≥ L1×aic 或 轮数 ≥ 4/核分支7/84 轮源码注释 + 192篇 6→3 次搬运的复用收益模型
全载数据 ×2 ≤ L1分支7/8×2逻辑:L1 全载区开 ping-pong 双 buffer
stepK ≤ 4(全载)/ ≤ 8(通用)分支7/8/94 / 8源码注释:"stepK最大不超过4"、"issue queue约束"(8);4 无进一步文档依据
ASW 窗长 = aicNum 的 ≤√aicNum 最大因子分支9/BASE√aicNum逻辑推断:方形窗 L2 足迹最小 + 因子保证整窗均分
workspace 20MB RPC基类20MB源码注释:"20MB workspace for RPC"(核间通信区)
ND_FIXPIPE_1_1/1_2 启用条件分支5、StreamK、helperN 非对齐 + 1C:2V源码(文档无该枚举说明);机制支撑:054/087篇 fixpipe 随路 NZ2ND、CrossCoreFlag MODE_4
+
依据分级说明:标文档的参数可在知识库 CANN 文档/950 白皮书中找到直接依据;标源码注释的参数源码注释给出了设计意图但无官方文档佐证;标源码经验值/逻辑推断的参数是调优得出的工程阈值(如 8、4、0.8、0.85、48KB/64KB),对 950PR 满配/降配不同档位未必是最优点,做具体 case 的最优方案决策时建议对阈值两侧的边界 case 实测复核。
+ + +

6. Swizzle 专题:核间分块执行顺序的三种实现机制

+

BMM v3 中"分块执行顺序 / 哪个分块在哪个核上做"由三套机制实现,分别服务于不同的分支族:

+ +

6.1 ASW 自适应滑窗 + 蛇形(ASW 系 / 全载系 kernel)

+

核心代码在 BatchMatMulAswBlock::UpdateBasicIndex()(arch35/batch_mat_mul_v3_asw_block_advanced.h):

+
params_.index = newBlockIdx + roundIdx * usedCoreNum;      // 块→核:每轮核号错开
+uint64_t matIndex = params_.index % (params_.mCnt * params_.nCnt);  // batch 内 m×n 平面下标
+uint64_t rowIdx = matIndex / params_.nCnt / params_.mainWindow;     // 第几个划窗行
+if (rowIdx < params_.mainRow) {                            // 主划窗区
+    params_.mCntIndex = rowIdx * params_.mainWindow + matIndex % params_.mainWindow;
+    params_.nCntIndex = (matIndex / params_.mainWindow) % params_.nCnt;
+} else {                                                   // 尾划窗区(tailWindow)
+    ...
+}
+if (rowIdx % 2 != 0) {                                     // 奇数划窗行反向扫描(蛇形)
+    params_.nCntIndex = params_.nCnt - 1 - params_.nCntIndex;
+}
+

机制解读:

+
    +
  1. 块到核映射index = blockIdx + round×usedCoreNum,第 r 轮核 c 处理第 c + r×usedCoreNum 个基本块,天然错轮;
  2. +
  3. 划窗:M 方向每 mainWindow = min(aswWindowLen, mCnt) 个 m 块作为一行窗口,遍历顺序是"窗口内先 m 后 n"——同一 A 行窗口内的所有 n 块连续被计算,A 的 GM/L2 数据在一个窗口内被 nCnt 次复用,最大化 L2 命中。窗长由 host 侧 GetAswWindowLen() 计算:取 aicNum 的不超过 sqrt(aicNum) 的最大因子(如 32 核 → 窗长 4,25 核 → 5),写入 tilingData.aswWindowLen。这个选择的直觉是:窗口越接近正方形,同窗口内 A 块与 B 块的 L2 足迹越小、并发核的访问局部性越好;取 aicNum 的因子保证整窗能被核数均分;
  4. +
  5. 蛇形:奇数窗口行 n 方向反向扫描,相邻行间 n 首尾相接,避免长距离跳址,让上一行窗口末尾的 B 列块与下一行开头的 B 列块相邻,提高 B 的 L2 复用;
  6. +
  7. 尾窗:mCnt 不能被窗长整除时剩余部分走 tailWindow 分支单独处理。
  8. +
+ +

6.2 对角线错位分核(老路径通用 kernel)

+

BatchMatMulCommonBaseBlock::UpdateBasicIndex()(batch_mat_mul_v3_com_base_block.h):

+
uint64_t newBlockIdx = (GetBlockIdx() + usedCoreNum - params_.blockIdxStart) % usedCoreNum
+                       + roundIdx * usedCoreNum;
+uint64_t mIdx = newBlockIdx % params_.mCntUse;
+uint64_t nIdx = (newBlockIdx + newBlockIdx / MMLcm(params_.mCntUse, params_.nCntUse)) % params_.nCntUse;
+params_.index = mIdx * params_.nCntUse + nIdx;
+

线性 id 先取 m,再用 newBlockIdx / lcm(mCnt,nCnt) 给 n 加相位偏移,使同一时刻各核落在 m×n 平面的不同对角线上,避免多核同时抢同一行 A / 同一列 B 的 GM 流量;配合每个 L2 tile 边界轮转起始核号(blockIdxStart)与 mTile 间蛇形(reverse 翻转 nTile 遍历方向)。

+ +

6.3 Host 侧 L2 cache tiling:calOrder + mTileBlock/nTileBlock(老路径)

+

Host 侧 BatchMatmulV3BaseTiling::DoL2CacheAndCalOrderTiling()(batch_mat_mul_v3_base_tiling.cpp)决定核间执行的宏观顺序:

+ + +

6.4 batch 组轮询(iterbatch/mergebatch/matmul2mul)

+

这些分支的调度粒度是"一组 batch"而非 m×n 基本块,核映射为简单轮询 for (tileIdx = curBlockIdx; tileIdx < tileNum; tileIdx += blockNum),坐标 {0,0,0, tileIdx×iterBatchL1};mergebatch 额外按主块/主尾块/最终尾块做负载均衡。因为并行维度只有 batch,不存在 m×n 平面上的访存冲突问题,无需滑窗/错位。

+ + + + + + + +
机制所在分支解决的问题核心参数
滑窗+蛇形(ASW)ASW_BASIC / BASE / 全载系(arch35)m×n 基本块遍历的 L2 复用aswWindowLen = aicNum 的 ≤√aicNum 最大因子
对角线错位老路径通用 kernel并发核抢同一行 A / 列 B 的 GM 冲突lcm(mCnt,nCnt) 相位偏移 + 起始核轮转
calOrder 行/列优先 + L2 tile 分组老路径 host tiling大 shape 下 L2 容量约束的宏观遍历方向mTileBlock/nTileBlock/transConflict ≤ 6
batch 组轮询iterbatch / mergebatch / matmul2mulbatch 为主并行维度时的均摊iterBatchL1 / batchAL1 / 主尾块均摊
+ + +

7. tiling_key 编码:7 位段如何锁定一条 kernel 路径

+

arch35 路径的 tilingKey 由 BatchMatMulV3TilingKey::GetTilingKey() 生成,7 个位段(声明于 op_kernel/arch35/batch_mat_mul_v3_tiling_key.h,低位到高位):

+ + + + + + + + +
位段位宽取值决定什么
BATCH_API_LEVEL4 bit0=HIGH_LEVEL(MatmulImpl 高阶 API)/ 1=BASIC_LEVEL(BasicAPI/Cmct)/ 2=TENSOR_LEVEL(张量级/Blaze)用哪一层搬移计算抽象
BATCH_A_TRANS / BATCH_B_TRANS2+2 bit0/1layout/分形方向
BATCH_ITER_MODEL4 bit0=FOR_BATCH / 1=ITER_BATCH_SINGLE_BIAS / 2=BATCH_MATMUL_TO_MUL / 3=MERGE_BATCH / 4=FOR_FUSED_BATCH / 5=BROADCAST_BATCH / 6=ITER_BATCH_BROADCAST_A / 7=ITER_BATCH_BROADCAST_Bbatch 处理模式(≈ 分支 id)
BMODEL4 bit0=BASIC / 1=STREAM_K / 2=K_EQUAL_ZERO计算模型特判
BATCH_FULL_LOAD4 bit0=NONE / 1=A_FULL_LOAD / 2=B_FULL_LOAD / 3=AB_FULL_LOADL1 全载模式
BATCH_L0C2OUT_MODEL4 bit0=ON_THE_FLY / 1=1V1_ND_ALIG_FIXPIPE / 2=1V2_ND_ALIG_FIXPIPEL0C→GM 输出通路(fixpipe)
+

每个合法位段组合通过 ASCENDC_TPL_SEL 宏同时绑定三样东西:kernel 类型(AIC_ONLY / AIV_ONLY / MIX_AIC_1_2)、tiling 结构体(BatchMatMulV3TilingData / ...IterBatchBasicTilingData / ...MergeBatchBasicTilingData / ...ToMulBasicTilingData / MatMulV3KEqZeroBasicTilingData 等)、kernel 模板实例化。编译期为每个 tilingKey 生成确定的 kernel,运行时零分发开销。

+

kernel 入口是一张编译期 if constexpr 分发树(arch35/batch_mat_mul_v3.cpp),节选主干:

+
// tilingKey 位段 → kernel 类(节选)
+HIGH_LEVEL  + BASIC + NO_FULL_LOAD + FOR_BATCH        → BatchMatMulAswKernel + BatchMatMulAswBlock(ASW 主分支)
+BASIC_LEVEL + BASIC + A_FULL_LOAD + FOR_BATCH         → MatMulActKernel<..., A_FULL_LOAD_MODE>
+TENSOR_LEVEL+ BASIC + B_FULL_LOAD + FOR_BATCH         → Blaze: MatMulBL1FullLoadKernel
+TENSOR/BASIC+ STREAM_K + FOR_BATCH                    → MatMulStreamKKernel(MIX_AIC_1_2)
+BASIC_LEVEL + BASIC + ITER_BATCH_SINGLE_BIAS          → BatchMatMulActIterBatchKernel(Cmct,可带 ND_FIXPIPE_1_2)
+HIGH_LEVEL  + BASIC + ITER_BATCH_SINGLE_BIAS          → BatchMatMulMultiBatchKernel(IterateBatch 高阶版)
+BASIC_LEVEL + BASIC + MERGE_BATCH                     → BatchMatMulActMergeBatchKernel(Cmct)
+BASIC_LEVEL + BATCH_MATMUL_TO_MUL(AIV_ONLY)          → BatchMatMulToMulActKernel(K=1)
+BASIC_LEVEL + K_EQUAL_ZERO(AIV_ONLY)                 → MatMulInputKEqZeroClearOutput(K=0)
+TENSOR_LEVEL+ BROADCAST_BATCH                         → Blaze: BatchMatMulBroadcastKernel
+TENSOR_LEVEL+ ITER_BATCH_BROADCAST_A/B                → Blaze: BatchMatMulIterBatchBroadcastKernel
+
注意 IS_BLAZE 分叉:TENSOR_LEVEL 组合在 ASC_DEVKIT ≥ 9.1(Blaze GEMM 框架可用)时走 Blaze 的 GemmUniversal 系 kernel,否则回落到 MatMulActKernel/AswKernel。这是同一位段组合在不同编译环境下的第二维分叉,不改变分支语义。
+ + +

8. 分支完备性论证与批判性讨论

+

8.1 完备性论证

+
    +
  1. 覆盖完备:优先级表尾部 BASE(999) 的 IsCapable 恒 true、ASW_BASIC 仅校验 dtype/batch 一致性,任何通过 Validate 的合法输入必然命中某分支;TENSOR_LEVEL 的 BROADCAST_BATCH_MODEL 由高阶 API 内建支持任意广播形态,广播 case 无遗漏。
  2. +
  3. 特判 ⊂ 通用:前 10 个分支覆盖的 case 用通用 cube 模板也能正确计算(只是更慢),即"任何 case 的最优实现必然落在 {特判分支} ∪ {通用模板} 内",满足"性能最优分支集是完备全集子集"的方法论要求。排除逻辑体现在 IsCapable 的每个拒绝条件上——例如 StreamK 的 batchC×mCnt×nCnt ≤ aicNum/2 保证了只有切 K 收益明确的 case 才分流出去,其余留在通用路径。
  4. +
  5. 互斥性与优先级无冲突:多数特判条件天然互斥(K=0 与 K=1 互斥;单边广播与 batch 完全相等互斥;mergebatch 要求 batch≥4×核数而 StreamK 要求并行度 ≤ 核数一半……);少数有交集的(如 iterbatch basicapi 与 iterbatch 高阶版)靠优先级顺序取先命中者,语义等价只搬移路径不同。
  6. +
+

8.2 批判性观察(源码未必最优,值得审视的点)

+
+
    +
  • 经验常数硬编码:mmadCount=8(issue queue)、fullCopySize=64KB(单次搬满带宽)、48KB(L1_SINGLE_SIZE_LIMIT)、均衡率 0.8、transConflict ≤ 6、TAIL_CONFLICT_RATIO=0.5 等都是拍死的经验阈值,与 950PR 满配 32 核/降配 28 核、不同 HBM 档位的实际曲线未必吻合。给定具体 case 做最优方案决策时,这些阈值两侧的边界 case 值得用实测复核。
  • +
  • cubeBound 解析模型依赖平台带宽参数:GetRebalanceBlock 的 cubeBoundEdge 公式使用 l2BW/hbmBW/computePower 等静态配置,若平台库中 PR 降配版(1.4TB/s)参数不准确,baseM/baseN 寻优结果会系统性偏移。
  • +
  • ASW 窗长取"aicNum 的 ≤√aicNum 最大因子"是启发式:对 mCnt 很小或 nCnt≫mCnt 的细长 shape,方形窗假设不成立;此时滑窗退化为普通行优先(mainWindow=min(aswWindowLen,mCnt)),L2 复用收益可能不如按 shape 长宽比自适应的窗形。
  • +
  • TO_MUL 的 N 区间裁剪比较"碎"(n∈(32B,256B]/dtype 排除、n%256B≠0、n≠1……),说明该分支是按已知 case 调出来的,对区间外 N 的 K=1 case 会回落到 cube 路径——存在"K=1 但 N 对齐"的 case 被迫走 mmad 的潜在劣化点。
  • +
  • StreamK 的 fp32 K≤200 万阈值源于 binary accumulation 精度,属保守设定;对精度不敏感的场景本可放宽,但 tiling 层没有暴露该旋钮。
  • +
  • iterbatch basicapi 与 iterbatch 高阶版职责重叠:两者目标 case 几乎相同,仅搬移抽象层不同(BasicAPI/Cmct vs MatmulImpl),优先级 5 先于 6,意味着高阶 API 版只在 basicapi 的 L1/L0 条件不满足时才有机会——若某些 case 下高阶 API 实际更优,当前顺序无法表达。
  • +
+
+ + +

9. 附:非 arch35 老路径分支(910B 等平台)简述

+

非 DAV_3510/RESV 平台走 BatchMatmulV3BaseTiling::DoLibApiTiling()(batch_mat_mul_v3_base_tiling.cpp),不是短路遍历而是固定函数序列 + flag 覆盖

+
DoLibApiTiling(父类基础 tiling) +→ SetBatchDimInfo / CalcBatchDimAll +→ CheckNd2NzOnTheFlyLimit()(内轴 > 65535)→ DoUnAlignCommonTiling(mixNd2Nz,直接返回) +→ DoCommonTiling()(TuneBaseMKN 选 baseM/N/K,复位全部 TilingEnable flag) +→ CheckVectorComputationCondition() → DoVectorTiling(AIV/UB 模式 GEMV,直接返回) +→ DoL1FullLoadTiling()(AL1/BL1 全载判定) +→ DoL2CacheAndCalOrderTiling()(核间执行顺序/swizzle,见 §6.3) +→ DoMultiBatchAndL1FullLoadTiling()(多 batch,命中则把 loadMode 从 AL1/BL1 改写回 BASE——多 batch 优先级实际高于 L1 全载) +→ DoTilingKeyCustom():5 个 4-bit flag 编码 tilingKey + = {MultiBatchL1FullLoad, MultiBatch, LoadMode(0=BASE/1=AL1/2=BL1/3=VECTOR_FULLLOAD), IsMultiBatchOut, MixNd2Nz}
+

对应 kernel(op_kernel/batch_mat_mul_v3.cpp 分发):Common(对角错位 swizzle)、UnAlignCommon(AIV 做 ND2NZ + AIC cube 的 CV 协同,CrossCoreFlag 双缓冲)、AL1/BL1_FULLLOAD(MatmulCallBackFunc 回调式全载)、MultiBatch / MultiBatchOut、MultiBatchL1FullLoad(多 batch Nd2Nz 一次搬 L1 内层复用)、VECTOR_FULLLOAD(BatchMatmulVectorKernel:UB 流水 Mul+ReduceSum 的 GEMV)。

+ + +

10. 参考来源清单

+

源码(昇腾NPU知识库/代码仓/ops-nn)

+ +

硬件与编程文档(昇腾NPU知识库)

+ +

说明:本文所有分支条件、阈值、代码片段均直接引用自上述源码文件;硬件数值均标注来源。资料未覆盖的规格项(MTE 各通路带宽、SSBuffer 容量、芯片频率等)未做编造。

+ +
+ +