v0.94: ASW_Basic补Step0 singleCoreM/N确定+步骤编号统一
This commit is contained in:
@@ -405,6 +405,47 @@ max 取更严格的 8192。修正后的归约阈值(θ_c≈12,grid_K=32 时
|
||||
|
||||
### 实现方案
|
||||
|
||||
**Step 0:singleCoreM / singleCoreN 的确定**(每核输出基本块尺寸)
|
||||
|
||||
singleCoreM × singleCoreN 是 ASW_Basic 的基石参数——它决定了每核每次计算的输出 tile 大小,进而决定 mCnt/nCnt、swizzle 窗口、L2 执行组等所有后续参数。确定逻辑按约束链推导:
|
||||
|
||||
*约束 1——L0C 容量*(输出 tile 必须放得下 L0C 双缓冲):
|
||||
|
||||
$$
|
||||
\text{singleCoreM} \times \text{singleCoreN} \times 4\text{B} \times 2 \le L0C \Rightarrow \text{singleCoreM} \times \text{singleCoreN} \le 32768 \text{ 元素}
|
||||
$$
|
||||
|
||||
*约束 2——L0A/L0B 容量*(输入 tile 决定 baseK 上限):
|
||||
|
||||
$$
|
||||
baseK \le \min\Big(\frac{L0A}{2 \cdot \text{singleCoreM} \cdot \text{dtype}},\; \frac{L0B}{2 \cdot \text{singleCoreN} \cdot \text{dtype}}\Big)
|
||||
$$
|
||||
|
||||
*约束 3——dValue*:$baseK \cdot \text{dtype} \ge 256\text{B}$(BF16 为 128 元素)。
|
||||
|
||||
*约束 4——L1 容量*(双缓冲下驻留输入):
|
||||
|
||||
$$
|
||||
2 \cdot (\text{singleCoreM} + \text{singleCoreN}) \cdot k_{L1} \cdot \text{dtype} \le L1
|
||||
$$
|
||||
|
||||
*约束 5——搬移效率*:$\text{singleCoreM} \cdot K \cdot \text{dtype} \ge min\_TileSize$ 且 $K \cdot \text{singleCoreN} \cdot \text{dtype} \ge min\_TileSize$。
|
||||
|
||||
*选取策略*:在约束 1 的上界(32768 元素)内,singleCoreM/singleCoreN 的长宽比应**跟随 M/N 的长宽比**——$\text{singleCoreM}/\text{singleCoreN} \approx M/N$,使 GM 访问的空间局部性最优(减少跨行/跨列的 strided 访问)。同时对齐到 16 的倍数(Cube 基本块粒度)。
|
||||
|
||||
*例*:M=N=2048,方形 → singleCoreM=singleCoreN=$\lfloor\sqrt{32768}\rfloor_{16}$=176。约束 2:$baseK \le 16384/176 = 93$ → 取 80(16 对齐)。约束 3:$80 \times 2 = 160\text{B} \ge 128\text{B}$ ✓。
|
||||
|
||||
*例*:M=4096、N=512,长条 → singleCoreM=256、singleCoreN=128($256 \times 128 = 32768$ 恰好满载 L0C/2)。约束 2:$baseK \le \min(16384/256, 16384/128) = \min(64, 128) = 64$。
|
||||
|
||||
**Step 1:mCnt / nCnt 与核间分配**
|
||||
|
||||
$$
|
||||
mCnt = \Big\lceil \frac{M}{\text{singleCoreM}} \Big\rceil,\qquad nCnt = \Big\lceil \frac{N}{\text{singleCoreN}} \Big\rceil
|
||||
$$
|
||||
|
||||
总输出块数 = $B \times mCnt \times nCnt$,按 B→M→N 优先级分配到 C 核。
|
||||
|
||||
|
||||
**核间组织**:先按 B/M/N 切出输出块,剩余核预算折成 K 向份数:
|
||||
|
||||
$$
|
||||
@@ -440,9 +481,9 @@ mCnt、nCnt 收拢为 blocksPerBatch 的因子(避免碎核尾块);由条
|
||||
|
||||
### 实现方案
|
||||
|
||||
**1、核间切分维度选择(按共享代价从低到高)**:切 B(零共享,先试)→ 切 M(右矩阵 $KN\cdot\text{dtype} \le L2$ 则驻留 L2)→ 切 N(对称)→ 混合切(靠 swizzle + L2 切分管理)→ 降核(见第 6 条)。
|
||||
**Step 2:核间切分维度选择(按共享代价从低到高)**:切 B(零共享,先试)→ 切 M(右矩阵 $KN\cdot\text{dtype} \le L2$ 则驻留 L2)→ 切 N(对称)→ 混合切(靠 swizzle + L2 切分管理)→ 降核(见 Step 7)。
|
||||
|
||||
**2、swizzle:ASW 滑窗蛇形**
|
||||
**Step 3:swizzle——ASW 滑窗蛇形**
|
||||
|
||||
**问题**:核间切 M/N 后,同一时刻 C 个核各算一个输出块,它们所需的 A 行块与 B 列块集合就是当前"活跃工作集"。若按行优先顺序朴素分配,一波 C 个块横跨的 A 行、B 列很宽,活跃工作集超过 L2 就回 GM 读(1.6TB/s),重复读代价真实发生。**swizzle 要做的就是编排输出块的执行顺序,把每一波核的活跃工作集压到最小。**
|
||||
|
||||
@@ -483,7 +524,7 @@ C=32 时 $\sqrt{32} \approx 5.66$,因子 {1,2,4,8,…} 中不超过它的最
|
||||
* **窗内列间边界**(ν0→ν1):相邻两段共享的是同一组 A 行块(W 个),它们在整个窗口期间**全程驻留 L2**,无论按什么顺序扫,工作集不变——窗内蛇形零收益;
|
||||
* **窗口行边界**(窗口0→窗口1):A 行整体换血(μ0..3 → μ4..7),此时 B 列带的连续性决定换血成本——不蛇形则下一窗口从 ν0 开始(LRU 上最久未用、早已被挤出 L2 的冷带),蛇形则延续上一窗口末尾的 ν7(最热线带)。**蛇形只标在窗口行号上**(源码 `BatchMatMulAswBlock::UpdateBasicIndex`:仅 `rowIdx` 为奇时 n 反向,窗内 m 最快序不反向),正是这个收益结构的直接实现。
|
||||
|
||||
**3、L2 切分(工作集超 L2 时)**
|
||||
**Step 4:L2 分组(工作集超 L2 时)**
|
||||
|
||||
**切的是什么**:将 mCnt×nCnt 个基本块划分为若干**执行组**——每组覆盖输出平面上一个连续矩形区域(若干 singleCoreM × singleCoreN 基本块的集合),使该组所需的 A 行带 + B 列带输入工作集 ≤ L2 可用读入空间;组内所有基本块算完再进下一组,输入只在跨组时换一次。
|
||||
|
||||
@@ -576,11 +617,11 @@ $$
|
||||
|
||||
补充:若输出会被后续算子立即消费(融合场景),输出驻留 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。
|
||||
**Step 5:核内 tiling**:$\text{singleCoreM} \times \text{singleCoreN} \times 4\text{B} \times DB \le L0C$;$\text{singleCoreM} \times k_{L0} \times \text{dtype} \times 2 \le L0A$、$k_{L0} \times \text{singleCoreN} \times \text{dtype} \times 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 全载)。
|
||||
**Step 6:内部特化(参数极限,不是独立分支)**:单边无 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 时延的瓶颈所在,也是"时延绝对值小、不再继续优化"的定量注脚。
|
||||
**Step 7:降核模式实现**: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 时延的瓶颈所在,也是"时延绝对值小、不再继续优化"的定量注脚。
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user