diff --git a/BMM算子优化分析_Release/BMM算子优化分析_v0.6.html b/BMM算子优化分析_Release/BMM算子优化分析_v0.6.html index 5ea90e1..34c31ab 100644 --- a/BMM算子优化分析_Release/BMM算子优化分析_v0.6.html +++ b/BMM算子优化分析_Release/BMM算子优化分析_v0.6.html @@ -301,13 +301,36 @@ $$ μ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)"的说明):蛇形的收益来自"相邻遍历段共享边界数据",要分两种边界看:
+BatchMatMulAswBlock::UpdateBasicIndex:仅 rowIdx 为奇时 n 反向,窗内 m 最快序不反向),正是这个收益结构的直接实现。3、L2 切分(工作集超 L2 时)
-问题:滑窗压缩的是"同一波"的足迹;若整个工作集(全部 A 行 + 全部 B 列,跨所有窗口)超过 128MB L2,跨波次的复用仍会落空——上一波窗口的 A 行早被挤出 L2,下一波又得回 GM 读。
-做法:把 M×N 平面按 mL2TileNum × nL2TileNum 切成若干"L2 块",每块的工作集控制在 L2 容量内,逐块计算——块内滑窗复用充分,块间才发生一次性换入。块内分配用错位分核(对角线分配):线性块号先取 m,n 方向再叠加一个随块号递增的相位偏移,使同一时刻各核落在 M×N 平面的不同对角线上——避免多核同一拍并发读同一行 A / 同一列 B 的同一地址(同地址并发读会串行化,等效带宽打折)。冲突度量取
+问题:滑窗压缩的只是"同一波"的足迹;若整个工作集超 128MB L2,跨波次复用落空——上一波窗口的 A 行早被挤出,下一波又得回 GM 读。且 L2 是读写共用的:输出经 fixpipe 写出时若驻留 L2(dirty),会压缩读入可用空间;若直写 GM,则占用与读共享的 1.6TB/s 总线。所以 L2 切分必须与写出策略联合决策。记输入总量 $S_{in} = B(MK+KN)\cdot\text{dtype}$,输出总量 $S_{out} = B \cdot MN \cdot outB$。
+两个不变量(一切分析的起点):
+先判定写出会不会 Bound。平均写出带宽需求:
即同一时刻并发核访问同一 A/B 块的最大冲突数,要求不超过阈值(经验值 6);切分方案中优先选尾波不满载占比小(拖尾 < 一半)的。遍历大方向由 calOrder 决定(0=M 优先、1=N 优先),按形状选共享矩阵更能驻留 L2 的方向。
+只与 K、outB 有关(K 越小,单位时间输出越密)。例(BF16 输出,C·Q₁₆=432 TFLOPS):K=512 → 844 GB/s;K=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$),输出驻留 L2(dirty)异步回写 GM——写出走 5.2TB/s L2 写口,不与读争,也削平了 GM 写突发。无需切分。
+**场景 B:$S_{in} \le L2$ 但 $S_{in} + S_{out} > L2$(输入能驻留,加上输出超了)。输入驻留、输出直写 GM**(fixpipe L0C→GM,不占 L2),保住 $r_{in}=1$;校验总线:$(S_{in} + S_{out})/T_{MMAD} \le W_{GM}$。例:B=8、M=N=4096、K=512、BF16——$S_{in}$=64MB ≤ L2,$S_{out}$=256MB 直写 GM;T_MMAD≈318µs,总流量速率 (64+256)MB/318µs ≈ 1.06TB/s < 1.6TB/s ✓。
+**场景 C:$S_{in} > L2$(输入本身超)**。必须 L2 切分。输出直写 GM 以最大化 $L2_{read}$,切分数满足
+每块输入工作集 ≤ $L2_{read}$,逐块计算——块内滑窗复用充分,块间只发生一次性换入。块内分配用错位分核(对角线分配):线性块号先取 m,n 方向叠加随块号递增的相位偏移,使同一时刻各核落在 M×N 平面的不同对角线上——避免多核同一拍并发读同一行 A / 同一列 B 的同一地址(同地址并发读会串行化,等效带宽打折)。冲突度量与优选规则:
+即同一时刻并发核访问同一 A/B 块的最大冲突数不超过阈值(经验值 6);切分方案中优先选尾波不满载占比小(拖尾 < 一半)的。遍历大方向由 calOrder 决定(0=M 优先、1=N 优先),按形状选共享矩阵更能驻留 L2 的方向。例:B=64、M=N=2048、K=1024、BF16——$S_{in}$=512MB > L2,输出直写($L2_{read}$=128MB),切 3×2=6 块(每块输入约 89MB ≤ 128MB);总流量 1GB,T_MMAD≈1.27ms,速率 844GB/s < 1.6TB/s ✓。
+补充:若输出会被后续算子立即消费(融合场景),输出驻留 L2 让下游读命中,场景 B/C 的策略反过来;本文按单算子边界分析。
4、核内 tiling:$M^t N^t \cdot 4\text{B} \cdot DB \le L0C$;$M^t K^t \cdot \text{dtype} \cdot 2 \le L0A$、$K^t N^t \cdot \text{dtype} \cdot 2 \le L0B$;内轴按 dValue 256B/512B 对齐;L1 按容量开双缓冲,余量充足开 4 buffer。
5、内部特化(参数极限,不是独立分支):单边无 batch 且该侧矩阵小($M \le 256$、$MK\cdot\text{dtype}\cdot 2 \le L1$、对侧每核循环 ≥4 轮)时小侧整个常驻 L1、只搬一次(L1 全载)。
6、降核模式实现:tiling 时 usedCoreNum = ⌈P⌉(不强制 C),基本块在 L0C 容量内取最大($M^t N^t \cdot 4\text{B} \le L0C$),每核按标准核内流水(L1→L0→Cube→L0C→Fixpipe)处理自己的输出块;核间无共享无依赖,无需 swizzle 与 L2 切分。降核后 GM 并发搬移核数若 < minCoreNum,带宽利用率上限被压低——这正是降核区 case 时延的瓶颈所在,也是"时延绝对值小、不再继续优化"的定量注脚。