v1.2: 结构重整——尾轮融入Step2+dValue分Bound两级+编号统一

This commit is contained in:
2026-08-27 07:57:44 +00:00
parent aa778ecd0c
commit 086c2c2785

View File

@@ -4,9 +4,9 @@
## 摘要
ASW_Basic 是 BMM 的兜底分支——核间切 M/N或混合切不做 batch 合并或 K 维切分。本文给出完整的时延建模、核间分配策略分析(证明 B 优先分组在任何场景下都不优于线性映射)、实现方案的逐步推导,以及尾轮处理策略
ASW_Basic 是 BMM 的兜底分支——核间切 M/N或混合切不做 batch 合并或 K 维切分。本文给出完整的时延建模、核间分配策略分析(证明 B 优先分组在任何场景下都不优于线性映射)、实现方案的逐步推导(含尾轮重切的 Bound 类型分级策略),以及与源码实现的逐维度对比
v1.2 修正dValue 正确定义ND 连续维字节数)、尾轮重切约束分访存 Bound/计算 Bound 两级(计算 Bound 下 dValue 可放宽)、§七源码对比。核心结论:**源码的 swizzle核间分配AL1 全载与理论高度一致主要差距在降核模式未实现、IsCapable 无并行度校验、尾轮未按 Bound 类型区分重切策略。**
核心结论:**B 优先分组不优于线性映射;尾轮重切在 host 端零代价、$n_{wave} \le 3$ 时应重切且计算 Bound 下 dValue 可放宽源码的 swizzle/核间分配/AL1 全载与理论高度一致主要差距在降核模式未实现、IsCapable 无并行度校验、尾轮未按 Bound 类型区分重切策略。**
---
@@ -65,7 +65,7 @@ GM ──MTE2──> L1 ──MTE1──> L0A/L0B ──MMAD──> L0C ──Fi
层次关系$K = \text{singleCoreK} \ge k_{L1} \ge baseK$$\text{singleCoreM} \ge \text{BaseM}$$\text{singleCoreN} \ge \text{BaseN}$。
### 1.4 GM→L1 搬移效率dValue 与 min_TileSize
**搬移效率dValue 与 min_TileSize**
GML1 搬移使用 Nd2Nz DMA每次搬移的关键参数
@@ -194,13 +194,121 @@ B 分组每组空闲 $C_g - mCnt \cdot nCnt$ 个核,总算力浪费 $C - B \cd
---
## 五、尾轮处理与负载均衡
## 五、实现方案
### 5.1 尾轮的定义
### 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 的长宽比跟随 SingleCoreM/SingleCoreN进而跟随 M/N对齐 16 的倍数。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 单缓冲上限)
2. 检查 baseK = $\min(L0A/(2 \cdot 256 \cdot dtype),\; L0B/(2 \cdot 256 \cdot dtype))$ ≥ 128B/dtype最小高效粒度
3. 若 baseK 不足,按比例缩小 BaseM/BaseN保持长宽比 ≈ M/N直到 baseK 满足
4. 若 M ≪ N或反之长宽比跟随 M/NBaseM/BaseN ≈ M/N面积保持 65536
*与源码的差异*:源码默认 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 越大 → 单次 GM→L1 搬移量越大dValue 越有保障L2 中同一份 A 行带/B 列带被更多核复用
- SingleCoreM/N 越小 → 总块数 mCnt×nCnt 越多,并行度越高,但搬移效率降低
*约束链*
**约束 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 边界对齐)。
*选取策略*:在满足约束 1 的前提下SingleCoreM/N 尽量大(搬移效率和 L2 复用最大化)。长宽比跟随 M/N$\text{singleCoreM}/\text{singleCoreN} \approx M/N$),对齐到 BaseM/BaseN 的整数倍。
*SingleCoreM/N 的具体确定过程*host 端枚举,与尾轮处理联动):
1. 从约束 1 得到最小块数:$mnCnt_{min} = \lceil C/B \rceil$
2. 枚举 $(mCnt, nCnt)$ 组合,$mCnt \cdot nCnt \ge mnCnt_{min}$,从大到小遍历 singleCoreM = $\lceil M/mCnt \rceil_{16}$、singleCoreN = $\lceil N/nCnt \rceil_{16}$
3. 对每个组合检查约束 2/3/4L1 容量、搬移效率、BaseM/N 整数倍)
4. 计算尾波占比:$r = B \cdot mCnt \cdot nCnt \bmod C$,优先选 $r = 0$ 或 $r$ 大的组合(尾波核数多 → 重切后 $s^*$ 小 → 小块 dValue 有保障)
5. 若 $n_{wave} = \lceil B \cdot mCnt \cdot nCnt / C \rceil \le 3$ 且 $r > 0$,计算尾轮重切参数 $s^*$ 并评估重切后收益
6. 选总时延最短的组合:$T_{total} = (n_{wave} - 1) \cdot T_{block} + T_{tail}$,其中 $T_{tail} = T_{block}/s^*$(重切)或 $T_{block}$(不重切)
*与源码的差异*:源码用 cubeBound 模型 + balanceRate ≥ 0.9 剪枝,不解耦 SingleCoreM/N 与 BaseM/NbaseM=256 已是 SingleCore 级参数L0 tile 由 stepM/stepN 二次切分。理论的两层分离使约束链更清晰——SingleCoreM/N 由 L1 + 并行度 + 搬移效率决定BaseM/N 由 L0C 决定,各司其职。
*例*B=8、M=N=2048、K=1024、BF16$\lceil C/B \rceil = 4$,取 $mCnt = nCnt = 2$ → singleCoreM = singleCoreN = 1024。BaseM = BaseN = $\lfloor\sqrt{32768}\rfloor_{16} = 176$。SingleCoreM/BaseM = 1024/176 ≈ 5.8 → 取 5整数倍→ singleCoreM = 880。约束 2$k_{L1} \le 512\text{KB}/(2 \times 1760 \times 2\text{B}) = 74$ → 取 6416 对齐)= 128B ✓。
### 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$。核间分配采用 B→M→N 线性映射(分析见 §四):块按 (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$ 个核有活干,其余核空闲。
### 5.2 尾轮重切:是否值得?
**尾轮重切:是否值得?**
**关键前提tiling 在 hostCPU上完成不占 NPU 时间。** 重切只需 host 多算一套 tiling 参数下发给 NPU零 NPU 开销。这改变了收益/代价的平衡——重切的代价仅为 host 端多一次枚举,而收益是 NPU 端尾波时延的缩短。
@@ -277,7 +385,7 @@ $$
**结论**$n_{wave} \le 3$ $r < C$ 时应重切尾轮——host 端零代价NPU 端收益 $T_{block}(1-1/s^*)$。$n_{wave} \ge 4$ 时收益 < 25%可不重切通过选择使尾波占比小的 mCnt/nCnt 组合来优化)。
### 5.3 尾轮影响的量化
**尾轮影响的量化**
设总块数 $N_{blk} = B \cdot mCnt \cdot nCnt$总波数 $n_{wave} = \lceil N_{blk} / C \rceil$尾波块数 $r = N_{blk} \bmod C$$r = 0$ 时无尾波)。
@@ -297,119 +405,11 @@ $$
---
## 六、实现方案
### Step 0BaseM / BaseN 的确定L0 级 tile先把 L0C 用满)
L0C 是 Cube 的累加器BaseM × BaseN 是每次 Cube 计算的输出 tile。BaseM/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}$ 倍(如 256→362mCnt/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 的长宽比跟随 SingleCoreM/SingleCoreN进而跟随 M/N对齐 16 的倍数。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 单缓冲上限)
2. 检查 baseK = $\min(L0A/(2 \cdot 256 \cdot dtype),\; L0B/(2 \cdot 256 \cdot dtype))$ ≥ 128B/dtype最小高效粒度
3. 若 baseK 不足,按比例缩小 BaseM/BaseN保持长宽比 ≈ M/N直到 baseK 满足
4. 若 M ≪ N或反之长宽比跟随 M/NBaseM/BaseN ≈ M/N面积保持 65536
*与源码的差异*:源码默认 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)。
### Step 1SingleCoreM / 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 越大 → 单次 GM→L1 搬移量越大dValue 越有保障L2 中同一份 A 行带/B 列带被更多核复用
- SingleCoreM/N 越小 → 总块数 mCnt×nCnt 越多,并行度越高,但搬移效率降低
*约束链*
**约束 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 边界对齐)。
*选取策略*:在满足约束 1 的前提下SingleCoreM/N 尽量大(搬移效率和 L2 复用最大化)。长宽比跟随 M/N$\text{singleCoreM}/\text{singleCoreN} \approx M/N$),对齐到 BaseM/BaseN 的整数倍。
*SingleCoreM/N 的具体确定过程*host 端枚举,与尾轮处理联动):
1. 从约束 1 得到最小块数:$mnCnt_{min} = \lceil C/B \rceil$
2. 枚举 $(mCnt, nCnt)$ 组合,$mCnt \cdot nCnt \ge mnCnt_{min}$,从大到小遍历 singleCoreM = $\lceil M/mCnt \rceil_{16}$、singleCoreN = $\lceil N/nCnt \rceil_{16}$
3. 对每个组合检查约束 2/3/4L1 容量、搬移效率、BaseM/N 整数倍)
4. 计算尾波占比:$r = B \cdot mCnt \cdot nCnt \bmod C$,优先选 $r = 0$ 或 $r$ 大的组合(尾波核数多 → 重切后 $s^*$ 小 → 小块 dValue 有保障)
5. 若 $n_{wave} = \lceil B \cdot mCnt \cdot nCnt / C \rceil \le 3$ 且 $r > 0$,计算尾轮重切参数 $s^*$ 并评估重切后收益
6. 选总时延最短的组合:$T_{total} = (n_{wave} - 1) \cdot T_{block} + T_{tail}$,其中 $T_{tail} = T_{block}/s^*$(重切)或 $T_{block}$(不重切)
*与源码的差异*:源码用 cubeBound 模型 + balanceRate ≥ 0.9 剪枝,不解耦 SingleCoreM/N 与 BaseM/NbaseM=256 已是 SingleCore 级参数L0 tile 由 stepM/stepN 二次切分。理论的两层分离使约束链更清晰——SingleCoreM/N 由 L1 + 并行度 + 搬移效率决定BaseM/N 由 L0C 决定,各司其职。
*例*B=8、M=N=2048、K=1024、BF16$\lceil C/B \rceil = 4$,取 $mCnt = nCnt = 2$ → singleCoreM = singleCoreN = 1024。BaseM = BaseN = $\lfloor\sqrt{32768}\rfloor_{16} = 176$。SingleCoreM/BaseM = 1024/176 ≈ 5.8 → 取 5整数倍→ singleCoreM = 880。约束 2$k_{L1} \le 512\text{KB}/(2 \times 1760 \times 2\text{B}) = 74$ → 取 6416 对齐)= 128B ✓。
### Step 2mCnt / 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$。核间分配采用 B→M→N 线性映射(分析见 §四):块按 (b, m, n) 字典序编号,核 i 处理块 i, i+C, i+2C, …。B 不整除 C 时尾波不满载的核少分一块,无需 B | C。
### Step 3核间切分维度选择按共享代价从低到高
### 5.4 核间切分维度选择(按共享代价从低到高)
切 B零共享先试→ 切 M右矩阵 $KN\cdot\text{dtype} \le L2$ 则驻留 L2→ 切 N对称→ 混合切(靠 swizzle + L2 切分管理)→ 降核(见 Step 8
### Step 4swizzle——ASW 滑窗蛇形
### 5.5 swizzle——ASW 滑窗蛇形
**问题**:核间切 M/N 后,同一时刻 C 个核各算一个输出块,它们所需的 A 行块与 B 列块集合就是当前"活跃工作集"。若按行优先顺序朴素分配,一波 C 个块横跨的 A 行、B 列很宽,活跃工作集超过 L2 就回 GM 读1.6TB/s重复读代价真实发生。**swizzle 要做的就是编排输出块的执行顺序,把每一波核的活跃工作集压到最小。**
@@ -450,7 +450,7 @@ C=32 时 $\sqrt{32} \approx 5.66$,因子 {1,2,4,8,…} 中不超过它的最
* **窗内列间边界**ν0→ν1相邻两段共享的是同一组 A 行块W 个),它们在整个窗口期间**全程驻留 L2**,无论按什么顺序扫,工作集不变——窗内蛇形零收益;
* **窗口行边界**窗口0→窗口1A 行整体换血μ0..3 → μ4..7),此时 B 列带的连续性决定换血成本——不蛇形则下一窗口从 ν0 开始LRU 上最久未用、早已被挤出 L2 的冷带),蛇形则延续上一窗口末尾的 ν7最热线带。**蛇形只标在窗口行号上**(源码 `BatchMatMulAswBlock::UpdateBasicIndex`:仅 `rowIdx` 为奇时 n 反向,窗内 m 最快序不反向),正是这个收益结构的直接实现。
### Step 5L2 分组(工作集超 L2 时)
### 5.6 L2 分组(工作集超 L2 时)
**切的是什么**:将 mCnt×nCnt 个基本块划分为若干**执行组**——每组覆盖输出平面上一个连续矩形区域(若干 singleCoreM × singleCoreN 基本块的集合),使该组所需的 A 行带 + B 列带输入工作集 ≤ L2 可用读入空间;组内所有基本块算完再进下一组,输入只在跨组时换一次。
@@ -543,25 +543,25 @@ $$
补充若输出会被后续算子立即消费融合场景输出驻留 L2 让下游读命中场景 B/C 的策略反过来本文按单算子边界分析
### Step 6核内 tilingBaseM/BaseN/BaseK——L0 级 tile受 L0 容量直接约束)
### 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
### Step 7内部特化(参数极限,不是独立分支)
### 5.8 内部特化(参数极限,不是独立分支)
单边无 batch 且该侧矩阵小$M \le 256$、$MK\cdot\text{dtype}\cdot 2 \le L1$、对侧每核循环 4 时小侧整个常驻 L1只搬一次L1 全载)。
### Step 8降核模式实现
### 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 逐维度对比
### 7.1 进入条件IsCapable
### 6.1 进入条件IsCapable
**源码**`batch_matmul_v3_asw_basic_tiling.cpp` IsCapable
@@ -582,7 +582,7 @@ tiling 时 `usedCoreNum = ⌈P⌉`(不强制 CSingleCoreM/N 在 L0C 容
**影响**源码缺少降核分支P < C case 进入 ASW_Basic 所有 32 核仍参与调度但部分核无实际工作——引入不必要的调度开销理论上这些 case 应走降核模式usedCoreNum = ⌈P⌉
### 7.2 BaseM / BaseN 确定(Step 0
### 6.2 BaseM / BaseN 确定(§5.1
| 维度 | 理论 | 源码 |
|---|---|---|
@@ -604,7 +604,7 @@ $$
**UnitFlag 现状**BMM ASW kernel 中 `unitFlag = 0``block_mmad_iterbatch.h` L315 注释:"each l0 only process one block, disable unit flag"——MMAD 与 Fixpipe 之间是**指令级同步**(整个 L0C tile 算完才写出)。若启用 UnitFlagMMAD 每完成一个 16×16×16 块512BFixpipe 立即写出——tile 内部细粒度流水替代 tile 间粗粒度流水,单缓冲即可达到双缓冲的流水效果。对计算 Bound 的 caseUnitFlag 可将单 tile 时延从 $T_{comp} + T_{write}$ 降至 $\max(T_{comp}, T_{write})$。
### 7.3 SingleCoreM / SingleCoreN 确定(Step 1
### 6.3 SingleCoreM / SingleCoreN 确定(§5.2
| 维度 | 理论 | 源码 |
|---|---|---|
@@ -614,7 +614,7 @@ $$
**关键差异**:理论把 SingleCoreM/N 和 BaseM/N 分为两层SingleCoreM ≥ BaseM源码中 baseM=256 实际上已经是 SingleCoreM 级参数。理论的分层更清晰,源码更保守但经实测调优。
### 7.4 核间分配(Step 2
### 6.4 核间分配(§5.3
| 维度 | 理论 | 源码 |
|---|---|---|
@@ -624,7 +624,7 @@ $$
**结论**:核间分配策略与理论一致。源码的 `UpdateBasicIndex``batch_mat_mul_v3_asw_block_advanced.h`)实现了 B→M→N 线性映射 + 划窗蛇形。
### 7.5 SwizzleStep 4
### 6.5 Swizzle§5.5
| 维度 | 理论 | 源码 |
|---|---|---|
@@ -634,7 +634,7 @@ $$
**结论**swizzle 实现与理论一致。源码的窗长公式与理论 $W = \max\{d : d \mid C, d \le \lfloor\sqrt{C}\rfloor\}$ 完全相同。但源码对细长 shapemCnt 小或 nCnt≫mCnt的方形窗假设不成立时退化为行优先`mainWindow = min(aswWindowLen, mCnt)`),未按 shape 长宽比自适应窗形。
### 7.6 L2 管理(Step 5
### 6.6 L2 管理(§5.6
| 维度 | 理论 | 源码 |
|---|---|---|
@@ -649,7 +649,7 @@ $$
源码中理论要求的"输出直写 GM vs 驻留 L2"的写出策略联合决策场景 B$S_{in} \le L2 < S_{in}+S_{out}$未见显式实现
### 7.7 AL1 全载特化(Step 7
### 6.7 AL1 全载特化(§5.8
| 维度 | 理论 | 源码 |
|---|---|---|
@@ -659,7 +659,7 @@ $$
**结论**AL1 全载是理论与源码**最吻合的分支**。源码注释有笔误"m should be larger than 256" 实为 256需注意
### 7.8 尾轮处理
### 6.8 尾轮处理§5.3
| 维度 | 理论 | 源码 |
|---|---|---|
@@ -668,7 +668,7 @@ $$
**结论**尾轮处理策略一致源码通过选择使尾波占比尽量小的 mCnt/nCnt 组合来优化而非重新切分尾轮
### 7.9 降核模式(Step 8
### 6.9 降核模式(§5.9
| 维度 | 理论 | 源码 |
|---|---|---|
@@ -676,7 +676,7 @@ $$
**影响**P < C case M/N B进入 ASW_Basic 所有 32 核参与调度但部分核无实际工作理论上应只调度 P 这类 case 的时延绝对值小但多余核的调度开销tiling 计算kernel 启动上下文切换是真实存在的
### 7.10 综合评价
### 6.10 综合评价
| 维度 | 评价 |
|---|---|