归档 BMM算子优化分析_Release/BMM算子优化分析_v0.4.md

This commit is contained in:
2026-08-22 07:10:54 +00:00
parent cb37ff1805
commit 5074ad92cb

View File

@@ -0,0 +1,873 @@
# BMM 算子优化分析
> 版本v0.4 | 目标芯片:昇腾 950PRDAV_351032 AIC / 64 AIVGM 1.6TB/sL2 128MB/5.2TB/s
> 基于 issue#3 (v0.3) 完善:补全 StreamK/ASW_Basic 实现方案,精化全部分支的进入条件与实现步骤,加入源码对照与硬件依据
---
## 一、算子功能与接口说明
### 1.1 算子功能
Batch MatmulBMM完成**带 batch 维的矩阵乘**:对 batch 维的每个索引独立执行一次矩阵乘,再加可选偏置:
$$
C[b, m, n] = \sum_{k=0}^{K-1} A[b, m, k] \cdot B[b, k, n] + bias[b, 1, n]
$$
`C = A @ B + bias`。A、B 输入维度典型为 3 维(支持最多 4 级 batch 维 $b = b_0 b_1 b_2 b_3$ 展平),最后两维做矩阵乘。
### 1.2 接口参数
| 参数 | 形状 | 数据类型 | layout | 说明 |
|---|---|---|---|---|
| 左矩阵 A | `[BatchA, M, K]` | dtypeFP16/BF16/FP8/…) | 典型 ND | 可带转置标记isATrans |
| 右矩阵 B | `[BatchB, K, N]` | dtype | 典型 ND | 可带转置标记isBTrans推理场景可为 weightNz |
| 偏置 bias | `[B, 1, N]` | dtype | 固定 ND | 可为空 |
| 输出 C | `[BatchC, M, N]` | dtype可随路量化 | 典型 ND | BatchC = broadcast(BatchA, BatchB) |
**广播语义**batch 维兼容广播——每一级 batch 维上,两侧取值要么相等、要么为 1输出该维取两者的最大值。两类广播的形态差异
- **单边全广播**`BatchA = 1``BatchB = 1`(一侧整体只有 1 个 batch
- **交叉广播**:两侧均大于 1 但不同级为 1例如 `BatchA = (6,1)``BatchB = (1,6)`,则 `BatchC = (6,6)` 共 36 个输出 batch。这类 case **不属于**单边广播,只能落入通用分支。
### 1.3 case 空间
一个 BMM case 的实现方式完全由以下特征量决定:
$$
(\; B,\; M,\; N,\; K,\; \text{dtype},\; \text{layout/转置},\; \text{广播形态} \;)
$$
其中 $B$ = BatchC展平后的输出 batch 数)。后续全部分析就是回答:**给定这组特征量,最优实现是什么。**
---
## 二、性能模型:什么叫"最优"
### 2.1 总时延 = 流水线最慢的一级
NPU 上 BMM 的执行是核内多级硬件流水的并行——Cube 计算MMAD、GM/L2→L1 搬移MTE2、L1→L0 搬移MTE1、L0C 写出Fixpipe。多核并行时各流水级时延可被双缓冲double buffer等机制相互掩盖最终
$$
\boxed{\;T_{total} = \max\big(T_{MMAD},\; T_{MTE2},\; T_{MTE1},\; T_{Fixpipe}\;[,\;T_{Reduce}]\big)\;}
$$
**总时延 = 流水线最慢的一级。** 算子优化的关键就是对瓶颈流水级的优化。由此得出一个重要的设计自由度——**瓶颈交换**
> 当 MTE2搬移是瓶颈、MMAD计算不是瓶颈时可以牺牲一定 MMAD 时延(例如冗余计算)换取 MTE2 性能提升;反之,当 MMAD 是瓶颈时,可以牺牲一定 MTE2 时延(例如重复搬移)换取计算效率提升。只要瓶颈级时延下降,总时延就下降。
后文会看到:**MergeBatch 就是"牺牲算力换搬移效率"的典型ASW_Basic 切 M/N 就是"牺牲搬移(重复读)换并行度"的典型**——它们的存在正当性都来自这个 max 模型。
### 2.2 目标芯片关键规格与经验常数
| 规格项 | 数值 | 对设计的意义 |
|---|---|---|
| AIC / AIV 核数 | 32 / 641:2 | 核间并行度上限 $C = 32$StreamK 归约可用 AIV |
| Cube 算力 FP16/BF16 | ≈486 TFLOPS单核 ≈ 16×16×16 MAC/拍 × 1.65GHz | 算存比分子 |
| GM 带宽 | 1.6 TB/s读写共享总线 | 访存 Bound 的分母;读+写互相挤占 |
| L2 Cache | 128MB5.2 TB/s读写各独享 | 重复读取的吸收层Cube 输出写 L2 即算完成 |
| L1 / L0A / L0B / L0C | 512KB / 64KB / 64KB / 256KB每 AIC | 核内分块的容量约束L0C 按 FP32 累加计 |
| UB | 512KB每 AIV | 向量通路/归约 |
**GM→L1 搬移效率ND2NZ达到 90%+ 带宽利用率的四条经验约束**(重要性递减):
1. **参与核数**:建议 32 核并行搬移,至少 3/4 核24 核)并发——核数不足时带宽利用率上限被压低;
2. **单核搬移数据总量** ≥ 480KB`min_DatamountPerCore`
3. **单次搬移 tile 大小** ≥ 16KB`min_TileSize`
4. **dValue**(单次搬移的连续内轴字节数,如非转置 A 的 K 向、非转置 B 的 N 向)≥ 128B建议 256B最好 512B。
> 这些常数480KB、16KB、256B/512B以及后文的 $b_{thr}=4$、$k_{thr}$=32B/dtype、$minCoreNum \approx 0.8 \times 32$)都是**该档芯片的实测经验值**;换芯片时所有逻辑结构不变,只需替换常数表。
### 2.3 算存比与 Bound 判定
**约定**:以"元素"为访存单位1 元素 = dtype 字节),算存比单位为 FLOP/元素。
- case 的固有算存比(不计输出写出):
$$
AI = \frac{2MNK}{(MK + KN)} = \frac{2MN}{M + N} \quad [\text{FLOP/元素}]
$$
- 计入输出写出(写 L2/GM的完整算存比
$$
AI_{full} = \frac{2MNK}{MK + KN + MN} \quad [\text{FLOP/元素}]
$$
- 芯片硬件平衡点(该 dtype 位宽下):
$$
R = \frac{\text{Cube 峰值算力}}{\text{GM 带宽} / \text{dtype 字节}} = \frac{486 \times 2}{1.6} \approx 607.5 \quad [\text{FLOP/元素BF16/FP16}]
$$
**判定规则**$AI < R$ **访存 Bound**瓶颈在 MTE2优化重心是搬移效率与复用$AI > R$ → **计算 Bound**(瓶颈在 MMAD优化重心是 Cube 利用率与流水掩盖)。
> **大白话**矩阵乘就像搬砖盖楼——Cube 是起重机算力GM 带宽是供货卡车(带宽)。算存比就是"每块砖要盖多少平米"。每块砖盖的平米数低于起重机的额定配比时,楼永远盖不快,问题出在供货而不在起重机——这时候优化要围绕"怎么少堵车、怎么一次多运点",而不是换更大的起重机。
---
## 三、分支划分的系统推导
本章是全文逻辑主线:**从分块计算的本质出发,不靠经验罗列,推导出六大分支的划分。**
### 3.1 分块计算的本质与四个可切维度
BMM 在 NPU 上实现的本质是把参与计算的数据分块tile由 32 个 AIC 核**并行 + 串行**地完成这些分块的计算,再组合成最终结果:
$$
C[B, M, N] = \sum_{\kappa} A[B, M, K_\kappa] \cdot B[B, K_\kappa, N]
$$
分块有 4 个维度:**B、M、N、K**。**核间怎么分这 4 个维度,就是分支划分的第一性问题**(核内分块是第二性问题,属于各分支内部的 tiling
### 3.2 四个维度的切分特征:一张决定性的表
从"读入 / 计算 / 写出"三个视角考察每个维度的核间切分特征(这是后续一切推导的基石):
| 切分维度 | 读入特征 | 计算特征 | 写出特征 |
|---|---|---|---|
| **切 B** | 每个数据块只被固定的 1 个核读取,核间**零重复读** | 每个输出块由 1 个核独立完成,**无核间依赖** | 只写最终结果,无中间结果 |
| **切 M / 切 N** | 切 M 则同一右矩阵块被多核**重复读**;切 N 则同一左矩阵块被多核重复读 | 每个输出块由 1 个核独立完成,无核间依赖 | 只写最终结果,无中间结果 |
| **切 K** | 每个数据块只被固定的 1 个核读取,零重复读 | 每个输出块由**多核共同**完成,存在核间依赖 | **有中间结果写出**,需核间 Reduce 归约 |
为什么特征差异这么大?两条硬件层面的根本原因:
- **B 维在数学上独立**BMM 语义就是逐 batch 独立矩阵乘),所以切 B 天然零重复、零依赖;
- **K 维有 L0C 累加机制**:核内切 K 时,$\tilde{K}$ 轮 mmad 在 256KB 的 L0C 上原地累加(`cmatrixInitVal=false`),中间结果不出核。一旦把 K 切到**核间**,单核的 L0C 装不下"别的核算的 K 段",部分和必须写出到 GM/L2 workspace再由 AIV 归约——**切 K 是唯一同时破坏"累加不出核"和"输出独占"两条性质的切法**。
### 3.3 完备枚举15 种核间切分组合
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 种是**完备的**。按 §3.2 的特征分组:
| 组 | 组合 | 共同特征 | 优化重心 |
|---|---|---|---|
| 纯 B | {B} | 零重复读,读写数据量固定 | 访存 Bound搬移效率高计算 Bound计算效率高 |
| 含 M/N 不含 K | {M},{N},{M,N},{B,M},{B,N},{B,M,N} | 可能有重复读 | 访存 Bound重复读尽量少 + 搬移效率高;计算 Bound计算效率高 |
| 含 K | {K},{B,K},{M,K},{K,N},{B,M,K},{B,K,N},{M,K,N},{B,M,K,N} | 有中间结果写出 + 归约 | 计算效率高,且归约引入的额外 MTE2/Fixpipe 时延不能成为新瓶颈 |
### 3.4 代价不对称性:切分维度的"价格表"
15 种组合的**代价结构只由两个布尔特征决定**——是否含 K、是否含 M/N。三个维度的"核间切分价格"严格排序:
$$
\text{cost}(\text{切}B) = 0 \;<\; \text{cost}(\text{}M/N) \;\ll\; \text{cost}(\text{}K)
$$
- ** B 免费**零重复读零依赖零中间写出
- ** M/N 廉价但有价**共享矩阵被重复读但若共享部分能驻留 128MB L2重复读以 5.2TB/s 命中 L2 而非 1.6TB/s GM——代价大部分被 L2 吸收配合 swizzle执行顺序编排压缩同时活跃的工作集代价进一步压低
- ** K 昂贵**归约流量 $T_{Reduce} \propto grid_K \times$ 输出量且引入核间同步——这是结构性代价L2 吸收不掉
**"价格表"不是经验 BMM 语义 + L0C 累加机制 + L2/GM 带宽结构三条事实的推论。** 整条分支决策树就是一句话**按价格从低到高购买并行度买不够才加价。**
### 3.5 推导主链:从价格表到六大分支
**第 0 层:问题归约与通路选择。**
- **K = 0**无任何计算C = bias 0 AIV 写值 **分支一特殊分支K=0**
- **K = 1**:退化为逐元素乘 $C = A \odot B$无累加深度Cube 16×16×16 粒度浪费 15/16 **分支二特殊分支K=1**
- **BatchA = 1 BatchB = 1**BMM 可通过维度折叠**转化为普通 Matmul**复用 Matmul 的成熟优化体系tilingL2 切分全载)→ **分支三转Matmul**
**第 1 层:归约不掉的 caseBatchA = BatchB = B > 1K ≥ 2必须在 BMM 框架内组织 4 维分块。先买免费的 B 维。**
- $B \ge C = 32$ B 就能填满核此时分两种情形
- ** batch $M \times N$ 够大**能开出大 tileL0C 利用率高Cube 喂得饱)→ 每核逐个 batch 做完整 Matmul 就是最优 **分支四IterBatch**
- ** batch $M \times N$ 太小** 64×64L0C 利用率仅 6.25%Cube 大片闲置)→ 把多个 batch 在核内合并成大 tile 计算$[bM,K]@[K,bN]$取块对角线输出用冗余算力换 Cube 利用率与搬移效率 **分支五MergeBatch**
**第 2 层:免费的 B 买不满 32 核,加价买廉价的 M/N。**
- $B < 32$ $P = B \times \lceil M/16 \rceil \times \lceil N/16 \rceil \ge 32$M/N 平面的分块补得上并行缺口)→ M/N或混合切共享矩阵的重复读交给 L2 + swizzle 吸收 **分支六ASW_Basic**
**第 3 层:廉价维度也买不满,才买昂贵的 K。**
- $P < 32$BMN 都小通常 K )→ 唯一剩余的并行维度是 K付归约代价换并行度 **分支七StreamK**
```
case (B, M, N, K, dtype, layout, 广播形态)
[0] K=0? → AIV 清零K=1? → AIV 逐元素乘 ← 前置通路层(正交于切分)
│ K ≥ 2
[1] BatchA=1 或 BatchB=1? ──是──→ 转Matmul问题归约
│ 否BatchA=BatchB=B>1或交叉广播
[2] 并行度账本P = B·⌈M/16⌉·⌈N/16⌉ 与 C=32 比较
├─ B ≥ 32切 B 可满核)─────────────────────────┐
│ M×N 大Cube 饱 → IterBatch │
│ M×N 小Cube 饿,且访存 Bound → MergeBatch │
│ │
├─ B < 32 且 P ≥ 32 → ASW_Basic切 M/N 补并行, │
│ 共享读取由 L2+swizzle 吸收) │
│ │
└─ P < 32B/M/N 用尽仍缺并行K 大)→ StreamK │
(切 K 买并行,付归约代价)◀──────────────────┘
```
由此推导出**六大 BMM 本体分支**转MatmulMergeBatchIterBatchASW_BasicStreamK加一个**前置通路层**K 退化共七个决策路径
### 3.6 完备性与极小性:不多不少
**完备性**对归约不掉的 case15 种组合按"是否含 K是否含 M/N"坍缩
- K 8 代价结构相同必付归约grid_K × grid_B × grid_M × grid_N 只是参数差异 1 个分支StreamK统一覆盖
- 不含 K M/N 6 代价结构相同共享重复读)⇒ 1 个分支ASW_Basic统一覆盖
- {B} 1 核内组织只有两种本质不同的方式——**合并多 batch 一起算**MergeBatch**逐个 batch **IterBatch不存在第三种 2 个分支
$1 + 1 + 2 = 4$加上前置的归约分支转Matmul与通路层K 退化= **7**任何合法 case 必落其一无空洞
**极小性**每个分支都有它"唯一最优" shape 区域去掉任何一个都会有 case 失去最优实现
| 分支 | 独占最优的代表 caseBF16 | 替代方案为何更差 |
|---|---|---|
| 特殊K=0/1 | K=0 K=1 | Cube 通路完全或几乎无用 |
| 转Matmul | BatchB=1, B=128, M=N=K=2048 | 免费折叠后 Matmul 体系的 L2 切分/全载直接可用BMM 分支内重做无收益 |
| IterBatch | B=32, M=N=K=4096 | ASW M/N 引入无谓共享读MergeBatch 引入无谓冗余算力 |
| MergeBatch | B=128, M=32, N=128, K=64 | IterBatch L0C 利用率仅 ~1.6%Cube 空转搬移 tile <16KB带宽利用率崩 |
| ASW_Basic | B=2, M=N=8192, K=1024 | B 2 核干活StreamK 付无谓归约 |
| StreamK | B=1, M=N=64, K=65536 | 不切 K P=16 < 32近半核闲置时延差数量级 |
七大路径构成**极小完备集**。
### 3.7 大白话总结
> 把 BMM 想成给 32 个工人分一批"矩阵乘订单"
> - **K=0/1 的订单太简单,不需要起重机**Cube 派不上用场直接让搬运工AIV干完特殊分支
> - **能整单外包的就别自己干**:一侧只有一个 batch问题其实就是一次普通矩阵乘直接交给成熟的 Matmul 产线转Matmul
> - **按订单分(切 B最省心**每人几单互不干扰零重复搬料、零协调。订单多时单大就一单接一单干IterBatch单太小时一人同时干几单、拼成大活干虽然多做了点无用功反正瓶颈在等料不在干活MergeBatch
> - **订单不够分,就按行/列拆单(切 M/N**大家会重复领同一份料但料放在近处仓库L2多跑几趟近仓库很便宜ASW_Basic
> - **行/列也不够拆,只好沿深度 K 拆**几个人合做同一块输出各算一段再汇总——汇总是要额外开会的Reduce所以只在实在分不满、且 K 足够长时才这么干StreamK
---
## 四、各分支详解:进入条件与实现方案
> 每节按统一结构展开:**做什么(定义)→ 什么时候进(进入条件逐条 + 每条的理由)→ 怎么做(实现方案与参数求解)→ 与源码对照(批判性)**。
### 4.0 前置通路层K 退化 case
在切分决策之前先做计算通路判断
| K | 数学本质 | 计算通路 | 数据通路 | 核类型 |
|---|---|---|---|---|
| **K = 0** | 无任何计算C = bias 0 | mmad | GM AIV GM | AIV_ONLY |
| **K = 1** | 退化为逐元素乘 $C = A \odot B$无累加深度 | mmad向量 Mul | GM UB Mul GM | AIV_ONLY |
**为什么是 AIV 而非 AIC**Cube 阵列一拍完成 16×16×16fp16 fractal 乘加K=1 K 维只有 1/16 被利用——阵列 15/16 MAC 空转还要付出 GML1L0A/L0B 的分形搬运L0C 累加与 fixpipe 写出全链路开销 AIV 每拍处理 256B 连续数据逐元素乘 + UB batch pingpong 能把 MTE 带宽吃满64 AIV 的聚合向量算力FP16 54 TFLOPS对这个计算密度绰绰有余
**源码对照**K_EQUAL_ZERO策略 0 TO_MUL策略 1两个独立策略处理tiling 直接下发 AIV_ONLY kernel触发条件 TO_MUL 要求 `batchC ≥ 128`= aivNum×2 UB pingpong)、N 不在 (32B/dtype, 256B/dtype] 区间中间区让给其它分支)、UB 容量放得下
### 4.1 转Matmul问题归约分支
**做什么** `BatchA = 1` `BatchB = 1` batch 维折叠进矩阵维度转化为普通 Matmul计算完成后按需恢复 batch
**为什么这么做**单边 batch=1 BMM 与普通 Matmul 在数学上只差一个维度标签Matmul 的优化体系L2 切分AL1/BL1 全载swizzle BMM 各分支在"单样本"情形下更成熟——**站在巨人肩膀上不重复造轮子**。
**折叠规则与代价(关键的不对称性)**
| 情形 | 折叠方式 | 是否免费 | 代价分析 |
|---|---|---|---|
| `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 |
**BatchA = 1 时的决策规则**
- **A 矩阵较小**$MK \cdot \text{dtype} \le L1_{size}$ A 可全载 L1优先考虑将 A 常驻 L1各核均匀读取 B 完成全部计算无需输入/输出重排的额外开销——留在 BMM 分支内广播友好的 IterBatch/ASW_Basic
- **A 矩阵较大** `BatchA=1` 扩展为 `BatchA=BatchB`广播语义然后比较"B 折叠转 Matmul + 重排""BMM 分支"的预估时延择优选择
**进入条件**`BatchA = 1 || BatchB = 1`其中 `BatchB = 1` 恒进免费`BatchA = 1` 时按上述代价比较决定
**源码对照**源码中的 `MergeBatchAndMAxis()` 只做了 `batchB=1` 方向的折叠`args_.mValue = batchA * mValue`"该方向免费"的判断互为印证`batchA=1` 方向源码未做折叠而是走 AL1_FULL_LOAD ASW_Basic 的广播处理
### 4.2 MergeBatch多 batch 合并计算)
#### 4.2.1 做什么
核间按 B 分核每核负责 $b_{core}$ batch核间无同步无通信核内**将多个 batch 合并计算**
$$
[b, M, K] @ [b, K, N] \;\Rightarrow\; [bM, K] @ [K, bN] = [bM, bN] \;\xrightarrow{\text{BlockTrace}}\; [b, M, N]
$$
其中 **BlockTrace** 指以 $[M, N]$ block 粒度取结果矩阵的块对角线作为各 batch 的有效输出$C[i, m, n] = R[iM + m,\; iN + n]$。交叉项 i batch A 乘第 ji batch B被算出但丢弃——这就是"算力浪费"浪费比例 $(b-1)/b$。
**为什么允许浪费**进入该分支的 case 必然是访存 Bound条件三保证瓶颈在 MTE2 不在 MMAD浪费的算力被搬移时延掩盖(§2.1 瓶颈交换)——**用本来闲置的 Cube 算力 tile 变大后的搬移效率与 Cube 利用率**。
#### 4.2.2 进入条件(逐条 + 理由)
设计原则四条:① 硬件时延可流水掩盖double buffer 乒乓);② 满足容量约束每次计算 L1/L0A/L0B/L0C 放得下);③ GML1 搬移高效(§2.2 四条经验约束);④ 算力有浪费但计算不能成为瓶颈
形式化后进入 MergeBatch **同时**满足
**条件 1batch 够分且够合并)**`BatchA = BatchB`无广播
$$
b_{core} = \frac{B}{C} \ge b_{thr}
$$
$b_{thr}$ " batch 合并搬移能拿到效率收益"的最小合并数——$b$ 太小时合并的搬移收益抵不过实现复杂度不如 IterBatch 且完全不浪费算力功耗)。经验值 $b_{thr} = 4$源码 `MIN_BATCH_L0 = 4`注释 "each aic should process at least 4 batchs")。
**条件 2合并数 b 的上下界)**设核内单次 Cube 计算合并 $b$ batch$b \le b_{core}$要求 $b \ge b_{thr}$ $b$ 由以下约束共同封顶
- **算存比约束**计算不能变成瓶颈合并后单次计算的算存比 $AI(b) = \dfrac{2bMN}{M + N}$保持访存 Bound 要求
$$
AI(b) < R \;\Longleftrightarrow\; b < b_{AI} = \frac{R \cdot (M + N)}{2MN}
$$
- **L0C 容量约束**输出 $[bM, bN]$ FP32 累加块须放入 256KB L0C考虑乒乓double buffer则两份
$$
2 \cdot (bM)(bN) \cdot 4\text{B} \le 256\text{KB} \;\Rightarrow\; b \le b_{L0C} = \sqrt{\frac{256\text{KB}}{2 \cdot MN \cdot 4\text{B}}}
$$
- **L0A/L0B 容量约束**$2 \cdot bM \cdot k_{L0} \cdot \text{dtype} \le 64\text{KB}$ $2 \cdot k_{L0} \cdot bN \cdot \text{dtype} \le 64\text{KB}$乒乓两份同时 $k_{L0}$ 不得低于 Cube 分形下限16bit 位宽下典型 min baseK = 16
> **乒乓取舍**:若 $b_{core}$ 小于"不乒乓时 L0C 允许的合并数上限",说明 batch 余量不足,只能不乒乓(牺牲流水掩盖换合并数);否则乒乓。形式上 $b_{max} = \min(b_{AI},\; b_{L0C}^{(\text{是否乒乓})},\; b_{core})$。
**条件 3访存 Bound 性质)**
$$
\frac{2MN}{M + N} < \frac{R}{b}
$$
case 固有算存比显著低于平衡点低一个合并倍数 $b$ 的量级)——这是"浪费可被掩盖"的定量保证
**条件 4K 向搬移效率)**L1 K 切分 $k_{L1} = K / StepK$StepK 为正整数要求 $k_{L1} \ge k_{thr}$$k_{thr} = 32\text{B}/\text{dtype}$BF16 16 元素)。理由$k_{L1}$ 决定 ND2NZ dValue过碎则搬移指令效率崩(§2.2 4 )。
**条件 5L1 驻留与 tile 效率)**L1 batch 驻留数
$$
b_{L1} = \frac{L1_{size}}{(M k_{L1} + k_{L1} N) \cdot \text{dtype}}, \qquad b_{L1} > b
$$
L1 驻留的 batch 组必须大于单次计算的合并数,否则合并无从谈起),且搬移 tile 大小满足效率:左矩阵非转置时 $\max(b_{L1} M k_{L1} \cdot \text{dtype},\; k_{L1} N \cdot \text{dtype}) > min\_TileSize$16KB左矩阵转置时相应调整。
**条件 6单核搬移总量**
$$
b_{core} \cdot (M k_{L1} + k_{L1} N) \cdot \text{dtype} \ge min\_DatamountPerCore = 480\text{KB}
$$
对应 §2.2 第 2 条:单核搬移数据总量不足时带宽利用率上限被压低。
**条件汇总逻辑**:条件 1 定资格batch 够多),条件 2/3 定上限(别算出瓶颈、别撑爆 L0条件 4/5/6 定下限(搬移效率不能崩)。**上限与下限之间必须有交集,交集为空则该 case 与 MergeBatch 无缘。**
#### 4.2.3 核内参数求解
进入分支后,$b$ 与 baseK 的具体取值有一个重要性质:**在访存 Bound 前提下,$b < b_{AI}$ b 的取值不影响性能baseK 只需满足分形约束16 倍数也不影响算存比**——因为瓶颈是搬移算力余量内怎么切都一样据此
1. 先由条件 2 $b_{max}$实际 $b$ $[b_{thr}, b_{max}]$ 内取**尽量均匀**$b_{core}$ 整除 $b$)——每次计算的 $b$ 均匀一致对功耗更有利
2. 再由 $b$ 反查 L0AB 允许的最大 baseK
$$
baseK_{max} = \min\left(\frac{64\text{KB}}{2 \cdot bM \cdot \text{dtype}},\; \frac{64\text{KB}}{2 \cdot bN \cdot \text{dtype}},\; \frac{C0_{size}}{\text{dtype}}\right)
$$
向下取 16 倍数
3. L1 级参数$k_{L1} \ge \min(k_{L0\_max},\; 128\text{B}/\text{dtype})$$b_{L1} = \min(b_{L1\_max},\; b_{core})$。
#### 4.2.4 数值例子
caseB=128, M=32, K=64, N=128, BF1632
1. **资格**$b_{core} = 128/32 = 4 \ge b_{thr} = 4$ ✓;
2. **算存比**$AI = 2 \times 32 \times 128 / (32 + 128) = 51.2$ FLOP/元素 $< R = 607.5$ 访存 Bound$b_{AI} = 607.5 / 51.2 \approx 11.86$ $b \le 11$ 都不会进入算力 Bound——算存比约束很宽
3. **L0C 约束**乒乓$2(b \cdot 32)(b \cdot 128) \times 4\text{B} \le 256\text{KB} \Rightarrow b^2 \le 8 \Rightarrow b \le 2$不乒乓则 $b \le 4$)——**L0C 才是真正的紧约束**
4. **取 b = 2**= $b_{core}$ 的因子均匀L0B 给出 $k_{L0} \le 64\text{KB} / (2 \times 2 \times 128 \times 2\text{B}) = 64$$K = 64$ 一步到位baseK = 64
5. **搬移效率**$k_{L1} = 64 \ge k_{thr} = 16$ ✓;单核搬移量 $4 \times (32 \times 64 + 64 \times 128) \times 2\text{B} = 80\text{KB}$ —— **不足 480KB**条件 6 不满足
结论 case "单核搬移总量"一项上不达标——这正是 MergeBatch 条件体系的用处**它提前告诉你瓶颈不在算力而在搬移效率**。
#### 4.2.5 执行流程
```
核间32 核,每核 b_core 个 batch
核内(每核):
for k_l1 in range(0, K, kL1): # L1 级 K 切分
MTE2: A[bL1组, M, k_l1:k_l1+kL1]、B[bL1组, k_l1:k_l1+kL1, N] → L1乒乓
for b_start in range(0, bL1, b): # 按合并数 b 分组
for k_l0 in range(0, kL1, kL0): # L0 级 K 切分
MTE1: A[bM, kL0] → L0AB[kL0, bN] → L0B
Cube: [bM, kL0] @ [kL0, bN] → L0C 累加
Fixpipe: BlockTrace 取 b 个 [M, N] 对角块 → L2/GM
```
#### 4.2.6 与源码对照
源码 `batch_matmul_v3_mergebatch_basicapi_tiling.cpp` IsCapable 条件各级 batchA_i == batchB_i`batchC ≥ 4 × aicNum``alignK ≥ 64``M ≤ N` bias NZ拒绝非连续转置等对照分析
| 源码条件 | 本文对应 | 差异 |
|---|---|---|
| `batchC ≥ 4 × aicNum` | 条件 1$b_{core} \ge b_{thr} = 4$ | **一致**每核至少 4 batch)——合理 |
| `alignK ≥ 64` | 条件 4$k_{L1} \ge k_{thr}$=32B/dtypeBF16 16 元素 | 源码保守 **4 倍**可能误杀 K∈[16,64) 的可获益 case |
| `M ≤ N` | 无此限制M > N 的镜像 case 原理上同样可合并,交换合并方向即可) | 源码放弃镜像 case属于**覆盖缺口** |
| 无显式算存比/搬移效率判定 | 条件 3/4/6 | 源码把"是否最优"推给优先级顺序,本文条件体系是补全 |
### 4.3 IterBatch逐 batch 计算)
#### 4.3.1 做什么
核间按 B 分核(每核 1 个或多个 batch核间无同步无通信核内**逐个 batch 分别执行标准 Matmul 分块**L1→L0A/L0B→Cube→L0C→Fixpipe并输出。无算力浪费、无跨 batch 依赖——是"切 B"最朴素的形态。
#### 4.3.2 进入条件(三大类 + 理由)
满足以下**任一**
**类 1计算 Bound 型)**
$$
AI_{full} = \frac{2MKN}{MK + KN + MN} \ge R \quad \text{且} \quad BatchA = BatchB,\; b_{core} = \frac{B}{C} \in \mathbb{Z}^+
$$
理由:连输出写出都计入仍是计算 Bound则瓶颈恒在 Cube——只要负载均衡$b_{core}$ 整除,各核同量),逐 batch 计算就是满算力实现,无需任何花哨。
**类 2输出驻留 L2 的均衡型)**
$$
\frac{2MN}{M+N} \ge R_{读GM} \;\;\text{且}\;\; 2K \ge R_{写L2} \;\;\text{且}\;\; MN \cdot \text{dtype} \le L2_{size} \;\;\text{且}\;\; BatchA = BatchB,\; b_{core} \in \mathbb{Z}^+
$$
理由:读侧访存不弱($AI \ge R_{读GM}$);单 batch 输出 $MN \cdot \text{dtype} \le 128$MB 可驻留 L2——Cube 输出写 L2 即算完成,写 GM 的流量被省掉;$2K$ 是"每写出一个元素对应的计算量"$2K \ge R_{写L2}$ 保证写 L2 的 5.2TB/s 也不是瓶颈。三个条件合起来 = **读、算、写三条路都不堵**
**类 3访存 Bound 型)**$\dfrac{2MN}{M+N} < R$ 且同时满足
1. $BatchA = BatchB$ $b_{core} = B / C \ge 1$
2. **负载均衡**访存 Bound 时核负载利用率建议 $b_{Avg}/b_{Max} > 0.8$(计算 Bound 时建议 100%。操作化表述B 整除核数或尾波B mod C活跃的核数 $\ge minCoreNum$(取 $0.8 \times 32 \approx 26$)。注意:若只写 `B mod C > minCoreNum` 会误杀整除 case余数 0 恰恰是完全均衡),**判据应以均衡比为准、余数规则为其近似**
3. **单核搬移不重复读**(访存 Bound 下重复读就是纯损失),按 L1 容量分五种形态之一:
- (a) $b_{core} = 1$ 且 $(MK + KN) \cdot \text{dtype} \le L1_{size}$:单 batch 左右矩阵同时驻留 L1零重复读
- (b) $b_{core} > 1$ 且 $2(MK + KN) \cdot \text{dtype} \le L1_{size}$L1 放下 2 个 batch 形成乒乓流水;
- (c) 放不下的,$(MK + KN/Step) \cdot \text{dtype} \le L1_{size}$(或 M/Step 对称):一矩阵不切、另一切分,分块大小仍须 > min_TileSize
- (d) $b_{core} > 1$ 时上一条的半容量版本L1 双 batch 乒乓预算减半);
- (e) 左右都切 K$(M \cdot K/Step + K/Step \cdot N) \cdot \text{dtype} \le L1_{size}$
- 以上 (c)(d)(e) 切分后的分块均须满足搬移效率tile ≥ 16KB、dValue ≥ 128B/256B
#### 4.3.3 设计原理
1. 核间切 B 零共享零依赖,**唯一的系统性风险是负载不均**——所以均衡是第一条件;
2. 输出须满足 L0C 容量:$MN \cdot 4\text{B} \le L0C$(否则核内还要切 M/N那就不是纯 IterBatch 而是 ASW 行为——但 IterBatch 核内**允许**对单 batch 做标准 M/N/K tiling此处的准确含义是"核内 tiling 不构成跨 batch 的耦合"
3. 访存 Bound 时**单核数据不得重复读**——L1 装得下才不重复,装不下就按 Step 切分且切分后仍满足搬移效率下限;
4. 与 MergeBatch 的分工IterBatch 不浪费算力,但需要"单 batch 足够大"撑搬移效率与 Cube 利用率MergeBatch 用浪费换效率,专治小 M×N。两者在 $M \times N$ 的中段重叠,由时延模型仲裁(第五章)。
#### 4.3.4 核内参数求解
**情形 (a)$b_{core} = 1$ 且 L1 可放完整单 batch**
```
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))
```
**情形 (b)$b_{core} > 1$ 且 L1 可放 2 batch 乒乓**
```
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))
```
**情形 (c)/(d)/(e)L1 放不下完整 batch**
一矩阵不切、另一切分:假设 L1 放完整左矩阵和部分右矩阵,则右矩阵应搬入的 K 向长度:
$$
k_{L1\_b} = \min\!\left(\frac{L1_{size} - M \cdot K \cdot \text{dtype}}{N \cdot \text{dtype}},\; K\right)
$$
且 $k_{L1\_b} \ge k_{thr}$,切分后 tile ≥ 16KB。
#### 4.3.5 执行流程
```
核间32 核分 batch尽量整除尾波核数 ≥ minCoreNum
核内(每核):
for batch in 本核的 b_core 个 batch: # 逐个 batch
for m_tile / n_tile核内标准 tiling:
for k_tile in range(0, K, baseK):
MTE2 预取下一 k_tile → L1双缓冲
MTE1: L1 → L0A/L0B
Cube: mmad → L0C 原地累加 # K 循环不出核
Fixpipe: L0C → L2写 L2 即完成GM 回写可异步)
```
#### 4.3.6 与源码对照
源码有三个 iterbatch 变体:`ITER_BATCH_BROADCAST_BASICAPI`(单边广播)、`ITER_BATCH_BASICAPI`(基础 API`ITER_BATCH`(高阶 API。关键差异
| 维度 | 源码 basicapi | 源码高阶 API | 本文 |
|---|---|---|---|
| L1 容量计算 | $(sizeA + sizeB + bias) \times 2 \le l1Size$(除 DB | $iterBatch = l1Size / inputSizeOneBatch$(不除 DB | 按 L1 形态 (a)~(e) 分类 |
| 截断 | `mmadCount=8`issue queue`fullCopySize=64KB` | `iterBatch ≤ 4` 时 singleCoreK 减半 | 统一为搬移效率 + 均衡率约束 |
| 均衡率 | 0.8 | 0.8 | 0.8(访存 Bound/ 1.0(计算 Bound |
| 广播 | 独立分支(单边单轴) | 不支持 | 由转Matmul 或 ASW_Basic 吸收 |
### 4.4 StreamKK 维核间切分)
#### 4.4.1 做什么
当 B、M、N 三个维度切到最碎仍填不满 32 核时,把 K 维切到核间:多核各算一段 K 的部分和,再归约:
$$
C_{(\beta,\mu,\nu)} = \sum_{c \in \text{group}} C^{(c)}_{(\beta,\mu,\nu)} \quad \text{(部分和写 workspaceAIV 归约或原子加)}
$$
#### 4.4.2 进入条件(两条缺一不可)
**条件 1并行缺口存在**:不切 K 时的独立输出块数
$$
P = B \times \lceil M / 16 \rceil \times \lceil N / 16 \rceil < C = 32
$$
源码取更保守的 $B \cdot mCnt \cdot nCnt \le aicNum/2$并附加 ND-only无交叉广播确定性等级 1 等工程限制)。注意**严格的 StreamK 不要求核间完全不切 B/M/N**——它是一般框架 `grid_K × grid_B × grid_M × grid_N ≤ C`纯切 K 只是 grid_B=grid_M=grid_N=1 的特例 B/M/N 能提供部分并行度时应该用组合 grid 把归约组 $grid_K$ 压到最小grid_K 小一档K 的门槛降一档平方级)。
**条件 2归约代价可接受**每核计算时延须远大于归约时延安全系数 $\alpha = 10$
$$
T_{MMAD/core} \ge \alpha \cdot T_{Reduce}
\;\Longleftrightarrow\;
\frac{K}{grid_K} \;\gtrsim\; grid_K \times 1690
$$
$K \gtrsim grid_K^2 \times 1690$grid_K=2 K 6.8Kgrid_K=4 K 27Kgrid_K=8 K 108Kgrid_K=32 K 1.7M仅极端 case)。同时 $K / grid_K \ge 256$单核 K 段过碎则 tiling 效率崩)。**grid_K 越大对 K 的要求越苛刻——StreamK 内部的 grid 搜索自然淘汰归约过重的配置。**
#### 4.4.3 实现方案
**核间组织**grid_K 个核组成一个归约组共享同一个输出块 $(\beta, \mu, \nu)$ 的计算核间分配
$$
\text{ } c \text{ K } \left[\frac{c \cdot K}{grid_K},\; \frac{(c+1) \cdot K}{grid_K}\right)
$$
**核内流水**每核
```
输入K 段 [k_start, k_end),输出块 (β, μ, ν)
for m_tile / n_tile核内标准 tiling:
for k_tile in range(k_start, k_end, baseK):
MTE2: A[β, m_tile, k_tile:k_tile+baseK] → L1
MTE1: L1 → L0A/L0B
Cube: mmad → L0C 原地累加 # 本核的 K 段内累加
Fixpipe: L0C → workspaceGM 或 L2 # 写出部分和
```
**归约阶段**
- **方式 Aworkspace + AIV 归约**确定性)。部分和写入 GM workspace每核一块 256×256 fp32 缓冲归约由 AIV 执行AIC:AIV = 1:22 AIV 伺候 1 AIC 的部分和流)。workspace 大小 = $C \times 256 \times 256 \times 4\text{B} + 20\text{MB}$RPC
- **方式 B原子加AtomicAdd**非确定性)。部分和直接原子累加到输出 GM省一遍读回但归约顺序不定——源码在确定性等级 > 1 时禁用此变体。
**fixpipe 优化**:当 N 不对齐且输出块足够大时($n > 64$ 且 $n \% 16 \ne 0$ 且 $m > 2$ 且 $m \times n \ge 256$),启用 ND_FIXPIPE_1_21 AIC : 2 AIV 的 ND fixpipe 通路),由 AIV 分担搬出。
**grid 搜索**$grid_K$ 从 2 开始递增2, 4, 8, ...),每档检查条件 2 是否满足;同时检查 $K / grid_K \ge 256$。取满足条件的最小 $grid_K$(归约代价最小)。
**核间同步**:复用 Matmul 高阶 API 的 StreamK 模板(避免手写 CrossCore flagId 冲突087篇/054篇
#### 4.4.4 与不切 K 分支的关系
StreamK 从不"硬切换"进入:当不切 K 的分支候选已足够快(如已 Cube BoundStreamK 候选的归约开销使其自然落败;只有当 B/M/N 并行度不足导致大量核闲置时StreamK 才以数量级优势胜出B=1、M=N=64、K=65536 时,不切 K 仅 16 核可用StreamK 32 核满负荷)。
#### 4.4.5 与源码对照
源码 `batch_matmul_v3_basic_streamk_tiling.cpp` 的条件:
| 源码条件 | 本文对应 | 差异 |
|---|---|---|
| `batchC × mCnt × nCnt ≤ aicNum/2` | 条件 1$P < C$ | 源码取一半核数为阈值更保守本文用精确并行缺口 |
| `CeilAlign(k,256) ≥ max(8192, aicNum×256B/dtype)` | 条件 2$K \gtrsim grid_K^2 \times 1690$ $K/grid_K \ge 256$ | 源码用固定阈值本文用 grid 搜索动态判定 |
| fp32 hf32 K 200 | 本文不限 | 源码因 binary accumulation 精度限制 fp32 超长 K本文不限 dtype通过 grid 搜索控制归约深度 |
| `aivNum == 2 × aicNum` | 归约方式 A 的硬件前提 | 一致 |
| workspace = aicNum×256×256×4B + 20MB | 归约方式 A workspace | 一致 |
### 4.5 ASW_Basic通用切分框架
#### 4.5.1 做什么
不切 K允许切 B/M/N **任意组合**——它是"不含 K 且含共享读取" 6 种切分组合{M},{N},{M,N},{B,M},{B,N},{B,M,N}的统一实现框架也是 B < 32 P 32 时的最优归宿同时兜住 B 32 IterBatch/MergeBatch 条件不满足的剩余 case。**B 可以大可以小可以等于 1ASW_Basic 是实践中最常命中的分支。**
#### 4.5.2 核心机制swizzle + L2 管理
M/N 的固有代价是共享矩阵的重复读ASW_Basic 用两件武器把代价压到最低
**武器 1ASW 滑窗蛇形 swizzle**
M 向按窗口 $W$ 分组窗口内蛇形遍历 N
$$
W = \max\{\,d \mid d \mid C,\; d \le \lfloor\sqrt{C}\rfloor \,\}
$$
32 核取 $W = 4$。数学效果:同一时刻 32 个核活跃的工作集被压缩到"$W$ A 行块 + 一条 B 列块带"L2 足迹最小 共享读取基本命中 5.2TB/s L2 而非 1.6TB/s GM
**为什么窗口取 $\lfloor\sqrt{C}\rfloor$ 的最大因子**窗口越接近正方形A 行块 + B 列块的 L2 足迹越小方形窗的二维足迹是 $W \cdot K + K \cdot N$ W 偏离 $\sqrt{C}$ 而增大且因子性保证整窗被核数均分窗口边界不碎
窗口内的遍历顺序
$$
\sigma(\mu, \nu) = \big(\mu_{row} \cdot W + \mu_{col}\big) \cdot \tilde{N} + \nu'
$$
其中 $\mu_{row} = \lfloor \mu / W \rfloor$$\mu_{col} = \mu \bmod W$$\nu'$ 由蛇形决定
$$
\nu' = \begin{cases} \nu & \text{ } \mu_{row} \text{ 为偶数正向} \\ \tilde{N} - 1 - \nu & \text{ } \mu_{row} \text{ 为奇数反向} \end{cases}
$$
核号与轮次
$$
c = \mathrm{idx}(\beta, \mu, \nu) \bmod C,\qquad r = \lfloor \mathrm{idx}(\beta, \mu, \nu) / C \rfloor
$$
**武器 2L2 切分 + 错位分核**
工作集超过 128MB mL2TileNum × nL2TileNum 切分每个 L2 **错位分核**对角线分配
$$
\sigma_{diag}(\mu, \nu) = \mu \cdot \tilde{N} + \left(\nu + \left\lfloor \frac{\mu \cdot C}{\mathrm{lcm}(\tilde{M}, \tilde{N})} \right\rfloor \right) \bmod \tilde{N}
$$
避免多核同时抢同一地址的读读冲突并优先选拖尾小的方案
#### 4.5.3 核间切分维度的选择顺序
按共享代价从低到高
| 优先级 | 切分方式 | 共享矩阵 | 条件 | 代价 |
|---|---|---|---|---|
| 1 | **切 B**B 核数 | | $B \ge C$ | 零共享永远先试 |
| 2 | **切 M**B 不够 | 右矩阵 [K, N] | $KN \cdot \text{dtype} \le 128$MB 则驻留 L2 | 右矩阵重复读L2 吸收 |
| 3 | **切 N** | 左矩阵 [M, K] | $MK \cdot \text{dtype} \le 128$MB 则驻留 L2 | 左矩阵重复读L2 吸收 |
| 4 | **混合切**B×MM×N、…) | 双向共享 | swizzle + L2 切分管理 | 代价最高最后试 |
| 5 | **降核** | | P 远小于 32 K 也不够格走 StreamK | 宁可部分核闲置 case 时延绝对值小 |
#### 4.5.4 基本块寻优cubeBound 模型)
ASW_Basic baseM/baseN cubeBound 解析模型求解平台指标hbmBW/l2BW 由频率×核数×平台速率算出computePower = 单核算力 × aicNum
cubeBoundEdge 公式
$$
\text{cubeBoundEdge} = \frac{l2BW}{\text{computePower}} + l2CacheUsage \cdot \left(1 - \frac{l2BW}{hbmBW}\right) \cdot \text{cmr} - \frac{1 + l2BW/hbmBW}{kValue}
$$
其中 $\text{cmr} = (m+n)/(m \cdot n)$每单位输出元素对应的输入行/列搬运代价$l2CacheUsage = \max(batch \cdot (m+n) \cdot k \cdot \text{dtype} / l2Size, 1.0)$。
逐项含义
- ** 1 **L2 供数速率 ÷ cube 耗数速率——cube bound 的理论阈值
- ** 2 **L2 装不下工作集时的访存惩罚 HBM L2 带宽差加权
- ** 3 **K 向流水复用修正——K 越大A/B 复用越充分
求解流程 baseM/baseN 候选解空间内枚举"计算访存比 cubeBoundParam = 1/baseM + 1/baseN 与尾块负载均衡率 balanceRate"综合评分
- 剪枝 ①:balanceRate 0.9 且候选 cubeBoundParam 既差于当前最优又大于 edge 跳过
- 剪枝 ②:fp32 cubeBound 且多轮时 baseM/N < 64 跳过
- 评分cubeBoundCond满足 cubebound 且更均衡优先否则以 cubeBoundParam/balanceRate 为综合分
进入枚举前 edge 先乘 **CUBE_BOUND_RATIO = 0.85** 预留 15% 余量
#### 4.5.5 L1 步进与 buffer 管理
$$
\max StepK = \min\left(\left\lceil\frac{K}{baseK}\right\rceil,\; 8\right)
$$
stepK 8 issue queue 深度约束源码注释"Shape约束 && issue queue约束")。容量约束
$$
(aL1 + bL1) \times 2 \le L1_{size} - bias - scale
$$
且单边不超过 L1 一半
$$
\max(aL1, bL1) \times 4 \le L1_{size}
$$
stepK 枚举优先级首个可行解 > 256B 对齐 > 单次搬运 ≥ 48KB。
#### 4.5.6 内部特化(不是独立分支)
- **AL1/BL1 全载**:当单边无 batch 且该侧矩阵很小(如 batchA=1 且 M ≤ 256把整个 A 常驻 L1$\tilde{M} = 1$、A 的 GM→L1 搬运只发生一次——这是 ASW 内部"M 不切 + L1 驻留深度拉满"的参数极限,不是新分支。源码条件:`batchA ≤ 1``m ≤ 256``alignMatASize × 2 ≤ l1Size` 且 B 侧"值得全载"(总量 ≥ L1×aicNum 或 每核循环 ≥ 4 轮);
- **广播友好形态**:交叉广播 case§1.2)在 ASW 内通过对广播侧做 L1/L2 驻留处理,共享关系与切 M/N 同构。
#### 4.5.7 执行流程
```
核间32 核,按 swizzle 滑窗分配 (β, μ, ν) 块
核内(每核):
for round in range(R): # R = ⌈B̃·M̃·Ñ / C⌉
(β, μ, ν) = swizzle_inv(round × C + core_id)
for k_tile in range(0, K, baseK):
MTE2: A[β, μ, k_tile:k_tile+baseK] → L1双缓冲/4buffer
MTE1: L1 → L0A/L0B
Cube: mmad → L0C 原地累加 # K 循环不出核
Fixpipe: L0C → L2/GM
```
#### 4.5.8 与源码对照
源码 `batch_matmul_v3_asw_basic_tiling.cpp` 的 IsCapableA、B 的非连续转置状态必须一致batch 完全相等batchBias ≤ 1dtype 限 fp16/bf16 系。DoOpTilingResetBase → GetRebalanceBlock → CalL1Tiling。
| 维度 | 源码 | 本文 |
|---|---|---|
| 基本块默认值 | baseM=256, baseN=256, baseK=128B/dtype | 由 cubeBound 模型求解,不固定 |
| 寻优模型 | GetRebalanceBlockcubeBound 解析模型) | 同§4.5.4但源码的经验常数0.85/0.9/48KB需注意移植 |
| L1 buffer | 4 buffer 判定:`abL1TensorSize × 4 ≤ l1Size` | 同§4.5.5 |
| swizzle | ASW 滑窗(窗长取 √C 的最大因子) | 同§4.5.2 |
| fp32 splitK | K > 1024或 8192 当 K > 2^28时启用 | 由 StreamK grid 搜索统一管理 |
---
## 五、分支仲裁与整体决策流程
### 5.1 为什么需要仲裁层
第四章的进入条件给出的是各分支的"主场",但主场之间有**重叠区**(例如 B ≥ 32 且 M×N 中等时MergeBatch 与 IterBatch 都合法B 较大时 IterBatch 与 ASW_Basic 切 B 等效)。重叠区的最优归属没有解析解,必须由统一的时延模型仲裁:
$$
\text{branch}^* = \arg\min_{cand \in \bigcup \text{各分支候选}} \max\big(T_{MMAD},\; T_{MTE2},\; T_{MTE1},\; T_{Fixpipe}\;[,\;T_{Reduce}]\big)
$$
分支体系的价值在于**候选集完备且无冗余**:每个等价类只派一个代表框架生成候选,时延模型在等价类内部和边界上做精细仲裁。两层缺一不可——只有模型没有分支,搜索空间是 15 种组合 × 全部 grid 参数的爆炸;只有分支没有模型,边界 case 被硬阈值误杀。
### 5.2 总决策流程
```
输入B, M, N, K, dtype, layout, 广播形态, bias
├─[0] K = 0 → AIV 清零K = 1 → AIV 逐元素乘(特殊分支)
├─[1] BatchB = 1 → 转Matmul免费折叠 [B·M, K],必选)
│ BatchA = 1 → 比较"重排 B + Matmul" vs "广播友好 IterBatch/ASW"
│ 按时延模型选小者
├─[2] BatchA = BatchB = B > 1
│ 生成四类候选并逐一估算 T_total
│ MergeBatch条件 1~6 全过才生成,见 4.2.2
│ IterBatch三大类条件见 4.3.2
│ ASW_Basic按 4.5.3 顺序试切 B/M/N/混合,含 swizzle 与 L2 切分)
│ StreamKP < 32 且 K 满足 4.4.2 条件 2 时生成grid 搜索)
│ → argmin T_total
└─[3] 输出:分支 + tiling 参数baseM/baseN/baseK、b、bL1、kL1…
+ swizzle 方案 + L2 切分方案 + 预估端到端时延
```
---
## 六、完备性审视(漏洞检查)
按"转Matmul / MergeBatch / IterBatch / StreamK / ASW_Basic + 前置通路层 cover 全部 BMM case 最优实现"的设计目标,逐条拷问。
### 6.1 覆盖矩阵:无空洞
| shape 区域 | 主分支 | 兜底 |
|---|---|---|
| K = 0 / K = 1 | 前置通路层AIV | — |
| BatchA=1 或 BatchB=1 | 转Matmul | ASW_Basic广播友好形态 |
| BatchA=BatchB>1B≥32M×N 大 | IterBatch | ASW_Basic 切 B |
| BatchA=BatchB>1B≥32M×N 小,访存 Bound | MergeBatch | IterBatch |
| 上述但 B<32P32 | ASW_Basic | 降核 ASW |
| P<32K | StreamK | 降核 ASW |
| P<32K 也小 | 降核 ASW_Basic | 此时时延绝对值小调度开销主导分支选择不敏感 |
| 交叉广播BatchABatchB 且均>1 | ASW_Basic | — |
### 6.2 审视发现的问题与修正
设计骨架成立,但有五处需要修正或显式声明,否则会被反例拷问:
**问题 1转Matmul 的方向不对称(原设想的最大漏洞)。** "一侧 batch=1 就折叠成 Matmul"在 BatchB=1 方向免费batch 与 M 内存相邻,输出布局一致);但在 BatchA=1 方向,右矩阵 [B,K,N] 折叠成 [K,B·N] 需要一次真实的转置重排,且输出 [M,B·N] 与目标 [B,M,N] 之间存在置换——"只是输出后按 batch split"的设想在 ND 布局下不成立split 实际是 scatter。修正BatchA=1 时按"重排代价 vs Matmul 收益"走模型仲裁,小 case 留在 BMM 分支内。
**问题 2交叉广播 case 的归属必须显式声明。** BatchA=(6,1)、BatchB=(1,6) 这类 case 既不进转Matmul两侧都>1也不满足 MergeBatch/IterBatch 的 BatchA=BatchB 前提——它们落入 ASW_Basic对广播侧做驻留。不声明就会显得"五分支有洞"。声明后完备性闭合。
**问题 3K 退化不在五分支内,需前置通路层。** K=0无计算、K=1无累加深度走 AIV 向量通路;这是"计算通路选择"维度,与"切分维度"正交,放在五分支之前判断。源码用 K_EQUAL_ZERO / TO_MUL 两个独立策略处理,印证了这一层的必要性。
**问题 4IterBatch 草稿条件的字面表述有误杀。** `B mod C > minCoreNum` 会拒绝 B 整除核数的完全均衡 case余数 0。正确表述应以负载均衡比为准访存 Bound 要求 $b_{Avg}/b_{Max} > 0.8$,计算 Bound 要求 100%;余数规则只是它的近似操作化。
**问题 5经验常数必须可标定且源码硬编码条件偏粗。** $b_{thr}=4$、$k_{thr}$=32B/dtype、480KB、16KB、dValue≥256B 都是该档芯片的实测经验值,文档中全部显式参数化(换芯片只换常数表)。对照之下源码把 `batchC ≥ 4×aicNum``alignK ≥ 64``M ≤ N` 等直接写死,其中 `alignK ≥ 64` 比草稿阈值保守 4 倍、`M ≤ N` 放弃了镜像 case——**做最优实现时不应继承这些硬编码,而应按第四章的条件体系重新判定**。
### 6.3 与源码策略的关系
源码 arch35 的 11 个策略K_EQUAL_ZERO / TO_MUL / STREAM_K / MERGE_BATCH / ITER_BATCH_BROADCAST / ITER_BATCH_BASICAPI / ITER_BATCH / AL1_FULL_LOAD / BL1_FULL_LOAD / ASW_BASIC / BASE与本文分支不是同层概念
| 源码策略 | 本文归属 |
|---|---|
| K_EQUAL_ZERO、TO_MUL | 前置通路层§4.0),正交于切分 |
| STREAM_K | StreamK§4.4 |
| MERGE_BATCH | MergeBatch§4.2 |
| ITER_BATCH、ITER_BATCH_BASICAPI、ITER_BATCH_BROADCAST | IterBatch§4.3broadcast 变体是广播输入下的数据复用特化单边广播在本文更多由转Matmul 吸收) |
| AL1_FULL_LOAD、BL1_FULL_LOAD | ASW_Basic 的 L1 驻留参数极限§4.5.6 |
| ASW_BASIC、BASE | ASW_Basic§4.5 |
**结论**:源码策略集 = 本文分支 × 计算通路 × 驻留/广播特化的展开。本文的分支是"切分等价类"的最小完备骨架,源码的冗余策略可在新实现中收敛,源码遗漏的(条件 3/4/6 的显式判定、M>N 的 MergeBatch 镜像、转Matmul 的 BatchA=1 方向)正是优化的增量空间。
---
## 七、总结
1. **最优的定义**$T_{total} = \max(T_{MMAD}, T_{MTE2}, T_{MTE1}, T_{Fixpipe}[, T_{Reduce}])$ 最小;瓶颈交换是合法且必要的手段。
2. **分支的推导**4 维可切 → 15 种组合完备 → 切分特征表(切 B 免费 / 切 M/N 廉价被 L2 吸收 / 切 K 昂贵需归约)→ 按价格从低到高购买并行度 → 坍缩为 MergeBatch、IterBatch、ASW_Basic、StreamK 四个等价类加前置的转Matmul 问题归约层与 K 退化通路层,共七大路径,**完备且极小**。
3. **各分支的条件**都不是孤立经验,而是"资格(并行度够不够)→ 上限(容量/算存比封顶)→ 下限(搬移效率托底)"三层逻辑的实例化;所有经验常数可标定、可移植。
4. **边界不靠硬切**:重叠区由端到端时延模型统一仲裁,分支体系负责候选集的完备无冗余。
5. **设计经受了漏洞拷问**修正了转Matmul 的方向不对称与 IterBatch 均衡条件的字面误杀,显式声明了交叉广播与 K 退化的归属;相对源码实现,本文条件体系更细、更真、覆盖更全。
---
## 附录 A经验常数表可标定
| 常数 | 数值 | 出处/依据 | 说明 |
|---|---|---|---|
| $b_{thr}$ | 4 | 源码 `MIN_BATCH_L0` | 合并搬移的最小有效合并数 |
| $k_{thr}$ | 32B/dtype | §2.2 第 4 条 | ND2NZ dValue 下限BF16 为 16 元素) |
| $min\_DatamountPerCore$ | 480KB | §2.2 第 2 条 | 单核搬移总量下限 |
| $min\_TileSize$ | 16KB | §2.2 第 3 条 | 单次搬移 tile 下限168篇官方为 16KB |
| $minCoreNum$ | 26≈0.8×32 | §2.2 第 1 条 | 尾波活跃核数下限 |
| $CUBE\_BOUND\_RATIO$ | 0.85 | 源码 GetRebalanceBlock | cubeBound edge 余量 |
| $balanceRateEdge$ | 0.9 | 源码 GetRebalanceBlock | 负载均衡剪枝阈值 |
| 均衡率(访存 Bound | 0.8 | 源码 iterbatch | $b_{Avg}/b_{Max}$ 下限 |
| $\alpha$StreamK 安全系数) | 10 | 本文设定 | $T_{MMAD} \ge \alpha \cdot T_{Reduce}$ |
| $R$BF16/FP16 | 607.5 FLOP/元素 | §2.3 | 芯片算存比平衡点 |
## 附录 B与 issue#3 (v0.3) 的差异
| 章节 | v0.3 状态 | v0.4 补全内容 |
|---|---|---|
| StreamK 实现方案 | 空 | §4.4.3 完整实现核间组织、核内流水、归约两方式、fixpipe 优化、grid 搜索、核间同步 |
| ASW_Basic 实现方案 | 空 | §4.5.2~4.5.7 完整实现swizzle 公式、L2 切分、切分顺序、cubeBound 模型、L1 步进、执行流程 |
| MergeBatch 进入条件 | 5 条(简略) | 6 条(含公式推导),补充算存比约束 $b_{AI}$、L0C 解析式 $b_{L0C}$、乒乓取舍 |
| IterBatch 实现方案 | 部分伪代码 | 补全 (a)~(e) 五种情形的完整伪代码 + 设计原理 + 源码对照 |
| 转Matmul 实现方案 | 简略 | 补充 BatchA=1 的决策规则A 大小判断 + 广播扩展 + 时延比较) |
| 特殊分支 | 简述 | 补充 AIV vs AIC 的定量分析、源码触发条件 |
| 源码对照 | 无 | 每个分支新增"与源码对照"小节,指出源码的保守/缺口/一致项 |
| 完备性论证 | 无 | §3.6 + 第六章15 种组合 → 7 路径的完备性与极小性论证 |
---
*参考文档《BMM分块计算数学公式》《BMM最优软件实现方案设计》《BatchMatMulV3算子分支实现分析》《BatchMatmul算子特性分析》源码`ops-nn/matmul/batch_mat_mul_v3`arch35芯片资料昇腾 950PR 架构白皮书与 CANN 9.0.0 性能建模文档。*