Files
matmul-analysis/BMM/BMM算子优化分析_Release/BMM算子优化分析_v0.6.html

385 lines
38 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BMM 算子优化分析 v0.6 — 昇腾 950PR</title>
<script>
MathJax = {
tex: {
inlineMath: [['$','$']],
displayMath: [['$$','$$']],
tags: 'ams',
processEscapes: true
}
};
</script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<style>
:root{--ink:#1f2933;--muted:#5f6b7a;--accent:#0b6bcb;--accent2:#0e9f6e;--line:#d9e2ec;--code-bg:#f4f6f9}
*{box-sizing:border-box}
body{font-family:"PingFang SC","Microsoft YaHei","Helvetica Neue",Arial,sans-serif;color:var(--ink);background:#eef2f6;margin:0;line-height:1.8}
.page{max-width:1020px;margin:0 auto;padding:32px 44px 80px;background:#fff;box-shadow:0 0 24px rgba(0,0,0,.06)}
h1{font-size:28px;border-bottom:3px solid var(--accent);padding-bottom:12px;margin-top:8px}
h2{font-size:22px;margin-top:44px;border-left:6px solid var(--accent);padding-left:12px;color:#0b3d73}
h3{font-size:18px;margin-top:30px;color:#0b3d73;border-bottom:1px dashed var(--line);padding-bottom:6px}
h4{font-size:16px;margin-top:20px;color:#123}
table{border-collapse:collapse;width:100%;margin:14px 0;font-size:14px}
th,td{border:1px solid var(--line);padding:7px 10px;text-align:left;vertical-align:top}
th{background:#eaf2fb;color:#0b3d73}
tr:nth-child(even) td{background:#f8fafc}
code,pre{font-family:"JetBrains Mono",Consolas,Menlo,monospace;font-size:13px}
code{background:var(--code-bg);padding:1px 5px;border-radius:4px;color:#9d2c5e}
pre{background:var(--code-bg);border:1px solid var(--line);border-radius:8px;padding:14px;overflow-x:auto;line-height:1.55}
pre code{background:none;color:#243447;padding:0}
blockquote{background:#eafaf3;border-left:5px solid var(--accent2);padding:10px 16px;border-radius:0 8px 8px 0;margin:14px 0}
.math{background:#fafbfc;border:1.5px solid #c3d6ee;border-radius:10px;padding:10px 22px;margin:14px 0;overflow-x:auto}
ul.tight li,ol.tight li{margin:3px 0}
hr{border:none;border-top:1px solid var(--line);margin:24px 0}
</style>
</head>
<body>
<div class="page">
<h1>BMM 算子优化分析v0.6</h1>
<blockquote>基于 issue#3 扩展。目标芯片:昇腾 950PRDAV_3510。所有分支进入条件只含 case 形状参数B、M、N、K、dtype与芯片规格参数。</blockquote>
<hr>
<h2>一、算子功能与接口说明</h2>
<p>完成带 batch 的矩阵乘:<code>C = A @ B + bias</code></p>
<ul class="tight">
<li>左矩阵 A<code>[BatchA, M, K]</code>dtype典型 ND可带转置</li>
<li>右矩阵 B<code>[BatchB, K, N]</code>dtype典型 ND可带转置</li>
<li>偏置 bias<code>[B, 1, N]</code>,固定 ND可为空</li>
<li>输出 C<code>[BatchC, M, N]</code>BatchC = broadcast(BatchA, BatchB)</li>
</ul>
<hr>
<h2>二、符号与芯片参数约定</h2>
<table><tr><th>符号</th><th>含义</th><th>950PR 取值</th></tr>
<tr><td>C</td><td>AIC 核数aicNum</td><td>32</td></tr>
<tr><td>L1</td><td>每核 L1 Buffer</td><td>512KB</td></tr>
<tr><td>L0A / L0B</td><td>每核 L0A / L0B</td><td>64KB / 64KB</td></tr>
<tr><td>L0C</td><td>每核 L0CFP32 累加4B/元素)</td><td>256KB</td></tr>
<tr><td>L2</td><td>L2 Cache 容量 / 带宽</td><td>128MB / 5.2TB/s</td></tr>
<tr><td>W_GM</td><td>GM 带宽(读写共享)</td><td>1.6TB/s</td></tr>
<tr><td>R₁₆</td><td>16bit 对应位宽算存比</td><td>≈607.5 FLOP/元素</td></tr>
<tr><td>dValue</td><td>单数据块内数据连续排布长度</td><td>推荐 256B/512B不建议 &lt;128B</td></tr>
<tr><td>min_TileSize</td><td>确保高带宽利用率的单块搬移数据量最小值</td><td>16KB</td></tr>
<tr><td>min_DatamountPerCore</td><td>确保高带宽利用率的单核搬移数据量最小值</td><td>480KB</td></tr>
<tr><td>minCoreNum</td><td>确保高带宽利用率的并行搬移核数最小值</td><td>≈0.8C = 26</td></tr></table>
<p>以上数值基于 950PR 实测分析总结;对搬移带宽利用率的影响重要性排序为:<b>核数 &gt; 单核搬移总数据量 &gt; 单分块大小 &gt; dValue</b>。不同 NPU 芯片数值可能略有差异,换芯片时逻辑结构不变、只换常数表。</p>
<hr>
<h2>三、最优实现分析</h2>
<h3>3.1 性能模型</h3>
<p>BMM 的执行是核内多级硬件流水的并行——Cube 计算MMAD、GM/L2→L1MTE2、L1→L0MTE1、L0C 写出Fixpipe各流水级时延可被双缓冲相互掩盖</p>
<div class="math">$$
T_{total} = \max\big(T_{MMAD},\; T_{MTE2},\; T_{MTE1},\; T_{Fixpipe}\;[,\;T_{Reduce}]\big)
$$</div>
<p><b>总时延 = 最慢一级流水</b>,优化的关键是对瓶颈级的优化。由此得到设计自由度——<b>瓶颈交换</b>搬移是瓶颈时可牺牲算力冗余计算换搬移效率计算是瓶颈时可牺牲搬移重复读取换计算效率。MergeBatch 是前者的典型ASW_Basic 切 M/N 是后者的典型。</p>
<p>case 固有算存比与 16bit 位宽平衡点:</p>
<div class="math">$$
AI = \frac{2MN}{M+N},\qquad AI_{full} = \frac{2MNK}{MK+KN+MN},\qquad R_{16} = \frac{486 \times 2}{1.6} \approx 607.5
$$</div>
<p>$AI < R_{16}$ 访存 Bound瓶颈在 MTE2反之计算 Bound瓶颈在 MMAD</p>
<h3>3.2 实现本质逻辑</h3>
<p>BMM 的实现本质是把数据分块tile由全部 AIC 核并行 + 串行完成这些分块的计算,再组合成最终结果:</p>
<div class="math">$$
C[B,M,N] = \Big\{\,C[B_u, M_i, N_j] = \sum_k A[B_u, M_i, K_k] \cdot B[B_u, K_k, N_j]\,\Big\}
$$</div>
<p>分块有 4 个维度B、M、N、K。<b>核间怎么分这 4 个维度,就是分支划分的第一性问题</b>(核内分块是第二性问题,属于各分支内部 tiling</p>
<p>四个维度的核间切分特征(后续一切推导的基石):</p>
<table><tr><th>切分维度</th><th>读入特征</th><th>计算特征</th><th>写出特征</th></tr>
<tr><td>切 B</td><td>核间零重复读(每个数据块只被 1 个核读取);<b>核内是否重复读另有条件</b>——若 L1 放不下单 batch 完整的 M、N 维输入K 维可切段放入kL1&lt;K单 batch 计算中切 M 会重复读 B、切 N 会重复读 A</td><td>每个输出块由 1 个核独立完成,无核间依赖</td><td>只写最终结果,无中间结果</td></tr>
<tr><td>切 M / 切 N</td><td>切 M 则同一右矩阵块被多核<b>重复读</b>;切 N 则同一左矩阵块被多核重复读</td><td>每个输出块由 1 个核独立完成,无核间依赖</td><td>只写最终结果,无中间结果</td></tr>
<tr><td>切 K</td><td>每个数据块只被固定的 1 个核读取,零重复读</td><td>每个输出块由<b>多核共同</b>完成,存在核间依赖</td><td><b>有中间结果写出,需核间 Reduce 归约</b></td></tr></table>
<p>差异的根本原因B 维在数学上独立(逐 batch 独立矩阵乘),切 B 核间天然零重复、零依赖K 维有 L0C 累加机制——核内切 K 时多轮 mmad 在 L0C 原地累加、中间结果不出核,一旦切到核间,部分和必须写出 workspace 再归约——<b>切 K 是唯一同时破坏"累加不出核"和"输出独占"的切法</b></p>
<p>4 维的任意非空子集共 $2^4-1=15$ 种切分组合,任何实现方案必属其一(完备):</p>
<table><tr><th></th><th>组合</th><th>共同特征</th><th>优化重心</th></tr>
<tr><td>纯 B</td><td>{B}</td><td>核间零重复读(核内重复读取决于 L1 驻留形态);无中间写出</td><td>搬移效率 / 计算效率</td></tr>
<tr><td>含 M/N 不含 K</td><td>{M},{N},{M,N},{B,M},{B,N},{B,M,N}</td><td>核间可能有重复读</td><td>重复读尽量少L2+swizzle 吸收)+ 搬移/计算效率</td></tr>
<tr><td>含 K</td><td>其余 8 种</td><td>有中间结果写出 + 归约</td><td>计算效率,且归约时延不能成为新瓶颈</td></tr></table>
<p>核间切分价格严格排序cost(切 B) = 0 &lt; cost(切 M/N) ≪ cost(切 K)。切 B 核间免费;切 M/N 的重复读可被 128MB L25.2TB/s vs GM 1.6TB/s+ swizzle 大部分吸收;切 K 的归约流量 ∝ grid_K×输出量且引入核间同步是结构性代价。<b>整条分支决策树就是:按价格从低到高购买并行度,买不够才加价。</b></p>
<h3>3.3 分支推导</h3>
<ol class="tight">
<li><b>问题规约</b>BatchA=1 或 BatchB=1 → 折叠转普通 Matmul转MatmulK=0 / K=1 → Cube 无用,走 AIV 向量通路(特殊分支,与切分正交的前置判断);</li>
<li><b>先买免费的 B</b>B ≥ C 时切 B 可满核。单 batch M×N 够大 → 逐 batch 算IterBatchM×N 太小 → 多 batch 合并成大 tile 算冗余算力换搬移效率MergeBatch</li>
<li><b>B 买不满,加价买 M/N</b>:切 M/N 或混合切,重复读交给 L2 + swizzleASW_Basic</li>
<li><b>B/M/N 都买不满,才买昂贵的 K</b>:核间切 K + 归约StreamK</li>
</ol>
<p>由此得 6 大分支:<b>转Matmul、特殊分支、IterBatch、MergeBatch、ASW_Basic、StreamK</b>。重叠区(如 B≥C 且 M×N 中等时 MergeBatch 与 IterBatch 都合法)由端到端时延模型 $T_{total}$ 仲裁;分支体系保证候选集完备无冗余。</p>
<hr>
<h2>四、可转 Matmul 分支</h2>
<h3>进入分支条件</h3>
<div class="math">$$
BatchA = 1 \;\lor\; BatchB = 1
$$</div>
<p>解释:单边 batch=1 的 BMM 与 Matmul 只差一个维度标签,直接复用 Matmul 的成熟优化体系。</p>
<h3>实现方案</h3>
<ul class="tight">
<li><b>BatchB=1</b>:左矩阵 <code>[B,M,K]</code> 的 batch 维与 M 维在 ND 下内存相邻紧排,直接视图为 <code>[B·M,K]</code>,输出布局逐元素一致——零重排、零 split免费转换</li>
<li><b>BatchA=1</b>:右矩阵折叠为 <code>[K, B·N]</code> 需一次真实转置重排O(B·K·N)),且输出存在置换需 scatter——有代价。A 较小($MK\cdot\text{dtype} \le L1$)时优先 A 常驻 L1、留在 BMM 分支内广播友好形态A 较大时按广播扩展后分别预估"BMM 分支"与"重排+转Matmul"的时延,择优。</li>
</ul>
<hr>
<h2>五、MergeBatch 分支</h2>
<p>核间切 B每核 $b_{core}$ 个 batch核间无同步核内把 b 个 batch 合并计算:$[b,M,K]@[b,K,N] \Rightarrow [bM,K]@[K,bN]=[bM,bN]$BlockTrace 取块对角线得 $[b,M,N]$。交叉项被算出但丢弃(浪费比例 (b1)/b——进入该分支的 case 必然访存 Bound条件 5 保证),浪费的算力被搬移时延掩盖。</p>
<h3>进入分支条件(汇总)</h3>
<p>同时满足b0 = 单次合并计算的 batch 数下限b0 ≥ 2</p>
<ol class="tight">
<li>$BatchA = BatchB \;\land\; b_{core} = B/C \ge 2b_0$</li>
<li>$2 \cdot (b_0 M)(b_0 N) \cdot 4\text{B} \le L0C$</li>
<li>$b_{core} \cdot (MK + KN) \cdot \text{dtype} \ge min\_DatamountPerCore$</li>
<li>$\max(MK,\; KN) \cdot \text{dtype} \ge min\_TileSize$</li>
<li>$\dfrac{2MN}{M+N} < \dfrac{R_{16}}{b_0}$</li>
</ol>
<h3>逐条解释</h3>
<ol class="tight">
<li><b>batch 关系与每核份额</b>:无广播才能逐 batch 对应合并;每核至少分到 $2b_0$ 个 batch——$b_0$ 是合并搬移有收益的最小合并数(取 22 组起步才能构成合并组间乒乓流水(即 $b_{core} \ge 4$)。</li>
<li><b>L0C 容量</b>:合并 $b_0$ 个 batch 的输出块 $[b_0M, b_0N]$FP32 累加、双缓冲两份)必须放得下 L0C连最小合并都放不下合并无从谈起。</li>
<li><b>单核搬移总量</b>:单核搬移数据总量低于 min_DatamountPerCore 时GM 带宽利用率上不去(重要性第 2 位的经验约束)。</li>
<li><b>搬移 tile 大小</b>:单 batch 单矩阵的最大连续搬移块须达到 min_TileSize合并是在此之上进一步放大不是替代。</li>
<li><b>访存 Bound</b>:合并把单次计算的算存比放大 $b_0$ 倍后仍须低于 16bit 对应位宽算存比 $R_{16}$,保证瓶颈留在搬移侧,冗余算力被掩盖而非成为新瓶颈。</li>
</ol>
<h3>实现方案</h3>
<p><b>Step 1合并数 bL0C + 算存比双上限)</b></p>
<div class="math">$$
b \le \sqrt{\frac{L0C}{2 \cdot MN \cdot 4\text{B}}},\qquad b < \frac{R_{16}(M+N)}{2MN}
$$</div>
<div class="math">$$
b = \min\big(\text{两上限},\; b_{core}\big),\quad b_{L0} = b
$$</div>
<p>b 尽量取 $b_{core}$ 的因子(每次合并数均匀,负载与功耗更优)。</p>
<p>**Step 2L0 级 K 粒度 $k_{L0}$**</p>
<div class="math">$$
k_{L0} = \min\Big(\frac{L0A}{2\,bM\cdot\text{dtype}},\; \frac{L0B}{2\,bN\cdot\text{dtype}}\Big)\ \text{向下 16 对齐,且 } k_{L0}\cdot\text{dtype} \ge 128\text{B}
$$</div>
<p>解释L0A/L0B 各 64KB、双缓冲两份装入合并后 $bM$ 行($bN$ 列)× $k_{L0}$ 的 fractal末项是 dValue 下限。</p>
<p>**Step 3L1 级 $k_{L1}$、$b_{L1}$**</p>
<div class="math">$$
k_{L1} \ge \min\big(k_{L0\_max},\; 256\text{B}/\text{dtype}\big),\qquad
2\,b_{L1}(M k_{L1} + k_{L1} N)\cdot\text{dtype} \le L1,\qquad
b_{L1} = \min(b_{L1\_max},\; b_{core}) \;\ge\; b
$$</div>
<p>解释:$k_{L1}$ 是 GM→L1 的 K 向粒度,按 dValue 推荐值 256B 取L1 双缓冲两份,每份驻留 $b_{L1}$ 个 batch 的 A/B 各一块;$b_{L1} \ge b$ 保证合并不断供。</p>
<hr>
<h2>六、IterBatch 分支</h2>
<p>核间切 B每核 $b_{core} \ge 1$ 个 batch核间无同步核内逐个 batch 做标准 Matmul 分块计算。无算力浪费、无跨 batch 依赖,是"切 B"最朴素的形态。</p>
<h3>进入分支条件(汇总)</h3>
<p>同时满足:</p>
<ol class="tight">
<li>$BatchA = BatchB \;\land\; b_{core} = \lceil B/C \rceil \ge 1$</li>
<li>$B \bmod C = 0 \;\lor\; B \bmod C \ge minCoreNum$</li>
<li>L1 容量约束五选一Step 为 &gt;1 的整数):</li>
<ul class="tight">
<li>a) $b_{core}=1 \;\land\; (MK+KN)\cdot\text{dtype} \le L1$</li>
<li>b) $b_{core}>1 \;\land\; 2(MK+KN)\cdot\text{dtype} \le L1$</li>
<li>c) $b_{core}=1 \;\land\; (MK+\tfrac{KN}{Step})\cdot\text{dtype} \le L1 \;\lor\; (\tfrac{MK}{Step}+KN)\cdot\text{dtype} \le L1$</li>
<li>d) $b_{core}>1 \;\land\; (MK+\tfrac{KN}{Step})\cdot\text{dtype} \le \tfrac{L1}{2} \;\lor\; (\tfrac{MK}{Step}+KN)\cdot\text{dtype} \le \tfrac{L1}{2}$</li>
<li>e) $(M\cdot\tfrac{K}{Step}+\tfrac{K}{Step}\cdot N)\cdot\text{dtype} \le L1$</li>
</ul>
<li>c/d/e 切分后:搬移分块 $\ge min\_TileSize \;\land\; dValue \ge 128\text{B}$</li>
</ol>
<h3>逐条解释</h3>
<ol class="tight">
<li><b>batch 关系与每核份额</b>:无广播;每核至少 1 个 batch。</li>
<li><b>负载均衡</b>:切 B 核间零共享零依赖,唯一系统性风险是负载不均。整除时完全均衡;不整除时尾波活跃核数须 ≥ minCoreNum保证尾波仍有足够核并发搬移重要性第 1 位的约束是核数)。</li>
<li><b>L1 容量——核心要求:单 batch 计算不重复读</b></li>
</ol>
<p> 重复读发生在<b>单 batch 内部</b>L1 放不下单 batch 完整的 M、N 维输入K 维允许切段放入kL1&lt;K核内切 M 会在 K 循环中重复读 B、切 N 会重复读 A。IterBatch 进入与否不由算存比判定——即使 case 是计算 Bound重复读引入的额外搬移也可能把它重新拖回访存 Bound。所以进入条件直接由 L1 驻留形态刻画:</p>
<ul class="tight">
<li><b>a)</b> 每核 1 batch左右矩阵同时驻留 L1零重复读</li>
<li><b>b)</b> 每核多 batchL1 放下 2 个 batch 构成 batch 间乒乓;</li>
<li><b>c)</b> 单 batch 装不下:一侧完整驻留(只搬一次、零重复读),对侧按 K 切 Step 段流水;</li>
<li><b>d)</b> 多 batch 时 (c) 的乒乓版:每 batch 只占 L1/2另一半预取下一 batch 的驻留侧(详见下文流水分析);</li>
<li><b>e)</b> 两侧都按 K 切 Step 段A/B 成段配套搬入batch 间无缝。</li>
</ul>
<ol class="tight">
<li><b>搬移效率下限</b>:切分把粒度切小,必须守住 min_TileSize 与 dValue否则切分本身把带宽打崩。</li>
</ol>
<h3>batch 间流水掩盖分析c/d/e 的分工)</h3>
<p>先明确流水结构fixpipe 开 unitflag 后,写出由硬件随路完成(每个 16×16×16 fractal 算完即自动搬出),<b>写出侧不需要软件排流水</b>;要掩盖的只有"读入MTE2: GM→L1↔ 计算Cube"。</p>
<ul class="tight">
<li>**(c)$b_{core}=1$**:每核只有 1 个 batch不存在 batch 边界;驻留侧全程复用,对侧 K 段双缓冲,段间无缝。</li>
<li>**$b_{core}>1$ 直接套 (c) 会断流**:全量 L1 给了当前 batch到 batch 边界时下一 batch 的驻留侧矩阵(如 A$MK\cdot\text{dtype}$)必须整体换入,而当前 batch 尾部只剩几个 K 段的计算,掩盖不了整个驻留侧的换入 → 气泡。</li>
<li><b>(d) 的修正</b>:每 batch 只占 L1/2驻留侧 + 对侧 K 段都在这一半),另一半在计算当前 batch 期间预取下一 batch 的驻留侧。无气泡条件自然成立:驻留侧换入量 $MK\cdot\text{dtype} \le$ 当前 batch 总搬入量 $(MK+KN)\cdot\text{dtype}$,访存 Bound 下计算时间 ≥ 搬入时间,当前 batch 的尾部窗口足够完成预取。上一 batch 做最后一个分块时,下一 batch 的首个 K 段(连同其驻留侧已就绪)即可搬入另一半 L1。</li>
<li><b>(e) 是兜底</b>K 段槽位在 batch 间完全同质连续(上一 batch 最后一段计算时搬下一 batch 第一段),天然无缝;当驻留侧单矩阵 &gt; L1/2 时 (d) 不成立,只能走 (e),代价是两侧都有 K 段级重复读。</li>
</ul>
<p>结论:(c)(d) 是同族(一侧驻留 + 对侧切 K仅 L1 预算不同——$b_{core}=1$ 用全量 L1$b_{core}>1$ 用 L1/2 换 batch 间乒乓;<b>两者不宜合并</b>(合并会丢失 batch 边界预取这一关键差异),(e) 在驻留侧超 L1/2 时接管。</p>
<h3>实现方案</h3>
<p><b>(a)</b>:每核 1 batch 直接搬入 L1。L1→L0 先看 L0C 能否放下完整单 batch 输出:</p>
<pre><code>if (L0C &gt;= M*N*4B): # L0C 放得下完整输出:不切 M/N只切 K
BaseM = M; BaseN = N
BaseK = min(align(L0A/M, 16), align(L0B/N, 16))
else: # L0C 放不下:按较小维切
if M &lt; N: BaseM = align(M,16); BaseN = floor(L0C/4B / BaseM)
else: BaseN = align(N,16); BaseM = floor(L0C/4B / BaseN)
BaseK = min(floor_align(L0A/BaseM,16), floor_align(L0B/BaseN,16))</code></pre>
<p><b>(b)</b>L1 双 batch 乒乓,核内 GM→L1→L0→Cube→L0C→GM/L2 流水L1→L0 分块同理但各级预算减半L0C/L0A/L0B 按 2 份)。</p>
<p><b>(c)/(d)</b>:一侧驻留 + 对侧切 K。假设驻留左矩阵右矩阵搬入的 K 向长度:</p>
<div class="math">$$
k_{L1\_b} = \min\Big(\frac{L1_{budget} - MK\cdot\text{dtype}}{N\cdot\text{dtype}},\; K\Big),\qquad L1_{budget} = L1\ (\text{c})\ \text{或}\ L1/2\ (\text{d})
$$</div>
<p>(d) 中另一半 L1 在计算期间预取下一 batch 的驻留侧,实现 batch 间无气泡衔接fixpipe 开 unitflag。</p>
<p><b>(e)</b>:两侧都切 K 段,$k_{L1}$ 取满足容量与 dValue 的最大值L0C 按 batch 乒乓(各占 L0C/2batch 边界由硬件 fixpipe 自动排空、下一 batch 立即在另一半 L0C 累加。</p>
<hr>
<h2>七、StreamK 分支</h2>
<h3>进入分支条件(汇总)</h3>
<ol class="tight">
<li>$P = \dfrac{B \cdot MN \cdot 4\text{B}}{L0C} < C$</li>
<li>$\dfrac{K}{grid_K} \ge \dfrac{256\text{B}}{\text{dtype}}$</li>
<li>$K \ge grid_K^{\,2} \cdot \theta$,其中 $\theta = \dfrac{2\,\alpha \cdot 4\text{B} \cdot Q_{16}}{W_{eff}} \approx 1.7\times10^{3}$$\alpha = 10$</li>
<li>工程约束:确定性等级 ≤ 1核间归约顺序不定ND 格式</li>
</ol>
<h3>逐条解释</h3>
<ol class="tight">
<li><b>并行缺口</b>P 以"L0C 满载的输出基本块"为粒度估计不切 K 的最大并行度——基本块按 L0C 最大利用率取($M^t N^t \cdot 4\text{B} = L0C$),免去预先估计 M/N 具体切分。P &lt; C 意味着即使按最大块切B/M/N 三维也填不满 32 核,唯一剩余的并行维度是 K。</li>
<li><b>单核 K 段下限</b>:每核 K 段内轴连续长度不小于 dValue 推荐值 256BBF16 为 128 元素),保证段内搬移效率不崩。</li>
<li><b>归约代价可接受</b>:每核计算时延与归约时延分别为</li>
</ol>
<div class="math">$$
T_{MMAD/core} = \frac{2MNK}{grid_K \cdot Q_{16}},\qquad
T_{Reduce} = \frac{2 \cdot grid_K \cdot MN \cdot 4\text{B}}{W_{eff}}
$$</div>
<p>其中 $Q_{16}$ = 单核 BF16 算力 ≈ 13.5 TFLOPS归约流量 = grid_K 份部分和写出 + 读回归约共 2 遍,$W_{eff}$ 取 GM 有效带宽 ≈ 0.4 × 1.6TB/s归约是多核小块读写达不到满带宽。要求 $T_{MMAD/core} \ge \alpha \cdot T_{Reduce}$——安全系数 α=10 的含义是归约新增流水级的占比压到 ~10% 以内、不改变瓶颈归属。代入得:</p>
<div class="math">$$
K \ge \alpha \cdot grid_K^2 \cdot \frac{2 \cdot 4\text{B} \cdot Q_{16}}{W_{eff}} = grid_K^2 \cdot \theta,\qquad
\theta = \frac{10 \times 2 \times 4 \times 13.5}{0.64} \approx 1.7\times10^{3}
$$</div>
<p>数值上grid_K=2 → K≥6.8K4 → K≥27K8 → K≥108K32 → K≥1.7M。<b>对 K 的要求随 grid_K 平方增长——grid 搜索自然淘汰归约过重的配置。</b></p>
<ol class="tight">
<li><b>工程约束</b>:归约顺序不定引入浮点非确定性,确定性等级 2/3 的业务禁用。</li>
</ol>
<p><b>源码对照</b><code>batch_matmul_v3_basic_streamk_tiling.cpp</code> 中 K 的固定门槛为 <code>CeilAlign(K,256) ≥ max(8192, aicNum×256B/dtype)</code>。其含义:<code>aicNum×256B/dtype</code>2048bit/核)= 全部 AIC 参与切 K 时每核至少分到 256B 的 K 向内轴数据——正是条件 2 的 dValue 推荐值;<code>8192</code> = C×256 元素是绝对下限,保证每核至少 256 个 K 元素BF16 512B摊薄切 K 的固定开销workspace 建立、归约同步)。源码用固定门槛,是条件 2/3 的保守近似;本文的 grid_K 平方式给出随切份数变化的解析门槛,更细。</p>
<h3>实现方案</h3>
<p><b>核间组织</b>:先按 B/M/N 切出输出块,剩余核预算折成 K 向份数:</p>
<div class="math">$$
blocksPerBatch = \Big\lfloor \frac{C}{B} \Big\rfloor,\qquad
grid_K = \frac{blocksPerBatch}{mCnt \cdot nCnt}
$$</div>
<p>mCnt、nCnt 收拢为 blocksPerBatch 的因子(避免碎核尾块);由条件 1 知 $mCnt \cdot nCnt \le blocksPerBatch/2$,故 $grid_K \ge 2$。归约组内核 c 负责 K 段 $[cK/grid_K,\; (c{+}1)K/grid_K)$。</p>
<p><b>核内流水</b>:对自己的 K 段做标准分块流水MTE2→L1→L0→mmad段内多轮在 L0C 原地累加;段完部分和经 Fixpipe 写出。</p>
<p><b>归约</b></p>
<ul class="tight">
<li><b>workspace + AIV 归约(确定性)</b>:部分和写 GM workspace每核 256×256×4B另加 20MB 核间通信区AIV 读出各段累加输出AIC:AIV=1:2</li>
<li><b>AtomicAdd非确定性</b>:部分和直接原子累加到输出 GM省一遍读回确定性等级 &gt;1 禁用。</li>
</ul>
<p><b>参数搜索</b>$grid_K$ 从 2 起按 2 的幂递增,取同时满足条件 2/3 的最小值;都不满足则退为降核 ASW_Basic。</p>
<hr>
<h2>八、ASW_Basic 分支</h2>
<h3>进入分支条件(汇总)</h3>
<ol class="tight">
<li>$P = \dfrac{B \cdot MN \cdot 4\text{B}}{L0C} \ge C$</li>
<li>无 batch 结构限制BatchA=BatchB、交叉广播均可典型进入路径$B < C$ B 买不满核 $B \ge C$ IterBatch/MergeBatch 条件不满足时的兜底</li>
<li><b>降核模式</b>$P < C$ 且不满足 StreamK 进入条件 只用 $\lceil P \rceil$ 个核其余核闲置</li>
</ol>
<h3>逐条解释</h3>
<ol class="tight">
<li><b>并行度补齐</b>:以 L0C 满载为基本块粒度B×M×N 能切出至少 C 个独立输出块,则切 M/N或混合切并行度够用。切 M/N 的固有代价是共享矩阵被多核重复读,但共享部分驻留 128MB L2 时重复读以 5.2TB/s 命中 L2 而非 1.6TB/s 的 GM代价大部分被吸收。</li>
<li><b>兜底性质</b>ASW_Basic 是实践中最常命中的分支——B 可大可小可等 1交叉广播也由此承接对广播侧做 L1/L2 驻留,共享关系与切 M/N 同构)。</li>
<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>问题</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">
<li>同一波 C 个核的块集中在同一个窗口内 ⇒ 活跃 A 行块只有 W 个、活跃 B 列块只有 C/W 条带;</li>
<li>蛇形反向使相邻窗口行首尾相接——上一窗口末尾的 B 列带与下一窗口开头的 B 列带是同一条,跨窗口切换时工作集增量最小。</li>
</ul>
<p><b>W 怎么取</b>:一波 C 个块的 L2 足迹约为</p>
<div class="math">$$
footprint \approx \big(W \cdot M^t K + \tfrac{C}{W} \cdot K N^t\big) \cdot \text{dtype}
$$</div>
<p>由均值不等式,$W + C/W$ 在 $W = \sqrt{C}$ 处取最小——窗口越接近"方形"W 行 × C/W 列),足迹越小。同时 W 须整除 C保证每个窗口恰好被整数波核覆盖、窗口边界不把波次切碎。合起来即</p>
<div class="math">$$
W = \max\{\,d \mid d \mid C,\; d \le \lfloor\sqrt{C}\rfloor\,\}
$$</div>
<p>C=32 时 $\sqrt{32} \approx 5.66$,因子 {1,2,4,8,…} 中不超过它的最大者是 4故 W=4。</p>
<p><b>实例</b>C=32W=4M̃=8Ñ=8数字为块的全局执行顺序一波 32 块):</p>
<pre><code>窗口0N 正向) 窗口1N 蛇形反向)
ν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</code></pre>
<p>块 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。</p>
<p><b>窗内为什么不蛇形</b>(对上例中"块 3=(μ3,ν0) → 块 4=(μ0,ν1) 而非 (μ3,ν1)"的说明):蛇形的收益来自"相邻遍历段共享边界数据",要分两种边界看:</p>
<ul class="tight">
<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>问题</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>
<ul class="tight">
<li>GM 流量下界 $= S_{in} + S_{out}$:输入至少读一遍、输出最终至少要写一遍到 GM与 L2 策略无关;</li>
<li>L2 读入可用空间:$L2_{read} = L2 - S_{out}^{resident}$$S_{out}^{resident}$ 为驻留 L2 的输出量)——写出驻留 L2 会压缩读入空间,这是写出策略影响读入复用的通道。</li>
</ul>
<p><b>先判定写出会不会 Bound</b>。平均写出带宽需求:</p>
<div class="math">$$
BW_{out} = \frac{S_{out}}{T_{MMAD}} = \frac{B \cdot MN \cdot outB}{2BMNK\,/\,(C \cdot Q_{16})} = \frac{C \cdot Q_{16} \cdot outB}{2K}
$$</div>
<p>只与 K、outB 有关K 越小单位时间输出越密。例BF16 输出C·Q₁₆=432 TFLOPSK=512 → 844 GB/sK=256 → 1.69 TB/s已超 GM 总线——此时<b>任何策略都写出 Bound</b>L2 缓冲只能削峰fixpipe 以 5.2TB/s 写 L2 吸收突发),平均速率仍受总线限制,应预期 Fixpipe 成为 $T_{total}$ 的 max 项。</p>
<p><b>分场景决策</b></p>
<p>**场景 A$S_{in} + S_{out} \le L2$(全驻留)**。输入读一遍($r_{in}=1$),输出驻留 L2dirty异步回写 GM——写出走 5.2TB/s L2 写口,不与读争,也削平了 GM 写突发。无需切分。</p>
<p>**场景 B$S_{in} \le L2$ 但 $S_{in} + S_{out} > L2$(输入能驻留,加上输出超了)<b></b>输入驻留、输出直写 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}$≈67MB ≤ L2$S_{out}$≈268MB 直写 GMT_MMAD≈318µs总流量速率 (67+268)MB/318µs ≈ 1.05TB/s &lt; 1.6TB/s ✓。</p>
<p>**场景 C$S_{in} > L2$(输入本身超)**。必须 L2 切分。输出直写 GM 以最大化 $L2_{read}$,切分数满足</p>
<div class="math">$$
mL2TileNum \times nL2TileNum \;\ge\; \frac{S_{in}}{L2_{read}}
$$</div>
<p>每块输入工作集 ≤ $L2_{read}$,逐块计算——块内滑窗复用充分,块间只发生一次性换入。块内分配用<b>错位分核</b>(对角线分配):线性块号先取 mn 方向叠加随块号递增的相位偏移,使同一时刻各核落在 M×N 平面的不同对角线上——避免多核同一拍并发读同一行 A / 同一列 B 的同一地址(同地址并发读会串行化,等效带宽打折)。冲突度量与优选规则:</p>
<div class="math">$$
transConflict = \max\big(\lceil C / mCnt \rceil,\; \lceil C / nCnt \rceil\big) \le 6
$$</div>
<p>即同一时刻并发核访问同一 A/B 块的最大冲突数不超过阈值(经验值 6切分方案中优先选尾波不满载占比小拖尾 &lt; 一半)的。遍历大方向由 calOrder 决定0=M 优先、1=N 优先),按形状选共享矩阵更能驻留 L2 的方向。例B=64、M=N=2048、K=1024、BF16——$S_{in}$≈537MB &gt; L2输出直写$L2_{read}$=128MB切 3×2=6 块(每块输入约 89MB ≤ 128MB总流量约 1.07GBT_MMAD≈1.27ms,速率 844GB/s &lt; 1.6TB/s ✓。</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>
<hr>
<h2>九、特殊分支</h2>
<ul class="tight">
<li><b>K=0</b>无任何计算C = bias 或 0纯 AIV 写值;</li>
<li><b>K=1</b>:退化为逐元素乘 <code>C = A ⊙ B</code>无累加深度Cube 的 16×16×16 粒度浪费 15/16走 AIV 向量通路GM→UB→Mul→GM优于 Cube 通路。触发需 $B \ge 2 \times 64$AIV 核数×2开 UB 乒乓)且单 batch 输入输出能驻留 UB。</li>
</ul>
<hr>
<h2>十、case 遍历:各分支的覆盖区域</h2>
<p>对 $B \in [1, 2048]$、$M,N,K \in [1, 10240]$ 按对数网格采样 20736 个 case严格按上述进入条件分类BF16结果</p>
<h3>分支覆盖统计</h3>
<table><tr><th>分支</th><th>case 数</th><th>占比</th><th>B 范围</th><th>区域特征</th></tr>
<tr><td>ASW_Basic</td><td>7290</td><td>35.2%</td><td>2 ~ 2048</td><td>通用B&lt;C 且 P≥C或 B≥C 但 L1 五形态不满足M/N 大)</td></tr>
<tr><td>IterBatch</td><td>4544</td><td>21.9%</td><td>32 ~ 2048</td><td>B≥C、负载均衡、L1 五形态之一满足</td></tr>
<tr><td>降核 ASW_Basic</td><td>3190</td><td>15.4%</td><td>2 ~ 128</td><td>P&lt;C 且 K 不满足 StreamK 阈值 → 只用 ⌈P⌉ 核(定义与实现见 §八.6</td></tr>
<tr><td>特殊分支</td><td>1728</td><td>8.3%</td><td>任意</td><td>K=0 / K=1</td></tr>
<tr><td>MergeBatch</td><td>1674</td><td>8.1%</td><td>128 ~ 2048</td><td>$b_{core}\ge 4$ 且 $MN \le L0C/(2b_0^2\cdot4\text{B})=8192$ 等五条全过</td></tr>
<tr><td>转Matmul</td><td>1584</td><td>7.6%</td><td>B=1</td><td>单边 batch=1</td></tr>
<tr><td>StreamK</td><td>726</td><td>3.5%</td><td>2 ~ 128</td><td>P&lt;C 且 K≥8192</td></tr></table>
<h3>IterBatch 五形态命中分布</h3>
<table><tr><th>形态</th><th>命中数</th><th>说明</th></tr>
<tr><td>b) 双 batch 乒乓</td><td>1787</td><td>最多B 大且单 batch 较小</td></tr>
<tr><td>e) 两侧切 K</td><td>1456</td><td>次之K 可切段的通用兜底</td></tr>
<tr><td>a) 单 batch 全驻留</td><td>528</td><td>B=C 附近</td></tr>
<tr><td>d) 半容量驻留+预取</td><td>522</td><td>多 batch 且单 batch 偏大</td></tr>
<tr><td>c) 单侧驻留</td><td>251</td><td>最少b_core=1 且单侧可驻留的窄区)</td></tr></table>
<h3>典型边界 case</h3>
<table><tr><th>B</th><th>M</th><th>N</th><th>K</th><th>分支</th><th>说明</th></tr>
<tr><td>1</td><td>2048</td><td>2048</td><td>2048</td><td>转Matmul</td><td>单 batch 纯 Matmul</td></tr>
<tr><td>128</td><td>64</td><td>64</td><td>512</td><td><b>MergeBatch</b></td><td>五条全过:$b_{core}$=4MN=4096≤8192单核搬移 512KB≥480KBAI=64&lt;304</td></tr>
<tr><td>128</td><td>64</td><td>64</td><td>256</td><td>IterBatch</td><td>与上行仅 K 不同:单核搬移 256KB &lt; 480KB条件 3 不满足 → 落 IterBatch形态 b</td></tr>
<tr><td>512</td><td>128</td><td>128</td><td>128</td><td>IterBatch</td><td>MN=16384 &gt; 8192MergeBatch 条件 2 不满足 → 落 IterBatch</td></tr>
<tr><td>32</td><td>4096</td><td>4096</td><td>4096</td><td>ASW_Basic</td><td>L1 五形态均不满足M/N 太大),切 M/N</td></tr>
<tr><td>2</td><td>8192</td><td>8192</td><td>1024</td><td>ASW_Basic</td><td>B&lt;CP=2048 ≥ 32</td></tr>
<tr><td>16</td><td>256</td><td>256</td><td>128</td><td>降核 ASW</td><td>P=4 &lt; 32 且 K=128 不满足 StreamK → 用 4 核,其余闲置</td></tr>
<tr><td>4</td><td>128</td><td>128</td><td>10240</td><td>StreamK</td><td>P=0.25 &lt; 32K≥8192</td></tr>
<tr><td>2048</td><td>1024</td><td>1024</td><td>512</td><td>IterBatch</td><td>大 batch形态 b</td></tr>
<tr><td>8</td><td>512</td><td>512</td><td>512</td><td>ASW_Basic</td><td>B&lt;CP=32 恰好满核</td></tr>
<tr><td>64</td><td>64</td><td>64</td><td>8192</td><td>IterBatch</td><td>小 M×N 但 K 大,形态 e</td></tr></table>
<h3>遍历结论</h3>
<ol class="tight">
<li><b>六个分支全部有真实 case 命中</b>无空分支覆盖矩阵无空洞P&lt;C 且 K 小的残余由降核 ASW_Basic 兜底——此时时延绝对值小,调度开销主导,分支选择不敏感);</li>
<li>**MergeBatch 的区域由条件 2$MN \le 8192$)与条件 3min_DatamountPerCore夹出**:小 M×N 且单核搬移量足够的大 batch case两个条件缺一不可。典型分界对照B=128/M=N=64 时 K=256 → 单核搬移 256KB 不达标落 IterBatchK=512 → 512KB 达标进 MergeBatch</li>
<li><b>IterBatch 与 ASW_Basic 的分界就是 L1 五形态是否满足</b>:单 batch 输入 $(MK+KN)\cdot\text{dtype}$ 相对 L1 的比例决定归属——这正是"核内零重复读"原则的定量体现;</li>
<li><b>StreamK 的区域为 P&lt;C 且 K≥8192</b>B 小、M/N 小、K 大的"细长" case与理论预期一致</li>
<li><b>降核 ASW_Basic 是 P&lt;C 且 K 小区域的理性归宿</b>B∈[2,128],占 15.4%):并行度凑不满、切 K 又不划算时,只用 ⌈P⌉ 个核、每核一个 L0C 满载输出块比强行碎切tile 跌破搬移效率下限)更快。</li>
</ol>
</div>
</body>
</html>