v0.94: 同步HTML

This commit is contained in:
2026-08-26 06:34:25 +00:00
parent 586e8b2d1f
commit 95861e026c

View File

@@ -344,6 +344,30 @@ $$</div>
</ul>
<p>max 取更严格的 8192。修正后的归约阈值θ_c≈12grid_K=32 时 K&gt;396远低于 8192说明 <b>8192 的绑定约束是 dValue条件 2不是归约代价条件 3</b>。源码不动态计算 grid_K用固定阈值同时覆盖条件 2 的最保守情形和条件 3是两条条件的保守合并近似。</p>
<h3>实现方案</h3>
<p><b>Step 0singleCoreM / singleCoreN 的确定</b>(每核输出基本块尺寸)</p>
<p>singleCoreM × singleCoreN 是 ASW_Basic 的基石参数——它决定了每核每次计算的输出 tile 大小,进而决定 mCnt/nCnt、swizzle 窗口、L2 执行组等所有后续参数。确定逻辑按约束链推导:</p>
<p>*约束 1——L0C 容量*(输出 tile 必须放得下 L0C 双缓冲):</p>
<div class="math">$$
\text{singleCoreM} \times \text{singleCoreN} \times 4\text{B} \times 2 \le L0C \Rightarrow \text{singleCoreM} \times \text{singleCoreN} \le 32768 \text{ 元素}
$$</div>
<p>*约束 2——L0A/L0B 容量*(输入 tile 决定 baseK 上限):</p>
<div class="math">$$
baseK \le \min\Big(\frac{L0A}{2 \cdot \text{singleCoreM} \cdot \text{dtype}},\; \frac{L0B}{2 \cdot \text{singleCoreN} \cdot \text{dtype}}\Big)
$$</div>
<p>*约束 3——dValue*$baseK \cdot \text{dtype} \ge 256\text{B}$BF16 为 128 元素)。</p>
<p>*约束 4——L1 容量*(双缓冲下驻留输入):</p>
<div class="math">$$
2 \cdot (\text{singleCoreM} + \text{singleCoreN}) \cdot k_{L1} \cdot \text{dtype} \le L1
$$</div>
<p>*约束 5——搬移效率*$\text{singleCoreM} \cdot K \cdot \text{dtype} \ge min\_TileSize$ 且 $K \cdot \text{singleCoreN} \cdot \text{dtype} \ge min\_TileSize$。</p>
<p>*选取策略*:在约束 1 的上界32768 元素singleCoreM/singleCoreN 的长宽比应<b>跟随 M/N 的长宽比</b>——$\text{singleCoreM}/\text{singleCoreN} \approx M/N$,使 GM 访问的空间局部性最优(减少跨行/跨列的 strided 访问)。同时对齐到 16 的倍数Cube 基本块粒度)。</p>
<p>*例*M=N=2048方形 → singleCoreM=singleCoreN=$\lfloor\sqrt{32768}\rfloor_{16}$=176。约束 2$baseK \le 16384/176 = 93$ → 取 8016 对齐)。约束 3$80 \times 2 = 160\text{B} \ge 128\text{B}$ ✓。</p>
<p>*例*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$。</p>
<p><b>Step 1mCnt / nCnt 与核间分配</b></p>
<div class="math">$$
mCnt = \Big\lceil \frac{M}{\text{singleCoreM}} \Big\rceil,\qquad nCnt = \Big\lceil \frac{N}{\text{singleCoreN}} \Big\rceil
$$</div>
<p>总输出块数 = $B \times mCnt \times nCnt$,按 B→M→N 优先级分配到 C 核。</p>
<p><b>核间组织</b>:先按 B/M/N 切出输出块,剩余核预算折成 K 向份数:</p>
<div class="math">$$
blocksPerBatch = \Big\lfloor \frac{C}{B} \Big\rfloor,\qquad
@@ -372,8 +396,8 @@ $$</div>
<li><b>降核模式</b>P &lt; C 且 K 也不够格走 StreamK 时,并行度凑不满核。此时与其强行把 M/N 切得更碎tile 跌破 min_TileSize、dValue 跌破 128B搬移效率崩塌反而更慢不如<b>只用 ⌈P⌉ 个核</b>、每核承担一个完整输出块L0C 满载粒度),其余核闲置。这类 case 的时延绝对值小,继续切分引入的调度与搬移效率损失大于并行收益——降核是理性选择而非偷懒。</li>
</ol>
<h3>实现方案</h3>
<p><b>1、核间切分维度选择(按共享代价从低到高)</b>:切 B零共享先试→ 切 M右矩阵 $KN\cdot\text{dtype} \le L2$ 则驻留 L2→ 切 N对称→ 混合切(靠 swizzle + L2 切分管理)→ 降核(见第 6 条)。</p>
<p><b>2、swizzleASW 滑窗蛇形</b></p>
<p><b>Step 2核间切分维度选择(按共享代价从低到高)</b>:切 B零共享先试→ 切 M右矩阵 $KN\cdot\text{dtype} \le L2$ 则驻留 L2→ 切 N对称→ 混合切(靠 swizzle + L2 切分管理)→ 降核(见 Step 7)。</p>
<p><b>Step 3swizzle——ASW 滑窗蛇形</b></p>
<p><b>问题</b>:核间切 M/N 后,同一时刻 C 个核各算一个输出块,它们所需的 A 行块与 B 列块集合就是当前"活跃工作集"。若按行优先顺序朴素分配,一波 C 个块横跨的 A 行、B 列很宽,活跃工作集超过 L2 就回 GM 读1.6TB/s重复读代价真实发生。<b>swizzle 要做的就是编排输出块的执行顺序,把每一波核的活跃工作集压到最小。</b></p>
<p><b>做法</b>:把 M 向每 W 个基本块划为一个"窗口",遍历顺序为"窗口内先扫 M、扫满 W 行再进下一列 N一个窗口扫完再进下一个窗口",且奇数窗口行 N 向反向(蛇形)。效果有二:</p>
<ul class="tight">
@@ -402,7 +426,7 @@ $$</div>
<li><b>窗内列间边界</b>ν0→ν1相邻两段共享的是同一组 A 行块W 个),它们在整个窗口期间<b>全程驻留 L2</b>,无论按什么顺序扫,工作集不变——窗内蛇形零收益;</li>
<li><b>窗口行边界</b>窗口0→窗口1A 行整体换血μ0..3 → μ4..7),此时 B 列带的连续性决定换血成本——不蛇形则下一窗口从 ν0 开始LRU 上最久未用、早已被挤出 L2 的冷带),蛇形则延续上一窗口末尾的 ν7最热线带<b>蛇形只标在窗口行号上</b>(源码 <code>BatchMatMulAswBlock::UpdateBasicIndex</code>:仅 <code>rowIdx</code> 为奇时 n 反向,窗内 m 最快序不反向),正是这个收益结构的直接实现。</li>
</ul>
<p><b>3、L2 分(工作集超 L2 时)</b></p>
<p><b>Step 4L2 分(工作集超 L2 时)</b></p>
<p><b>切的是什么</b>:将 mCnt×nCnt 个基本块划分为若干<b>执行组</b>——每组覆盖输出平面上一个连续矩形区域(若干 singleCoreM × singleCoreN 基本块的集合),使该组所需的 A 行带 + B 列带输入工作集 ≤ L2 可用读入空间;组内所有基本块算完再进下一组,输入只在跨组时换一次。</p>
<p><b>为什么需要它</b>:滑窗压缩的只是"同一波"的足迹;若整个工作集超 128MB L2跨波次复用落空——上一波窗口的 A 行早被挤出,下一波又得回 GM 读。且 L2 是<b>读写共用</b>的:输出经 fixpipe 写出时若驻留 L2dirty会压缩读入可用空间若直写 GM则占用与读共享的 1.6TB/s 总线。所以 L2 切分必须与写出策略联合决策。记输入总量 $S_{in} = B(MK+KN)\cdot\text{dtype}$,输出总量 $S_{out} = B \cdot MN \cdot outB$。</p>
<p><b>两个不变量</b>(一切分析的起点):</p>
@@ -466,9 +490,9 @@ transConflict = \max\big(\lceil C / mCnt \rceil,\; \lceil C / nCnt \rceil\big) \
$$</div>
<p>即同一时刻并发核访问同一 A/B 块的最大冲突数不超过阈值(经验值 6切分方案中优先选尾波不满载占比小拖尾 &lt; 一半)的。遍历大方向由 calOrder 决定0=M 优先、1=N 优先),按形状选共享矩阵更能驻留 L2 的方向。</p>
<p>补充:若输出会被后续算子立即消费(融合场景),输出驻留 L2 让下游读命中,场景 B/C 的策略反过来;本文按单算子边界分析。</p>
<p><b>4、核内 tiling</b>$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。</p>
<p><b>5、内部特化(参数极限,不是独立分支)</b>:单边无 batch 且该侧矩阵小($M \le 256$、$MK\cdot\text{dtype}\cdot 2 \le L1$、对侧每核循环 ≥4 轮)时小侧整个常驻 L1、只搬一次L1 全载)。</p>
<p><b>6、降核模式实现</b>tiling 时 <code>usedCoreNum = ⌈P⌉</code>(不强制 C基本块在 L0C 容量内取最大($M^t N^t \cdot 4\text{B} \le L0C$每核按标准核内流水L1→L0→Cube→L0C→Fixpipe处理自己的输出块核间无共享无依赖无需 swizzle 与 L2 切分。降核后 GM 并发搬移核数若 &lt; minCoreNum带宽利用率上限被压低——这正是降核区 case 时延的瓶颈所在,也是"时延绝对值小、不再继续优化"的定量注脚。</p>
<p><b>Step 5核内 tiling</b>$\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。</p>
<p><b>Step 6内部特化(参数极限,不是独立分支)</b>:单边无 batch 且该侧矩阵小($M \le 256$、$MK\cdot\text{dtype}\cdot 2 \le L1$、对侧每核循环 ≥4 轮)时小侧整个常驻 L1、只搬一次L1 全载)。</p>
<p><b>Step 7降核模式实现</b>tiling 时 <code>usedCoreNum = ⌈P⌉</code>(不强制 C基本块在 L0C 容量内取最大($M^t N^t \cdot 4\text{B} \le L0C$每核按标准核内流水L1→L0→Cube→L0C→Fixpipe处理自己的输出块核间无共享无依赖无需 swizzle 与 L2 切分。降核后 GM 并发搬移核数若 &lt; minCoreNum带宽利用率上限被压低——这正是降核区 case 时延的瓶颈所在,也是"时延绝对值小、不再继续优化"的定量注脚。</p>
<hr>
<h2>九、特殊分支</h2>
<ul class="tight">