v1.0: 同步HTML

This commit is contained in:
2026-08-26 01:41:22 +00:00
parent 1acde9ced4
commit 402b6390e7

View File

@@ -0,0 +1,286 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BMM 理论最优 vs 源码实现对比分析</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 理论最优 vs 源码实现对比分析</h1>
<blockquote>基于《BMM 算子优化分析 v0.93》与 <code>ops-nn/matmul/batch_mat_mul_v3</code> 源码DAV_3510</blockquote>
<hr>
<h2>一、分支选择机制对比</h2>
<h3>理论v0.93):按切分代价从低到高推导</h3>
<pre><code>转Matmul → 特殊分支 → MergeBatch/IterBatch → ASW_Basic → StreamK → 降核ASW</code></pre>
<p>核心逻辑:<b>按价格从低到高购买并行度</b>——切 B 免费 → 切 M/N 有重复读代价 → 切 K 有归约代价。每个分支有独立的进入条件B/M/N/K 闭式表达式case 落入哪个分支由条件判定。</p>
<h3>源码:优先级有序匹配</h3>
<pre><code>// batch_matmul_v3_tiling_strategy.h, DAV_3510 优先级
{K_EQUAL_ZERO(0), TO_MUL(1), STREAM_K(2), MERGE_BATCH(3),
ITER_BATCH_BROADCAST(4), ITER_BATCH_BASICAPI(5), ITER_BATCH(6),
AL1_FULL_LOAD(7), BL1_FULL_LOAD(8), ASW_BASIC(9), BASE(999)}</code></pre>
<p><b>优先级从高到低逐个尝试 IsCapable(),第一个返回 true 的分支胜出。</b></p>
<h3>关键差异</h3>
<table><tr><th>维度</th><th>理论</th><th>源码</th></tr>
<tr><td>选择方式</td><td>各分支独立条件判定</td><td>优先级有序匹配first-match</td></tr>
<tr><td>StreamK 位置</td><td>最后检查P&lt;C/2 才考虑)</td><td>优先级 3K=0/K=1 之后立即检查)</td></tr>
<tr><td>转Matmul</td><td>独立分支BatchA=1BatchB=1</td><td>无独立分支,由广播/ASW 承接</td></tr>
<tr><td>降核 ASW</td><td>独立分支P&lt;C 且不满足 StreamK</td><td>无独立分支ASW_Basic 内部处理</td></tr></table>
<p><b>源码优先级顺序的风险</b>StreamK 优先级高于 MergeBatch/IterBatch。若一个 case 同时满足 StreamK 和 IterBatch 的条件B&gt;C 且 K≥8192源码会先进入 StreamK——但理论上 B&gt;C 时切 B 免费,不应优先切 K。不过 StreamK 的 IsCapable 条件(<code>batchC×mCnt×nCnt ≤ aicNum/2</code>)实际上排除了 B&gt;C 的 case所以优先级顺序在实际中不会造成误判。</p>
<hr>
<h2>二、逐分支对比</h2>
<h3>2.1 特殊分支K=0 / K=1</h3>
<p><b>理论条件</b>v0.93 §九):</p>
<ul class="tight">
<li>K=0无计算C = bias 或 0纯 AIV 写值</li>
<li>K=1退化为逐元素乘走 AIV 向量通路GM→UB→Mul→GM触发需 B ≥ 2×64 且单 batch 输入输出驻留 UB</li>
</ul>
<p><b>源码实现</b></p>
<p><code>BATCH_MATMUL_INPUT_K_EQUAL_ZERO</code>(优先级 0</p>
<pre><code>// batch_matmul_v3_k_equal_zero_tiling.cpp IsCapable()
bool IsCapable() {
if (aFormat == NZ || bFormat == NZ) return false; // 不支持 NZ
if (hasBias) return false; // 不支持 bias
if (kValue != 0) return false; // K=0
return true;
}
// DoOpTiling: usedCoreNum = aivNum用 AIV 核)</code></pre>
<p><code>BATCH_MATMUL_TO_MUL</code>(优先级 1K=1 时退化为逐元素乘,用 AIV 核。</p>
<p><b>对比结论</b>理论与源码一致。源码实现正确——K=0 和 K=1 都用 AIV 通路,不用 Cube。理论中的"B ≥ 2×64"条件在源码中未显式检查,而是通过 UB 容量隐式限制(<code>ubLimitBatchNum = ubSize / singleBatchSize</code>)。</p>
<p><b>差异</b>:理论要求"单 batch 输入输出驻留 UB"作为进入条件;源码通过 <code>batchNum = min(singleCoreBatch, ubLimitBatchNum)</code> 自动处理 UB 放不下的情况(分批处理),无需在进入条件中排除。<b>源码更通用,理论更保守。</b></p>
<hr>
<h3>2.2 MergeBatch</h3>
<p><b>理论条件</b>v0.93 §五):</p>
<ol class="tight">
<li>BatchA = BatchB ∧ b_core = B/C ≥ 2b₀b₀=2 → b_core ≥ 4</li>
<li>2(b₀M)(b₀N)·4B ≤ L0C</li>
<li>b_core(MK+KN)·dtype ≥ min_DatamountPerCore (480KB)</li>
<li>max(MK, KN)·dtype ≥ min_TileSize (16KB)</li>
<li>2MN/(M+N) &lt; R₁₆/b₀</li>
</ol>
<p><b>源码条件</b><code>batch_matmul_v3_mergebatch_basicapi_tiling.cpp</code> IsCapable</p>
<pre><code>bool IsCapable() {
if (非连续转置) return false;
if (NZ格式) return false;
if (hasBias || (FP32 &amp;&amp; !HF32)) return false;
if (BatchA != BatchB) return false; // 对应理论条件1
if (batchC &lt; MIN_BATCH_L0 * aicNum) return false; // B &lt; 128即 b_core &lt; 4
if (alignK &lt; 64 || M &gt; N) return false; // K≥64M≤N
// L0 buffer check with b0=MIN_BATCH_L0=4
if (al0Size &gt; L0A || bl0Size &gt; L0B ||
tempAlignM * tempAlignN * 4B * 2 &gt; L0C) return false; // 对应理论条件2
return true;
}</code></pre>
<p><b>逐条对比</b></p>
<table><tr><th>理论条件</th><th>源码对应</th><th>差异分析</th></tr>
<tr><td>1. b_core ≥ 2b₀</td><td>batchC ≥ 128b_core ≥ 4</td><td>理论 b₀=2 → b_core ≥ 4源码 MIN_BATCH_L0=4 → batchC ≥ 128 = b_core ≥ 4。<b>一致</b>b₀=2 时 b_core ≥ 4 与源码等价)</td></tr>
<tr><td>2. L0C 容量</td><td>L0 buffer check</td><td>理论2(b₀M)(b₀N)·4B ≤ L0C源码tempAlignM × tempAlignN × 4B × 2 ≤ L0C。<b>一致</b>tempAlignM = b₀×M 对齐)</td></tr>
<tr><td>3. 单核搬移量 ≥ 480KB</td><td><b></b></td><td>源码缺少此条件——可能导致小数据量 case 进入 MergeBatch 后 GM 带宽利用率不足</td></tr>
<tr><td>4. 搬移 tile ≥ 16KB</td><td><b></b></td><td>源码缺少此条件——小 tile 时搬移效率无保障</td></tr>
<tr><td>5. AI &lt; R₁₆/b₀</td><td><b></b></td><td>源码缺少算存比约束——计算 Bound 的 case 进入 MergeBatch 后冗余计算成为瓶颈</td></tr></table>
<p><b>源码额外条件</b>alignK ≥ 64K ≥ 64和 M ≤ N。理论无此限制。</p>
<p><b>Tiling 参数对比</b></p>
<table><tr><th>参数</th><th>理论</th><th>源码</th></tr>
<tr><td>合并数 b</td><td>b = min(L0C上限, R₁₆上限, b_core)</td><td>mergeBatchL0 = min(多项式求解, maxBatchL0, batchNumPerCore)</td></tr>
<tr><td>L0 K 粒度</td><td>kL0 = min(L0A/2bM, L0B/2bN)</td><td>baseK = min(maxBasek, <b>64</b>)</td></tr>
<tr><td>L1 K 粒度</td><td>kL1 ≥ max(kL0, 256B/dtype)</td><td>stepKa × baseK由 L1/4 容量决定</td></tr></table>
<p><b>关键差异</b>:源码 <code>baseK ≤ 64</code>硬编码上限理论无此限制。64 元素 = 128B BF16 = dValue 下限。这是保守的安全下限。</p>
<p><b>MergeBatch vs IterBatch 分界</b></p>
<ul class="tight">
<li>理论b_core &gt; b₀(T_comp+T_write)/T_cmdGM→L1 搬移命令节省 vs drain 惩罚)</li>
<li>源码无显式分界——MergeBatch 优先级高于 IterBatch先匹配先进</li>
</ul>
<p><b>结论</b>:源码的 MergeBatch 进入条件<b>偏宽</b>——缺少单核搬移量、tile 大小、算存比三条约束。这导致一些本应走 IterBatch 的 case如单核搬移量不足、计算 Bound被错误分配到 MergeBatch。建议补充条件 3/4/5。</p>
<hr>
<h3>2.3 IterBatch</h3>
<p><b>理论条件</b>v0.93 §六)——四种 L1 驻留形态:</p>
<ul class="tight">
<li>a) b_core=1 ∧ (MK+KN)·dtype ≤ L1单 batch 全驻留)</li>
<li>b) b_core&gt;1 ∧ 2(MK+KN)·dtype ≤ L1双 batch 乒乓)</li>
<li>c) 一侧驻留 + 对侧切 K含 b_core≥2 的半预算预取档)</li>
<li>d) 两侧都切 K</li>
</ul>
<p><b>源码条件</b><code>batch_matmul_v3_iterbatch_basicapi_tiling.cpp</code> IsCapable</p>
<pre><code>bool IsCapable() {
if (非连续转置A) return false;
if (NZ格式) return false;
if (batchBias &gt; 1) return false;
if (BatchA != BatchB) return false;
if (batchC &lt;= aicNum) return false; // B ≤ 32 不进 IterBatch
// L1 双缓冲容量检查(形态 b 等价)
if ((alignM*alignK + alignK*alignN)*dtype*2 &gt; L1) return false;
// L0 能放至少 1 个 batch
if (!l0CanLoadBatch_) {
// 负载均衡检查balanceRate ≥ 0.8
if (balanceRateOfBatch &lt; 0.8) return false;
}
return true;
}</code></pre>
<p><b>逐条对比</b></p>
<table><tr><th>理论形态</th><th>源码对应</th><th>差异分析</th></tr>
<tr><td>a) 单 batch 全驻留</td><td><b>无独立分支</b></td><td>源码不区分 a/b——只要 (MK+KN)×dtype×2 ≤ L1 就进 IterBatch。b_core=1 的 case 被 B&gt;32 条件排除B≤32 时 batchC≤aicNum不进 IterBatch</td></tr>
<tr><td>b) 双 batch 乒乓</td><td>✓ 直接对应</td><td>源码的 L1 检查就是形态 b 的条件</td></tr>
<tr><td>c) 一侧驻留+对侧切 K</td><td><b></b></td><td>源码不实现形态 c——当 (MK+KN)×dtype×2 &gt; L1 时直接退出 IterBatch</td></tr>
<tr><td>d) 两侧都切 K</td><td><b></b></td><td>源码不实现形态 d</td></tr></table>
<p><b>关键差异</b></p>
<ol class="tight">
<li><b>源码只实现形态 b</b>,形态 a/c/d 不覆盖。形态 ab_core=1被 B&gt;32 排除——这些 case 由 ASW 或 AL1/BL1_FULL_LOAD 承接。形态 c/dL1 放不下双 batch的 case 落到 ASW_Basic。</li>
<li><b>B ≤ 32 不进 IterBatch</b>:源码用 <code>batchC &lt;= aicNum</code> 排除了 B ≤ C 的 case。理论上 b_core=1 时形态 a/c 仍可行,但源码将这些 case 交给 ASW/AL1/BL1 分支。</li>
<li><b>负载均衡</b>:源码有 <code>balanceRateOfBatch &lt; 0.8</code> 检查(对应理论条件 2 的 B mod C ≥ minCoreNum但实现方式不同——源码用实际最大 batch 数与平均值的比率,理论用尾波核数。</li>
</ol>
<p><b>Tiling 参数对比</b></p>
<table><tr><th>参数</th><th>理论</th><th>源码</th></tr>
<tr><td>iterBatchL1L1 级 batch 数)</td><td>由 L1 容量决定</td><td>min(L1容量/batch, mmadCount=8, ceil(B/C))</td></tr>
<tr><td>iterBatchL0L0 级 batch 数)</td><td>由 L0 容量决定</td><td>min(L0A/L0B/L0C容量, iterBatchL1)</td></tr>
<tr><td>baseM/baseN/baseK</td><td>由 L0 容量和 dValue 决定</td><td>由 MatMulV3TilingHelper::ResetBase 统一计算</td></tr></table>
<p><b>结论</b>:源码的 IterBatch <b>只覆盖形态 b</b>,且要求 B &gt; C。形态 a/c/d 的缺失意味着:</p>
<ul class="tight">
<li>B ≤ C 且 L1 放得下的 case形态 a由 AL1/BL1_FULL_LOAD 或 ASW 承接——<b>合理</b>,因为这些 case 的并行度不足,需要切 M/N</li>
<li>L1 放不下双 batch 但放得下单侧驻留的 case形态 c落到 ASW——<b>可能不是最优</b>,因为 ASW 引入了 M/N 维的核间重复读,而形态 c 可以避免</li>
</ul>
<hr>
<h3>2.4 StreamK</h3>
<p><b>理论条件</b>v0.93 §七):</p>
<ol class="tight">
<li>P = B·MN·4B/L0C ≤ C/2</li>
<li>K/grid_K ≥ 256B/dtypegrid_K = ⌊C/⌈P⌉⌋</li>
<li>K &gt; grid_K²/(grid_K-1)·θ_cθ_c ≈ 12</li>
</ol>
<p><b>源码条件</b><code>batch_matmul_v3_basic_streamk_tiling.cpp</code></p>
<pre><code>bool IsCapable() {
if (deterministicLevel &gt; 1) return false; // 确定性
if (BatchA != BatchB) return false; // batch 一致
if (FP32 &amp;&amp; K &gt; 2000000) return false; // FP32 精度
// K 阈值CeilAlign(K,256) ≥ max(8192, aicNum×256B/dtype)
if (CeilAlign(K,256) &lt; max(8192, 32×256/2)) return false;
// 即 K ≥ 8192BF16
// 并行缺口batchC × mCnt × nCnt ≤ aicNum/2
mCnt = ceil(M/256); nCnt = ceil(N/256); // 256B 粒度
if (batchC * mCnt * nCnt &gt; aicNum/2) return false; // B·⌈M/256⌉·⌈N/256⌉ ≤ 16
return true;
}</code></pre>
<p><b>逐条对比</b></p>
<table><tr><th>理论条件</th><th>源码对应</th><th>差异分析</th></tr>
<tr><td>1. P ≤ C/2</td><td>B·⌈M/256⌉·⌈N/256⌉ ≤ C/2</td><td><b>粒度不同</b>:理论用 L0C 满载粒度M^t·N^t = L0C/4B = 65536 元素),源码用固定 256 元素粒度。<b>理论粒度更粗</b>65536 vs 65536=256²实际上一致——⌈M/256⌉·⌈N/256⌉ 就是 L0C 满载 tile 的个数</td></tr>
<tr><td>2. K/grid_K ≥ 128 元素</td><td>K ≥ 8192固定阈值</td><td>源码用固定 8192 = C×256 元素 = dValue 推荐值在最大 grid_K=C 下的保障。<b>理论更精确</b>(动态 grid_K<b>源码更保守</b>(固定最大 grid_K</td></tr>
<tr><td>3. K &gt; grid_K²/(grid_K-1)·θ_c</td><td><b></b></td><td>源码不归约代价建模,直接用固定 K 阈值覆盖。θ_c≈12 时 grid_K=2 只需 K&gt;49远低于 8192——<b>源码的条件 3 被条件 2 覆盖,过于保守</b></td></tr>
<tr><td>4. 确定性 ≤ 1</td><td>deterministicLevel &gt; 1 排除</td><td>一致</td></tr></table>
<p><b>关键差异</b></p>
<ol class="tight">
<li><b>grid_K 取值</b>:理论 grid_K = ⌊C/⌈P⌉⌋动态源码用固定阈值隐式假设 grid_K = C最大。这导致源码的 K 阈值8192远大于理论阈值grid_K=2 时 K&gt;49</li>
<li><b>StreamK 优先级过高</b>:源码中 StreamK 优先级2高于 MergeBatch3和 IterBatch5。虽然 StreamK 的 IsCapable 条件实际上排除了 B&gt;C 的 case但如果 B≤C 且 K≥8192 且 P≤C/2源码会优先进入 StreamK 而非 ASW_Basic。这在理论上是正确的P&lt;C/2 时切 K 是唯一剩余的并行维度)。</li>
</ol>
<p><b>结论</b>:源码的 StreamK 实现<b>基本合理但过于保守</b>。固定 K≥8192 阈值排除了大量理论上可走 StreamK 的 caseK 在 49~8192 之间)。建议改为动态计算 grid_K 和 K 阈值。</p>
<hr>
<h3>2.5 ASW_Basic</h3>
<p><b>理论条件</b>v0.93 §八):</p>
<ol class="tight">
<li>P = B·MN·4B/L0C ≥ C</li>
<li>无 batch 结构限制(交叉广播均可)</li>
<li>兜底B ≥ C 但 IterBatch/MergeBatch 条件不满足</li>
</ol>
<p><b>源码条件</b><code>batch_matmul_v3_asw_basic_tiling.cpp</code> IsCapable</p>
<pre><code>bool IsCapable() {
if (A/B 非连续转置不匹配) return false;
if (BatchA != BatchB) return false;
if (batchBias &gt; 1) return false;
if (!FP16/BF16) return false; // 类型限制
return true; // 无其他条件——兜底
}</code></pre>
<p><b>对比</b>:源码的 ASW_Basic <b>几乎没有进入条件</b>——它是优先级最低的实质分支priority 9任何不被前面分支捕获的 case 都落入 ASW。这与理论的"兜底"定位一致。</p>
<p><b>Tiling 实现对比</b></p>
<table><tr><th>维度</th><th>理论</th><th>源码</th></tr>
<tr><td>核间切分</td><td>切 B → 切 M → 切 N → 混合切</td><td>MatMulV3TilingHelper::ResetBase 统一计算</td></tr>
<tr><td>swizzle</td><td>ASW 滑窗蛇形W=4 for C=32</td><td>GetRebalanceBlock + CalL1Tiling</td></tr>
<tr><td>L2 切分</td><td>场景 A/B/C 分场景决策</td><td>由 MatMulV3TilingHelper 内部处理</td></tr>
<tr><td>L1 buffer</td><td>双缓冲/4-buffer 按容量决定</td><td>l1BufferNum = 4 if fits else 2</td></tr></table>
<p><b>结论</b>ASW_Basic 作为兜底分支,源码实现合理。理论和源码在"兜底"定位上一致。差异主要在 tiling 参数的具体计算方法——源码用统一的 Helper 函数,理论给出了更细粒度的 swizzle/L2 切分策略。</p>
<hr>
<h3>2.6 ASW L1 全载特化</h3>
<p><b>理论</b>v0.93 §八.5):单边无 batch 且该侧矩阵小M ≤ 256、MK·dtype·2 ≤ L1、对侧每核循环 ≥ 4 轮)时小侧整个常驻 L1。</p>
<p><b>源码</b><code>batch_matmul_v3_asw_al1_full_load_basic_tiling.cpp</code> IsCapable</p>
<pre><code>bool IsCapable() {
if (非连续转置) return false;
if (!FP16/BF16) return false;
if (batchA &gt; 1) return false; // A 无 batch
if (M &gt; 256) return false; // M ≤ 256
if (batchBias &gt; 1) return false;
if (A数据量×2 &gt; L1) return false; // A 放得下 L1/2
// B 数据量足够:每核至少 4 轮
if (B总数据量 &lt; L1×aicNum &amp;&amp; B轮数 &lt; 4×aicNum) return false;
return true;
}</code></pre>
<p><b>对比</b>:理论与源码<b>高度一致</b>——M ≤ 256、batchA=1、A 驻留 L1、B 每核 ≥ 4 轮。这是理论和源码最吻合的分支。</p>
<hr>
<h3>2.7 降核 ASW</h3>
<p><b>理论</b>v0.93 §八.3/6P &lt; C 且不满足 StreamK → 只用 ⌈P⌉ 核,其余核闲置。</p>
<p><b>源码</b><b>无独立的降核分支</b>。ASW_Basic 的 <code>GetNumBlocks()</code> 返回 <code>aicNum</code>(固定 32 核),不降核。</p>
<p><b>差异</b>:理论上 P &lt; C 时应该降核(只用 ⌈P⌉ 核),但源码始终用全部 32 核。对于 P &lt; C 的 case部分核分到的输出块为空idle但仍参与调度。这可能引入不必要的调度开销。</p>
<p><b>建议</b>:源码应在 ASW_Basic 中加入降核逻辑——当 P &lt; C 时 <code>usedCoreNum = ⌈P⌉</code></p>
<hr>
<h2>三、总体结论</h2>
<h3>源码实现评价</h3>
<table><tr><th>维度</th><th>评价</th></tr>
<tr><td>分支覆盖完备性</td><td>✓ 7 个分支覆盖所有 case无空洞</td></tr>
<tr><td>分支选择正确性</td><td>⚠️ 优先级有序匹配基本合理,但 StreamK 优先级偏高(实际不影响)</td></tr>
<tr><td>MergeBatch 条件</td><td>⚠️ 偏宽——缺少搬移量/tile/算存比约束</td></tr>
<tr><td>IterBatch 形态覆盖</td><td>⚠️ 只覆盖形态 b缺少 a/c/d</td></tr>
<tr><td>StreamK K 阈值</td><td>⚠️ 固定 8192 过于保守</td></tr>
<tr><td>降核 ASW</td><td>✗ 未实现</td></tr>
<tr><td>L1 全载特化</td><td>✓ 与理论高度一致</td></tr>
<tr><td>特殊分支</td><td>✓ 与理论一致</td></tr></table>
<h3>理论最优方案评价</h3>
<p>理论分析的 7 大分支体系在逻辑上完备15 种切分组合 → 按代价排序 → 6+1 分支),进入条件全部为 B/M/N/K 闭式表达式。与源码对比后确认:</p>
<ol class="tight">
<li><b>理论分支体系是最优的候选集</b>——源码的 10 个 tiling 模板可以映射到理论的 7 大分支上,无遗漏;</li>
<li><b>理论条件更精确</b>——MergeBatch 的 5 条件、StreamK 的 3 条件都比源码更严格/精确;</li>
<li><b>理论缺的工程细节</b>——源码中的负载均衡检查balanceRate ≥ 0.8、格式限制NZ/ND、bias 限制等在理论中未建模。</li>
</ol>
<h3>源码改进建议(按优先级)</h3>
<ol class="tight">
<li><b>MergeBatch 补条件 3/4/5</b>(搬移量/tile/算存比),避免不应进入的 case 被误捕获;</li>
<li><b>IterBatch 补形态 c/d</b>(一侧驻留/两侧切 K覆盖 L1 放不下双 batch 的 case</li>
<li><b>StreamK 改动态 grid_K</b>grid_K = ⌊C/⌈P⌉⌋K 阈值随 grid_K 变化而非固定 8192</li>
<li><b>ASW_Basic 加降核逻辑</b>P &lt; C 时 usedCoreNum = ⌈P⌉</li>
<li><b>补转Matmul 分支</b>BatchA=1 BatchB=1 时折叠为 Matmul避免走 BMM 的冗余路径。</li>
</ol>
</div>
</body>
</html>