Files
matmul-analysis/BMM算子优化分析_Release/BMM理论vs源码对比分析_v1.1.html
2026-08-26 08:03:11 +00:00

322 lines
24 KiB
HTML
Raw 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 理论最优 vs 源码实现对比分析 v1.1</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.95》与 <code>ops-nn/matmul/batch_mat_mul_v3</code> 源码DAV_3510</blockquote>
<hr>
<h2>一、分支选择机制对比</h2>
<h3>理论v0.95):按切分代价从低到高推导</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.95 §九):</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.95 §五):</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.95 §六)——四种 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.95 §七):</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.95 §八):</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>进入条件对比与 v1.0 一致——源码兜底定位与理论一致。以下重点对比 <b>tiling 参数确定和 swizzle/L2 管理</b>的实现差异。</p>
<h4>2.5.1 BaseM / BaseN 确定</h4>
<table><tr><th>维度</th><th>理论v0.95</th><th>源码</th></tr>
<tr><td>目标</td><td>BaseM × BaseN = L0C/(2×4B) = 32768<b>先把 L0C 用满</b></td><td>baseM=256, baseN=256硬编码 "256 is better base"</td></tr>
<tr><td>长宽比</td><td>跟随 SingleCoreM/N进而跟随 M/N</td><td>由 GetRebalanceBlock 的 cubeBound 模型寻优</td></tr>
<tr><td>baseK</td><td>min(L0A/2·BaseM, L0B/2·BaseN),仅受 L0 容量约束</td><td>baseK = 128B/dtypeL0A/L0B 最小高效粒度)</td></tr></table>
<p><b>源码的 cubeBound 模型</b><code>GetRebalanceBlock</code>matmul_v3_tiling_helper.cpp L387-492</p>
<ul class="tight">
<li>由三项组成L2 供数能力 + L2 溢出惩罚 + K 向复用修正</li>
<li>乘以 CUBE_BOUND_RATIO=0.85 余量</li>
<li>枚举 baseM/baseN 递减,以 balanceRate≥0.9 剪枝</li>
<li>尾块感知评分GetBalanceRateWithTail</li>
</ul>
<p><b>差异分析</b>:理论直接取 L0C 满载32768 元素),源码用 256×256=65536 元素——<b>源码的 baseM×baseN 是理论上限的 2 倍</b>。这说明源码的 baseM/baseN 不是 L0C 满载的 tile 尺寸,而是 SingleCoreM/N 级别的参数。源码中 L0 级 tile 的实际尺寸由后续 stepM/stepN 切分决定。</p>
<h4>2.5.2 SingleCoreM / SingleCoreN 确定</h4>
<table><tr><th>维度</th><th>理论v0.95</th><th>源码</th></tr>
<tr><td>约束</td><td>不受 L0 直接约束,由 GM→L1 效率 + L2 复用决定</td><td>由 baseM/baseN + stepM/stepN 组合决定</td></tr>
<tr><td>并行度</td><td>mCnt × nCnt ≥ ⌈C/B⌉</td><td>GetRebalanceBlock 中 balanceRate ≥ 0.9</td></tr>
<tr><td>L1 约束</td><td>2(SM+SN)·kL1·dtype ≤ L1</td><td>CalL1Tiling 中 (aL1+bL1)×2 ≤ L1</td></tr></table>
<p><b>关键差异</b>:理论把 SingleCoreM/N 和 BaseM/N 分为两层SingleCoreM ≥ BaseM源码中 baseM=256 实际上已经是 SingleCoreM 级别的参数。源码没有显式的 SingleCoreM/N 概念——per-core tile 尺寸 = baseM × stepM或 baseN × stepN</p>
<h4>2.5.3 Swizzle 实现</h4>
<table><tr><th>维度</th><th>理论v0.95</th><th>源码</th></tr>
<tr><td>窗口大小</td><td>W = max{d : d\</td><td>C, d ≤ ⌊√C⌋}C=32 时 W=4</td><td>mainWindow = GetAswWindowLen(),逻辑相同</td></tr>
<tr><td>蛇形</td><td>窗口间 N 向反向</td><td><code>UpdateBasicIndex</code>:奇数行 n 反向</td></tr>
<tr><td>窗内</td><td>不蛇形A 行全程驻留 L2零收益</td><td>一致</td></tr></table>
<p><b>结论</b>swizzle 实现与理论一致。</p>
<h4>2.5.4 L2 管理</h4>
<table><tr><th>维度</th><th>理论v0.95</th><th>源码</th></tr>
<tr><td>L2 定位</td><td>Cache 非 Buffer软件可控手段Cache Hint + CMO + swizzle</td><td><code>enableL2Cache</code> flag + <code>SetL2CacheHint</code></td></tr>
<tr><td>启用条件</td><td>S_in &gt; L2 时启用分组</td><td>isBigSize(&gt;100MB) &amp;&amp; cBatchDimAll&lt;usedCoreNum &amp;&amp; transConflict≤6</td></tr>
<tr><td>分组策略</td><td>执行组m_grp × n_grp 块,工作集 ≤ L2</td><td>DoL2CacheAndCalOrderTiling 内部处理</td></tr>
<tr><td>冲突控制</td><td>transConflict ≤ 6</td><td>一致</td></tr></table>
<p><b>差异分析</b>:源码的 L2 管理有两个额外限制:</p>
<ol class="tight">
<li><b>size &gt; 100MB 阈值</b>:小于 100MB 的 case 不启用 L2 cache 管理——理论上 S_in ≤ L2=128MB 时不需要分组100MB &lt; 128MB 留有余量,合理;</li>
<li><b>cBatchDimAll &lt; usedCoreNum</b>batch 数小于核数时才启用——因为 B ≥ C 时切 B 已经满核,不需要 M/N 切分(不由 ASW 承接)。</li>
</ol>
<h4>2.5.5 综合评价</h4>
<table><tr><th>维度</th><th>评价</th></tr>
<tr><td>BaseM/BaseN 确定</td><td>源码用硬编码 256×256 + cubeBound 寻优,理论用 L0C 满载——<b>源码更保守但经实测调优</b></td></tr>
<tr><td>SingleCoreM/N</td><td>源码无显式概念,由 baseM×stepM 组合——<b>理论的分层更清晰</b></td></tr>
<tr><td>Swizzle</td><td>✓ 一致</td></tr>
<tr><td>L2 管理</td><td>源码有额外的 100MB 和 batch 数阈值——<b>工程化的保守限制</b></td></tr>
<tr><td>负载均衡</td><td>源码有 balanceRate ≥ 0.9 + 尾块感知评分——<b>理论未建模,源码更完善</b></td></tr></table>
<hr>
<h3>2.6 ASW L1 全载特化</h3>
<p><b>理论</b>v0.95 §八.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.95 §八.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>