diff --git a/BMM算子优化分析_Release/BMM算子优化分析_v0.6.html b/BMM算子优化分析_Release/BMM算子优化分析_v0.6.html index cd6ca19..5ea90e1 100644 --- a/BMM算子优化分析_Release/BMM算子优化分析_v0.6.html +++ b/BMM算子优化分析_Release/BMM算子优化分析_v0.6.html @@ -277,12 +277,37 @@ $$

实现方案

1、核间切分维度选择(按共享代价从低到高):切 B(零共享,先试)→ 切 M(右矩阵 $KN\cdot\text{dtype} \le L2$ 则驻留 L2)→ 切 N(对称)→ 混合切(靠 swizzle + L2 切分管理)→ 降核(见第 6 条)。

-

2、swizzle:ASW 滑窗蛇形。M 向按窗口 W 分组,窗内 N 向蛇形遍历:

+

2、swizzle:ASW 滑窗蛇形

+

问题:核间切 M/N 后,同一时刻 C 个核各算一个输出块,它们所需的 A 行块与 B 列块集合就是当前"活跃工作集"。若按行优先顺序朴素分配,一波 C 个块横跨的 A 行、B 列很宽,活跃工作集超过 L2 就回 GM 读(1.6TB/s),重复读代价真实发生。swizzle 要做的就是编排输出块的执行顺序,把每一波核的活跃工作集压到最小。

+

做法:把 M 向每 W 个基本块划为一个"窗口",遍历顺序为"窗口内先扫 M、扫满 W 行再进下一列 N;一个窗口扫完再进下一个窗口",且奇数窗口行 N 向反向(蛇形)。效果有二:

+ +

W 怎么取:一波 C 个块的 L2 足迹约为

$$ -W = \max\{\,d \mid d \mid C,\; d \le \lfloor\sqrt{C}\rfloor\,\} \quad (C{=}32 \Rightarrow W{=}4) +footprint \approx \big(W \cdot M^t K + \tfrac{C}{W} \cdot K N^t\big) \cdot \text{dtype} $$
-

同一时刻 C 个核的活跃工作集被压缩到"W 个 A 行块 + 一条 B 列块带",L2 足迹最小。窗取 $\lfloor\sqrt C\rfloor$ 的最大因子:窗越接近方形两侧足迹之和越小,且因子性保证整窗被核数均分、边界不碎。

-

3、L2 切分:工作集超 128MB 时按 mL2TileNum×nL2TileNum 切块,块内错位分核(对角线分配),避免多核同时抢同一地址的读读冲突,优先选拖尾小的方案。

+

由均值不等式,$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=32,W=4,M̃=8,Ñ=8;数字为块的全局执行顺序,一波 32 块):

+
窗口0(N 正向)                窗口1(N 蛇形反向)
+      ν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、L2 切分(工作集超 L2 时)

+

问题:滑窗压缩的是"同一波"的足迹;若整个工作集(全部 A 行 + 全部 B 列,跨所有窗口)超过 128MB L2,跨波次的复用仍会落空——上一波窗口的 A 行早被挤出 L2,下一波又得回 GM 读。

+

做法:把 M×N 平面按 mL2TileNum × nL2TileNum 切成若干"L2 块",每块的工作集控制在 L2 容量内,逐块计算——块内滑窗复用充分,块间才发生一次性换入。块内分配用错位分核(对角线分配):线性块号先取 m,n 方向再叠加一个随块号递增的相位偏移,使同一时刻各核落在 M×N 平面的不同对角线上——避免多核同一拍并发读同一行 A / 同一列 B 的同一地址(同地址并发读会串行化,等效带宽打折)。冲突度量取

+
$$ +transConflict = \max\big(\lceil C / mCnt \rceil,\; \lceil C / nCnt \rceil\big) +$$
+

即同一时刻并发核访问同一 A/B 块的最大冲突数,要求不超过阈值(经验值 6);切分方案中优先选尾波不满载占比小(拖尾 < 一半)的。遍历大方向由 calOrder 决定(0=M 优先、1=N 优先),按形状选共享矩阵更能驻留 L2 的方向。

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 时延的瓶颈所在,也是"时延绝对值小、不再继续优化"的定量注脚。