v0.96: MergeBatch实现方案重写——b0→kL0→kL1反推→bL1最大化

This commit is contained in:
2026-08-26 09:28:21 +00:00
parent 5ceaeafc97
commit 667241719f

View File

@@ -0,0 +1,699 @@
# BMM 算子优化分析v0.96
> 目标芯片:昇腾 950PRDAV_3510。所有分支进入条件只含 case 形状参数B、M、N、K、dtype与芯片规格参数。
---
## 一、算子功能与接口说明
完成带 batch 的矩阵乘:`C = A @ B + bias`
* 左矩阵 A`[BatchA, M, K]`dtype典型 ND可带转置
* 右矩阵 B`[BatchB, K, N]`dtype典型 ND可带转置
* 偏置 bias`[B, 1, N]`,固定 ND可为空
* 输出 C`[BatchC, M, N]`BatchC = broadcast(BatchA, BatchB)
---
## 二、符号与芯片参数约定
| 符号 | 含义 | 950PR 取值 |
|---|---|---|
| C | AIC 核数aicNum | 32 |
| Q₁₆ | 单核 Cube BF16 峰值算力 | 486/C ≈ 15.2 TFLOPS |
| Q_AIV | AIV 向量求和吞吐64 核合计) | 64×128 fp32/拍×1.65GHz ≈ 13.5 Tops/s |
| L1 | 每核 L1 Buffer | 512KB |
| L0A / L0B | 每核 L0A / L0B | 64KB / 64KB |
| L0C | 每核 L0CFP32 累加4B/元素) | 256KB |
| L2 | L2 Cache 容量 | 128MB |
| W_L2 | L2 读写带宽 | 5.2TB/s |
| W_GM | GM 带宽(读写共享) | 1.6TB/s |
| R₁₆ | 16bit 对应位宽算存比 | ≈607.5 FLOP/元素 |
| dValue | 单数据块内数据连续排布长度 | 推荐 256B/512B不建议 <128B |
| min_TileSize | 确保高带宽利用率的单块搬移数据量最小值 | 16KB |
| min_DatamountPerCore | 确保高带宽利用率的单核搬移数据量最小值 | 480KB |
| minCoreNum | 确保高带宽利用率的并行搬移核数最小值 | 0.8C = 26 |
以上数值基于 950PR 实测分析总结对搬移带宽利用率的影响重要性排序为**核数 > 单核搬移总数据量 > 单分块大小 > dValue**。不同 NPU 芯片数值可能略有差异,换芯片时逻辑结构不变、只换常数表。
---
## 三、最优实现分析
### 3.1 性能模型
BMM 的执行是核内多级硬件流水的并行——Cube 计算MMAD、GM/L2→L1MTE2、L1→L0MTE1、L0C 写出Fixpipe各流水级时延可被双缓冲相互掩盖
$$
T_{total} = \max\big(T_{MMAD},\; T_{MTE2},\; T_{MTE1},\; T_{Fixpipe}\;[,\;T_{Reduce}]\big)
$$
**总时延 = 最慢一级流水**,优化的关键是对瓶颈级的优化。由此得到设计自由度——**瓶颈交换**搬移是瓶颈时可牺牲算力冗余计算换搬移效率计算是瓶颈时可牺牲搬移重复读取换计算效率。MergeBatch 是前者的典型ASW_Basic 切 M/N 是后者的典型。
case 固有算存比与 16bit 位宽平衡点:
$$
AI = \frac{2MN}{M+N},\qquad AI_{full} = \frac{2MNK}{MK+KN+MN}
$$
$$
R_{16} = \frac{\text{Cube 峰值算力}}{\text{GM 带宽} / \text{元素字节数}} = \frac{486\ \text{TFLOPS}}{1.6\ \text{TB/s} \,/\, 2\ \text{B}} \approx 607.5\ \text{FLOP/元素}
$$
其中 486 TFLOPS 是乘加各计一次后的标称算力;分母中的 2B 是 16bit 元素字节数,作用是把 GM 带宽折算成元素速率。
$AI < R_{16}$ 访存 Bound瓶颈在 MTE2反之计算 Bound瓶颈在 MMAD)。
### 3.2 实现本质逻辑
BMM 的实现本质是把数据分块tile由全部 AIC 核并行 + 串行完成这些分块的计算再组合成最终结果
$$
C[B,M,N] = \Big\{\,C[B_u, M_i, N_j] = \sum_k A[B_u, M_i, K_k] \cdot B[B_u, K_k, N_j]\,\Big\}
$$
分块有 4 个维度BMNK。**核间怎么分这 4 个维度就是分支划分的第一性问题**核内分块是第二性问题属于各分支内部 tiling)。
四个维度的核间切分特征后续一切推导的基石
| 切分维度 | 读入特征 | 计算特征 | 写出特征 |
|---|---|---|---|
| B | 核间零重复读每个数据块只被 1 个核读取**核内是否重复读另有条件**—— L1 放不下单 batch 完整的 MN 维输入K 维可切段放入kL1<K batch 计算中切 M 会重复读 B N 会重复读 A | 每个输出块由 1 个核独立完成无核间依赖 | 只写最终结果无中间结果 |
| M / N | M 则同一右矩阵块被多核**重复读** N 则同一左矩阵块被多核重复读 | 每个输出块由 1 个核独立完成无核间依赖 | 只写最终结果无中间结果 |
| K | 每个数据块只被固定的 1 个核读取零重复读 | 每个输出块由**多核共同**完成存在核间依赖 | **有中间结果写出,需核间 Reduce 归约** |
差异的根本原因B 维在数学上独立 batch 独立矩阵乘 B 核间天然零重复零依赖K 维有 L0C 累加机制——核内切 K 时多轮 mmad L0C 原地累加中间结果不出核一旦切到核间部分和必须写出 workspace 再归约——** K 是唯一同时破坏"累加不出核""输出独占"的切法**。
4 维的任意非空子集共 $2^4-1=15$ 种切分组合任何实现方案必属其一完备
| | 组合 | 共同特征 | 优化重心 |
|---|---|---|---|
| B | {B} | 核间零重复读核内重复读取决于 L1 驻留形态无中间写出 | 搬移效率 / 计算效率 |
| M/N 不含 K | {M},{N},{M,N},{B,M},{B,N},{B,M,N} | 核间可能有重复读 | 重复读尽量少L2+swizzle 吸收+ 搬移/计算效率 |
| K | 其余 8 | 有中间结果写出 + 归约 | 计算效率且归约时延不能成为新瓶颈 |
核间切分价格严格排序cost( B) = 0 < cost( M/N) cost( K)。 B 核间免费 M/N 的重复读可被 128MB L25.2TB/s vs GM 1.6TB/s+ swizzle 大部分吸收 K 的归约流量 grid_K×输出量且引入核间同步是结构性代价。**整条分支决策树就是按价格从低到高购买并行度买不够才加价。**
### 3.3 分支推导
1. **问题规约**BatchA=1 BatchB=1 折叠转普通 Matmul转MatmulK=0 / K=1 Cube 无用 AIV 向量通路特殊分支与切分正交的前置判断
2. **先买免费的 B**B C 时切 B 可满核 batch M×N 够大 batch IterBatchM×N 太小 batch 合并成大 tile 冗余算力换搬移效率MergeBatch
3. **B 买不满,加价买 M/N** M/N 或混合切重复读交给 L2 + swizzleASW_Basic
4. **B/M/N 都买不满,才买昂贵的 K**核间切 K + 归约StreamK)。
由此得 6 大分支**转Matmul特殊分支IterBatchMergeBatchASW_BasicStreamK**。重叠区 BC M×N 中等时 MergeBatch IterBatch 都合法由端到端时延模型 $T_{total}$ 仲裁分支体系保证候选集完备无冗余
---
## 四、可转 Matmul 分支
### 进入分支条件
$$
BatchA = 1 \;\lor\; BatchB = 1
$$
解释单边 batch=1 BMM Matmul 只差一个维度标签直接复用 Matmul 的成熟优化体系
### 实现方案
* **BatchB=1**:左矩阵 `[B,M,K]` batch 维与 M 维在 ND 下内存相邻紧排直接视图为 `[B·M,K]`输出布局逐元素一致——零重排 split免费转换
* **BatchA=1**:右矩阵折叠为 `[K, B·N]` 需一次真实转置重排O(B·K·N)且输出存在置换需 scatter——有代价A 较小$MK\cdot\text{dtype} \le L1$时优先 A 常驻 L1留在 BMM 分支内广播友好形态A 较大时按广播扩展后分别预估"BMM 分支""重排+转Matmul"的时延择优
---
## 五、MergeBatch 分支
核间切 B每核 $b_{core}$ batch核间无同步核内把 b batch 合并计算$[b,M,K]@[b,K,N] \Rightarrow [bM,K]@[K,bN]=[bM,bN]$BlockTrace 取块对角线得 $[b,M,N]$。交叉项被算出但丢弃浪费比例 (b1)/b)——进入该分支的 case 必然访存 Bound条件 5 保证浪费的算力被搬移时延掩盖
### 进入分支条件(汇总)
同时满足b0 = 单次合并计算的 batch 数下限b0 2
1. $BatchA = BatchB \;\land\; b_{core} = B/C \ge 2b_0$
2. $2 \cdot (b_0 M)(b_0 N) \cdot 4\text{B} \le L0C$
3. $b_{core} \cdot (MK + KN) \cdot \text{dtype} \ge min\_DatamountPerCore$
4. $\max(MK,\; KN) \cdot \text{dtype} \ge min\_TileSize$
5. $\dfrac{2MN}{M+N} < \dfrac{R_{16}}{b_0}$
### 逐条解释
1. **batch 关系与每核份额**无广播才能逐 batch 对应合并每核至少分到 $2b_0$ batch——$b_0$ 是合并搬移有收益的最小合并数 22 组起步才能构成合并组间乒乓流水 $b_{core} \ge 4$)。
**MergeBatch vs IterBatch 分界建模**
*执行模型*两分支的核间切分相同——B 维切分到 C 每核 $b_{core} = B/C$ batch核内不切 M/N。**核心差异在 GML1 搬移粒度**
- **IterBatch** batch 搬移—— batch $A[M,K]+B[K,N]$ 作为独立 DMA 操作搬入 L1源码 `ndNum = curIterBatchL1`多块独立寻址然后在 L1 内逐 batch 计算
- **MergeBatch**合并搬移——$b_0$ batch $A'[b_0M,K]+B'[K,b_0N]$ 合并为一个大矩阵作为**单次 DMA 操作**搬入 L1源码 `ndNum = 1`单块连续搬移然后合并计算
*符号*
| 符号 | 含义 | 表达式 |
|---|---|---|
| $k_{L1}$ | L1 K 分块粒度双缓冲 | $\min\big(K,\; L1/(2(M{+}N)\cdot\text{dtype})\big)$ |
| $n_K$ | K 分块数 | $\lceil K/k_{L1} \rceil$ |
| $T_{load}$ | K 分块搬移时延 | $k_{L1}(M{+}N)\cdot\text{dtype}/BW_{pc}$ |
| $T_{comp}$ | K 分块计算时延 | $2MN \cdot k_{L1}/Q_{16}$ |
| $T_{write}$ | batch 输出写回时延 | $MN \cdot outB/W_{GM}$ |
| $T_{cmd}$ | 单次 GML1 DMA 搬移固定开销 | 描述符配置 + 地址生成 + 突发启动 |
| $BW_{pc}$ | 单核 GM 带宽份额 | $W_{GM}/C$ |
*端到端时延模型*L0C 双缓冲使 fixpipe Cube 完全交叠** batch 边界同步开销**。差异仅来自 GML1 搬移次数和 drain 暴露
$$
T_{iter} = \underbrace{b_{core} \cdot n_K \cdot (T_{load} + T_{cmd})}_{\text{搬移 batch}} + \underbrace{T_{comp} + T_{write}}_{\text{ batch drain}}
$$
$$
T_{mb} = \underbrace{\frac{b_{core}}{b_0} \cdot n_K^m \cdot (T_{load}^m + T_{cmd})}_{\text{搬移合并}} + \underbrace{b_0(T_{comp} + T_{write})}_{\text{末合并 batch drain}}
$$
**K 截断情形**$k_{L1} = K$整个 K 装入 L1 一块$n_K = n_K^m = 1$。IterBatch batch 一次 GML1 搬移MergeBatch 每合并 batch 一次覆盖 $b_0$ batch搬移次数少 $b_0$
$$
T_{iter} = b_{core}(T_{load} + T_{cmd}) + T_{comp} + T_{write}
$$
$$
T_{mb} = \frac{b_{core}}{b_0}(b_0 T_{load} + T_{cmd}) + b_0(T_{comp} + T_{write}) = b_{core} T_{load} + \frac{b_{core}}{b_0}T_{cmd} + b_0(T_{comp} + T_{write})
$$
$$
\Delta_K = \underbrace{(b_0-1)(T_{comp} + T_{write})}_{\text{drain 惩罚}} - \underbrace{b_{core}\Big(1-\frac{1}{b_0}\Big) T_{cmd}}_{\text{搬移命令节省}}
$$
**L1 绑定情形**$k_{L1} < K$K 需分块MergeBatch 合并后 tile 更大 $k_{L1}^m = k_{L1}/b_0$ $n_K^m = b_0 \cdot n_K$K 分块数 $b_0$ )。IterBatch batch $n_K$ 次搬移MergeBatch 每合并 batch $b_0 n_K$ 每块覆盖 $b_0$ batch K 段只有 $k_{L1}/b_0$)。
$$
T_{iter} = b_{core} \cdot n_K \cdot (T_{load} + T_{cmd}) + T_{comp} + T_{write}
$$
$$
T_{mb} = \frac{b_{core}}{b_0} \cdot b_0 n_K \cdot (T_{load} + T_{cmd}) + b_0(T_{comp} + T_{write}) = b_{core} \cdot n_K \cdot (T_{load} + T_{cmd}) + b_0(T_{comp} + T_{write})
$$
搬移次数相同$b_{core} \cdot n_K$每次搬移量也相同$k_{L1}(M{+}N)\cdot\text{dtype}$)——**L1 绑定情形 MergeBatch 无搬移优势**
$$
\Delta_L = \underbrace{(b_0-1)(T_{comp} + T_{write})}_{\text{drain 惩罚}} > 0
$$
**L1 绑定情形 MergeBatch 恒劣于 IterBatch**——合并只放大了 drain 暴露,没有换来搬移命令节省。
**统一分界条件**
$$
\text{MergeBatch 最优} \;\Longleftrightarrow\; k_{L1} = K \;\land\; b_{core} > \frac{b_0 \cdot (T_{comp} + T_{write})}{T_{cmd}}
$$
即 MergeBatch 仅在 **K 截断**$k_{L1} = K$,小 M/N 使 $L1/(2(M{+}N)\cdot\text{dtype}) \ge K$)且 $b_{core}$ 足够大时才优于 IterBatch。小 MN 时 $T_{comp}$ 小且 $k_{L1}$ 大(容易 K 截断)→ MergeBatch 最容易赢;大 B 时 $b_{core}$ 大 → 搬移节省多 → MergeBatch 更容易赢。
**$T_{cmd}$ 的物理成因**:每次 GM→L1 DMA 搬移的固定开销,与搬移数据量无关。从源码可直接观察:
- `Nd2NzParams` 描述符配置ndNum/nValue/dValue/srcStride 等 7 个字段写入 DMA 寄存器)
- 地址生成与突发启动
- 与 L1 buffer 的同步握手(`SetFlag<MTE2_MTE1>` / `WaitFlag<MTE1_MTE2>`
IterBatch 每 batch 需一次完整配置(`ndNum = curIterBatchL1`多块独立寻址MergeBatch 合并后只需一次配置(`ndNum = 1`,单块连续搬移)。**$T_{cmd}$ 就是每次 DMA 命令的描述符配置 + 启动延迟**,量级估计为数十 ns。
**MergeBatch vs IterBatch 优势总结**
| 维度 | IterBatch | MergeBatch | 差异来源 |
|---|---|---|---|
| 稳态搬移吞吐 | 相同 | 相同 | 总搬移量相同 |
| GM→L1 搬移命令数 | $b_{core}$ 次(每 batch 一次) | $b_{core}/b_0$ 次(每合并 batch 一次) | **MergeBatch 少 $b_0$ 倍** ← 核心优势 |
| drain 暴露 | $T_{comp} + T_{write}$ | $b_0(T_{comp} + T_{write})$ | IterBatch 少 $b_0$ 倍 ← 核心劣势 |
| L0C 利用率 | $MN \cdot 4\text{B}$ | $b_0^2 MN \cdot 4\text{B}$ | 访存 Bound 下不影响时延 |
净收益 = 搬移节省 - drain 惩罚 = $b_{core}(1-\frac{1}{b_0})T_{cmd} - (b_0-1)(T_{comp}+T_{write})$。大 B$b_{core}$ 大)且小 MN$T_{comp}$ 小)时 MergeBatch 最优。
**L0C 利用率说明**:小 MN 时 IterBatch 的 L0C tile$MN \cdot 4\text{B}$)远小于 L0C 容量MergeBatch 合并后更接近满载。但访存 Bound 下计算被搬移掩盖L0C 利用率不影响总时延——**不构成 MergeBatch 的优势**。
2. **L0C 容量**:合并 $b_0$ 个 batch 的输出块 $[b_0M, b_0N]$FP32 累加、双缓冲两份)必须放得下 L0C连最小合并都放不下合并无从谈起。
3. **单核搬移总量**:单核搬移数据总量低于 min_DatamountPerCore 时GM 带宽利用率上不去(重要性第 2 位的经验约束)。
4. **搬移 tile 大小**:单 batch 单矩阵的最大连续搬移块须达到 min_TileSize合并是在此之上进一步放大不是替代。
5. **访存 Bound**:合并把单次计算的算存比放大 $b_0$ 倍后仍须低于 16bit 对应位宽算存比 $R_{16}$,保证瓶颈留在搬移侧,冗余算力被掩盖而非成为新瓶颈。
### 实现方案
**Step 1合并数 bL0C + 算存比双上限)**
$$
b \le \sqrt{\frac{L0C}{2 \cdot MN \cdot 4\text{B}}},\qquad b < \frac{R_{16}(M+N)}{2MN}
$$
$$
b_0 = \min\big(\text{两上限},\; b_{core}\big)
$$
b 尽量取 $b_{core}$ 的因子每次合并数均匀负载与功耗更优)。
**Step 2L0 级 K 粒度 $k_{L0}$(由 b₀ 和 L0A/L0B 决定)**
$$
k_{L0} = \min\Big(\frac{L0A}{2\,b_0 M\cdot\text{dtype}},\; \frac{L0B}{2\,b_0 N\cdot\text{dtype}}\Big)\ \text{向下 16 对齐}
$$
解释L0A/L0B 64KB双缓冲两份装入合并后 $b_0 M$ $b_0 N$ × $k_{L0}$ fractalL1L0 搬移无 dValue 要求dValue 约束的是 GML1 $k_{L1}$)。
**Step 3L1 级 $k_{L1}$、$b_{L1}$**
先令 $b_{L1}^* = b_0$最低合并数保障 L1 容量反推 $k_{L1}^*$
$$
k_{L1}^* = \frac{L1}{2 \cdot b_0 \cdot (M+N) \cdot \text{dtype}}
$$
L1 双缓冲两份每份驻留 $b_0$ batch $A[b_0 M, k_{L1}^*]+B[k_{L1}^*, b_0 N]$)。
然后取 $k_{L1} = \min(k_{L1}^*,\; K,\; 512\text{B}/\text{dtype})$——不超过 KK 截断也不超过 dValue 推荐值 512B搬移效率拐点更大不带来额外收益)。
最后由 $k_{L1}$ L1 容量确定 $b_{L1}$力求尽量大提升 batch 间流水深度
$$
b_{L1} = \min\Big(\frac{L1}{2 \cdot k_{L1} \cdot (M+N) \cdot \text{dtype}},\; b_{core}\Big)
$$
$b_{L1} \ge b_0$ 保证合并不断供 $k_{L1}^* > 512\text{B}/\text{dtype}$L1 充裕),$k_{L1}$ 被 512B 截断,省出的 L1 空间可容纳更多 batch$b_{L1} > b_0$),提升 batch 间流水掩盖能力。
---
## 六、IterBatch 分支
核间切 B每核 $b_{core} \ge 1$ 个 batch核间无同步核内逐个 batch 做标准 Matmul 分块计算。无算力浪费、无跨 batch 依赖,是"切 B"最朴素的形态。
### 进入分支条件(汇总)
同时满足:
1. $BatchA = BatchB \;\land\; b_{core} = \lceil B/C \rceil \ge 1$
2. $B \bmod C = 0 \;\lor\; B \bmod C \ge minCoreNum$
3. L1 容量约束四选一Step 为 >1 的整数):
* a) $b_{core}=1 \;\land\; (MK+KN)\cdot\text{dtype} \le L1$
* b) $b_{core}>1 \;\land\; 2(MK+KN)\cdot\text{dtype} \le L1$
* c) $\big(MK + 2\cdot\tfrac{KN}{Step}\big)\cdot\text{dtype} \le \dfrac{L1}{\min(b_{core},\,2)} \;\;\lor\;\; \big(KN + 2\cdot\tfrac{MK}{Step}\big)\cdot\text{dtype} \le \dfrac{L1}{\min(b_{core},\,2)}$
* d) $2\cdot\tfrac{K}{Step}(M+N)\cdot\text{dtype} \le L1$
4. c/d 切分后:搬移分块 $\ge min\_TileSize \;\land\; dValue \ge 128\text{B}$
### 逐条解释
1. **batch 关系与每核份额**:无广播;每核至少 1 个 batch。
2. **负载均衡**:切 B 核间零共享零依赖,唯一系统性风险是负载不均。整除时完全均衡;不整除时尾波活跃核数须 ≥ minCoreNum保证尾波仍有足够核并发搬移重要性第 1 位的约束是核数)。
3. **L1 容量——核心要求:单 batch 计算不重复读**
重复读发生在**单 batch 内部**L1 放不下单 batch 完整的 M、N 维输入K 维允许切段放入kL1<K核内切 M 会在 K 循环中重复读 B N 会重复读 AIterBatch 进入与否不由算存比判定——即使 case 是计算 Bound重复读引入的额外搬移也可能把它重新拖回访存 Bound所以进入条件直接由 L1 驻留形态刻画
* **a)** 每核 1 batch左右矩阵同时驻留 L1零重复读
* **b)** 每核多 batchL1 同时放下 2 batchbatch 间直接乒乓
* **c)** 一侧驻留 + 对侧切 K$b_{core} \ge 1$ 统一式驻留侧只搬一次零重复读对侧 K 段在预算内双缓冲故系数 2)。**预算分档由 batch 间流水掩盖决定**$b_{core}=1$ 时无 batch 边界全量 L1 可用$b_{core} \ge 2$ 时预算减半L1/2另一半用于在计算当前 batch 期间预取下一 batch 的驻留侧——定量分析见下文"batch 间流水掩盖"
* **d)** 两侧都切 KA/B K 段成对流水双缓冲故系数 2无驻留侧batch 边界天然无缝是驻留侧放不进 L1/2 时的兜底
4. **搬移效率下限**切分把粒度切小必须守住 min_TileSize dValue否则切分本身把带宽打崩
### batch 间流水掩盖分析c/d 的分工)
先明确流水结构fixpipe unitflag 写出由硬件随路完成每个 16×16×16 fractal 算完即自动搬出**写出侧不需要软件排流水**要掩盖的只有"读入MTE2: GML1)↔ 计算Cube"。
* **(c) $b_{core}=1$**每核只有 1 batch不存在 batch 边界驻留侧全程复用对侧 K 段双缓冲段间无缝
* **(c) $b_{core} \ge 2$——关键问题驻留侧在 batch 边界要整体换入怎么掩盖** 若不减预算全量 L1 给当前 batch batch 边界时下一 batch 的驻留侧 A$MK\cdot\text{dtype}$必须整体换入而当前 batch 尾部只剩几个 K 段的计算——掩盖不了整个驻留侧的换入产生气泡。**修正做法** batch 只占 L1/2驻留侧 + 对侧 K 段双缓冲都在这一半另一半 L1 在计算当前 batch 期间预取下一 batch 的驻留侧上一 batch 做最后一个 K 段时下一 batch 的驻留侧已就绪首个 K 段随即搬入——边界无气泡无气泡的定量条件驻留侧换入量 $MK\cdot\text{dtype} \le$ 当前 batch 总搬入量 $(MK+KN)\cdot\text{dtype}$恒成立访存 Bound 下计算时间 搬入时间尾部窗口必然足够
* **(d)两侧都切 K**K 段槽位在 batch 间完全同质连续——上一 batch 最后一段计算时搬下一 batch 第一段天然无缝当驻留侧单矩阵放不进 L1/2 (c) 不成立 (d) 接管代价是两侧都有 K 段级重复读
结论(c) 统一了原 (c)/(d)——同一族"一侧驻留 + 对侧切 K"预算按 $b_{core}$ 分档$b_{core}=1$ 全量 L1、$b_{core} \ge 2$ 减半以容纳下一 batch 驻留侧预取驻留侧超 L1/2 时由 (d) 接管
### 实现方案
**(a)**每核 1 batch 直接搬入 L1L1L0 先看 L0C 能否放下完整单 batch 输出
```
if (L0C >= M*N*4B): # L0C 放得下完整输出:不切 M/N只切 K
BaseM = M; BaseN = N
BaseK = min(align(L0A/M, 16), align(L0B/N, 16))
else: # L0C 放不下:按较小维切
if M < N: BaseM = align(M,16); BaseN = floor(L0C/4B / BaseM)
else: BaseN = align(N,16); BaseM = floor(L0C/4B / BaseN)
BaseK = min(floor_align(L0A/BaseM,16), floor_align(L0B/BaseN,16))
```
**(b)**L1 batch 乒乓核内 GML1L0CubeL0CGM/L2 流水L1L0 分块同理但各级预算减半L0C/L0A/L0B 2 )。
**(c)**一侧驻留 + 对侧切 K假设驻留左矩阵右矩阵搬入的 K 向长度
$$
k_{L1\_b} = \min\Big(\frac{L1_{budget} - MK\cdot\text{dtype}}{N\cdot\text{dtype}},\; K\Big),\qquad L1_{budget} = \frac{L1}{\min(b_{core},\,2)}
$$
$b_{core} \ge 2$ 时另一半 L1 在计算期间预取下一 batch 的驻留侧实现 batch 间无气泡衔接fixpipe unitflag
**(d)**两侧都切 K $k_{L1}$ 取满足容量与 dValue 的最大值L0C batch 乒乓各占 L0C/2batch 边界由硬件 fixpipe 自动排空下一 batch 立即在另一半 L0C 累加
---
## 七、StreamK 分支
### 进入分支条件(汇总)
1. $P = \dfrac{B \cdot MN \cdot 4\text{B}}{L0C} \le \dfrac{C}{2}$
2. $\dfrac{K}{grid_K} \ge \dfrac{256\text{B}}{\text{dtype}}$其中 $grid_K = \Big\lfloor \dfrac{C}{\lceil P \rceil} \Big\rfloor$
3. $K > \dfrac{grid_K^{\,2}}{grid_K-1}\cdot\theta_c$$\theta_c = \dfrac{Q_{16}}{2}\Big(\dfrac{8\text{B}}{W_{L2}}+\dfrac{1}{Q_{AIV}}\Big) \approx 12$
4. 工程约束:确定性等级 ≤ 1核间归约顺序不定ND 格式
### 逐条解释
1. **并行缺口**P 以"L0C 满载的输出基本块"为粒度估计不切 K 的最大并行度——基本块按 L0C 最大利用率取($M^t N^t \cdot 4\text{B} = L0C$),免去预先估计 M/N 具体切分。**阈值取 C/2 而非 C**StreamK 的定义就是 grid_K ≥ 2至少 2 路切 K且同一 batch 内所有输出块共享同一个 grid_Kgrid_K=2 时每块需要 2 个核,总核数需求 = ⌈P⌉ × 2 ≤ C即 P ≤ C/2等号成立时 grid_K=2 恰好填满 C 核)。若 P > C/2切 2 路就超核数2⌈P⌉ > C不切又浪费核——由降核 ASW_Basic 承接更合适。P ≤ C/2 意味着不切 K 时至多一半核有事做K 是唯一剩余的并行维度。
2. **单核 K 段下限**:每核 K 段内轴连续长度不小于 dValue 推荐值 256BBF16 为 128 元素),保证段内搬移效率不崩。
**grid_K 取值**P 个输出 tile 各需 grid_K 个核,总核数 ⌈P⌉ × grid_K ≤ C → grid_K = ⌊C/⌈P⌉⌋最大化 K 并行度。例P=10, C=32 → grid_K=⌊32/10⌋=3P=5, C=32 → grid_K=⌊32/5⌋=6P=16, C=32 → grid_K=⌊32/16⌋=2。
3. **归约代价可接受**。StreamK 切 K 引入归约串行尾,收益判据——切 K 后芯片级总时延须小于不切 K降核 ASW
**降核 ASW 的芯片级→核级映射**P < C/2 B×M×N 填不满 C L0C 满载粒度切为 P 个输出 tile tile 尺寸 $M^t N^t = L0C/4\text{B}$$\lceil P \rceil$ 个核各处理一个 tile芯片级总时延 $T_{alt}$ = tile 流水时延 $T_{pipe}$所有核并行)。$T_{pipe} = \max(T_{MMAD}^t,\,T_{MTE2}^t)$其中
$$
T_{MMAD}^t = \frac{2 M^t N^t K}{Q_{16}} = \frac{2K}{Q_{16}}\cdot\frac{L0C}{4\text{B}},\qquad
T_{MTE2}^t = \frac{K(M^t+N^t)\cdot\text{dtype}}{BW_{pc}}
$$
**StreamK 的芯片级→核级映射**同样 P tile tile $grid_K$ 个核共同完成各算 $K/grid_K$ )。流水时延 $T_{pipe}/grid_K$归约时延 $T_{Reduce}^t$ 串行追加芯片级总时延
$$
T_{SK} = \frac{T_{pipe}}{grid_K} + T_{Reduce}^t
$$
**收益判据** $T_{SK} < T_{alt}$ $T_{Reduce}^t < T_{pipe}\left(1-\dfrac{1}{grid_K}\right)$。等价于 $T_{pipe} > \dfrac{grid_K}{grid_K-1}\cdot T_{Reduce}^t$——α = grid_K/(grid_K-1) 由流水分析导出grid_K=2 时 α=2非经验值。
**$T_{Reduce}^t$ 构成**(每 tile部分和驻留 L2、AIV 归约;$M^t N^t = L0C/4\text{B}$,符号定义见 §二):
$$
T_{Reduce}^t = \underbrace{\frac{grid_K \cdot M^t N^t \cdot 4\text{B}}{W_{L2}}}_{\text{AIC 写部分和}} + \underbrace{\frac{grid_K \cdot M^t N^t \cdot 4\text{B}}{W_{L2}}}_{\text{AIV 读回}} + \underbrace{\frac{grid_K \cdot M^t N^t}{Q_{AIV}}}_{\text{AIV 求和}} + \underbrace{\frac{M^t N^t \cdot outB}{W_{L2}}}_{\text{写回}}
$$
**K 闭式阈值推导**——分两种瓶颈情形:
**计算 Bound**$T_{pipe} = T_{MMAD}^t$),代入判据:
$$
\frac{2K}{Q_{16}}\cdot\frac{L0C}{4\text{B}}\cdot\frac{grid_K-1}{grid_K} > grid_K\cdot\frac{L0C}{4\text{B}}\Big(\frac{8\text{B}}{W_{L2}}+\frac{1}{Q_{AIV}}\Big) + \frac{L0C}{4\text{B}}\cdot\frac{outB}{W_{L2}}
$$
两边除以 $L0C/4\text{B}$tile 输出元素数),**tile 尺寸消去**——K 阈值不依赖 M、N 的具体值:
$$
K > \frac{grid_K^2}{grid_K-1}\cdot\theta_c,\qquad
\theta_c = \frac{Q_{16}}{2}\Big(\frac{8\text{B}}{W_{L2}}+\frac{1}{Q_{AIV}}\Big)
$$
代入数值($Q_{16}$=15.2 TFLOPS$W_{L2}$=5.2 TB/s$Q_{AIV}$≈13.5 Tops/s
$$
\theta_c = \frac{15.2\times10^{12}}{2}\Big(\underbrace{\frac{8}{5.2\times10^{12}}}_{1.54\,\text{ps/元素}} + \underbrace{\frac{1}{13.5\times10^{12}}}_{0.07\,\text{ps/元素}}\Big) = 7.6\times10^{12} \times 1.61\times10^{-12} \approx 12
$$
L2 读写1.54 ps/元素是主导项AIV 求和0.07)仅占 5%。grid_K=2→K>494→K>668→K>112。
**访存 Bound**$T_{pipe} = T_{MTE2}^t$),同理($M^t N^t/(M^t+N^t)$ 不消去,但阈值远低于计算 Bound
$$
K > \frac{grid_K^2}{grid_K-1}\cdot\frac{M^t N^t}{M^t+N^t}\cdot\theta_m,\qquad
\theta_m = \frac{BW_{pc}}{\text{dtype}}\Big(\frac{8\text{B}}{W_{L2}}+\frac{1}{Q_{AIV}}\Big) \approx 0.04
$$
**访存 Bound 阈值远低于计算 Bound**——两情形阈值比值:
$$
\frac{\theta_m \cdot M^t N^t/(M^t+N^t)}{\theta_c} = \frac{2\cdot BW_{pc}}{Q_{16}\cdot\text{dtype}}\cdot\frac{M^t N^t}{M^t+N^t} = \frac{M^t N^t/(M^t+N^t)}{304}
$$
$M^t N^t/(M^t+N^t)$ 的范围tile 面积 $M^t N^t \le L0C/4\text{B} = 65536$ 元素L0C 容量上限)。由均值不等式 $M^t+N^t \ge 2\sqrt{M^t N^t}$,比值上界为 $\sqrt{M^t N^t}/2 \le \sqrt{65536}/2 = 128$(正方形 tile 取到);极端长宽比($M^t{=}16, N^t{=}4096$)时下界 $\approx 16$。StreamK case 的 tile 通常接近正方形 → 比值 $\sim$ O(64128),上界 128。无论取何值$128/304 \approx 0.42 < 1$——**访存 Bound 阈值恒低于计算 Bound**。直觉访存 Bound $T_{pipe} = T_{MTE2}^t > T_{MMAD}^t$,瓶颈时延更大,归约预算更充裕。**汇总条件取计算 Bound 阈值**(保守,同时覆盖两种情形)。
注意 θ_c 对 workspace 落点敏感:部分和落 GM 时读写带宽从 5.2TB/s 降到 ~0.64TB/sθ_c 升至约 97。设计时应优先保证 workspace 驻留 L2。
4. **工程约束**:归约顺序不定引入浮点非确定性,确定性等级 2/3 的业务禁用。
**源码对照**`batch_matmul_v3_basic_streamk_tiling.cpp` 中 K 的固定门槛为 `CeilAlign(K,256) ≥ max(8192, aicNum×256B/dtype)`
* `aicNum×256B/dtype` = 32×128 = 4096BF16= **dValue 下限256B在最大 grid_K=C 下的保障**——对应条件 2
* `8192` = 32×256 = C×512BBF16= **dValue 推荐值512B在最大 grid_K=C 下的保障**——同为条件 2取推荐值而非下限。
max 取更严格的 8192。修正后的归约阈值θ_c≈12grid_K=32 时 K>396远低于 8192说明 **8192 的绑定约束是 dValue条件 2不是归约代价条件 3**。源码不动态计算 grid_K用固定阈值同时覆盖条件 2 的最保守情形和条件 3是两条条件的保守合并近似。
### 实现方案
**Step 0BaseM / BaseN 的确定**L0 级 tile先把 L0C 用满)
L0C 是 Cube 的累加器BaseM × BaseN 是每次 Cube 计算的输出 tile。BaseM/N 应**尽量把 L0C 用满**——L0C 利用率越高,每次 Cube 计算的输出越大,单位计算的启动/排空开销摊得越薄:
$$
\text{BaseM} \times \text{BaseN} = \frac{L0C}{2 \times 4\text{B}} = 32768 \text{ 元素}
$$
双缓冲两份FP32 4B/元素。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 容量约束)。
**Step 1SingleCoreM / SingleCoreN 的确定**(每核输出 tile$\ge$ 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 核。
$$
mCnt \times 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——搬移效率**
$$
\text{singleCoreM} \cdot k_{L1} \cdot \text{dtype} \ge min\_TileSize,\qquad k_{L1} \cdot \text{singleCoreN} \cdot \text{dtype} \ge min\_TileSize
$$
**约束 4——SingleCoreM/N 是 BaseM/N 的整数倍**(工程实现要求,保证 L0 tile 边界对齐)。
*选取策略*:在满足约束 1 的前提下SingleCoreM/N 尽量大(搬移效率和 L2 复用最大化)。长宽比跟随 M/N$\text{singleCoreM}/\text{singleCoreN} \approx M/N$),对齐到 BaseM/BaseN 的整数倍。
*例*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 优先级分配到 C 核。
**Step 3核间切分维度选择按共享代价从低到高**:切 B零共享先试→ 切 M右矩阵 $KN\cdot\text{dtype} \le L2$ 则驻留 L2→ 切 N对称→ 混合切(靠 swizzle + L2 切分管理)→ 降核(见 Step 8
**Step 4swizzle——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 最快序不反向),正是这个收益结构的直接实现。
**Step 5L2 分组(工作集超 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 的策略反过来本文按单算子边界分析
**Step 6核内 tiling**BaseM/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内部特化参数极限不是独立分支**单边无 batch 且该侧矩阵小$M \le 256$、$MK\cdot\text{dtype}\cdot 2 \le L1$、对侧每核循环 4 时小侧整个常驻 L1只搬一次L1 全载)。
**Step 8降核模式实现**tiling `usedCoreNum = ⌈P⌉`不强制 CSingleCoreM/N L0C 容量内取最大$\text{singleCoreM} \times \text{singleCoreN} \times 4\text{B} \le L0C$每核按标准核内流水L1L0CubeL0CFixpipe处理自己的输出块核间无共享无依赖无需 swizzle L2 切分降核后 GM 并发搬移核数若 < minCoreNum带宽利用率上限被压低——这正是降核区 case 时延的瓶颈所在也是"时延绝对值小不再继续优化"的定量注脚
---
## 九、特殊分支
* **K=0**无任何计算C = bias 0 AIV 写值
* **K=1**:退化为逐元素乘 `C = A ⊙ B`无累加深度Cube 16×16×16 粒度浪费 15/16 AIV 向量通路GMUBMulGM优于 Cube 通路触发需 $B \ge 2 \times 64$AIV 核数×2 UB 乒乓且单 batch 输入输出能驻留 UB
---
## 十、case 遍历:各分支的覆盖区域
### 方法论说明
**采样方式**B [1, 2048] 全量遍历2048 个值M/N/K [1, 10240] 对数网格采样 60 /值按指数增长1, 2, 3, 5, 7, 10, 14, 19, 26, 35, ...)。 case 3.2 亿耗时约 4 分钟
**为什么不做全量遍历**全量 = 2048 × 10240³ 2.2×10¹⁵ casePython 分类器约需 1400 万小时C 实现约需 6 万小时——完全不可行对数采样在小值区密集大值区稀疏恰好覆盖了分支边界集中的区域
**分布依赖测度**对数均匀采样和线性均匀采样给出的分支占比**不同**。对数采样在小值区密集特殊分支K=0/1、降核 ASWP )、StreamKK 大但 M/N 的占比被放大线性采样被大 shape 主导M/N/K > 512 占 [1,10240] 的 95%+ASW_Basic 占比显著升高。**本文遍历的目的是验证覆盖性(无空洞),不是统计真实工作负载的分布。**
### 分支覆盖统计对数采样B 全量 2048M/N/K 60 点/维,共 3.2 亿 case
| 分支 | case 数 | 占比 | B 范围 | 区域特征 |
|---|---|---|---|---|
| ASW_Basic | 1.75 亿 | 54.1% | 2 ~ 2048 | 通用B<C PC BC L1 四形态不满足M/N |
| MergeBatch | 6303 | 19.5% | 97 ~ 2048 | $b_{core}\ge 4$ $MN \le 8192$ 等五条全过 |
| 降核 ASW_Basic | 4051 | 12.6% | 2 ~ 2048 | P<C K 不满足 StreamK 阈值 只用 P |
| IterBatch | 3804 | 11.8% | 32 ~ 2048 | BC负载均衡L1 四形态之一满足 |
| 特殊分支 | 597 | 1.9% | 任意 | K=0 / K=1 |
| StreamK | 25 | 0.08% | 2 ~ 128 | P<C/2 K8192 |
| 转Matmul | 15 | 0.05% | B=1 | 单边 batch=1 |
**对照:线性等距采样**M/N/K 步长 256B 全量 1.3 亿 case ASW_Basic 占比升至 92.1%MergeBatch 降至 3.6%——因为线性采样被大 shape 主导两种测度的结论一致**无空分支无覆盖空洞**只是占比不同
### IterBatch 四形态命中分布(对数采样)
| 形态 | 命中数 | 占比 | 说明 |
|---|---|---|---|
| b) batch 乒乓 | 2684 | 70.5% | 最多B 大且单 batch 较小 |
| d) 两侧切 K | 741 | 19.5% | 次之K 可切段的通用兜底 |
| c) 一侧驻留+对侧切 K | 370 | 9.7% | 单侧可驻留 b_core2 的半预算预取档 |
| a) batch 全驻留 | 9 | 0.2% | B=C 附近的窄区 |
### 典型边界 case
| B | M | N | K | 分支 | 说明 |
|---|---|---|---|---|---|
| 1 | 2048 | 2048 | 2048 | 转Matmul | batch Matmul |
| 128 | 64 | 64 | 512 | **MergeBatch** | 五条全过$b_{core}$=4MN=4096≤8192单核搬移 512KB480KBAI=64<304 |
| 128 | 64 | 64 | 256 | IterBatch | 与上行仅 K 不同单核搬移 256KB < 480KB条件 3 不满足 IterBatch形态 b |
| 512 | 128 | 128 | 128 | IterBatch | MN=16384 > 8192MergeBatch 条件 2 不满足 → 落 IterBatch |
| 32 | 4096 | 4096 | 4096 | ASW_Basic | L1 四形态均不满足M/N 太大),切 M/N |
| 2 | 8192 | 8192 | 1024 | ASW_Basic | B<CP=2048 32 |
| 16 | 256 | 256 | 128 | 降核 ASW | P=4 < 32 K=128 不满足 StreamK 4 其余闲置 |
| 4 | 128 | 128 | 10240 | StreamK | P=0.25 < 32K8192 |
| 2048 | 1024 | 1024 | 512 | IterBatch | batch形态 b |
| 8 | 512 | 512 | 512 | ASW_Basic | B<CP=32 恰好满核 |
| 64 | 64 | 64 | 8192 | IterBatch | M×N K 形态 d |
### 遍历结论
1. **七个分支全部有真实 case 命中**无空分支覆盖矩阵无空洞P<C K 小的残余由降核 ASW_Basic 兜底——此时时延绝对值小调度开销主导分支选择不敏感
2. **MergeBatch 的区域由条件 2$MN \le 8192$)与条件 3min_DatamountPerCore夹出** M×N 且单核搬移量足够的大 batch case两个条件缺一不可典型分界对照B=128/M=N=64 K=256 单核搬移 256KB 不达标落 IterBatchK=512 512KB 达标进 MergeBatch
3. **IterBatch 与 ASW_Basic 的分界就是 L1 四形态是否满足** batch 输入 $(MK+KN)\cdot\text{dtype}$ 相对 L1 的比例决定归属——这正是"核内零重复读"原则的定量体现
4. **StreamK 的区域为 P<C/2 且 K≥8192**B M/N K 大的"细长" caseC/2 P < C K 大的 case 由降核 ASW_Basic 承接 2 K 会超核数不切又不满核不如直接用 P
5. **降核 ASW_Basic 是 P<C 且 K 小区域的理性归宿** 12.6%并行度凑不满 K 又不划算时只用 P 个核每核一个 L0C 满载输出块比强行碎切tile 跌破搬移效率下限更快