v0.6 修订:ASW swizzle 与 L2 切分补充原理推导与实例
This commit is contained in:
@@ -312,15 +312,53 @@ mCnt、nCnt 收拢为 blocksPerBatch 的因子(避免碎核尾块);由条
|
|||||||
|
|
||||||
**1、核间切分维度选择(按共享代价从低到高)**:切 B(零共享,先试)→ 切 M(右矩阵 $KN\cdot\text{dtype} \le L2$ 则驻留 L2)→ 切 N(对称)→ 混合切(靠 swizzle + L2 切分管理)→ 降核(见第 6 条)。
|
**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 向反向(蛇形)。效果有二:
|
||||||
|
|
||||||
|
* 同一波 C 个核的块集中在同一个窗口内 ⇒ 活跃 A 行块只有 W 个、活跃 B 列块只有 C/W 条带;
|
||||||
|
* 蛇形反向使相邻窗口行首尾相接——上一窗口末尾的 B 列带与下一窗口开头的 B 列带是同一条,跨窗口切换时工作集增量最小。
|
||||||
|
|
||||||
|
**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$ 的最大因子:窗越接近方形两侧足迹之和越小,且因子性保证整窗被核数均分、边界不碎。
|
由均值不等式,$W + C/W$ 在 $W = \sqrt{C}$ 处取最小——窗口越接近"方形"(W 行 × C/W 列),足迹越小。同时 W 须整除 C,保证每个窗口恰好被整数波核覆盖、窗口边界不把波次切碎。合起来即:
|
||||||
|
|
||||||
**3、L2 切分**:工作集超 128MB 时按 mL2TileNum×nL2TileNum 切块,块内错位分核(对角线分配),避免多核同时抢同一地址的读读冲突,优先选拖尾小的方案。
|
$$
|
||||||
|
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。
|
**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。
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user