目录整理:ASW_Basic分支分析系列移入独立子目录

This commit is contained in:
2026-08-31 01:03:18 +00:00
parent b30b8979ab
commit 513cc64874

View File

@@ -0,0 +1,910 @@
# ASW_Basic 分支BMM 兜底分支的理论最优实现分析
> 目标芯片:昇腾 950PRDAV_3510。本文为 ASW_Basic 分支的独立分析v1.5 扩充 §6 源码对比cubeBound 模型完整解析与优劣判定自包含完整推导链。v1.5 修正 dValue 定义与尾轮重切约束,新增与源码实现的对比分析。
## 摘要
ASW_Basic 是 BMM 的兜底分支——核间切 M/N或混合切不做 batch 合并或 K 维切分。本文给出完整的时延建模、核间分配策略分析(证明 B 优先分组在任何场景下都不优于线性映射)、实现方案的逐步推导(含尾轮重切的 Bound 类型分级策略),以及与源码实现的逐维度对比。
v1.5 扩充 §6 源码对比cubeBound 模型三项的物理推导(工作集超 L2 倾向更小 tile、K 大倾向更大 tile、**源码 singleCore = base 不分层导致过度切分**K 小时搬入时延可达理论 2 倍)、尾轮不实际重切。核心结论:**B 优先分组不优于线性映射;理论的最少切分原则 + SingleCore/Base 分层 + 尾轮重切是相对源码的三条实质改进;源码的 cubeBound 解析模型与平台自适应值得理论吸收。**
---
## 一、问题定义与执行模型
### 1.1 分支定位
BMM 中,当 B ≥ Cbatch 数 ≥ AIC 核数)时核间切 B 是免费的(每核独立处理若干 batch无共享无依赖由 IterBatch/MergeBatch 承接。当 B < C B C IterBatch/MergeBatch 条件不满足时需要切 M/N 来填满所有核——这就是 ASW_Basic
ASW_Basic 是实践中最常命中的分支B 可大可小可等 1交叉广播也由此承接对广播侧做 L1/L2 驻留共享关系与切 M/N 同构)。
### 1.2 执行模型
```
核间B × mCnt × nCnt 个输出块,按 B→M→N 线性映射分配到 C 核
核内:每核处理若干 [singleCoreM, singleCoreN] 输出块
└── 每块内部GM→L1→L0→Cube→L0C→Fixpipe 标准流水
└── K 维不切singleCoreK = K按 kL1 分块搬入 L1
```
数据流
```
GM ──MTE2──> L1 ──MTE1──> L0A/L0B ──MMAD──> L0C ──Fixpipe──> GM
↑_____________ L2 Cache读 5.2TB/s_____________↑
```
### 1.3 符号定义
| 符号 | 含义 | 表达式/取值 |
|---|---|---|
| $B$ | batch | 输入参数 |
| $M, N, K$ | 矩阵维度 | 输入参数 |
| $C$ | AIC 核数 | 32 |
| $dtype$ | 输入元素字节数 | BF16 2B |
| $outB$ | 输出元素字节数 | BF16 2B |
| $L0C$ | L0C 容量/ | 256KB |
| $L0A, L0B$ | L0A/L0B 容量/ | 64KB |
| $L1$ | L1 容量/ | 512KB |
| $L2$ | L2 容量共享 | 128MB |
| $BW_{pc}$ | 单核 GM 带宽份额 | $W_{GM}/C = 50$ GB/s |
| $Q_{16}$ | 单核 Cube BF16 峰值算力 | 486/32 15.2 TFLOPS |
| $W_{GM}$ | GM 带宽 | 1.6 TB/s |
| $BW_{L2}$ | L2 读带宽 | 5.2 TB/s |
*Tiling 参数*
| 符号 | 含义 | 约束层级 |
|---|---|---|
| $\text{BaseM}, \text{BaseN}$ | L0 tile M/N 维度 | L0C 容量直接约束 |
| $baseK$ | L0 tile K 维度 | L0A/L0B 容量约束 |
| $\text{singleCoreM}, \text{singleCoreN}$ | 每核输出 tile M/N 维度 | L1 容量 + 并行度 + 搬移效率 |
| $k_{L1}$ | GML1 K 向粒度 | dValue 256B |
| $mCnt, nCnt$ | batch M/N 向块数 | $\lceil M/\text{singleCoreM} \rceil$ |
| $W$ | swizzle 窗口宽度 | $\max\{d : d \mid C, d \le \lfloor\sqrt{C}\rfloor\}$ |
层次关系$K = \text{singleCoreK} \ge k_{L1} \ge baseK$$\text{singleCoreM} \ge \text{BaseM}$$\text{singleCoreN} \ge \text{BaseN}$。
**搬移效率dValue 与 min_TileSize**
GML1 搬移使用 Nd2Nz DMA每次搬移的关键参数
| 参数 | 含义 | A 矩阵 [singleCoreM, $k_{L1}$] | B 矩阵 [$k_{L1}$, singleCoreN] |
|---|---|---|---|
| nValue | 行数非连续维 | singleCoreM | $k_{L1}$ |
| dValue | 每行连续字节数连续维 | $k_{L1} \cdot dtype$ | $\text{singleCoreN} \cdot dtype$ |
| 总搬移量 | nValue × dValue | $\text{singleCoreM} \cdot k_{L1} \cdot dtype$ | $k_{L1} \cdot \text{singleCoreN} \cdot dtype$ |
**dValue 是 ND 排布中连续维的字节数**—— ND 格式的右矩阵 B非转置时连续维为 NdValue = $\text{singleCoreN} \cdot dtype$转置时连续维为 KdValue = $k_{L1} \cdot dtype$。**dValue 不是两个维度的乘积**。
两级搬移效率阈值
1. **dValue ≥ 256B**DMA 硬件突发下限每行连续数据量不足 256B DMA 突发效率急剧下降
2. **总搬移量 ≥ min_TileSize**推荐 16KB单次搬移量太小则带宽利用率不足[昇腾 950 NPU 架构白皮书](https://public-download.obs.cn-east-2.myhuaweicloud.com/ascend/%E6%98%87%E8%85%BE950%20NPU%E6%9E%B6%E6%9E%84%E7%99%BD%E7%9A%AE%E4%B9%A6.pdf)推荐 dValue 256B/512B 对齐)。
---
## 二、进入条件
同时满足
1. $P = \dfrac{B \cdot MN \cdot 4\text{B}}{L0C} \ge C$并行度补齐
2. batch 结构限制BatchA=BatchB、交叉广播均可典型进入路径$B < C$ B 买不满核 $B \ge C$ IterBatch/MergeBatch 条件不满足时的兜底
3. **降核模式**$P < C$ 且不满足 StreamK 进入条件 只用 $\lceil P \rceil$ 个核其余核闲置
逐条解释
1. **并行度补齐** L0C 满载为基本块粒度B×M×N 能切出至少 C 个独立输出块则切 M/N或混合切并行度够用 M/N 的固有代价是共享矩阵被多核重复读但共享部分驻留 128MB L2 时重复读以 5.2TB/s 命中 L2 而非 1.6TB/s GM代价大部分被吸收
2. **兜底性质**ASW_Basic 是实践中最常命中的分支——B 可大可小可等 1交叉广播也由此承接对广播侧做 L1/L2 驻留共享关系与切 M/N 同构)。
3. **降核模式**P < C K 也不够格走 StreamK 并行度凑不满核此时与其强行把 M/N 切得更碎tile 跌破 min_TileSizedValue 跌破 256B搬移效率崩塌反而更慢不如**只用 P 个核**、每核承担一个完整输出块L0C 满载粒度其余核闲置这类 case 的时延绝对值小继续切分引入的调度与搬移效率损失大于并行收益——降核是理性选择而非偷懒
---
## 三、时延建模
### 3.1 端到端时延
$$
T = \max(T_{MTE2},\; T_{MMAD},\; T_{FIX}) + T_{drain}
$$
| | 含义 | 公式 |
|---|---|---|
| $T_{MTE2}$ | GML1 搬移时延 | $r_{in} \cdot S_{in} / (C \cdot BW_{pc})$ |
| $T_{MMAD}$ | Cube 计算时延 | $2BMNK / (C \cdot Q_{16})$ |
| $T_{FIX}$ | 输出写回时延 | $B \cdot MN \cdot outB / (C \cdot W_{pc})$ |
| $T_{drain}$ | 末块排空时延 | $O(T_{comp} + T_{write})$ |
其中 $S_{in} = B(MK + KN) \cdot dtype$ 为输入总量$r_{in} \ge 1$ 为重复读倍率GM 输入流量 / 输入总量)。$r_{in} = 1$ 表示每字节只从 GM 读一次后续复用全命中 L2)——**这是 GM 输入流量的下界**。
**关键观察**$T_{MMAD}$ $T_{FIX}$ 与核间分配策略无关——**分配策略只影响 $T_{MTE2}$**通过 L2 命中率影响 $r_{in}$)。优化目标 $r_{in}$ 尽量接近 1
### 3.2 并行度约束的正确形式
总输出块数 $= B \cdot mCnt \cdot nCnt$。并行度约束的正确形式是
$$
B \cdot mCnt \cdot nCnt \ge C
$$
即总块数至少能填满 C 这与 $mCnt \cdot nCnt \ge \lceil C/B \rceil$ 等价两边同乘 B 后取整)。
**不应要求 $B \cdot mCnt \cdot nCnt \;\%\; C = 0$**整除)。整除是充分不必要条件——不整除时产生尾轮尾轮的处理见 §
---
## 四、核间分配策略分析
### 4.1 两种候选策略
- **B 优先分组**C 核分为 B 每组 $C_g = \lfloor C/B \rfloor$ $\lceil C/B \rceil$ 核独立处理一个 batch $mCnt \times nCnt$ 个块组内做 swizzle
- **线性映射**块按 (b, m, n) 字典序编号block 0 = (0,0,0)block 1 = (0,0,1)block $mCnt \cdot nCnt$ = (1,0,0),…), i 依次处理块 i, i+C, i+2C,
### 4.2 L2 工作集对比
每波活跃核所需的 A 行带 + B 列带
$$
WS_{wave} = \big(W \cdot sM + \tfrac{C}{W} \cdot sN\big) \cdot K \cdot dtype
$$
其中 $sM$ = singleCoreM$sN$ = singleCoreN$W$ swizzle 窗口宽度
| 维度 | B 优先分组 | BMN 线性映射 |
|---|---|---|
| 每波活跃 batch | B 每组一个 | 1 $mCnt \cdot nCnt \ge C$ |
| 每组/每波 swizzle 窗口 | $W_g = \max\{d \mid d \mid C_g,\; d \le \sqrt{C_g}\}$ | $W = \max\{d \mid d \mid C,\; d \le \sqrt{C}\}$ |
| 每波 L2 工作集 | $B \cdot (W_g \cdot sM + \frac{C_g}{W_g} \cdot sN) \cdot K \cdot dtype$ | $(W \cdot sM + \frac{C}{W} \cdot sN) \cdot K \cdot dtype$ |
| C 不整除 B | 组间核数不等 负载不均 | 无影响总块数任意 |
**数值例**C=32、B=4、sM=sN=256、K=1024、BF16分组 $C_g=8$、$W_g=2$8 的最大因子 82.83总工作集 = $4 \times (2 \times 256 + 4 \times 256) \times 1024 \times 2 = 12$ MB线性 $W=4$,工作集 = $(4 \times 256 + 8 \times 256) \times 1024 \times 2 = 6$ MB——**线性映射的工作集是分组方案的一半**。
**为什么线性映射工作集更小**
1. **更多核参与同一 batch 的 swizzle** $W = \sqrt{C} > W_g = \sqrt{C/B}$ → 工作集更接近"方形"下限(均值不等式:$W + C/W$ 在 $W = \sqrt{C}$ 处最小);
2. **同一时刻只有 1 个 batch 的数据活跃** → L2 只需缓存 1 份 A/B 数据,而非 B 份。
### 4.3 分场景严格比较
**场景 I$mCnt \cdot nCnt \ge C$ 且单波工作集 ≤ L2**(最常见)
线性映射:每波 C 个块在同一 batch 内L2 命中率高。B 分组B 个 batch 同时活跃,总工作集 $B$ 倍。**线性严格更优。**
**场景 II$S_{in} \le L2$(全部输入放得下 L2**
两种策略的 GM 流量相同($r_{in} = 1$,每字节只读一次)。但 B 分组在 C % B ≠ 0 时负载不均(组间核数不等)。**线性不劣于分组。**
**场景 III$mCnt \cdot nCnt < C$(单 batch 块数不够填满所有核)**
线性映射:每波横跨 $\lceil C/(mCnt \cdot nCnt) \rceil$ 个 batch——这些 batch 的数据同时活跃。B 分组:每组 $C_g$ 核但只有 $mCnt \cdot nCnt < C_g$ 个块 **组内有空闲核**线性映射至少所有核都有活干。**线性严格更优。**
**场景 IV$mCnt \cdot nCnt < C/B$(每组连自己的核都填不满)**
B 分组每组空闲 $C_g - mCnt \cdot nCnt$ 个核总算力浪费 $C - B \cdot mCnt \cdot nCnt$ 个核线性映射无此问题。**线性严格更优。**
### 4.4 结论
**B 优先分组在任何场景下都不优于线性映射。** 根本原因
1. L2 是共享 Cache工作集越小命中率越高——线性映射同一时刻只激活 1 batch 的数据工作集最小
2. swizzle 窗口 $W \propto \sqrt{\text{参与核数}}$——更多核参与同一 batch swizzle窗口更大工作集更方形
3. 线性映射对 B C 的关系无要求B 不整除 C 时无负载不均
因此 ASW_Basic 采用 **BMN 线性映射**作为核间分配策略
---
## 五、实现方案
### 5.1 BaseM / BaseN 的确定L0 级 tile先把 L0C 用满)
L0C Cube 的累加器BaseM × BaseN 是每次 Cube 计算的输出 tileBaseM/N **尽量把 L0C 用满**——L0C 利用率越高每次 Cube 计算的输出越大单位计算的启动/排空开销摊得越薄
**L0C 双缓冲 vs UnitFlag 单缓冲**
传统做法用 L0C 双缓冲实现 tile 间流水——计算 tile N+1 fixpipe 同时写出 tile N
$$
\text{BaseM} \times \text{BaseN} = \frac{L0C}{2 \times 4\text{B}} = 32768 \text{ 元素双缓冲}
$$
但昇腾 950PR Fixpipe 支持 **UnitFlag**——MMAD 每完成一个 16×16×16 基本块512B 结果Fixpipe 立即将其写出无需等整个 L0C tile 算完UnitFlag 提供的是 **tile 内部的细粒度流水**16×16×16 粒度替代双缓冲的 **tile 间粗粒度流水**BaseM×BaseN 粒度)。
UnitFlag 单缓冲下L0C 只需一份 buffer
$$
\text{BaseM} \times \text{BaseN} = \frac{L0C}{4\text{B}} = 65536 \text{ 元素单缓冲}
$$
BaseM/N 可放大 $\sqrt{2}$ 256362mCnt/nCnt 相应减小**L2 重复读率降低** batch 块数减少 每行 A 被更少的组读取)。
*时延对比* tile 粒度BF16 输出
| 方案 | tile 大小 | tile 间流水 | tile 内流水 | tile 时延 |
|---|---|---|---|---|
| 双缓冲 | 181×18132761 元素 | ✓(tile N 写出 tile N+1 计算 | | max($T_{comp}$, $T_{write}$) |
| UnitFlag 单缓冲 | 256×25665536 元素 | | ✓(16×16×16 粒度 | max($T_{comp}$, $T_{write}$) |
两种方案的稳态时延相同都是 max($T_{comp}$, $T_{write}$) UnitFlag 单缓冲的 tile 更大 tile 数更少 循环开销更小。**但当前 BMM ASW kernel 未启用 UnitFlag**`unitFlag = 0`注释 "each l0 only process one block, disable unit flag"且源码在 baseM=baseN=256 时已自动选 dbL0C=1256×256×4B×2 > L0C——即**源码已经是单缓冲 + 无 UnitFlag**tile 到顶但无流水交叠。
*建议*:对计算 Bound 的 case 启用 UnitFlagMMAD 与 Fixpipe 流水并行),可将单 tile 时延从 $T_{comp} + T_{write}$ 降至 $\max(T_{comp}, T_{write})$。对访存 Bound 的 caseMTE2 BoundUnitFlag 收益小([CANN 文档](https://www.hiascend.com/document/detail/zh/CANNCommunityEdition/920beta1/API/ascendcopapi/atlasascendc_api_07_0003.html)MTE2 Bound 时 MMAD/FIX 流水可被搬移掩盖)。
BaseM/BaseN 的长宽比**应尽量方形**(而非跟随 M/N对齐 16 的倍数。**推导**
**1. GM→L1 搬移与 BaseM/N 无关**GM→L1 搬移的是 $[\text{singleCoreM}, k_{L1}]$A和 $[k_{L1}, \text{singleCoreN}]$B其 dValue 由 $k_{L1}$A和 $\text{singleCoreN}$B决定——BaseM/N 的长宽比不参与 GM→L1 搬移参数。**长宽比跟随 M/N 不会改善 GM→L1 效率。**
**2. L0A = L0B = 64KB 等容量 → 方形 tile 用满两侧**L0A 装 $\text{BaseM} \times baseK$L0B 装 $baseK \times \text{BaseN}$。若长宽比跟随 M/N如 M/N = 4BaseM=512、BaseN=128
$$
baseK = \min\Big(\frac{L0A}{2 \cdot \text{BaseM} \cdot dtype},\; \frac{L0B}{2 \cdot \text{BaseN} \cdot dtype}\Big) = \min(32,\; 128) = 32
$$
L0A 装满512×32×2×2 = 64KB**L0B 只用了 25%**32×128×2×2 = 16KB——一半 L0 容量闲置。
方形 tileBaseM=BaseN=256
$$
baseK = \min\Big(\frac{64\text{KB}}{2 \cdot 256 \cdot 2\text{B}},\; \frac{64\text{KB}}{2 \cdot 256 \cdot 2\text{B}}\Big) = 64
$$
L0A 和 L0B **同时装满**,且 baseK = 64 是长宽比跟随方案baseK=32**2 倍**——K 维迭代次数减半Cube 的 K 向流水切换和 SetFlag/WaitFlag 同步次数减半L1→L0 搬移本身不是瓶颈,但更少的迭代意味着更少的同步握手和更少的 MMAD 启动/排空轮次)。
**3. 方形 tile 是 L0C 面积约束下的最优**L0C 约束 $\text{BaseM} \times \text{BaseN} \le 65536$ 只限制面积。在面积固定下,方形使 $\min(\text{BaseM}, \text{BaseN})$ 最大——baseK 的上限由 $\min(\text{BaseM}, \text{BaseN})$ 决定(公式见上),所以方形最大化 baseK。
**4. 例外**:当 M 或 N 小于方形边长时tile 被迫非方形:
$$
\text{BaseM} = \min(\lfloor\sqrt{65536}\rfloor_{16},\; M),\qquad \text{BaseN} = \min\Big(\frac{65536}{\text{BaseM}},\; N\Big) \text{ 向下 16 对齐}
$$
M=128、N=4096 → BaseM=128M 限制、BaseN=512L0C 面积限制)——此时长宽比跟随 M/N 是**被迫的**M 太小),而非主动选择。
**结论**BaseM/N 长宽比跟随 M/N 的切分方法**站不住脚**——它使 L0A/L0B 容量利用率失衡一侧闲置、baseK 减半K 迭代翻倍)、且对 GM→L1 搬移无任何收益。正确做法是**方形 tile 优先**,仅当 M 或 N 小于方形边长时被迫跟随。
baseK 由 L0A/L0B 容量决定L1→L0 搬移无 dValue 要求dValue 约束的是 GM→L1 的 $k_{L1}$
$$
baseK = \min\Big(\frac{L0A}{2 \cdot \text{BaseM} \cdot \text{dtype}},\; \frac{L0B}{2 \cdot \text{BaseN} \cdot \text{dtype}}\Big) \text{ 向下 16 对齐}
$$
核间不切 K 时 K 维度层次关系:$K = \text{singleCoreK} \ge k_{L1} \ge baseK$——$k_{L1}$ 是 GM→L1 的 K 向粒度(须 $k_{L1} \cdot \text{dtype} \ge 256\text{B}$baseK 是 L1→L0 的 K 向粒度(仅受 L0A/L0B 容量约束)。
*BaseM/N 的具体确定过程*host 端枚举16 对齐遍历):
1. 从 BaseM = BaseN = $\lfloor\sqrt{L0C/4\text{B}}\rfloor_{16} = 256$ 开始方形L0C 单缓冲上限L0A/L0B 同时满载)
2. baseK 取 L0A/L0B 容量允许的最大值baseK = $\min(L0A/(2 \cdot \text{BaseM} \cdot dtype),\; L0B/(2 \cdot \text{BaseN} \cdot dtype))$ 向下 16 对齐(**16 对齐是 Cube K 向粒度要求,不是搬移效率要求**——L1→L0 搬移不是瓶颈,可被 GM→L1 或 Cube 计算掩盖。baseK 取大值的意义在 L0 容量利用率和 K 迭代次数,而非搬移效率)
3. 若 M < 256 N < 256BaseM = $\lfloor M \rfloor_{16}$ BaseN = $\lfloor N \rfloor_{16}$另一维取 $\min(65536/\text{BaseM},\; N)$或对称向下 16 对齐——此时 tile 被迫跟随 M/N但这是 M 太小的结果不是主动选择
4. 方形 tile L0C 面积利用率$256 \times 256 / 65536 = 100\%$;被迫非方形时面积利用率 = $\text{BaseM} \times \text{BaseN} / 65536$M N 小时必然 < 100%不可优化
*与源码的差异*源码默认 baseM=baseN=256硬编码 "256 is better base"再由 cubeBound 模型L2 供数能力 + 溢出惩罚 + K 向复用枚举收缩理论直接从 L0C 容量出发cubeBound 的三项修正可从第一性原理推导:①L2 供数速率 vs Cube 耗数速率 访存 Bound 时应缩 tile;②工作集超 L2 增大 tile 减少重复读;③K 越大越偏 compute bound 可放大 tile两者方向一致源码多了实测调优的余量CUBE_BOUND_RATIO=0.85)。
### 5.2 SingleCoreM / SingleCoreN 的确定(每核输出 tile≥ BaseM/N
SingleCoreM × SingleCoreN 是每核每次处理的输出区域**不受 L0 容量直接约束**——一个 [SingleCoreM, SingleCoreN] tile 内部由若干 [BaseM, BaseN] L0 tile 组成$\text{SingleCoreM} \ge \text{BaseM}$$\text{SingleCoreN} \ge \text{BaseN}$)。SingleCoreM/N 的核心影响是 **GM→L1 搬移效率和 L2 重复读率**
- SingleCoreM/N 越大 单次 GML1 搬移量越大dValue 越有保障L2 中同一份 A 行带/B 列带被更多核复用
- SingleCoreM/N 越小 总块数 mCnt×nCnt 越多并行度越高但搬移效率降低
**SingleCoreM/N 对 L2 重复读的定量影响**
每行 A 行带 $[\text{singleCoreM}, K]$ 被该行的 $nCnt$ 个列块各读一次每列 B 列带被 $mCnt$ 个行块各读一次L2 层的总读取次数
$$
T_{L2} = \big(nCnt \cdot M + mCnt \cdot N\big) \cdot K \cdot dtype
$$
SingleCoreM/N 越小 $mCnt = \lceil M/\text{singleCoreM} \rceil$、$nCnt = \lceil N/\text{singleCoreN} \rceil$ 越大 $T_{L2}$ 越大
但这不直接等于 GM 重复读——L2 命中时重复读由 L2 吸收5.2TB/s不消耗 GM 带宽。**GM 重复读倍率 $r_{in}$ 取决于执行组划分**(§5.6只有当工作集超 L2 时才需要分组此时
$$
r_{in} = \frac{\lceil nCnt/n_{grp} \rceil \cdot M + \lceil mCnt/m_{grp} \rceil \cdot N}{M + N}
$$
$m_{grp}$、$n_{grp}$ L2 容量约束反推$m_{grp} \cdot \text{singleCoreM} + n_{grp} \cdot \text{singleCoreN} \le D$$D = L2/(B \cdot K \cdot dtype)$)。SingleCoreM/N 减小时 $m_{grp}$、$n_{grp}$ 按比例增大$\lceil mCnt/m_{grp} \rceil$ $\lceil nCnt/n_{grp} \rceil$ 的变化取决于具体数值——** L2 带宽不是瓶颈**5.2TB/s GM 1.6TB/s所以 SingleCoreM/N 对性能的主要影响不在 L2 重复读而在 **GM→L1 搬移效率**dValue min_TileSize**并行度**$mCnt \cdot nCnt \ge \lceil C/B \rceil$之间的权衡
*约束链*
**约束 1——并行度下限**总块数须填满 C
$$
B \cdot mCnt \cdot nCnt \ge C \iff mCnt \cdot nCnt \ge \Big\lceil \frac{C}{B} \Big\rceil
$$
**约束 2——L1 容量**双缓冲下驻留当前 tile 的输入
$$
2 \cdot (\text{singleCoreM} + \text{singleCoreN}) \cdot k_{L1} \cdot \text{dtype} \le L1,\qquad k_{L1} \cdot \text{dtype} \ge 256\text{B}
$$
**约束 3——搬移效率**dValue §1.4
$$
\underbrace{k_{L1} \cdot \text{dtype} \ge 256\text{B}}_{\text{A 矩阵 dValue}},\qquad \underbrace{\text{singleCoreN} \cdot \text{dtype} \ge 256\text{B}}_{\text{B 矩阵 dValue非转置}}
$$
$$
\underbrace{\text{singleCoreM} \cdot k_{L1} \cdot \text{dtype} \ge min\_TileSize}_{\text{A 单次搬移量}},\qquad \underbrace{k_{L1} \cdot \text{singleCoreN} \cdot \text{dtype} \ge min\_TileSize}_{\text{B 单次搬移量}}
$$
**约束 4——SingleCoreM/N 是 BaseM/N 的整数倍**工程实现要求保证 L0 tile 边界对齐)。
*选取策略***最少切分原则 + 切分时方形分配**。
*建模* batch $mCnt \cdot nCnt$ 每块的 GML1 搬入A $[\text{singleCoreM}, k_{L1}]$ + B $[k_{L1}, \text{singleCoreN}]$时延为 $(\text{singleCoreM} + \text{singleCoreN}) \cdot k_{L1} \cdot dtype / BW$。每核处理 $B \cdot mCnt \cdot nCnt / C$ 单核总搬入时延
$$
T_{MTE2} = \frac{MN}{C/B} \cdot \Big(\frac{mCnt}{M} + \frac{nCnt}{N}\Big) \cdot \frac{k_{L1} \cdot dtype}{BW}
$$
展开验证 batch 总搬入 = $(M \cdot nCnt + N \cdot mCnt) \cdot k_{L1} \cdot dtype$——A 矩阵每 batch $nCnt$ 个列块共享读 $nCnt$ B 矩阵被 $mCnt$ 个行块共享读 $mCnt$ 与直觉一致。)
目标函数 $mCnt/M + nCnt/N$ $mCnt = nCnt = 1$ 时取得**全局最小值**$1/M + 1/N$)——**不切分时每 batch A/B 只搬一次零重复读**。因此第一步是**尝试最少切分**
$$
mCnt \cdot nCnt = \Big\lceil \frac{C}{B} \Big\rceil \triangleq P
$$
多切无益切分越多重复读越多搬入量单调增大。)
**情形 1B ≥ CP = 1**——不切分$mCnt = nCnt = 1$$\text{singleCoreM} = M$、$\text{singleCoreN} = N$。**tile 跟随 M/N非方形**。前提是 L1 容量与 dValue 满足约束 2/3不满足时须 K 分块$k_{L1} < K$或退化为情形 2
**情形 2B < CP > 1**——必须切分 $mCnt \cdot nCnt = P$ 下最小化 $mCnt/M + nCnt/N$
$$
\frac{\partial}{\partial mCnt}\Big(\frac{mCnt}{M} + \frac{P}{mCnt \cdot N}\Big) = 0 \Rightarrow mCnt^* = \sqrt{\frac{P \cdot M}{N}},\; nCnt^* = \sqrt{\frac{P \cdot N}{M}}
$$
$$
\frac{mCnt^*}{nCnt^*} = \frac{M}{N} \Rightarrow \text{singleCoreM} = \text{singleCoreN} = \sqrt{\frac{MN}{P}}
$$
——**方形**连续松弛下的理论最优)。整数 + BaseM/N 对齐约束下取离方形最近的可行组合
*数值验证*M=2048、N=512、C=32
| B | P = ⌈C/B⌉ | 最优 (mCnt, nCnt) | sM × sN | 形状 |
|---|---|---|---|---|
| 32 | 1 | (1, 1) | 2048 × 512 | 跟随 M/N不切分 |
| 16 | 2 | (2, 1) | 1024 × 512 | 2:1整数约束偏离方形 |
| 8 | 4 | (4, 1) | 512 × 512 | 方形M/N=4 P=4 匹配 |
| 4 | 8 | (4, 2) | 512 × 256 | 2:1 |
B=32 时不切分 cost = 0.002441 < B=8 时方形 0.003906——**切分越少搬入越少方形只是"被迫切分"时的次优选择**。用户反例成立M=2048、N=512、B≥32 SingleCoreM=2048、SingleCoreN=512 优于方形 512×512 batch 零重复读)。
*此前推导的错误* $mCnt \cdot nCnt = P$ 当作固定等式且未讨论 P=1 的情形——方形结论只适用于 B < C 的强制切分场景不适用于 B C 的不切分场景
*SingleCoreM/N 的具体确定过程*host 端枚举与尾轮处理联动
1. 计算最少切分 $P = \lceil C/B \rceil$
2. **若 P = 1B ≥ C**先试不切分 $mCnt = nCnt = 1$、$\text{singleCoreM} = M$、$\text{singleCoreN} = N$由约束 2 $k_{L1} = \min(K,\; \lfloor L1/(2(M{+}N) \cdot dtype) \rfloor_{16})$检查约束 3$k_{L1} \cdot dtype \ge 256\text{B}$ 满足则确定不满足L1 放不下且 $k_{L1}$ 降无可降则进入步骤 3 强制切分
3. **若 P > 1必须切分——完整枚举**枚举不是随意挑几个组合试而是**遍历整个可行空间每个候选计算搬入时延取最优**
**a. 枚举空间(有界)**由约束 4 给出上界 $mCnt \le \lceil M/\text{BaseM} \rceil$、$nCnt \le \lceil N/\text{BaseN} \rceil$SingleCore 不能小于 Base约束 1 给出 $B \cdot mCnt \cdot nCnt \ge C$。候选数最多 $\lceil M/\text{BaseM} \rceil \times \lceil N/\text{BaseN} \rceil$ M=N=4096、Base=256 256 host 端遍历开销可忽略
**b. 每个候选的评估流水线**$(mCnt, nCnt) \to$ 对齐 $\to$ 约束过滤 $\to$ 目标值
$$
\text{singleCoreM} = \text{Align}_{16}\Big(\Big\lceil \frac{M}{mCnt} \Big\rceil\Big),\qquad \text{singleCoreN} = \text{Align}_{16}\Big(\Big\lceil \frac{N}{nCnt} \Big\rceil\Big)
$$
*约束 4 过滤*singleCoreM/singleCoreN BaseM/BaseN 的整数倍*约束 2 $k_{L1}$*
$$
k_{L1} = \min\Big(K,\; \Big\lfloor \frac{L1}{2 \cdot (\text{singleCoreM} + \text{singleCoreN}) \cdot dtype} \Big\rfloor_{16}\Big)
$$
*约束 3 过滤*$k_{L1} \cdot dtype \ge 256\text{B}$ $\text{singleCoreM} \cdot k_{L1} \cdot dtype \ge min\_TileSize$ $k_{L1} \cdot \text{singleCoreN} \cdot dtype \ge min\_TileSize$
*目标值§3 时延模型的搬移项)*
$$
T_{MTE2} = \frac{MN}{C/B} \cdot \Big(\frac{1}{\text{singleCoreM}} + \frac{1}{\text{singleCoreN}}\Big) \cdot \frac{k_{L1} \cdot dtype}{BW_{pc}}
$$
**c. 为什么目标函数是 $T_{MTE2}$**$T_{MMAD} = 2BMNK/(C \cdot Q_{16})$ $T_{FIX} = B \cdot MN \cdot outB/(C \cdot W_{pc})$ 只依赖全量 B/M/N/K**与切分无关**——候选之间的唯一差异在搬入时延(§3)。约束 2/3/4 只是可行性过滤**不足以选最优**多个可行解的 $T_{MTE2}$ 可差 2 (§6.3 源码过度切分示例
**d. 最优选取**通过全部约束的候选中取 $T_{MTE2}$ 最小者并列时取尾轮块数 $r = B \cdot mCnt \cdot nCnt \bmod C$ 最大者尾轮块越多,§5.3 重切的 $s^*$ 越小越省
4. **尾轮修正**$n_{wave} = \lceil B \cdot mCnt \cdot nCnt / C \rceil \le 3$ $r > 0$ 时,按 §5.3 计算重切因子 $s^*$,尾轮时延 $T_{tail} = T_{block}/s^*$;否则 $T_{tail} = T_{block}$r=0 时为 0
5. **端到端校验**$T = \max(T_{MTE2},\; T_{MMAD},\; T_{FIX}) + T_{tail}$。若选出的候选 $T_{MTE2} < T_{MMAD}$搬移被计算掩盖候选间差异失效此时以尾轮最优者为准
*完整实例*B=8、M=N=2048、K=1024、BF16BaseM=BaseN=256、$min\_TileSize$=16KB
- $P = \lceil 32/8 \rceil = 4$,进入步骤 3枚举空间 $mCnt, nCnt \le \lceil 2048/256 \rceil = 8$$8 \cdot mCnt \cdot nCnt \ge 32$
- 枚举与评估
| (mCnt, nCnt) | sM × sN | $k_{L1}$约束 2 上限16 对齐 | 约束 3 过滤 | $T_{MTE2} \propto (1/sM + 1/sN) \cdot k_{L1}$ |
|---|---|---|---|---|
| (2, 2) | 1024 × 1024 | 64128B | dValue < 256B | |
| (4, 2) | 512 × 1024 | 80160B | | |
| (4, 4) | 512 × 512 | 128256B | ✓(512×128×2 = 128KB 16KB | 0.500 |
| (8, 4) | 256 × 512 | 160320B | | 0.938 |
| (4, 8) | 512 × 256 | 160 | | 0.938 |
| (8, 8) | 256 × 256 | 256512B | | 2.000 |
$k_{L1}$ 上限公式$\lfloor 131072/(sM{+}sN) \rfloor$131072 = L1/(2·dtype) = 512KB/4B)。评估过程展示三个要点
1. **最少切分 (2,2) 不可行**$k_{L1} \le 64$ 元素 = 128B违反 dValue 256B——若只做约束 1并行度检查会在 (2,2) 上误判达标必须连同约束 2/3 一起过滤
2. **多个候选通过约束**(4,4)、(8,4)、(4,8)、(8,8) 都满足约束 2/3/4——**约束检查不足以选最优**必须计算目标函数
3. **目标函数 $T_{MTE2}$ 定最优**拉格朗日方形点 (4,4) 的搬入时延 0.500 最小 (8,4) 47%、 (8,8) 75%)。注意此处 $k_{L1}$ 是变量K=1024 未截断 L1 容量决定 $T_{MTE2} \propto (1/sM + 1/sN) \cdot k_{L1}$ 而非 §5.2 的简化式 $(1/sM + 1/sN)$。最终 (4,4)总块数 $8 \times 16 = 128$$r = 128 \bmod 32 = 0$ 完美整除无尾轮
*与源码的差异*源码用 cubeBound 模型 + balanceRate 0.9 剪枝不解耦 SingleCoreM/N BaseM/NbaseM=256 已是 SingleCore 级参数L0 tile stepM/stepN 二次切分且枚举目标为"算存比/负载均衡帕累托"而非"搬入时延最小"。理论的两层分离使约束链更清晰目标函数$T_{MTE2}$有闭式表达——SingleCoreM/N L1 + 并行度 + 搬移效率决定BaseM/N L0C 决定各司其职
### 5.3 mCnt / nCnt 与核间分配(含尾轮处理)
$$
mCnt = \Big\lceil \frac{M}{\text{singleCoreM}} \Big\rceil,\qquad nCnt = \Big\lceil \frac{N}{\text{singleCoreN}} \Big\rceil
$$
总输出块数 = $B \times mCnt \times nCnt$。核间分配采用 BMN 线性映射分析见 §块按 (b, m, n) 字典序编号 i 处理块 i, i+C, i+2C, …。B 不整除 C 时尾波不满载的核少分一块无需 B | C
**尾轮处理**host 端预处理 NPU 开销
**尾轮定义**
$B \cdot mCnt \cdot nCnt \;\%\; C \neq 0$ 总块数不能被 C 整除最后一波尾轮不满载——只有 $B \cdot mCnt \cdot nCnt \;\%\; C$ 个核有活干其余核空闲
**尾轮重切:是否值得?**
**关键前提tiling 在 hostCPU上完成不占 NPU 时间。** 重切只需 host 多算一套 tiling 参数下发给 NPU NPU 开销这改变了收益/代价的平衡——重切的代价仅为 host 端多一次枚举而收益是 NPU 端尾波时延的缩短
**不重切时**尾波 $r$ 个块用 $r$ 个核每核处理 1 时延 = $T_{block}$$C - r$ 核空闲
**重切时** $r$ 个块沿 M N 维再切 $s$ 变成 $r \cdot s$ 个小块$r \cdot s$ 个核各处理 1 小块时延 $T_{block}/s$每小块计算量/搬移量/写回量均为原块的 $1/s$)。
*重切收益*
$$
\Delta T_{saved} = T_{block} \cdot \Big(1 - \frac{1}{s}\Big)
$$
*占总时延比例*
$$
\frac{\Delta T_{saved}}{T_{total}} \approx \frac{1 - 1/s}{n_{wave}}
$$
$n_{wave} = 2$、$s = C/r$ $r=16$ 收益 25%$r=8$ 收益 37.5%。$n_{wave} = 3$ $r=16$ 16.7%$r=8$ 25%。**$n_{wave} \le 3$ 时收益显著应重切。**
*重切约束的推导*沿 N $s$ 份后小块变为 $[\text{singleCoreM},\; \text{singleCoreN}/s]$。小块须满足两类约束——**但约束的严格程度取决于算子是访存 Bound 还是计算 Bound**。
**访存 Bound$T_{MTE2} \ge T_{MMAD}$**搬移是瓶颈小块的 dValue 和搬移量不能降——否则搬移更慢总时延反而增加约束与主 tile 相同
1. **dValue 下限**B 矩阵 dValue = $\text{singleCoreN}_{tail} \cdot dtype \ge 256\text{B}$沿 N 切时
$$
s \le \frac{\text{singleCoreN} \cdot dtype}{256\text{B}}
$$
2. **单次搬移量**$k_{L1} \cdot \text{singleCoreN}_{tail} \cdot dtype \ge min\_TileSize$
$$
s \le \frac{k_{L1} \cdot \text{singleCoreN} \cdot dtype}{min\_TileSize}
$$
3. **对齐**$\text{singleCoreN}_{tail} \ge 16$
$$
s \le \frac{\text{singleCoreN}}{16}
$$
**计算 Bound$T_{MMAD} > T_{MTE2}$**搬移被计算掩盖dValue 和搬移量约束可放宽——即使搬移效率降低只要搬移时间仍 计算时间总时延不变放宽的定量依据
小块计算时延 $T_{comp}^{tail} = 2 \cdot \text{singleCoreM} \cdot \text{singleCoreN}_{tail} \cdot K / Q_{16}$搬移时延 $T_{load}^{tail} = (\text{singleCoreM} + \text{singleCoreN}_{tail}) \cdot k_{L1} \cdot dtype / BW_{pc}$。搬移被掩盖的条件
$$
T_{load}^{tail} \le T_{comp}^{tail} \iff \text{singleCoreN}_{tail} \ge \frac{\text{singleCoreM} \cdot k_{L1} \cdot dtype \cdot Q_{16}}{BW_{pc} \cdot (2 \cdot \text{singleCoreM} \cdot K - k_{L1} \cdot dtype \cdot Q_{16} / BW_{pc})}
$$
记右端为 $x_{min}$搬移掩盖下限则计算 Bound 下的约束为
$$
s \le \min\Big(\frac{\text{singleCoreN}}{x_{min}},\; \frac{\text{singleCoreN}}{16}\Big)
$$
**计算 Bound 越严重K 越大),$x_{min}$ 越小,$s$ 上限越大**——极端情况 $x_{min} \to 16$对齐底线$s \le \text{singleCoreN}/16$。
*例*singleCoreM=singleCoreN=256、$k_{L1}$=128、K=1024、BF16$x_{min} = 256 \times 128 \times 2 \times 15.2 \times 10^{12} / (50 \times 10^9 \times (2 \times 256 \times 1024 - 128 \times 2 \times 15.2 \times 10^{12} / 50 \times 10^9)) \approx 1$——几乎无约束$s \le 256/16 = 16$。
*最优切分因子*先判定 Bound 类型$T_{MMAD}$ vs $T_{MTE2}$再选约束集
$$
s^* = \min\Big(\Big\lfloor \frac{C}{r} \Big\rfloor,\; s_{max}\Big),\qquad s_{max} = \begin{cases} \min\big(\frac{\text{singleCoreN} \cdot dtype}{256\text{B}},\; \frac{k_{L1} \cdot \text{singleCoreN} \cdot dtype}{min\_TileSize},\; \frac{\text{singleCoreN}}{16}\big) & \text{访存 Bound} \\ \frac{\text{singleCoreN}}{16} & \text{计算 Bound} \end{cases}
$$
*例*C=32、$N_{blk}=40$、$n_{wave}=2$、$r=8$、singleCoreM=singleCoreN=256、$k_{L1}$=128、BF16
- **访存 Bound**K dValue $s \le 2$搬移量 $s \le 4$对齐 $s \le 16$。$s^* = 2$——dValue 瓶颈16 核满载尾波减半总时延节省 25%。
- **计算 Bound**K $x_{min} \approx 1$$s^* = \min(4, 16) = 4$——32 核满载尾波降为 1/4总时延节省 37.5%。
*切分方向选择*优先沿 N 保持 A 行带完整L2 A 数据不变)。计算 Bound 下若 N 向对齐卡住$\text{singleCoreN}/16 < C/r$改沿 M A 矩阵 dValue = $k_{L1} \cdot dtype$ 不变仅受对齐约束 $\text{singleCoreM}/16$)。
**结论**$n_{wave} \le 3$ $r < C$ 时应重切尾轮——host 端零代价NPU 端收益 $T_{block}(1-1/s^*)$。$n_{wave} \ge 4$ 时收益 < 25%可不重切通过选择使尾波占比小的 mCnt/nCnt 组合来优化)。
**尾轮影响的量化**
设总块数 $N_{blk} = B \cdot mCnt \cdot nCnt$总波数 $n_{wave} = \lceil N_{blk} / C \rceil$尾波块数 $r = N_{blk} \bmod C$$r = 0$ 时无尾波)。
**不重切时**尾波导致的额外时延相对于完美整除
$$
\Delta T_{tail} = \begin{cases} 0 & r = 0 \\ T_{block} \cdot \big(1 - \frac{r}{C}\big) & r > 0 \end{cases}
$$
**重切后**尾波时延降为 $T_{block}/s^*$,额外时延:
$$
\Delta T_{tail}^{re} = \frac{T_{block}}{s^*} \cdot \Big(1 - \frac{r \cdot s^*}{C}\Big)
$$
当 $r \cdot s^* = C$(完美重切)时 $\Delta T_{tail}^{re} = 0$——尾波完全消除。
---
### 5.4 核间切分维度选择(按共享代价从低到高)
切 B零共享先试→ 切 M右矩阵 $KN\cdot\text{dtype} \le L2$ 则驻留 L2→ 切 N对称→ 混合切(靠 swizzle + L2 切分管理)→ 降核(见 Step 8
### 5.5 swizzle——ASW 滑窗蛇形
**问题**:核间切 M/N 后,同一时刻 C 个核各算一个输出块,它们所需的 A 行块与 B 列块集合就是当前"活跃工作集"。若按行优先顺序朴素分配,一波 C 个块横跨的 A 行、B 列很宽,活跃工作集超过 L2 就回 GM 读1.6TB/s重复读代价真实发生。**swizzle 要做的就是编排输出块的执行顺序,把每一波核的活跃工作集压到最小。**
**做法**:把 M 向每 W 个基本块划为一个"窗口",遍历顺序为"窗口内先扫 M、扫满 W 行再进下一列 N一个窗口扫完再进下一个窗口",且奇数窗口行 N 向反向(蛇形)。效果有二:
* 同一波 C 个核的块集中在同一个窗口内 ⇒ 活跃 A 行块只有 W 个、活跃 B 列块只有 C/W 条带;
* 蛇形反向使相邻窗口行首尾相接——上一窗口末尾的 B 列带与下一窗口开头的 B 列带是同一条,跨窗口切换时工作集增量最小。
**W 怎么取**:一波 C 个块的 L2 足迹约为
$$
footprint \approx \big(W \cdot M^t K + \tfrac{C}{W} \cdot K N^t\big) \cdot \text{dtype}
$$
由均值不等式,$W + C/W$ 在 $W = \sqrt{C}$ 处取最小——窗口越接近"方形"W 行 × C/W 列),足迹越小。同时 W 须整除 C保证每个窗口恰好被整数波核覆盖、窗口边界不把波次切碎。合起来即
$$
W = \max\{\,d \mid d \mid C,\; d \le \lfloor\sqrt{C}\rfloor\,\}
$$
C=32 时 $\sqrt{32} \approx 5.66$,因子 {1,2,4,8,…} 中不超过它的最大者是 4故 W=4。
**实例**C=32W=4M̃=8Ñ=8数字为块的全局执行顺序一波 32 块):
```
窗口0N 正向) 窗口1N 蛇形反向)
ν0 ν1 ν2 … ν7 ν0 ν1 … ν7
μ0 0 4 8 … 28 μ4 60 56 … 32
μ1 1 5 9 … 29 μ5 61 57 … 33
μ2 2 6 10 … 30 μ6 62 58 … 34
μ3 3 7 11 … 31 μ7 63 59 … 35
```
块 31 = (μ3, ν7),块 32 = (μ4, ν7)——相邻两个块共用同一条 B 列带ν7窗口切换几乎零增量。对比朴素行优先Ñ=16 时):一波横跨 2 个 A 行块 + 16 条 B 列带,足迹 (2·M^t + 16·N^t)·K·dtype滑窗为 (4·M^t + 8·N^t)·K·dtype——M^t≈N^t 时足迹缩小 1/3。
**窗内为什么不蛇形**(对上例中"块 3=(μ3,ν0) → 块 4=(μ0,ν1) 而非 (μ3,ν1)"的说明):蛇形的收益来自"相邻遍历段共享边界数据",要分两种边界看:
* **窗内列间边界**ν0→ν1相邻两段共享的是同一组 A 行块W 个),它们在整个窗口期间**全程驻留 L2**,无论按什么顺序扫,工作集不变——窗内蛇形零收益;
* **窗口行边界**窗口0→窗口1A 行整体换血μ0..3 → μ4..7),此时 B 列带的连续性决定换血成本——不蛇形则下一窗口从 ν0 开始LRU 上最久未用、早已被挤出 L2 的冷带),蛇形则延续上一窗口末尾的 ν7最热线带。**蛇形只标在窗口行号上**(源码 `BatchMatMulAswBlock::UpdateBasicIndex`:仅 `rowIdx` 为奇时 n 反向,窗内 m 最快序不反向),正是这个收益结构的直接实现。
### 5.6 L2 分组(工作集超 L2 时)
**切的是什么**:将 mCnt×nCnt 个基本块划分为若干**执行组**——每组覆盖输出平面上一个连续矩形区域(若干 singleCoreM × singleCoreN 基本块的集合),使该组所需的 A 行带 + B 列带输入工作集 ≤ L2 可用读入空间;组内所有基本块算完再进下一组,输入只在跨组时换一次。
**为什么需要它**:滑窗压缩的只是"同一波"的足迹;若整个工作集超 128MB L2跨波次复用落空——上一波窗口的 A 行早被挤出,下一波又得回 GM 读。且 L2 是**读写共用**的:输出经 fixpipe 写出时若驻留 L2dirty会压缩读入可用空间若直写 GM则占用与读共享的 1.6TB/s 总线。所以 L2 切分必须与写出策略联合决策。记输入总量 $S_{in} = B(MK+KN)\cdot\text{dtype}$,输出总量 $S_{out} = B \cdot MN \cdot outB$。
**两个不变量**(一切分析的起点):
* GM 流量下界 $= S_{in} + S_{out}$:输入至少读一遍、输出最终至少要写一遍到 GM与 L2 策略无关;
* L2 读入可用空间:$L2_{read} = L2 - S_{out}^{resident}$$S_{out}^{resident}$ 为驻留 L2 的输出量)——写出驻留 L2 会压缩读入空间,这是写出策略影响读入复用的通道。
**重复读倍率**$r_{in}$ = GM 输入流量 / $S_{in}$。$r_{in} = 1$ 表示每个输入数据从 GM 只读一遍(后续复用全在 L2 命中)——这是 GM 输入流量的下界L2 管理的全部目标就是让 $r_{in}$ 尽量接近 1。
**先判定写出会不会 Bound**。平均写出带宽需求:
$$
BW_{out} = \frac{S_{out}}{T_{MMAD}} = \frac{B \cdot MN \cdot outB}{2BMNK\,/\,(C \cdot Q_{16})} = \frac{C \cdot Q_{16} \cdot outB}{2K}
$$
只与 K、outB 有关K 越小单位时间输出越密。例BF16 输出C·Q₁₆=432 TFLOPSK=512 → 844 GB/sK=256 → 1.69 TB/s已超 GM 总线——此时**任何策略都写出 Bound**L2 缓冲只能削峰fixpipe 以 5.2TB/s 写 L2 吸收突发),平均速率仍受总线限制,应预期 Fixpipe 成为 $T_{total}$ 的 max 项。
**分场景决策**
**场景 A$S_{in} + S_{out} \le L2$(全驻留)**。输入读一遍($r_{in}=1$),输出驻留 L2dirty异步回写 GM——写出走 5.2TB/s L2 写口,不与读争,也削平了 GM 写突发。无需切分。
**场景 B$S_{in} \le L2$ 但 $S_{in} + S_{out} > L2$(输入能驻留,加上输出超了)**。策略:**输入驻留、输出直写 GM**。理由链:
1. $S_{in} \le L2$ ⇒ 全部输入可驻留 L2跨波次复用全部命中 ⇒ $r_{in} = 1$GM 输入流量达到下界 $S_{in}$
2. 输出在本算子内只写不读、零复用收益;若输出也驻留 L2dirty超出 L2 的部分会把输入挤出——被挤出的输入后续得回 GM 重读 ⇒ $r_{in} > 1$GM 流量超出下界;
3. 故让输出直写 GMfixpipe L0C→GM不占 L2把 128MB 全部留给输入,保住 $r_{in} = 1$——GM 总流量保持下界 $S_{in} + S_{out}$
4. 代价是输出即刻占用 GM 写带宽(与读共享总线),须校验总线不爆:$(S_{in} + S_{out})/T_{MMAD} \le W_{GM}$。
B=8、M=N=4096、K=512、BF16——$S_{in}$≈67MB ≤ L2$S_{out}$≈268MB 直写 GMT_MMAD≈318µs总流量速率 (67+268)MB/318µs ≈ 1.05TB/s < 1.6TB/s ✓。
**场景 C$S_{in} > L2$(输入本身超)**需要分组执行—— mCnt×nCnt 个基本块划分为若干**执行组**每组内所有基本块的输入工作集不超过 L2 可用空间输出直写 GM不占 L2 读入空间)。
**L2 的软件可控手段**L2 Cache 而非 Buffer软件无法精确控制"哪些数据在 L2 "。可用的控制手段
- **Cache Hint**`SetL2CacheHint`标记输入为 allocate读入 L2 non-allocate直读 GM 不过 L2标记输出为 non-allocate直写 GM 不占 L2
- **CMO**Cache Maintenance OperationPrefetch/Writeback/Invalidate在关键节点主动管理 L2 内容
- **执行顺序**swizzle通过编排基本块的执行顺序控制同一时刻的活跃工作集——**这是最主要的 L2 管理手段**。
**执行组的划分**
*问题*mCnt×nCnt 个基本块每块输出 singleCoreM×singleCoreN按什么粒度分组使每组的输入工作集 L2
*每组输入工作集*一组覆盖 M $m_{grp}$ 个基本块N $n_{grp}$ 个基本块即覆盖输出区域 $[m_{grp} \cdot \text{singleCoreM},\; n_{grp} \cdot \text{singleCoreN}]$。该区域需要读入的输入
$$
WS_{grp} = B \cdot K \cdot \big(m_{grp} \cdot \text{singleCoreM} + n_{grp} \cdot \text{singleCoreN}\big) \cdot \text{dtype} \;\le\; L2
$$
*目标*最小化组数组数越少输入从 GM 的重复读次数越少)。每行 A $n_{grp}$ 个组各读一次每列 B $m_{grp}$ 个组各读一次
$$
r_{in} = \frac{n_{grp} \cdot M + m_{grp} \cdot N}{M + N}
$$
*求解*约束 $m_{grp} \cdot \text{singleCoreM} + n_{grp} \cdot \text{singleCoreN} \le D$其中 $D = L2/(B \cdot K \cdot \text{dtype})$最小化 $n_{grp} \cdot M + m_{grp} \cdot N$。最优在组内 M/N 向基本块数与输出平面形状成正比时取到
$$
m_{grp} = \Big\lfloor \frac{D}{2 \cdot \text{singleCoreM}} \Big\rfloor,\qquad n_{grp} = \Big\lfloor \frac{D}{2 \cdot \text{singleCoreN}} \Big\rfloor
$$
总组数 $= \lceil mCnt/m_{grp} \rceil \times \lceil nCnt/n_{grp} \rceil$。
**组内 swizzle**每组内部按 ASW 滑窗蛇形执行窗口 $W = \max\{d \mid d \mid C,\; d \le \lfloor\sqrt{C}\rfloor\}$C=32 W=4保证同一波 C 个核的活跃工作集最小组间切换时输入整体换入——上一组的 A 行带和 B 列带全部失效 GM 重新读入下一组的数据
*例*B=64、M=N=2048、K=1024、BF16、singleCoreM=singleCoreN=256$S_{in} = 64 \times (2048 \times 1024 + 1024 \times 2048) \times 2 = 537\text{MB} > 128\text{MB}$。$D = 128\text{MB}/(64 \times 1024 \times 2\text{B}) = 1024$ 元素。$m_{grp} = \lfloor 1024/(2 \times 256) \rfloor = 2$$n_{grp} = 2$。每组覆盖 $[512, 512]$ 的输出区域,工作集 $= 64 \times 1024 \times (512+512) \times 2 = 128\text{MB} = L2$,恰好装满。总组数 $= (2048/256/2)^2 = 16$。$r_{in} = (2 \times 2048 + 2 \times 2048)/(2048+2048) = 2$——每行 A 被读 2 次,每列 B 被读 2 次。
块内分配用**错位分核**(对角线分配):线性块号先取 mn 方向叠加随块号递增的相位偏移,使同一时刻各核落在 M×N 平面的不同对角线上——避免多核同一拍并发读同一行 A / 同一列 B 的同一地址同地址并发读会串行化等效带宽打折。例8 核、4×4=16 个基本块k0~k7 为核号):
```
行优先(不错位): 错位分核(对角线):
n0 n1 n2 n3 n0 n1 n2 n3
m0 k0 k1 k2 k3 m0 k0 k4 . .
m1 k4 k5 k6 k7 m1 . k1 k5 .
m2 . . . . m2 . . k2 k6
m3 . . . . m3 k7 . . k3
同一波A 行带 m0 被 k0~k3 同读 同一波:每行带、每列带
4 路同地址冲突) 最多 2 核同读(冲突 4→2
```
冲突度量与优选规则:
$$
transConflict = \max\big(\lceil C / mCnt \rceil,\; \lceil C / nCnt \rceil\big) \le 6
$$
即同一时刻并发核访问同一 A/B 块的最大冲突数不超过阈值(经验值 6切分方案中优先选尾波不满载占比小拖尾 < 一半遍历大方向由 calOrder 决定0=M 优先1=N 优先按形状选共享矩阵更能驻留 L2 的方向
补充若输出会被后续算子立即消费融合场景输出驻留 L2 让下游读命中场景 B/C 的策略反过来本文按单算子边界分析
### 5.7 核内 tilingBaseM/BaseN/BaseK——L0 级 tile受 L0 容量直接约束)
$\text{BaseM} \times \text{BaseN} \times 4\text{B} \times DB \le L0C$$\text{BaseM} \times k_{L0} \times \text{dtype} \times 2 \le L0A$、$k_{L0} \times \text{BaseN} \times \text{dtype} \times 2 \le L0B$内轴按 dValue 256B/512B 对齐SingleCoreM/N 内部按 BaseM/BaseN 进一步切分为 L0 tile 逐个计算L1 按容量开双缓冲余量充足开 4 buffer
### 5.8 内部特化(参数极限,不是独立分支)
单边无 batch 且该侧矩阵小$M \le 256$、$MK\cdot\text{dtype}\cdot 2 \le L1$、对侧每核循环 4 时小侧整个常驻 L1只搬一次L1 全载)。
### 5.9 降核模式实现
tiling `usedCoreNum = ⌈P⌉`不强制 CSingleCoreM/N L0C 容量内取最大$\text{singleCoreM} \times \text{singleCoreN} \times 4\text{B} \le L0C$每核按标准核内流水L1L0CubeL0CFixpipe处理自己的输出块核间无共享无依赖无需 swizzle L2 切分降核后 GM 并发搬移核数若 < minCoreNum带宽利用率上限被压低——这正是降核区 case 时延的瓶颈所在也是"时延绝对值小不再继续优化"的定量注脚
---
## 六、与源码实现的对比
源码参考 [cann-ops-nn](https://gitcode.com/cann/ops-nn/tree/master/matmul/batch_mat_mul_v3)DAV_3510/arch35)。以下按实现方案的 Step 0-8 逐维度对比
### 6.1 进入条件IsCapable
**源码**`batch_matmul_v3_asw_basic_tiling.cpp` IsCapable
```cpp
// 源码https://gitcode.com/cann/ops-nn/tree/master/matmul/batch_mat_mul_v3
1. A/B 非连续转置状态一致(混合则拒绝)
2. BatchA == BatchB(广播由 ITER_BATCH_BROADCAST 承接)
3. batchBias <= 1
4. dtype FP16/BF16
// 无其他条件——兜底
```
| 维度 | 理论 | 源码 | 差异 |
|---|---|---|---|
| 并行度校验 | $P = B \cdot MN \cdot 4B / L0C \ge C$ | **无** | 源码不检查 P靠优先级序靠前分支截胡 |
| 降核模式 | $P < C$ usedCoreNum = ⌈P⌉ | **无** | GetNumBlocks() 固定返回 32 |
| batch 结构 | 无限制 | BatchA == BatchB | 源码排除广播由分支 4 承接 |
**影响**源码缺少降核分支P < C case 进入 ASW_Basic 所有 32 核仍参与调度但部分核无实际工作——引入不必要的调度开销理论上这些 case 应走降核模式usedCoreNum = ⌈P⌉
### 6.2 BaseM / BaseN 确定§5.1
**源码流程**`MatMulV3TilingHelper::GetRebalanceBlock``matmul_v3_tiling_helper.cpp` L387-492
*第 1 步——平台指标*动态读 platformInfo适配不同产品形态
$$
hbmBW = freq \times aicNum \times ddrRate/1024,\qquad l2BW = freq \times aicNum \times l2Rate/1024
$$
$$
computePower = freq \times 8 \times aicNum \; (\text{BF16}),\quad \text{FP32 } /16
$$
*第 2 步——cubeBoundEdge 闭式计算*L418-419
$$
edge = \underbrace{\frac{l2BW}{computePower}}_{\text{① L2 满速供数基准}} + \underbrace{l2CacheUsage \cdot \Big(1-\frac{l2BW}{hbmBW}\Big) \cdot cmr}_{\text{② L2 工作集超容惩罚}} - \underbrace{\frac{1 + l2BW/hbmBW}{kValue}}_{\text{③ K 向复用修正}}
$$
其中 $cmr = (M{+}N)/(MN)$、$l2CacheUsage = \max(B \cdot (M{+}N) \cdot K \cdot dtype/L2,\; 1)$。判据**每输出块相对搬运量** $1/baseM + 1/baseN \le edge$ tile 处于计算 Bound
三项物理含义第一性推导
1. ** 基准**单核计算 $2MNK$ FLOP 时长为 $2MNK/Q_{16}$搬运 $(M{+}N)K \cdot dtype$ 字节时长为 $(M{+}N)K \cdot dtype/l2BW$。计算 Bound $2MNK/Q_{16} \ge (M{+}N)K \cdot dtype/l2BW$ $1/M + 1/N \le 2 \cdot l2BW/(Q_{16} \cdot dtype)$—— 同构$2/dtype$ 常数吸收进后面的 CUBE_BOUND_RATIO 余量)。
2. ** 工作集超 L2 时抬高 edge 倾向更小 tile**$l2CacheUsage > 1$(工作集 $B(M{+}N)K \cdot dtype$ 超过 L2时实际供数带宽部分掉到 HBM$hbmBW < l2BW$)。 edge 使判定放宽——较小 tile工作集小L2 命中率高供数更接近 l2BW也能通过计算 Bound 判定。**若坚持大 tile工作集更大 L2 更多供数掉到 HBM反而访存 Bound**。
3. ** K 大时压低 edge 倾向更大 tile**K 大计算量大天然计算 Bound可接受更大 tile更小搬运量而不触访存 Bound
*第 3 步——枚举与评分*L448-483
- 候选上界由多重约束闭式卡出L0A/L0B 容量minKL0)、baseMNBufferLimitL0C/UB 面积)、bias tableK 内轴 512B 对齐BMM 固定)、shape内存 Bound 时对齐粒度 128计算 Bound 64
- 枚举 baseMbaseN 从大到小递减两层剪枝
- **skipCond**最优解已 balance 0.9 若当前 param 更差tile 更小且超过 edge访存 Bound)→ 跳过param 单调增后续更差
- **FP32**baseM/baseN < 64 且块数 > 核数 → 跳过
- 更新评分(帕累托双目标):
- **cubeBoundCond**$param \le edge$ 且 balance 更高 → 更新,并把 edge 收紧为 param**单调收敛**:进入计算 Bound 区域后只接受同样计算 Bound 且更均衡的解)
- **balanceCond**:综合分 $param/balanceRate$ 更小者胜(单位负载均衡率的搬运代价),相等取更均衡者
- 收尾GetBaseKK 全载或 256B/128B/64B/32B/16 递减、usedCoreNum = min(batch×mCore×nCore, 核数)、dbL0C 按容量置 2/1
**为什么源码这么做(设计考虑)**cubeBound 是 **host 端闭式解析模型**,把"该 tile 是否计算 Bound"做成一次不等式比较替代实测调优edge 随 shapeK、工作集和平台带宽/频率)自适应;双目标帕累托保证解在"算存比"与"负载均衡"之间取平衡。
**与理论最优的优劣判定**
| 维度 | 理论最优§5.1 | 源码 | 判定 |
|---|---|---|---|
| tile 面积 | L0C/4B = 65536单缓冲满 | baseM/baseN 上限 256×256 = 65536 | ✓ 一致 |
| 形状 | 方形优先L0A=L0B 同时装满、baseK 最大) | 枚举 M/N 独立递减,不强制方形 | ⚠️ 源码可能产出非方形 base如 256×192L0B 未满、baseK 被小边限制——可加方形约束改进 |
| baseK | min(L0A/2·BM, L0B/2·BN) 向下 16 对齐 | GetBaseKK 全载或 256B/128B/64B/32B/16 递减 | ✓ 一致 |
| 访存/计算 Bound 判定 | §5.2 用粗粒度 Bound 判定(分支级) | cubeBound 三项解析模型tile 级) | ⚠️ 源码更精细,理论可吸收 |
| 负载均衡 | §5.3 尾轮重切 | balanceRate 尾块感知评分 | 见 §6.8 |
**结论**:两者在 tile 面积上殊途同归(都取 L0C 单缓冲满),理论用"方形优先"推导、源码用枚举寻优。源码的 cubeBound 模型是理论"算存比判定"在 tile 粒度上的精细实现值得理论吸收源码的不足是枚举不保持方形、baseM/N 硬上限 256 使 per-core tile 无法超过 L0 容量(见 §6.3)。
### 6.3 SingleCoreM / SingleCoreN 确定§5.2
**源码事实singleCore 与 base 是同一个参数——源码没有分层概念。**
证据链:`ResetBaseDav3510`L144-149`baseM = baseN = 256``singleCoreM = baseM``CalL1TilingDefault`L77-78`singleCoreM = runInfo.baseM``singleCoreN = runInfo.baseN`;枚举后 `stepM = CeilDiv(singleCoreM, baseM) = 1``stepN = 1`。即**每核 tile = L0 tile = baseM×baseN≤ 256×256**,不存在"SingleCore 内部多个 L0 tile 多轮计算"的结构。
**源码做法**baseM/baseN 由 GetRebalanceBlock 枚举确定§6.2 的 cubeBound 模型 + balanceRate ≥ 0.9 剪枝 + 尾块感知评分),一次枚举同时决定"每核 tile 大小"与"L0 tile 大小"——因为两者不区分。
**为什么源码这么做(设计考虑)**:单一参数简化 tiling 生成与 kernel 实现——kernel 只需处理"每核一个 baseM×baseN tile"的循环,无需 stepM/stepN 多轮嵌套。代价是**每核 tile 被 L0 容量(≤ 256×256硬性限制**。
**与理论最优的优劣判定**(关键差异):
| 维度 | 理论最优§5.2 | 源码 | 判定 |
|---|---|---|---|
| 分层 | SingleCoreM/N ≥ BaseM/N 显式两层 | singleCore = base单层 | ⚠️ 源码是理论的退化特例 |
| SingleCore 上限 | L1 容量约束(可远超 256 | L0C 面积(≤ 256×256 | ⚠️ 差异 |
| mCnt/nCnt | 最少切分 ⌈C/B⌉ | ⌈M/baseM⌉×⌈N/baseN⌉base ≤ 256 | ⚠️ 差异 |
| 依据 | 搬入时延最小化§5.2 拉格朗日) | cubeBound + balanceRate 枚举 | 部分一致 |
**定量示例**B=64、M=N=2048、K=512、BF16ASW 承接的 B≥C 且 IterBatch/MergeBatch 不满足的 case
- 理论:$P = \lceil C/B \rceil = 1$,先试不切分——$mCnt = nCnt = 1$、singleCoreM = 2048、singleCoreN = 2048。L1 检查:$2 \times (2048{+}2048) \times k_{L1} \times 2\text{B} \le 512\text{KB}$ ⟹ $k_{L1} \le 32$ 元素 = 64B < 256B 不满足 dValue。**K 分块仍不满足则退化**$k_{L1} \ge 128$256B)⟹ $2 \times 4096 \times 128 \times 2 = 2\text{MB} > 512\text{KB}$ 溢出 ⟹ 不切分不可行,进入情形 2B < C 式切分此时 B C L1 放不下实际须切 M/N $mCnt \times nCnt \ge \lceil C/B \rceil$ 不成立——真正约束是 L1$2(mCnt 方向的分块)$… 精确说理论在 L1 约束下求最小 mCnt×nCnt$2 \cdot (\text{singleCoreM} + \text{singleCoreN}) \cdot k_{L1} \cdot dtype \le L1$ $mCnt \cdot nCnt$ 的联合 $k_{L1}=128$$\text{singleCoreM} + \text{singleCoreN} \le 512\text{KB}/(2 \times 128 \times 2) = 1024$。方形分配 singleCoreM = singleCoreN = 512mCnt = nCnt = 4 16 /核分配 64×16=1024 块到 32 搬入时延 mCnt/M + nCnt/N = 4/2048×2 = 0.0039
- 源码baseM = baseN = 256L0 上限mCnt = nCnt = 2048/256 = 8 64 /batch64×64=4096 搬入时延 8/2048×2 = 0.0078——**是理论的 2 **。
**结论** L1 容量富余K 理论的最少切分原则允许 singleCoreM/N 超过 256上例 512mCnt/nCnt 减半GML1 总搬入时延重复读减半源码的 singleCore = base 256 硬上限导致**过度切分**L2 重复读翻倍源码改进方向引入 SingleCore/Base 分层SingleCore L1 容量决定上限解除 L0 约束stepM/stepN 多轮 L0 计算这是理论对源码**最实质的一条改进建议**。
### 6.4 mCnt / nCnt 与核间分配§5.3
**源码做法**
$$
mCnt = \Big\lceil \frac{M}{baseM} \Big\rceil,\qquad nCnt = \Big\lceil \frac{N}{baseN} \Big\rceil
$$
核映射`UpdateBasicIndex``batch_mat_mul_v3_asw_block_advanced.h`)——`index = blockIdx + round × usedCoreNum``matIndex = index % (mCnt × nCnt)` 解出 batch (m, n)BMN 线性字典序与理论 §4 的线性映射一致`batchIdx = index / (mCnt × nCnt)` 天然支持广播usedCoreNum = min(batch×mCore×nCore, aicNum)GetRebalanceBlock L489)——**注意源码也有"降核"** batch×mCnt×nCnt < aicNum usedCoreNum 自动收缩 §6.1 所述"无降核"更准确源码无**独立的降核 tiling 模板** GetRebalanceBlock 收尾会把 usedCoreNum 压到实际块数
**与理论最优的优劣判定**
| 维度 | 理论最优 | 源码 | 判定 |
|---|---|---|---|
| 切分数 | 最少切分mCnt×nCnt = ⌈C/B⌉§5.2 | mCnt = ⌈M/baseM⌉baseM 256 固定 | 源码过度切分(§6.3 定量搬入时延 2 |
| 长宽比 | B < C 时方形分配拉格朗日 | baseM/baseN 枚举自然产生 | 一致方形成分由 256×256 上限保证 |
| 核间分配 | BMN 线性映射 | 一致 | |
| 降核 | P < C usedCoreNum = ⌈P⌉§5.9 | usedCoreNum = min(batch×m×n, aicNum) | 一致实现路径不同 |
**尾轮处理对比**对应 §5.3
- 源码 `GetBalanceRateWithTail`L223-246尾块感知的负载均衡率用于**枚举评分**——mainRound = ⌈totalRound/C⌉-1尾轮按 拆两维估算totalTailSplit = FloorDiv(usedCoreNum, 尾块数)$tailRound = 1/(\sqrt{t} \times (\sqrt{t}+offset-1))$$rate = (MN/C)/((mainRound+tailRound) \cdot baseM \cdot baseN)$。** BMM 场景batchInfo nullptr直接短路为简单比率**L237-240$rate = (B \cdot MN/C)/((mainRound+1) \cdot baseM \cdot baseN)$)——尾轮不做拆分估算理由是 batch 维已摊薄尾块效应
- 源码**不实际重切尾轮**只通过枚举选择尾块占比小的 (baseM, baseN) 组合
- 理论(§5.3$n_{wave} \le 3$ host 端计算切分因子 $s^*$ 并实际重切尾轮下发第二套 tiling 参数收益 $T_{block}(1-1/s^*)$。
**判定**理论更优源码对 BMM 一律不做尾轮拆分含估算对小 $n_{wave}$2~3 case 尾轮浪费可达 25%~50% 核时理论的重切方案 host 端零 NPU 代价收益可量化访存 Bound dValue 约束计算 Bound 可到对齐底线)。理论方案可作为源码改进建议
### 6.5 Swizzle§5.5
| 维度 | 理论 | 源码 |
|---|---|---|
| 窗口大小 | $W = \max\{d : d \mid C, d \le \lfloor\sqrt{C}\rfloor\}$ | `GetAswWindowLen`逻辑一致 |
| 蛇形 | 窗口行间 N 向反向窗内不蛇形 | 一致`rowIdx % 2 != 0` n 反向 |
| 尾窗 | 未提及 | tailWindow 分支处理 mCnt 不整除窗长 |
**结论**swizzle 实现与理论一致源码的窗长公式与理论 $W = \max\{d : d \mid C, d \le \lfloor\sqrt{C}\rfloor\}$ 完全相同但源码对细长 shapemCnt 小或 nCntmCnt的方形窗假设不成立时退化为行优先`mainWindow = min(aswWindowLen, mCnt)`未按 shape 长宽比自适应窗形
### 6.6 L2 管理§5.6
| 维度 | 理论 | 源码 |
|---|---|---|
| 启用条件 | $S_{in} > L2$ 时分组 | isBigSize(>100MB) && cBatchDimAll < usedCoreNum && transConflict 6 |
| 冲突度量 | transConflict 6 | 一致 |
| 写出策略 | $S_{in} \le L2 < S_{in}+S_{out}$ 时输出直写 GM | enableL2Cache flag + SetL2CacheHint无显式输出 non-allocate 决策链 |
| 尾波控制 | 优先选尾波不满载占比小的方案 | TAIL_CONFLICT_RATIO = 0.5 |
**差异分析**源码有两个额外限制
1. **100MB 阈值**小于 100MB case 不启用 L2 cache 管理——理论上 $S_{in} \le L2 = 128$ MB 时不需要分组100MB < 128MB 留有余量合理但是经验值
2. **cBatchDimAll < usedCoreNum**batch 数小于核数时才启用——B C 时不做 L2 分块 ASW 承接时不分)。
源码中理论要求的"输出直写 GM vs 驻留 L2"的写出策略联合决策场景 B$S_{in} \le L2 < S_{in}+S_{out}$未见显式实现
### 6.7 AL1 全载特化§5.8
| 维度 | 理论 | 源码 |
|---|---|---|
| 条件 | M 256batchA=1A 驻留 L1每核 4 | 一致IsCapable L31-72 |
| A 搬移 | GML1 一次搬入 | 一致Nd2Nz DataCopy 一次搬入 |
| B 搬移 | 每基本块一次 | 一致 |
**结论**AL1 全载是理论与源码**最吻合的分支**。源码注释有笔误"m should be larger than 256" 实为 256需注意
### 6.8 尾轮处理§5.3
源码的尾轮相关逻辑分布在三处与理论逐项对比
| 机制 | 源码 | 理论(§5.3 | 判定 |
|---|---|---|---|
| 尾块感知评分 | `GetBalanceRateWithTail`尾轮按 拆两维估算后计入 rate | $\Delta T_{tail}$ 量化公式 | 一致源码是估算理论是闭式 |
| BMM 尾轮估算 | batchInfo nullptr **短路为简单比率**不做拆分估算 | batch 维摊薄尾块效应 | 一致 |
| 实际重切 | **不做** AL1 全载的 `CalcTailBasicBlockAL1Full` 沿 N 切尾块逼近满载 | $n_{wave} \le 3$ $r < C$ host 端重切$s^*$ 分块 + 第二套 tiling 参数下发 | 理论更优 |
| 重切约束 | | 访存 BounddValue 256B搬移量 min_TileSize16 对齐计算 Bound 16 对齐 | |
**判定**源码对 BMM ASW 路径不做尾轮重切仅通过枚举选择尾块占比小的组合balanceRate 0.9 剪枝)。对于 $n_{wave} = \lceil B \cdot mCnt \cdot nCnt/C \rceil \le 3$ case理论的重切方案可节省 $T_{block}(1-1/s^*)$ 的尾波时延$n_{wave}=2$、$r=8$ 时总时延省 25% host 端零 NPU 代价源码改进建议在枚举收尾后增加尾轮重切判定$n_{wave} \le 3$ 时计算 $s^*$ 并生成第二套 tiling 参数)。
### 6.9 降核模式§5.9
| 维度 | 理论 | 源码 |
|---|---|---|
| 实现 | usedCoreNum = ⌈P⌉其余核闲置 | **未实现**——GetNumBlocks() 固定返回 32 |
**影响**P < C case M/N B进入 ASW_Basic 所有 32 核参与调度但部分核无实际工作理论上应只调度 P 这类 case 的时延绝对值小但多余核的调度开销tiling 计算kernel 启动上下文切换是真实存在的
### 6.10 综合评价
| 维度 | 评价 |
|---|---|
| 核间分配 + swizzle | 与理论一致 |
| AL1 全载特化 | 与理论最吻合 |
| 尾轮处理 | 一致不重切选小尾波组合 |
| BaseM/BaseN 分层 | 源码无显式 SingleCore/Base 分层baseM=256 实为 SingleCore |
| L2 管理 | 比理论保守100MB 阈值 + batch 数限制缺输出写出策略联合决策 |
| 降核模式 | 未实现 |
| 经验常数 | CUBE_BOUND_RATIO=0.85、balanceRate=0.9、transConflict=6、TAIL_CONFLICT_RATIO=0.5 等无官方文档推导边界 case 值得实测复核 |
**总体判断**源码的 ASW_Basic 实现在核间分配swizzleAL1 全载上与理论高度一致是经实测调优的工程实现理论分析的价值在于
1. **补齐降核模式**——P < C usedCoreNum = ⌈P⌉避免无效核调度
2. **显式分层**——SingleCoreM/N BaseM/N 分开约束链更清晰
3. **写出策略联合决策**——$S_{in} \le L2 < S_{in}+S_{out}$ 时输出直写 GM 的显式判断
4. **经验常数的理论依据**——cubeBound 模型的三项分解L2 供数/溢出惩罚/K 向复用可以从第一性原理推导
---
## 参考文献
1. [昇腾 950 NPU 架构白皮书](https://public-download.obs.cn-east-2.myhuaweicloud.com/ascend/%E6%98%87%E8%85%BE950%20NPU%E6%9E%B6%E6%9E%84%E7%99%BD%E7%9A%AE%E4%B9%A6.pdf)华为技术有限公司2026
2. [cann-ops-nn 源码仓](https://gitcode.com/cann/ops-nn/tree/master/matmul/batch_mat_mul_v3)