Add BMM_Theory: docs/02_分支理论/06_ASW_Basic分支.md
This commit is contained in:
131
BMM/BMM_Theory/docs/02_分支理论/06_ASW_Basic分支.md
Normal file
131
BMM/BMM_Theory/docs/02_分支理论/06_ASW_Basic分支.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# ASW_Basic 分支理论(含尾轮处理)
|
||||
|
||||
> 整理自《BMM算子优化分析 v0.98》§八 + 《BMM尾轮处理策略对比分析 v1.5》.
|
||||
> 尾轮策略的完整推导见 [07_尾轮处理策略.md](07_尾轮处理策略.md), 本文将其结论**内化为 ASW_Basic 最优实现的必要环节**.
|
||||
> 对应软件实现: `bmm_theory/branches/asw_basic.py`.
|
||||
|
||||
## 1. 定位:兜底分支,实践中最常命中
|
||||
|
||||
ASW_Basic 是 BMM 的**兜底分支**——核间切 M/N(或混合切),不做 batch 合并或 K 维切分。能进入更靠前特殊分支(转Matmul/特殊/MergeBatch/IterBatch/StreamK)的 case 会被优先截胡,剩下的都落到这里。v0.98 §十统计:对数采样下 ASW_Basic 占 54.1%,是命中最多的分支。B 可大可小可等 1,交叉广播也由此承接。
|
||||
|
||||
## 2. 进入条件
|
||||
|
||||
| # | 条件 | 说明 |
|
||||
|---|---|---|
|
||||
| 1 | `P = B·M·N·4B / L0C ≥ C` | **并行度补齐**:以 L0C 满载为基本块粒度,B×M×N 能切出至少 C 个独立输出块 |
|
||||
| 2 | 无 batch 结构限制 | BatchA=BatchB、交叉广播均可(广播侧做 L1/L2 驻留,共享关系与切 M/N 同构) |
|
||||
| 3 | **降核模式**:`P < C` 且不满足 StreamK 进入条件 | 只用 ⌈P⌉ 个核,其余核闲置 |
|
||||
|
||||
**降核的理性**:P < C 且 K 也不够格走 StreamK 时,与其强行把 M/N 切得更碎(tile 跌破 min_TileSize、dValue 跌破 128B,搬移效率崩塌反而更慢),不如只用 ⌈P⌉ 个核、每核一个 L0C 满载输出块。这类 case 时延绝对值小,继续切分的调度与搬移效率损失大于并行收益。
|
||||
|
||||
## 3. 时延建模
|
||||
|
||||
核间切 M/N(不切 K),每核处理若干 [singleCoreM, singleCoreN] 输出块:
|
||||
|
||||
$$T = \max(T_{MTE2},\; T_{MMAD},\; T_{FIX}) + T_{drain}$$
|
||||
|
||||
其中 `T_MMAD = 2BMNK/(C·Q16)`、`T_FIX = B·MN·outB/(C·W_pc)` 与分配策略无关——**分配策略只影响 T_MTE2**(通过 L2 命中率影响有效 GM 带宽):
|
||||
|
||||
$$T_{MTE2} = \frac{S_{in}^{GM}}{C\cdot BW_{pc}},\qquad S_{in}^{GM} = r_{in}\cdot S_{in}$$
|
||||
|
||||
`r_in` = 重复读倍率(GM 输入流量/输入总量),r_in≥1,下界 1 表示每字节只从 GM 读一次(后续复用全命中 L2)。**分配策略的全部目标就是让 r_in 尽量接近 1**。
|
||||
|
||||
## 4. 核间分配策略:B→M→N 线性映射
|
||||
|
||||
**结论:B 优先分组在任何场景下都不优于线性映射**,根本原因:
|
||||
|
||||
1. L2 是共享 Cache,工作集越小命中率越高——线性映射同一时刻只激活 1 个 batch 的数据,工作集最小;
|
||||
2. swizzle 窗口 W ∝ √参与核数——更多核参与同一 batch 的 swizzle,窗口更大,工作集更接近"方形"下限;
|
||||
3. 线性映射对 B 与 C 的关系无要求,B 不整除 C 时无负载不均。
|
||||
|
||||
数值例(C=32、B=4、sM=sN=256、K=1024、BF16):分组工作集 12MB,线性 6MB——**线性映射工作集是分组的一半**。
|
||||
|
||||
## 5. 实现方案
|
||||
|
||||
**Step 0: BaseM/BaseN**(L0 级 tile,先把 L0C 用满)
|
||||
|
||||
$$\text{BaseM}\times\text{BaseN} = \frac{L0C}{2\times 4B} = 32768\ \text{元素}$$
|
||||
|
||||
双缓冲两份、FP32 4B/元素。长宽比跟随 SingleCoreM/N,对齐 16。baseK = min(L0A/(2·BaseM·dt), L0B/(2·BaseN·dt)) 向下 16 对齐(L1→L0 无 dValue 要求)。
|
||||
|
||||
**Step 1: SingleCoreM/N**(每核输出 tile,≥ BaseM/N)
|
||||
|
||||
不受 L0 容量直接约束(内部由若干 BaseM×BaseN L0 tile 组成)。核心影响 **GM→L1 搬移效率和 L2 重复读率**:
|
||||
|
||||
- 约束 1 并行度:`mCnt×nCnt ≥ ⌈C/B⌉`;
|
||||
- 约束 2 L1 容量:`2(sM+sN)·k_L1·dt ≤ L1`,`k_L1·dt ≥ 256B`;
|
||||
- 约束 3 搬移效率:`sM·k_L1·dt ≥ 16KB`,`k_L1·sN·dt ≥ 16KB`;
|
||||
- 约束 4:SingleCoreM/N 是 BaseM/N 的整数倍。
|
||||
|
||||
选取策略:满足约束 1 前提下 SingleCoreM/N 尽量大,长宽比跟随 M/N。
|
||||
|
||||
**Step 2: mCnt/nCnt 与核间分配**:`mCnt=⌈M/sM⌉`,`nCnt=⌈N/sN⌉`,总块数 B·mCnt·nCnt,B→M→N 线性映射。
|
||||
|
||||
**Step 3: 切分维度选择(按共享代价从低到高)**:切 B(零共享先试)→ 切 M(右矩阵 ≤L2 则驻留)→ 切 N(对称)→ 混合切 → 降核。
|
||||
|
||||
**Step 4: swizzle——ASW 滑窗蛇形**
|
||||
|
||||
同一波 C 个核所需的 A 行块 + B 列块集合是"活跃工作集",超 L2 就回 GM 读。swizzle 编排输出块执行顺序,把每一波的活跃工作集压到最小。窗口宽度 `W = max{d | d|C, d ≤ ⌊√C⌋}`(C=32 时 W=4);窗口内先扫 M、扫满 W 行进下一列 N,奇数窗口行 N 向反向(蛇形)。
|
||||
|
||||
**Step 5: L2 分组(工作集超 L2 时)**
|
||||
|
||||
记 S_in = B(MK+KN)·dt,S_out = B·MN·outB。两个不变量:GM 流量下界 = S_in + S_out;L2 读入可用空间 L2_read = L2 − S_out^resident。
|
||||
|
||||
| 场景 | 条件 | 策略 |
|
||||
|---|---|---|
|
||||
| A 全驻留 | S_in + S_out ≤ L2 | 输入读一遍(r_in=1),输出驻留 L2 异步回写,无需切分 |
|
||||
| B 输入能驻留,加输出超了 | S_in ≤ L2 < S_in+S_out | **输入驻留、输出直写 GM**,保住 r_in=1;校验总线 (S_in+S_out)/T_MMAD ≤ W_GM |
|
||||
| C 输入本身超 | S_in > L2 | 分组执行,每组输入工作集 ≤ L2,输出直写 GM |
|
||||
|
||||
场景 C 分组:每组覆盖 M 向 m_grp、N 向 n_grp 个基本块,`m_grp = ⌊D/(2·sM)⌋`,`n_grp = ⌊D/(2·sN)⌋`,D = L2/(B·K·dt);重复读倍率 `r_in = (n_grp·M + m_grp·N)/(M+N)`。块内错位分核(对角线分配)避免同地址并发读串行化。
|
||||
|
||||
**Step 6: 核内 tiling**:BaseM×BaseN×4B×DB ≤ L0C;BaseM×k_L0×dt×2 ≤ L0A;k_L0×BaseN×dt×2 ≤ L0B;内轴按 dValue 256B/512B 对齐。
|
||||
|
||||
**Step 7: 内部特化**:单边无 batch 且该侧矩阵小时小侧整个常驻 L1 只搬一次。
|
||||
|
||||
**Step 8: 降核模式**:`usedCoreNum = ⌈P⌉`,SingleCoreM/N 在 L0C 容量内取最大,每核标准核内流水,无需 swizzle 与 L2 切分。
|
||||
|
||||
## 6. 尾轮处理(ASW_Basic 最优实现的必要组成)
|
||||
|
||||
总块数 `N_blk = B·mCnt·nCnt` 一般不是 C 的整数倍,尾轮只有 `r = N_blk mod C` 个核工作、C−r 核空转一个整块时间。**尾轮策略不是可选优化,而是 ASW_Basic 最优实现的必要组成**——首轮切分决定主轮效率,尾轮策略决定尾轮是否拖后腿。
|
||||
|
||||
### 6.1 候选策略
|
||||
|
||||
| 策略 | 做法 | tile 套数 |
|
||||
|---|---|---|
|
||||
| **A0 不重切** | 尾轮 r 核各处理 1 整块,C−r 核空转 | 1 |
|
||||
| **A1a 尾轮整数倍切分** | 尾轮每块沿 N(或 M)切 s* 份 | 2 |
|
||||
| **A1b 尾轮 tile 重选凑满核** | 尾轮 r 个原块区域用更小 tile 重切,凑满 C 核 | 2 |
|
||||
| **方案 B 整轮均匀重切** | 总块数向上取整到 n_wave·C,全局统一 tile | 1 |
|
||||
|
||||
### 6.2 最优选择规则(融入方案生成)
|
||||
|
||||
依据主导项的**缩放类型**(面积型 ∝ sM·sN / 周长型 ∝ sM+sN,判定见 07 文档 §2):
|
||||
|
||||
**主流场景(面积型主导,prefill/decode 均属此类)**:
|
||||
|
||||
- A1b 与方案 B **理论时延严格相等**(总量守恒 `T = c·B·M·N/C`);
|
||||
- **默认选方案 B(工程简洁:一套 tile、无尾轮分支)**;追求搬移下限选 A1b(周长和恒 ≤ 方案 B,均值不等式);
|
||||
- r 小(ρ < (187/s)²)时 A1b 尾轮翻出为周长型,**方案 B 微优 ~6%**;
|
||||
- A1a 是 A1b 真子集,仅在 r | C 且 s*=⌊C/r⌋ 完美时打平,工程上可不单列。
|
||||
|
||||
**边角场景(周长型主导,小 tile 或工作集超 L2 的 GM 直读)**:
|
||||
|
||||
- ρ ≥ ρ_dv(=(256B/(sN·dt))²)时 **A1b 恒优**;
|
||||
- ρ < ρ_dv 且方案 B 可行且 `√ρ_dv > n_wave/√g − (n_wave−1)` 时方案 B 反超;
|
||||
- 广义方案 B(允许核利用 ≥80%)恒不劣于 A0,但恒不优于 A1b。
|
||||
|
||||
**统一裁决**:软件实现中对每个 case 按 07 文档 §6 的五步闭式流程计算,**默认输出方案 B(主流安全、工程简洁),周长型且 ρ≥ρ_dv 时输出 A1b**,r=0 时 A0。A0 永不最优(r>0 时严格劣)。
|
||||
|
||||
### 6.3 软件结构体落位
|
||||
|
||||
尾轮参数落在 `ImplPlan` 的 tail_* 成员(参考源码 `MatMulV3TailInfo{mCnt,nCnt,kCnt,mTailMain,nTailMain}`):
|
||||
|
||||
- `tail_strategy`:A0 / A1a / A1b / 方案B;
|
||||
- `tail_m_cnt`/`tail_n_cnt`/`tail_k_cnt`:尾轮切分数(A1b 时为尾轮重切后的 mCnt'/nCnt');
|
||||
- `tail_m_main`/`tail_n_main`:尾轮主体块数;
|
||||
- `tail_block_cnt` = r,`tail_wave_num` = n_wave。
|
||||
|
||||
## 7. 与源码的差异
|
||||
|
||||
源码 ASW 的核间分配用 `UpdateBasicIndex` 线性映射 + ASW 滑窗蛇形(与理论一致),但**尾轮不重切**(`if (index < totalCnt)` 跳过空转核,即策略 A0)。理论证明 A0 在 r>0 时严格劣于 A1b/方案 B——这是源码相对理论最优的一个明确差距点。本软件按 v1.5 的闭式决策流程输出尾轮策略,补齐这一差距。
|
||||
Reference in New Issue
Block a user