diff --git a/BMM算子优化分析_Release/ASW_Basic分支分析_v1.1.md b/BMM算子优化分析_Release/ASW_Basic分支分析_v1.1.md index da3d4db..dd3657f 100644 --- a/BMM算子优化分析_Release/ASW_Basic分支分析_v1.1.md +++ b/BMM算子优化分析_Release/ASW_Basic分支分析_v1.1.md @@ -217,13 +217,38 @@ $$ ### Step 0:BaseM / BaseN 的确定(L0 级 tile,先把 L0C 用满) -L0C 是 Cube 的累加器,BaseM × BaseN 是每次 Cube 计算的输出 tile。BaseM/N 应**尽量把 L0C 用满**——L0C 利用率越高,每次 Cube 计算的输出越大,单位计算的启动/排空开销摊得越薄: +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{ 元素} +\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}$): +但昇腾 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→362),mCnt/nCnt 相应减小,**L2 重复读率降低**(单 batch 块数减少 → 每行 A 被更少的组读取)。 + +*时延对比*(单 tile 粒度,BF16 输出): + +| 方案 | tile 大小 | tile 间流水 | tile 内流水 | 单 tile 时延 | +|---|---|---|---|---| +| 双缓冲 | 181×181(32761 元素) | ✓(tile N 写出 ∥ tile N+1 计算) | ✗ | max($T_{comp}$, $T_{write}$) | +| UnitFlag 单缓冲 | 256×256(65536 元素) | ✗ | ✓(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=1(256×256×4B×2 > L0C)——即**源码已经是单缓冲 + 无 UnitFlag**,tile 到顶但无流水交叠。 + +*建议*:对计算 Bound 的 case 启用 UnitFlag(MMAD 与 Fixpipe 流水并行),可将单 tile 时延从 $T_{comp} + T_{write}$ 降至 $\max(T_{comp}, T_{write})$。对访存 Bound 的 case(MTE2 Bound),UnitFlag 收益小([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 对齐} @@ -465,7 +490,11 @@ $$ 三项含义:① L2 供数速率 ÷ Cube 耗数速率(理论阈值);② 工作集超 L2 时的访存惩罚(抬高 edge,倾向更大 tile);③ K 向复用修正(K 越大 edge 越小,偏 compute bound)。乘以 CUBE_BOUND_RATIO=0.85 余量。枚举 baseM/baseN 递减,balanceRate ≥ 0.9 剪枝。 -**差异分析**:理论直接取 L0C 满载(32768 元素),源码用 256×256=65536 元素——**源码的 baseM×baseN 是理论上限的 2 倍**。这说明源码的 baseM/baseN 不是 L0C 级 tile 尺寸,而是 SingleCoreM/N 级参数。源码中 L0 级 tile 由 stepM/stepN 二次切分决定(per-core tile = baseM × stepM)。理论的分层(SingleCoreM ≥ BaseM)在源码中无显式概念。 +**差异分析**:理论(修正前)按双缓冲取 L0C 满载的一半(32768 元素),源码用 256×256=65536 元素——**源码的 baseM×baseN 恰好等于 L0C 单缓冲上限**(256×256×4B = 256KB = L0C)。源码的 dbL0C 逻辑:`dbL0C = (baseM × baseN × 4B × 2 > L0C) ? 1 : 2`——baseM=baseN=256 时 512KB > 256KB,自动选 dbL0C=1(单缓冲)。 + +这说明源码的 baseM/baseN 不是 L0 级 tile 尺寸,而是 SingleCoreM/N 级参数。源码中 L0 级 tile 由 stepM/stepN 二次切分决定(per-core tile = baseM × stepM)。理论的分层(SingleCoreM ≥ BaseM)在源码中无显式概念。 + +**UnitFlag 现状**:BMM ASW kernel 中 `unitFlag = 0`(`block_mmad_iterbatch.h` L315 注释:"each l0 only process one block, disable unit flag")——MMAD 与 Fixpipe 之间是**指令级同步**(整个 L0C tile 算完才写出)。若启用 UnitFlag,MMAD 每完成一个 16×16×16 块(512B),Fixpipe 立即写出——tile 内部细粒度流水替代 tile 间粗粒度流水,单缓冲即可达到双缓冲的流水效果。对计算 Bound 的 case,UnitFlag 可将单 tile 时延从 $T_{comp} + T_{write}$ 降至 $\max(T_{comp}, T_{write})$。 ### 7.3 SingleCoreM / SingleCoreN 确定(Step 1)