添加 BatchMatMulV3算子分支实现分析.html

This commit is contained in:
2026-08-20 14:40:49 +00:00
parent ced23432d2
commit 63b039ac51

View File

@@ -0,0 +1,923 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BatchMatMulV3 (BMM v3) 算子分支实现深度分析 —— 昇腾 950 / DAV_3510</title>
<style>
:root{
--bg:#0f1419; --panel:#1a2230; --ink:#1f2933; --muted:#5f6b7a;
--accent:#0b6bcb; --accent2:#0e9f6e; --warn:#b45309; --line:#d9e2ec;
--code-bg:#f4f6f9; --hl:#fff7e6;
}
*{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.75}
.page{max-width:1180px;margin:0 auto;padding:32px 40px 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:48px;border-left:6px solid var(--accent);padding-left:12px;color:#0b3d73}
h3{font-size:18px;margin-top:32px;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;white-space:nowrap}
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}
.toc{background:#f6f9fc;border:1px solid var(--line);border-radius:10px;padding:18px 26px;font-size:14px}
.toc a{color:var(--accent);text-decoration:none}
.toc a:hover{text-decoration:underline}
.toc ol{margin:6px 0}
.note{background:#eafaf3;border-left:5px solid var(--accent2);padding:10px 16px;border-radius:0 8px 8px 0;margin:14px 0}
.warn{background:#fdf3e7;border-left:5px solid var(--warn);padding:10px 16px;border-radius:0 8px 8px 0;margin:14px 0}
.crit{background:#fdeeee;border-left:5px solid #c0392b;padding:10px 16px;border-radius:0 8px 8px 0;margin:14px 0}
.src{color:var(--muted);font-size:12.5px}
.tag{display:inline-block;background:#e3ecf7;color:#0b3d73;border-radius:4px;padding:0 7px;font-size:12px;margin-right:4px;font-family:Consolas,monospace}
.tag.aiv{background:#e6f7ee;color:#0e6e4c}
.tag.mix{background:#f3e8fd;color:#6d28a8}
.meta{color:var(--muted);font-size:13px;margin-bottom:24px}
.branch{border:1px solid var(--line);border-radius:10px;padding:4px 22px 16px;margin:22px 0;background:#fcfdfe}
.branch h3{border-bottom:none}
ul.tight li,ol.tight li{margin:3px 0}
.figbox{border:1px solid var(--line);border-radius:10px;padding:14px;margin:16px 0;background:#fbfcfe;text-align:center}
.figbox svg{max-width:100%;height:auto}
.figcap{font-size:12.5px;color:var(--muted);margin-top:8px;text-align:center}
.flow{font-family:Consolas,monospace;font-size:13px;background:#f8fafc;border:1px dashed #b9c6d3;border-radius:8px;padding:14px;white-space:pre;overflow-x:auto}
</style>
</head>
<body>
<div class="page">
<h1>BatchMatMulV3BMM v3算子分支实现深度分析</h1>
<div class="meta">
分析对象:<code>ops-nn/matmul/batch_mat_mul_v3</code>CANN 开源算子仓GitCode cann/ops-nn&nbsp;|&nbsp;
目标平台:昇腾 950PRNPU 架构版本 351x<code>NpuArch::DAV_3510</code>&nbsp;|&nbsp;
资料来源昇腾NPU知识库950 架构白皮书、CANN 9.0.0 AscendC 文档)+ 算子源码逐文件分析 &nbsp;|&nbsp;日期2026-08-20
</div>
<div class="toc">
<b>目录</b>
<ol>
<li><a href="#sec1">算子概览与代码地图</a></li>
<li><a href="#sec2">硬件基础:昇腾 950PR 微架构规格与 tiling 设计约束</a></li>
<li><a href="#sec3">Tiling 总体框架:入口、平台分流与分支短路遍历</a></li>
<li><a href="#sec4">为什么是这些分支:从 case 空间到分支全集的系统性论证</a></li>
<li><a href="#sec5">逐分支详解(进入条件 / 设计动机 / tiling 方法 / kernel 实现)</a>
<ol>
<li><a href="#b0">K_EQUAL_ZEROK=0 清零AIV</a></li>
<li><a href="#b1">TO_MULK=1 退化为向量乘AIV</a></li>
<li><a href="#b2">STREAM_K超大 K + 并行度不足时切 K</a></li>
<li><a href="#b3">MERGE_BATCH_BASICAPI小 M/N 大 batch 合并进 L0</a></li>
<li><a href="#b4">ITER_BATCH_BROADCAST_BASICAPI单边 batch 广播</a></li>
<li><a href="#b5">ITER_BATCH_BASICAPI普通大 batch基础 API</a></li>
<li><a href="#b6">ITER_BATCH大 batch高阶 API / 自定义搬移)</a></li>
<li><a href="#b7">AL1 / BL1_FULL_LOAD_BASIC单边无 batch 的 L1 全载</a></li>
<li><a href="#b8">ASW_BASIC 与 BASE通用兜底 + 自适应滑窗</a></li>
</ol>
</li>
<li><a href="#sec6">Swizzle 专题:核间分块执行顺序的三种实现机制</a></li>
<li><a href="#sec7">tiling_key 编码7 位段如何锁定一条 kernel 路径</a></li>
<li><a href="#sec8">分支完备性论证与批判性讨论</a></li>
<li><a href="#sec9">附:非 arch35 老路径分支910B 等平台)简述</a></li>
<li><a href="#sec10">参考来源清单</a></li>
</ol>
</div>
<!-- ============================== 1 ============================== -->
<h2 id="sec1">1. 算子概览与代码地图</h2>
<p>BatchMatMulV3 是 CANN ops-nn 仓中批量矩阵乘的主力算子,承载 <code>aclnnBatchMatMul / aclnnBaddbmm / aclnnAddbmm / aclnnEinsum / aclnnBatchMatMulWeightNz</code> 等全部 BMM 类 API。语义为 <code>C[b, m, n] = A[b, m, k] × B[b, k, n]</code>+ 可选 biasbatch 维最多 4 级(总维度 ≤ 6A/B/C 的 batch 维支持广播。</p>
<table>
<tr><th>目录</th><th>内容</th><th>关键文件</th></tr>
<tr><td><code>op_host/op_tiling/</code></td><td>Host 侧 tiling分支选择、tiling 参数计算</td><td><code>batch_mat_mul_v3_tiling.cpp</code>(入口)、<code>batch_mat_mul_v3_base_tiling.cpp</code>67KB老路径基类<code>arch35/</code>950 高级 tiling12 个策略文件)</td></tr>
<tr><td><code>op_kernel/</code></td><td>Device 侧 kernel</td><td><code>arch35/batch_mat_mul_v3.cpp</code>arch35 入口,编译期 if constexpr 分发树)、<code>batch_mat_mul_v3*.h</code>(老架构 kernel<code>arch35/batch_mat_mul_v3_*_advanced.h</code>(各策略 kernel/block scheduler</td></tr>
<tr><td><code>op_host/op_api/</code></td><td>aclnn API 层</td><td>aclnnBatchMatMul / Baddbmm / Einsum 等</td></tr>
<tr><td>依赖ops-nn <code>matmul/mat_mul_v3</code><code>matmul/common/cmct</code></td><td>公共 tiling 基类、MatmulImpl 高层 API、Cmct/Blaze GEMM 框架</td><td>matmul_v3_tiling_advanced、mat_mul_pingpong_basic、kernel_matmul_iterbatch 等</td></tr>
</table>
<p>BMM v3 的软件实现遵循昇腾算子的标准两层结构:</p>
<ul class="tight">
<li><b>Host tiling</b>:根据 shape/dtype/format/batch 结构 + 平台规格aicNum/aivNum、L1/L0A/L0B/L0C/UB/L2 容量),选择一个<b>分支strategy</b>并算出分块参数baseM/baseN/baseK、singleCoreM/N/K、iterBatch、mergeBatchL0、swizzle 窗长等),编码进 <b>tilingKey</b></li>
<li><b>Device kernel</b>tilingKey 在编译期实例化出唯一 kernel 模板组合7 个模板参数),运行时按 tilingData 执行「分块到各 AIC/AIV 核的搬移GM→L1→L0→Cube→L0C→GM与计算以及块到核的映射顺序swizzle」。</li>
</ul>
<!-- ============================== 2 ============================== -->
<h2 id="sec2">2. 硬件基础:昇腾 950PR 微架构规格与 tiling 设计约束</h2>
<p>BMM v3 全部分支的设计动机都可以追溯到 950PRDAV_3510的以下硬件事实来源昇腾950 NPU 架构白皮书表3-1/表4-2、CANN 9.0.0《NPU架构版本351x》文档</p>
<h3>2.1 关键规格表</h3>
<table>
<tr><th>规格项</th><th>昇腾 950PR</th><th>对 BMM tiling 的意义</th></tr>
<tr><td>AI 子系统</td><td>36 个(满配),每个 = 1 Cube Core + 2 Vector Core</td><td rowspan="2">AIC:AIV = 1:2 是 StreamK、fixpipe 1V2 等 MIX 分支的结构性前提(源码多处硬性要求 <code>aivNum == 2*aicNum</code></td></tr>
<tr><td>AIC / AIV 核数</td><td>32降配 28/ 64降配 56<b>AIC/AIV 分离架构</b></td></tr>
<tr><td>Cube 算力</td><td>BF16/FP16 432 TFLOPS纯 Cube一拍完成 FP16 16×16×16</td><td>决定 cubeBound计算访存比拐点进而决定 baseM/baseN 寻优</td></tr>
<tr><td>Vector 算力</td><td>FP16/BF16 54 TFLOPS每拍 256B</td><td>K=0/K=1 等低密度计算退到 AIV 更划算的依据</td></tr>
<tr><td>L1 Buffer</td><td><b>512KB</b> / AIC</td><td>L1 全载AL1/BL1 full load、iterBatchL1L1 驻留 batch 数)的容量上界</td></tr>
<tr><td>L0A / L0B</td><td><b>64KB</b> / AIC512B 对齐FRACTAL_NZ/ZN</td><td>baseM×baseK、baseN×baseK 单块上界mergeBatchL0 合并 batch 数的上界</td></tr>
<tr><td>L0C</td><td><b>256KB</b> / AIC较上代增大白皮书明言"提供更灵活的 Tiling 策略"</td><td>fp32 累加baseM×baseN×4B×DB ≤ 256KBiterbatch 的 batchOutNum 由它决定</td></tr>
<tr><td>UB</td><td><b>512KB</b> / AIV32B 对齐)</td><td>AIV 分支TO_MUL单轮驻留 batch 数 = UB / 单 batch 字节数</td></tr>
<tr><td>L2 Cache</td><td><b>128MB</b>(降配 112MB全局统一</td><td>滑窗/对角错位 swizzle 的收益来源:让并发核访问的 A/B 块在 L2 命中</td></tr>
<tr><td>片上内存</td><td>128GB / <b>1.6TB/s</b>PR 降配 1.4TB/sDT 为 4TB/s</td><td>PR 算存比高 → 偏计算受限 → "减少 HBM 搬运"类分支L1 全载、iterbatch收益更大</td></tr>
<tr><td>数据通路351x 变化)</td><td>新增 L0C→UB、UB↔L1、AIV UB↔L1 硬通道、SSBuffer 核间通信;<b>删除 GM→L0 直通与 L1→GM</b></td><td>所有数据必须经 L1 中转CV 协同AIC 算 + AIV 后处理)有硬化通路支撑</td></tr>
<tr><td>Fixpipe</td><td>L0C→GM/UB 随路量化/转置NZ2ND 等、ReLU 融合</td><td>tiling_key 的 L0C2OUT_MODEL 位段ON_THE_FLY / ND_FIXPIPE_1_1 / 1_2</td></tr>
<tr><td>NDDMA</td><td>AI Core 内置 N 维 DMA≤5 维重排随搬运完成</td><td>TENSOR_LEVEL张量级 API分支的硬件基础</td></tr>
<tr><td>核间同步</td><td>CrossCoreSetFlag/WaitFlag支持 AIC:AIV = 1:1 / 1:2</td><td>MIX_AIC_1_2 kernel 类型StreamK、fixpipe 1V2 epilogue的同步机制</td></tr>
</table>
<h3>2.2 规格 → 设计约束的映射</h3>
<ul class="tight">
<li><b>Cube 基本块 16×16×16 + L0 512B 对齐</b> → baseM/baseN 16 对齐、baseK 按内轴 128B/dtype 对齐,是几乎所有分支的默认对齐约束;</li>
<li><b>L1=512KB、L0A/B=64KB、L0C=256KB</b> → 「单 batch 的 A+B 能否 DB 驻留 L1」「L0 能否同时放多 batch」是 iterbatch 系分支的定量判据「A(或B) 全量 ×2(DB) ≤ L1」是 L1 全载分支的定量判据;</li>
<li><b>AIC 只有 32 核</b> → 当 batch×mCnt×nCnt &lt; aicNum/2 时核大量闲置 → 需要 StreamK 沿 K 拆出并行度;当 batch ≫ aicNum 时则反过来把 batch 当主并行维度iterbatch/mergebatch</li>
<li><b>AIC/AIV 分离、Vector 每拍 256B</b> → K∈{0,1} 时 Cube 阵列无累加深度可用AIV 逐元素乘/清零吞吐更高 → 两个 AIV_ONLY 分支;</li>
<li><b>L2 128MB 全局共享</b> → 块到核的遍历顺序swizzle直接影响 L2 命中率 → ASW 滑窗 + 蛇形、对角错位分核等调度优化。</li>
</ul>
<div class="note"><b>950PR vs 950DT 的差异动机</b>PR 片上内存带宽 1.6TB/s 显著低于 DT 的 4TB/s而 BF16 算力接近486 vs 547 TFLOPS<b>PR 算存比更高、更偏计算受限</b>(其定位即 LLM Prefill/推荐等吞吐场景)。这解释了为什么 950 的 BMM 分支体系如此重视"数据驻留复用"L1 全载、iterbatch、mergebatch——在 PR 上减少一字节 HBM 搬运比在 DT 上更值钱。</div>
<!-- ============================== 3 ============================== -->
<h2 id="sec3">3. Tiling 总体框架:入口、平台分流与分支短路遍历</h2>
<h3>3.1 入口与平台分流</h3>
<pre><code>// op_host/op_tiling/batch_mat_mul_v3_tiling.cpp
static ge::graphStatus BatchMatMulV3TilingFunc(gert::TilingContext* context) {
if (IsAdvancedSocVersion(context)) { // DAV_3510(950) / DAV_RESV(s8s4保留平台)
return batch_matmul_v3_advanced::BatchMatMulV3Tiling(context).DoTiling(); // arch35 高级 tiling
}
return TilingRegistry::GetInstance().DoTilingImpl(context); // 老路径 BatchMatmulV3BaseTiling
}
IMPL_OP_OPTILING(BatchMatMulV3)
.Tiling(BatchMatMulV3TilingFunc)
.TilingParse&lt;MatmulV3CompileInfo&gt;(TilingPrepareForBatchMatMulV3) // 提取平台规格
.GenSimplifiedKey(GenSimplifiedKey);</code></pre>
<p><code>TilingPrepareForBatchMatMulV3</code> 在编译期从平台信息提取 <code>MatmulV3CompileInfo</code>aicNum/aivNum、L1/L0A/L0B/L0C/L2/UB 容量、<code>supportL0c2out</code>fixpipe<code>supportL12BtBf16</code>、btSize1024/4096、npuArch——<b>所有分支的定量条件都建立在这组平台参数之上</b>,同一套代码因此能适配 950 满配/降配及其他 351x 产品。</p>
<h3>3.2 arch35 主调用链</h3>
<div class="flow">BatchMatMulV3TilingFunc
└─ BatchMatMulV3Tiling::DoTiling() [batch_matmul_v3_tiling_advanced.*, 继承 MatMulV3Tiling]
├─ GetShapeAttrsInfo / CheckArgs / GetArgs / Validate* M/N/K、转置、dtype、format 校验)
├─ ExtractMatrixBatchInfo() 提取 4 级 batchbatchA0~A3 / B0~B3 / C0~C3
├─ ValidateMatrixBatchInfo() 广播规则校验 + ★MergeBatchAndMAxis()
│ 当 batchB==1 且 A 不转置 → mValue = batchA*mValuebatch 折叠进 M 轴BMM→MM 降维)
├─ Extract/ValidateOptionalBatchInfo() bias batch
└─ MMTilingRegistry::DoTilingImpl(opType="BatchMatMulV3", npuArch, priorities)
└─ 按优先级表逐个构造策略类 → DoTiling()
GetShapeAttrsInfo → IsCapable() → DoOpTiling() → AdjustOpTiling() → PostTiling()
★ IsCapable()==false → GRAPH_PARAM_INVALID → 试下一个分支
★ 任一分支 GRAPH_SUCCESS → 立即短路返回</div>
<h3>3.3 分支优先级表DAV_3510</h3>
<p>定义于 <code>arch35/batch_matmul_v3_tiling_strategy.h</code></p>
<table>
<tr><th>优先级</th><th>strategy 常量</th><th>策略类</th><th>一句话定位</th><th>核类型</th></tr>
<tr><td>0</td><td>BATCH_MATMUL_INPUT_K_EQUAL_ZERO</td><td>BatchMatMulV3KEqZeroTiling</td><td>K=0输出置零</td><td><span class="tag aiv">AIV_ONLY</span></td></tr>
<tr><td>1</td><td>BATCH_MATMUL_TO_MUL</td><td>BatchMatMulV3Matmul2MulTiling</td><td>K=1退化为逐元素乘</td><td><span class="tag aiv">AIV_ONLY</span></td></tr>
<tr><td>2</td><td>BATCH_STREAM_K</td><td>BatchMatMulV3BasicStreamKTiling</td><td>K 巨大且 MN 并行度 &lt; 一半核数:切 K</td><td><span class="tag mix">MIX_AIC_1_2</span></td></tr>
<tr><td>3</td><td>MERGE_BATCH_BASICAPI</td><td>BatchMatMulV3MergeBatchBasicApiTiling</td><td>小 M/N、巨大 batch多 batch 合并进 L0</td><td><span class="tag">AIC_ONLY</span></td></tr>
<tr><td>4</td><td>ITER_BATCH_BROADCAST_BASICAPI</td><td>BatchMatMulV3IterbatchBroadcastBasicApiTiling</td><td>单边单轴 batch 广播</td><td><span class="tag">AIC_ONLY</span></td></tr>
<tr><td>5</td><td>ITER_BATCH_BASICAPI</td><td>BatchMatMulV3IterBatchBasicApiTiling</td><td>batch 相等且 &gt; 核数L1/L0 多 batch 流水</td><td><span class="tag">AIC_ONLY</span>/<span class="tag mix">MIX_AIC_1_2</span></td></tr>
<tr><td>6</td><td>ITER_BATCH</td><td>BatchMatMulV3IterBatchTiling</td><td>同上但高阶 APIIterateBatch</td><td><span class="tag">AIC_ONLY</span></td></tr>
<tr><td>7</td><td>AL1_FULL_LOAD_BASIC</td><td>BatchMatMulV3AswAL1FullLoadBasicTiling</td><td>A 无 batch 且 M≤256A 全量常驻 L1</td><td><span class="tag">AIC_ONLY</span></td></tr>
<tr><td>8</td><td>BL1_FULL_LOAD_BASIC</td><td>BatchMatMulV3AswBL1FullLoadBasicTiling</td><td>B 无 batch 且 N≤256B 全量常驻 L1</td><td><span class="tag">AIC_ONLY</span></td></tr>
<tr><td>9</td><td>ASW_BASIC</td><td>BatchMatMulV3AswBasicTiling</td><td>通用基础 API + cubeBound 模型寻优</td><td><span class="tag">AIC_ONLY</span></td></tr>
<tr><td>999</td><td>BASE</td><td>BatchMatMulV3AswTiling</td><td>最终兜底(高阶/张量级 APIIsCapable 恒 true</td><td><span class="tag">AIC_ONLY</span></td></tr>
</table>
<p class="src">DAV_RESVs8s4 保留平台仅有ITER_BATCH_BASICAPI → AL1_FULL_LOAD_BASIC → BL1_FULL_LOAD_BASIC → ASW_BASIC → BASE。</p>
<div class="note"><b>遍历顺序的设计逻辑</b>(短路语义下,顺序 = 特判优先级):
<ol class="tight">
<li><b>计算模式彻底改变的特判最先</b>K=0/K=1它们根本不需要 Cube若被后面的 cube 模板接住就是纯浪费,必须最先拦截;</li>
<li><b>改变并行结构的分支次之</b>StreamK它对"核怎么分"做全局性改变(沿 K 拆核 + workspace 归约),要在 batch 优化之前决策;</li>
<li><b>batch 维度优化居中</b>mergebatchL0 复用最高,条件最苛刻)→ 单边广播(复用次高)→ 普通 iterbatch 基础 API → 高阶 API iterbatch</li>
<li><b>数据驻留优化靠后</b>AL1/BL1 全载):消除单边重复搬运,是通用路径上的加强;</li>
<li><b>通用模板垫底</b>ASW_BASIC → BASE=999保证任意合法输入必然 tiling 成功。999 的大数值确保永远最后尝试。</li></ol></div>
<!-- ============================== 4 ============================== -->
<h2 id="sec4">4. 为什么是这些分支:从 case 空间到分支全集的系统性论证</h2>
<p>BMM 的 case 空间可由 5 个近似正交的特征维度刻画。<b>每个分支恰好占据其中一个特征区域,且这些区域合起来覆盖了全部合法输入</b>——这就是"为什么是这些分支"的系统回答:</p>
<table>
<tr><th>特征维度</th><th>取值区域</th><th>微架构矛盾</th><th>对应分支</th></tr>
<tr><td rowspan="2">① K 的退化程度</td><td>K=0</td><td>无计算Cube 阵列完全无用</td><td>K_EQUAL_ZEROAIV 清零)</td></tr>
<tr><td>K=1</td><td>无累加深度mmad 16×16×16 粒度浪费 15/16</td><td>TO_MULAIV 向量乘)</td></tr>
<tr><td>② MN×batch 并行度 vs 核数</td><td>batchC×mCnt×nCnt ≤ aicNum/2 且 K 巨大</td><td>32 个 AIC 大半闲置,唯一剩余并行维度是 K</td><td>STREAM_K切 K + 部分和归约)</td></tr>
<tr><td rowspan="3">③ batch 量级与 M/N 大小</td><td>M,N 很小M≤N、K≥64、batchC ≥ 4×aicNum</td><td>单 batch 基本块喂不饱 L0如 16×16mmad 效率极低</td><td>MERGE_BATCH多 batch 拼成大 L0 块)</td></tr>
<tr><td>batchC &gt; aicNumM/N/K 正常</td><td>每核要算多个 batch逐 batch 重复搬 A/B 浪费 MTE</td><td>ITER_BATCH_BASICAPI / ITER_BATCHL1/L0 一次驻留多 batch 流水)</td></tr>
<tr><td>batch ≤ 核数量级M/N 大</td><td>batch 不构成主并行维度,走 M/N 基本块切分</td><td>ASW_BASIC / BASE</td></tr>
<tr><td>④ batch 广播形态</td><td>恰好单边、单轴广播</td><td>被广播算子的单 batch 数据可被对端多 batch 复用</td><td>ITER_BATCH_BROADCAST广播侧 L1 驻留一份)</td></tr>
<tr><td rowspan="2">⑤ 单边无 batch 且该边很小</td><td>batchA=1 且 M≤256B 大)</td><td>A 全量可常驻 L1消除全部重复搬运</td><td>AL1_FULL_LOAD</td></tr>
<tr><td>batchB=1 且 N≤256A 大)</td><td>对称A 不转置时已被 MergeBatchAndMAxis 折叠成 MM</td><td>BL1_FULL_LOAD</td></tr>
</table>
<p><b>完备性的结构性保证</b>优先级表尾部是无条件分支BASE=999 的 IsCapable 恒 trueASW_BASIC 仅校验 dtype/batch 一致性),因此任何通过 Validate 的输入必然命中某一分支——不存在"所有分支都不收"的 case。而前 10 个分支的关系是<b>"特判 ⊂ 通用"</b>:每个特判分支覆盖的 case 落入通用分支也能跑,只是性能更差;优先级顺序保证收益最大的实现先被选中。这与"性能最优分支集必须是完备分支全集子集"的方法论一致:这里完备全集 = 通用 cube 模板BASE特判分支是它在各特征区域上的性能占优替代。</p>
<div class="warn"><b>一个容易忽略的降维优化</b><code>ValidateMatrixBatchInfo</code> 阶段的 <code>MergeBatchAndMAxis()</code> 在 batchB==1 且 A 不转置时直接把 batchA 折叠进 M 轴(<code>mValue = batchA * mValue</code>batch 置 1把 BMM 变成单个大 M 的 MatMul——此时后续所有 batch 类分支都不会命中case 直接落到 ASW/AL1 全载体系。这说明分支选择不是纯"分诊",前面还有一层<b>问题归约</b></div>
<!-- ============================== 5 ============================== -->
<h2 id="sec5">5. 逐分支详解</h2>
<p>本章对每个分支按统一结构展开:<b>目标 case → 进入条件逐条解析IsCapable含每个阈值的取值依据→ tiling 实现步骤DoOpTiling 编号步骤,含公式)→ 关键参数的"为什么"分析 → kernel 实现</b>。所有条件表达式、常量、注释均直接引用源码(文件:<code>op_host/op_tiling/arch35/batch_matmul_v3_*_tiling.cpp</code> 及 mat_mul_v3 公共实现),微架构依据标注知识库文档篇目;<b>官方文档没有依据、仅为源码经验值的参数会明确标注</b></p>
<h3>5.0 总览:分支决策流程与两条数据通路</h3>
<p>arch35 tiling 按优先级 0→9→999 短路遍历,每个分支的 IsCapable 是一组"与"条件。整体决策流如下(条件为简化表述,精确表达式见各分支小节):</p>
<div class="figbox">
<svg viewBox="0 0 980 660" xmlns="http://www.w3.org/2000/svg" font-family="PingFang SC,Microsoft YaHei,sans-serif" font-size="12">
<defs>
<marker id="ar" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" fill="#5f6b7a"/></marker>
</defs>
<style>
.dec{fill:#fff7e6;stroke:#d4a017;stroke-width:1.2}
.br{fill:#eaf2fb;stroke:#0b6bcb;stroke-width:1.2}
.aiv{fill:#e6f7ee;stroke:#0e9f6e;stroke-width:1.2}
.mix{fill:#f3e8fd;stroke:#6d28a8;stroke-width:1.2}
.no{fill:none;stroke:#5f6b7a;stroke-width:1.1}
.yes{fill:none;stroke:#c0392b;stroke-width:1.3}
.lb{font-size:11px;fill:#5f6b7a}
.lby{font-size:11px;fill:#c0392b}
</style>
<rect x="20" y="8" width="180" height="30" rx="6" fill="#0b3d73"/><text x="110" y="27" fill="#fff" text-anchor="middle">tiling 输入shape/dtype/batch</text>
<!-- decision chain -->
<polygon class="dec" points="110,58 230,86 110,114 -10,86" transform="translate(60,0)"/><text x="170" y="90" text-anchor="middle">K == 0 ?</text>
<polygon class="dec" points="110,58 250,86 110,114 -30,86" transform="translate(60,58)"/><text x="170" y="148" text-anchor="middle">K == 1 且 batch≥128 ?</text>
<polygon class="dec" points="110,58 270,86 110,114 -50,86" transform="translate(60,116)"/><text x="170" y="206" text-anchor="middle">b×mCnt×nCnt ≤ aic/2 且 K 巨大 ?</text>
<polygon class="dec" points="110,58 285,86 110,114 -65,86" transform="translate(60,174)"/><text x="170" y="264" text-anchor="middle">batch ≥ 4×aic 且 M≤N 小、K≥64 ?</text>
<polygon class="dec" points="110,58 265,86 110,114 -45,86" transform="translate(60,232)"/><text x="170" y="322" text-anchor="middle">恰好单边单轴 batch 广播 ?</text>
<polygon class="dec" points="110,58 275,86 110,114 -55,86" transform="translate(60,290)"/><text x="170" y="380" text-anchor="middle">batch 相等 &gt; aic 且 L1 可载 ?</text>
<polygon class="dec" points="110,58 280,86 110,114 -60,86" transform="translate(60,348)"/><text x="170" y="438" text-anchor="middle">batchA=1 且 M≤256 且 A 可全载 ?</text>
<polygon class="dec" points="110,58 280,86 110,114 -60,86" transform="translate(60,406)"/><text x="170" y="496" text-anchor="middle">batchB=1 且 N≤256 且 B 可全载 ?</text>
<polygon class="dec" points="110,58 255,86 110,114 -35,86" transform="translate(60,464)"/><text x="170" y="554" text-anchor="middle">batch 相等、AB转置连续态一致 ?</text>
<!-- vertical no-chain -->
<line class="no" x1="170" y1="38" x2="170" y2="58" marker-end="url(#ar)"/>
<line class="no" x1="170" y1="114" x2="170" y2="116" /><line class="no" x1="170" y1="114" x2="170" y2="144" marker-end="url(#ar)"/>
<line class="no" x1="170" y1="172" x2="170" y2="202" marker-end="url(#ar)"/>
<line class="no" x1="170" y1="230" x2="170" y2="260" marker-end="url(#ar)"/>
<line class="no" x1="170" y1="288" x2="170" y2="318" marker-end="url(#ar)"/>
<line class="no" x1="170" y1="346" x2="170" y2="376" marker-end="url(#ar)"/>
<line class="no" x1="170" y1="404" x2="170" y2="434" marker-end="url(#ar)"/>
<line class="no" x1="170" y1="462" x2="170" y2="492" marker-end="url(#ar)"/>
<line class="no" x1="170" y1="520" x2="170" y2="550" marker-end="url(#ar)"/>
<text class="lb" x="178" y="140"></text><text class="lb" x="178" y="198"></text><text class="lb" x="178" y="256"></text><text class="lb" x="178" y="314"></text><text class="lb" x="178" y="372"></text><text class="lb" x="178" y="430"></text><text class="lb" x="178" y="488"></text><text class="lb" x="178" y="546"></text>
<!-- branch boxes -->
<rect class="br aiv" x="560" y="70" width="400" height="34" rx="6"/><text x="570" y="84"><tspan font-weight="bold">分支0 K_EQUAL_ZEROAIV</tspan></text><text x="570" y="98" class="lb">输出=零矩阵AIV InitOutput 直写 GM</text>
<rect class="br aiv" x="560" y="128" width="400" height="34" rx="6"/><text x="570" y="142"><tspan font-weight="bold">分支1 TO_MULAIV</tspan></text><text x="570" y="156" class="lb">K=1 退化向量乘UB 多 batch pingpong</text>
<rect class="br mix" x="560" y="186" width="400" height="34" rx="6"/><text x="570" y="200"><tspan font-weight="bold">分支2 STREAM_KAIC+AIV 1:2</tspan></text><text x="570" y="214" class="lb">沿 K 切给空闲核算部分和workspace 归约</text>
<rect class="br" x="560" y="244" width="400" height="34" rx="6"/><text x="570" y="258"><tspan font-weight="bold">分支3 MERGE_BATCH</tspan></text><text x="570" y="272" class="lb">多 batch 合并成大 L0 块,喂饱 cube</text>
<rect class="br" x="560" y="302" width="400" height="34" rx="6"/><text x="570" y="316"><tspan font-weight="bold">分支4 ITER_BATCH_BROADCAST</tspan></text><text x="570" y="330" class="lb">广播侧 L1 驻留一份,对端多 batch 流水</text>
<rect class="br" x="560" y="360" width="400" height="34" rx="6"/><text x="570" y="374"><tspan font-weight="bold">分支5/6 ITER_BATCH基础API/高阶API</tspan></text><text x="570" y="388" class="lb">L1/L0 一次驻留多 batchIterateBatch 流水</text>
<rect class="br" x="560" y="418" width="400" height="34" rx="6"/><text x="570" y="432"><tspan font-weight="bold">分支7 AL1_FULL_LOAD</tspan></text><text x="570" y="446" class="lb">A 全量常驻 L1整个 kernel 只搬一次</text>
<rect class="br" x="560" y="476" width="400" height="34" rx="6"/><text x="570" y="490"><tspan font-weight="bold">分支8 BL1_FULL_LOAD</tspan></text><text x="570" y="504" class="lb">B 全量常驻 L1与分支7 镜像)</text>
<rect class="br" x="560" y="534" width="400" height="34" rx="6"/><text x="570" y="548"><tspan font-weight="bold">分支9 ASW_BASIC</tspan></text><text x="570" y="562" class="lb">cubeBound 模型寻优 baseM/baseN + 滑窗调度</text>
<rect class="br" x="330" y="600" width="400" height="34" rx="6"/><text x="340" y="614"><tspan font-weight="bold">分支999 BASEIsCapable 恒 true最终兜底</tspan></text><text x="340" y="628" class="lb">默认 256×256×(128B/dtype) 基块 + CalL1TilingTENSOR_LEVEL 时支持任意广播</text>
<!-- yes arrows -->
<line class="yes" x1="280" y1="86" x2="556" y2="86" marker-end="url(#ar)"/><text class="lby" x="420" y="80"></text>
<line class="yes" x1="310" y1="144" x2="556" y2="144" marker-end="url(#ar)"/><text class="lby" x="420" y="138"></text>
<line class="yes" x1="330" y1="202" x2="556" y2="202" marker-end="url(#ar)"/><text class="lby" x="430" y="196"></text>
<line class="yes" x1="345" y1="260" x2="556" y2="260" marker-end="url(#ar)"/><text class="lby" x="440" y="254"></text>
<line class="yes" x1="325" y1="318" x2="556" y2="318" marker-end="url(#ar)"/><text class="lby" x="430" y="312"></text>
<line class="yes" x1="335" y1="376" x2="556" y2="376" marker-end="url(#ar)"/><text class="lby" x="430" y="370"></text>
<line class="yes" x1="340" y1="434" x2="556" y2="434" marker-end="url(#ar)"/><text class="lby" x="435" y="428"></text>
<line class="yes" x1="340" y1="492" x2="556" y2="492" marker-end="url(#ar)"/><text class="lby" x="435" y="486"></text>
<line class="yes" x1="315" y1="550" x2="556" y2="550" marker-end="url(#ar)"/><text class="lby" x="425" y="544"></text>
<line class="no" x1="170" y1="578" x2="170" y2="617" marker-end="url(#ar)"/><line class="no" x1="170" y1="617" x2="326" y2="617" marker-end="url(#ar)"/><text class="lb" x="178" y="610"></text>
</svg>
<div class="figcap">图 5-1 arch35DAV_3510分支短路决策流程。菱形为各分支 IsCapable 的核心判据(简化);任一分支命中即返回,不再尝试后续分支</div>
</div>
<p>理解各分支前,先建立 950PR 上的两条数据通路模型——所有分支的本质都是在这两条通路上做不同的驻留/复用/并行安排:</p>
<div class="figbox">
<svg viewBox="0 0 980 300" xmlns="http://www.w3.org/2000/svg" font-family="PingFang SC,Microsoft YaHei,sans-serif" font-size="12">
<defs><marker id="ar2" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" fill="#0b6bcb"/></marker></defs>
<!-- Cube lane -->
<text x="20" y="24" font-weight="bold" fill="#0b3d73">通路 ACube 通路AIC分支 2~9/999</text>
<rect x="20" y="40" width="90" height="44" rx="6" fill="#fdeee2" stroke="#b45309"/><text x="65" y="58" text-anchor="middle">GM</text><text x="65" y="74" text-anchor="middle" font-size="10" fill="#5f6b7a">HBM 1.6TB/s</text>
<rect x="150" y="40" width="110" height="44" rx="6" fill="#eaf2fb" stroke="#0b6bcb"/><text x="205" y="58" text-anchor="middle">L1 512KB</text><text x="205" y="74" text-anchor="middle" font-size="10" fill="#5f6b7a">MTE2 搬运DB/4buffer</text>
<rect x="300" y="40" width="130" height="44" rx="6" fill="#eaf2fb" stroke="#0b6bcb"/><text x="365" y="52" text-anchor="middle">L0A/L0B 64KB</text><text x="365" y="66" text-anchor="middle" font-size="10" fill="#5f6b7a">MTE1512B对齐</text><text x="365" y="79" text-anchor="middle" font-size="10" fill="#5f6b7a">fractal 16×16</text>
<rect x="470" y="40" width="110" height="44" rx="6" fill="#e3ecf7" stroke="#0b3d73"/><text x="525" y="58" text-anchor="middle">Cube 阵列</text><text x="525" y="74" text-anchor="middle" font-size="10" fill="#5f6b7a">一拍 16×16×16</text>
<rect x="620" y="40" width="120" height="44" rx="6" fill="#eaf2fb" stroke="#0b6bcb"/><text x="680" y="58" text-anchor="middle">L0C 256KB</text><text x="680" y="74" text-anchor="middle" font-size="10" fill="#5f6b7a">fp32 累加,可驻留累加</text>
<rect x="780" y="40" width="90" height="44" rx="6" fill="#e6f7ee" stroke="#0e9f6e"/><text x="825" y="58" text-anchor="middle">fixpipe</text><text x="825" y="74" text-anchor="middle" font-size="10" fill="#5f6b7a">NZ2ND/量化随路</text>
<rect x="890" y="40" width="70" height="44" rx="6" fill="#fdeee2" stroke="#b45309"/><text x="925" y="66" text-anchor="middle">GM</text>
<line x1="110" y1="62" x2="146" y2="62" stroke="#0b6bcb" stroke-width="1.5" marker-end="url(#ar2)"/>
<line x1="260" y1="62" x2="296" y2="62" stroke="#0b6bcb" stroke-width="1.5" marker-end="url(#ar2)"/>
<line x1="430" y1="62" x2="466" y2="62" stroke="#0b6bcb" stroke-width="1.5" marker-end="url(#ar2)"/>
<line x1="580" y1="62" x2="616" y2="62" stroke="#0b6bcb" stroke-width="1.5" marker-end="url(#ar2)"/>
<line x1="740" y1="62" x2="776" y2="62" stroke="#0b6bcb" stroke-width="1.5" marker-end="url(#ar2)"/>
<line x1="870" y1="62" x2="886" y2="62" stroke="#0b6bcb" stroke-width="1.5" marker-end="url(#ar2)"/>
<path d="M 680 84 C 680 120, 525 120, 525 88" fill="none" stroke="#0e9f6e" stroke-width="1.3" stroke-dasharray="4 3" marker-end="url(#ar2)"/>
<text x="600" y="135" font-size="11" fill="#0e9f6e">L0C 驻留累加K 循环不回 GM文档191篇</text>
<text x="110" y="110" font-size="11" fill="#5f6b7a">351x 已删除 GM→L0 直通,所有数据必须经 L1 中转054篇</text>
<!-- Vector lane -->
<text x="20" y="176" font-weight="bold" fill="#0e6e4c">通路 BVector 通路AIV分支 0/1老架构 GEMV</text>
<rect x="20" y="192" width="90" height="44" rx="6" fill="#fdeee2" stroke="#b45309"/><text x="65" y="218" text-anchor="middle">GM</text>
<rect x="150" y="192" width="130" height="44" rx="6" fill="#e6f7ee" stroke="#0e9f6e"/><text x="215" y="210" text-anchor="middle">UB 512KB</text><text x="215" y="226" text-anchor="middle" font-size="10" fill="#5f6b7a">多 batch 驻留 pingpong</text>
<rect x="320" y="192" width="150" height="44" rx="6" fill="#e6f7ee" stroke="#0e9f6e"/><text x="395" y="210" text-anchor="middle">Vector 单元</text><text x="395" y="226" text-anchor="middle" font-size="10" fill="#5f6b7a">每拍 256BMul / InitOutput</text>
<rect x="510" y="192" width="70" height="44" rx="6" fill="#fdeee2" stroke="#b45309"/><text x="545" y="218" text-anchor="middle">GM</text>
<line x1="110" y1="214" x2="146" y2="214" stroke="#0e9f6e" stroke-width="1.5" marker-end="url(#ar2)"/>
<line x1="280" y1="214" x2="316" y2="214" stroke="#0e9f6e" stroke-width="1.5" marker-end="url(#ar2)"/>
<line x1="470" y1="214" x2="506" y2="214" stroke="#0e9f6e" stroke-width="1.5" marker-end="url(#ar2)"/>
<text x="600" y="218" font-size="11" fill="#5f6b7a">不经过 L1/L0/Cube64 个 AIV 纯向量流水</text>
</svg>
<div class="figcap">图 5-2 昇腾 950PR 上 BMM 的两条数据通路。分支 0/1 走通路 B其余走通路 A各分支的差异在于"哪一级驻留什么、驻留多少、并行维度选谁"</div>
</div>
<div class="branch">
<h3 id="b0">5.1 K_EQUAL_ZERO优先级 0—— K=0输出清零 <span class="tag aiv">AIV_ONLY</span></h3>
<p class="src">tilingarch35/batch_matmul_v3_k_equal_zero_tiling.cpp68 行kernelmat_mul_v3/arch35/mat_mul_input_k_eq_zero_clear_output.h</p>
<h4>① 目标 case</h4>
<p>K 轴长度为 0 的退化 BMM。语义上 C = 对空 K 维求和 = 全零矩阵,<b>没有任何乘加计算</b></p>
<h4>② 进入条件IsCapablecpp L27-41逐条</h4>
<table>
<tr><th>#</th><th>条件(源码表达式)</th><th>取值依据 / 源码注释</th></tr>
<tr><td>1</td><td><code>aFormat == FORMAT_FRACTAL_NZ || bFormat == FORMAT_FRACTAL_NZ</code> → 拒绝</td><td>注释:<i>"The NZ format is not supported in this strategy."</i> 清零 kernel 是纯 AIV vector kernel只吃 ND 连续布局NZ 分形格式对它无意义</td></tr>
<tr><td>2</td><td><code>hasBias</code> → 拒绝</td><td>有 bias 时输出应为 bias 广播值而非 0清零语义不成立</td></tr>
<tr><td>3</td><td><code>kValue != 0</code> → 拒绝</td><td>本分支唯一触发条件K==0</td></tr>
</table>
<h4>③ tiling 实现步骤DoOpTilingcpp L43-48</h4>
<ol class="tight">
<li><code>totalDataAmount = m × n × batchC</code> —— 输出总元素量作为负载均分指标本分支不再细分单核任务kernel 侧按总量均分);</li>
<li><code>usedCoreNum = aivNum</code> —— <b>用全部 64 个 AIV</b> 而非 32 个 AIC。<b>为什么</b>:清零是纯 vector 写操作Cube 阵列全程空转950 的 AIC/AIV 是分离架构、各有独立指令流,用 AIV 做这件事 AIC 可以完全不上电执行;且 AIV 核数是 AIC 的 2 倍memset 类带宽型任务并行度更高。<code>GetNumBlocks()</code> 同样返回 aivNum。</li>
</ol>
<h4>④ kernel 实现</h4>
<p><code>MatMulInputKEqZeroClearOutput</code><code>if ASCEND_IS_AIC { return; }</code>AIC 直接退出);每个 AIV 对均分的输出区间调用 <code>AscendC::InitOutput&lt;DTYPE_Y&gt;(outputGM[...], copyDataAmount, 0)</code> 向 GM 写 0尾区间用 tailDataCount 处理。数据通路即图 5-2 通路 B 的极简形态(连 UB 都不需要InitOutput 直接铺 GM</p>
<p><b>为什么要单列这个分支</b>K=0 在 shape 校验中是合法输入(<code>k∈[0, INT32_MAX]</code>),若不放行会被通用 cube 模板接住——白白建立 GM→L1→L0 流水做"0 深度累加",既浪费 AIC 又拖慢端到端时延。作为计算模式彻底改变的特判它必须排在最高优先级拦截。tilingKeyBASIC_LEVEL + BATCH_MODEL + <b>Model=K_EQUAL_ZERO</b> + NONE_FULL_LOAD + ON_THE_FLY。</p>
</div>
<div class="branch">
<h3 id="b1">5.2 TO_MUL / matmul2mul优先级 1—— K=1 退化为向量乘 <span class="tag aiv">AIV_ONLY</span></h3>
<p class="src">tilingarch35/batch_matmul_v3_matmul2mul_tiling.cpp133 行kernelbatch_mat_mul_v3_matmul2mul_cmct.h + common/cmct/kernel/kernel_batch_matmul_to_mul.h</p>
<h4>① 目标 case</h4>
<p>K==1 的 BMM<code>C[m,n] = A[m,0] × B[0,n]</code>,矩阵乘退化为列向量⊗行向量的外积,逐元素乘即可,<b>K 维无累加深度</b></p>
<h4>② 进入条件IsCapablecpp L67-115逐条</h4>
<table>
<tr><th>#</th><th>条件(源码表达式)</th><th>取值依据(源码注释原文 / 分析)</th></tr>
<tr><td>1</td><td>输入 0/1 为"非连续转置" → 拒绝</td><td>注释:<i>"Non-contiguous transpose does not support Matmul2Mul."</i> mul kernel 按连续内存寻址,不支持 view 转置的 stride 访问</td></tr>
<tr><td>2</td><td><code>aType != bType || aType != cType</code> → 拒绝</td><td>注释:<i>"Inconsistent a/b/c data types are not supported"</i> 向量路径没有类型提升逻辑,三端必须同型</td></tr>
<tr><td>3</td><td>NZ 格式 → 拒绝</td><td>同分支 0纯 vector kernel 只吃 ND</td></tr>
<tr><td>4</td><td>A/B 四级 batch 任一不等 → 拒绝</td><td>mul 是逐 batch 对应元素相乘,<b>不支持 broadcast</b>(广播 case 留给通用分支的 % 映射)</td></tr>
<tr><td>5</td><td><code>batchC &lt; 128</code> → 拒绝</td><td>注释原文:<i>"batch数大于等于128(AIV CoreNum *2) 才能开pingpong"</i><b>128 = aivNum(64) × 2</b>:每核至少分到 2 个 batch才能一份在算、一份在搬构成 UB pingpong 流水</td></tr>
<tr><td>6</td><td><code>hasBias</code> → 拒绝</td><td>bias 加法未在 mul kernel 实现</td></tr>
<tr><td>7</td><td><code>kValue != 1</code> → 拒绝</td><td>触发条件K==1</td></tr>
<tr><td>8</td><td><code>n &gt; 32B/dtype &amp;&amp; n &lt;= 256B/dtype</code> → 拒绝</td><td>注释原文:<i>"N&gt;256/DtypeSize才能用满Vector的计算能力小N走特殊优化"</i>。Vector 一拍处理 256B054篇N&gt;256B/dtype 时一次 Mul 指令打满吞吐N≤32B/dtype不足一个 32B block走 DoOpTiling 的小 N 特殊优化公式;<b>中间区</b> (32B, 256B]/dtype 两头不占,让给其它分支</td></tr>
<tr><td>9</td><td><code>n == 1</code> → 拒绝</td><td>注释原文:<i>"N=1时部分case劣化"</i>——实测劣化的经验排除项</td></tr>
<tr><td>10</td><td><code>(alignM + alignN + alignM×alignN)×dtype &gt; ubSize</code> → 拒绝</td><td>UB 容量校验:单 batch 要同时驻留 A 向量alignM 元素、B 行alignN、C 外积结果alignM×alignN三者放不下 UB512KB则该分支不可行。alignNum_=32B/dtype 是 vector 一次访存的最小对齐粒度</td></tr>
<tr><td>11</td><td><code>n % (256B/dtype) == 0</code> → 拒绝</td><td>N 恰为 256B/dtype 整数倍属规整形状,存在更优的规整路径,本策略主动让出(防重复覆盖)</td></tr>
</table>
<h4>③ tiling 实现步骤DoOpTilingcpp L29-65</h4>
<ol class="tight">
<li>对齐准备:<code>alignM = CeilAlign(m, 32B/dtype)</code><code>alignN = CeilAlign(n, 32B/dtype)</code><code>usedCoreNum = aivNum</code></li>
<li><code>singleBatchSize = (m + alignN + m×alignN) × dtypeSize</code> —— 单 batch 占 UB 字节数A + B + C 三段);<b>小 N 特殊优化</b>:若 <code>alignN ≤ 32B/dtype</code>(不足一条向量指令),改用 <code>(alignM + alignN + alignM×alignN) × dtypeSize</code>A/C 也按对齐块整块处理;</li>
<li><code>singleCoreBatch = CeilDiv(batchC, aivNum)</code> —— 每核应分 batch 数(负载均衡基准);</li>
<li><code>ubLimitBatchNum = ubSize / singleBatchSize</code> —— UB 容量允许的单轮驻留 batch 数;</li>
<li><code>batchNum = min(singleCoreBatch, ubLimitBatchNum)</code> —— <b>每核每轮实际处理 batch 数 = 负载均分与 UB 容量的较小者</b>。这个 min 是本分支的核心batchNum 决定 pingpong 的深度;</li>
<li>尾轮处理三连:<code>batchNumLastRound = singleCoreBatch % batchNum整除时为 batchNum</code><code>lastCoreNum = batchC % (batchNum×aivNum) / batchNumLastRound</code>(最后一轮满载核数);<code>batchNumLastRoundTail</code>(最后一个不满载核的 batch 数,恰整除时归并为满载核)——把"总 batch 不能整除 batchNum×核数"的余数精确拆成<b>若干满核 + 一个尾核</b>kernel 直接按这三参数索引,无需运行时再做除法。</li>
</ol>
<h4>④ 为什么 K=1 必须离开 Cube</h4>
<p>Cube 阵列一拍完成 16×16×16fp16的 fractal 乘加054篇。K=1 时,每次 mmad 的 K 维只有 1/16 被利用——<b>阵列 15/16 的 MAC 空转</b>,还要为此支付 GM→L1→L0A/L0B 的分形搬运、L0C 累加与 fixpipe 写出全链路开销,计算访存比极差。而 AIV 每拍处理 256B 连续数据,外积逐元素乘 + UB 多 batch pingpong 能把 MTE 带宽吃满64 个 AIV 的聚合向量算力FP16 54 TFLOPS对这个计算密度绰绰有余。<b>这就是为什么触发条件里 batchC≥128开 pingpong与 N&gt;256B/dtype打满向量指令都以"带宽/指令利用率"为判据</b>——该分支的收益模型是纯带宽模型,不是算力模型。</p>
<h4>⑤ kernel 实现</h4>
<p><code>KernelBatchMatMulToMul</code>Cmct 框架ProblemShape 的 k 直接写死 1<code>if ASCEND_IS_AIC { return; }</code>AIV 按 <code>BlockSchedulerBatchMatMulToMulBuiltIn</code> 给出的 usedCoreNum/singleCoreBatch/batchNum/lastCoreNum 做 batch 组轮询(<code>tileIdx += blockNum</code>),每组由 <code>BlockMmadBuilder&lt;..., BatchMatmulToMul&lt;&gt;&gt;</code> 生成的向量乘 block 处理。通路GM→UB→Mul→GM完全绕过 L1/L0/Cube图 5-2 通路 B。tilingKeyBASIC_LEVEL + <b>BatchModel=BATCH_MATMUL_TO_MUL</b>AIV_ONLY</p>
</div>
<div class="branch">
<h3 id="b2">5.3 STREAM_K优先级 2—— 超大 K + MN 并行度不足时切 K <span class="tag mix">MIX_AIC_1_2</span></h3>
<p class="src">tilingarch35/batch_matmul_v3_basic_streamk_tiling.cpp165 行kernelmat_mul_v3/arch35/mat_mul_streamk.hMatMulStreamKKernel / MatMulStreamKActKernel</p>
<h4>① 目标 case</h4>
<p><b>K 极大而 batch×M×N 基本块总数填不满 AIC</b> 的场景(典型:小 batch、小 M/N、超长 K。此时若只沿 batch/M/N 切分32 个 AIC 大半空转,唯一剩余的并行维度是 K。</p>
<h4>② 进入条件IsCapable cpp L70-98 + CheckStreamKSKTiling cpp L37-68逐条</h4>
<table>
<tr><th>#</th><th>条件(源码表达式)</th><th>取值依据(注释原文 / 分析)</th></tr>
<tr><td>1</td><td><code>GetDeterministicLevel() &gt; 1</code> → 拒绝</td><td>注释原文:<i>"batch一致性控制当开关等级为2或3时拒绝切k模板达到强一致性和batch一致性"</i>。切 K 后多核部分和的归约顺序不定,引入浮点累加非确定性;确定性等级 ≥2 的业务(如训练梯度对比)禁用</td></tr>
<tr><td>2</td><td>A/B 四级 batch 不等 → 拒绝</td><td>kernel 按等 batch 均摊核数blocksPerBatch = aicNum/batchC广播 batch 无法均摊</td></tr>
<tr><td>3</td><td><code>aFormat != FORMAT_ND</code> → 拒绝</td><td>注释:<i>"ND is the only supported format for tensor_a in basic api"</i></td></tr>
<tr><td>4</td><td><code>batchBias &gt; 1</code> → 拒绝</td><td>bias 多 batch 未支持</td></tr>
<tr><td>5</td><td>非连续转置 → 拒绝</td><td>注释:<i>"Non-contiguous transpose does not support StreamK."</i></td></tr>
<tr><td>6</td><td><code>aivNum != aicNum × 2</code> → 拒绝</td><td>注释:<i>"streamk only support aivNum == aicNum * 2"</i>。归约/fixpipe 后处理依赖 1 AIC : 2 AIV 的固定配比351x 硬件特征054篇非此配比平台直接拒绝</td></tr>
<tr><td>7</td><td>fp32 非 hf32 且 <code>k &gt; 2,000,000</code> → 拒绝</td><td>注释原文:<i>"如果dtype是fp32且k轴大于200万 则走基础模板来保证fp32的精度"</i><i>"Due to the requirement of binary accumulation..."</i>。fp32 超长 K 需要二叉树累加保精度StreamK 的串行归约不满足;<b>200 万是精度劣化可接受的经验上限(源码经验值,文档无依据)</b></td></tr>
<tr><td>8</td><td><code>CeilAlign(k,256) &lt; max(8192, aicNum×256B/dtype)</code> → 拒绝</td><td>K 下界双保险:<b>aicNum×256B/dtype</b> 保证"即使全部 AIC 都参与切 K每核也至少分到 256B一个 K 内轴搬运块 BASIC_BLOCK_K_256_BYTE",否则单核 K 段太短、搬运启动开销盖过收益;<b>8192</b> 是绝对收益门槛(元素数)。外层 CeilAlign(k,256) 先把 K 对齐到基本块再比较</td></tr>
<tr><td>9</td><td><code>batchC × CeilDiv(m,alignV) × CeilDiv(n,alignV) &gt; aicNum/2</code> → 拒绝</td><td>★ 核心判据。alignV=256fp32 非 hf32 时 32注释<i>"如果是Fp32 基本块判断要用32"</i>)。注释:<i>"判断bmn是否需要已经能切16份及以上"</i>。语义:<b>若 batch×M×N 已能切出超过"核数一半"的块MN 方向并行度已够</b>(留一半核作均衡余量),无需切 K反之才进 StreamK。此条件同时保证 DoOpTiling 中 mnCnt ≤ blocksPerBatch/2使 kCnt ≥ 2见步骤 4/7 推导)</td></tr>
</table>
<h4>③ tiling 实现步骤DoOpTilingcpp L100-138</h4>
<ol class="tight">
<li><code>ResetBase</code>DAV_3510 特化baseM=baseN=256baseK=128B/dtype"256 is better base"</li>
<li><code>mCnt = CeilDiv(m, baseM)</code><code>nCnt = CeilDiv(n, baseN)</code></li>
<li><code>blocksPerBatch = aicNum / batchC</code> —— 每个 batch 平均分到的 AIC 核数;</li>
<li><b>mCnt/nCnt 收拢</b><code>if (mCnt &gt; blocksPerBatch/3 &amp;&amp; mCnt &lt; blocksPerBatch/2) mCnt = blocksPerBatch/2</code>nCnt 同理)。<b>推导</b>:由条件 9 已知 mnCnt ≤ bpb/2若 mCnt 落在 (bpb/3, bpb/2),则 nCnt ≤ (bpb/2)/mCnt &lt; 1.5 即 nCnt 必为 1收拢后 mnCnt = bpb/2下一步 kCnt = bpb/mnCnt = 2恰好把该 batch 的核预算在 M 向和 K 向各分一半,得到规则的 2 路切 K不收拢则产生零散尾块、负载不均。这是"块数离散化取整到规则分点"的手法;</li>
<li>反推实际基本块:<code>baseM = CeilAlign(CeilDiv(m, mCnt), 16)</code>baseN 同理16 = cube fractal 粒度054篇</li>
<li><code>tailInfo.kCnt = blocksPerBatch / mnCnt</code> —— <b>kCnt 推导</b>:每 batch 的核预算 ÷ MN 已占块数 = 每个输出块还能再切几份 K由 mnCnt ≤ bpb/2 得 <b>kCnt ≥ 2</b>(至少 2 路切分);</li>
<li><code>singleCoreK = CeilDiv(k, kCnt)</code> —— 每核承担的 K 长度;</li>
<li><code>baseK = min(singleCoreK, FloorAlign(L0A半区/dtype/max(baseM,baseN), alignV))</code> —— K 基本块取"均分 K"与"L0A 容量上限"较小者。L0A 半区 = 64KB/2DB 两份067篇K 为内轴时A 不转置或 B 转置)按 128B 对齐056篇内轴 128B/256B/512B 对齐发挥带宽),否则按 16 对齐;</li>
<li><code>CalL1Tiling</code> 定 stepK/depthA1/depthB1</li>
<li><b>深度再平衡</b><code>if (baseM==baseN &amp;&amp; depthB1==2×depthA1) { depthA1×=2; depthB1÷=2; }</code>(注释<i>"depthb1 is less than deptha1"</i>——M、N 块相等时 A/B 应对称,若 B 的 L1 深度恰为 A 两倍则互换,使 depthA1 ≥ depthB1</li>
<li>apiLevel仅原生 BatchMatMulV3 节点且不回避 tensor API 时 TENSOR_LEVEL否则 BASIC_LEVEL注释<i>"DAV_RESV及CV自动融合当前只支持基础API"</i>)。</li>
</ol>
<h4>④ workspace 与 L0C2Out 的设计</h4>
<p><code>GetWorkspaceSize() = aicNum × 256×256 × 4B + 20MB</code>:前半是<b>每个 AIC 一块 256×256 fp32 部分和缓冲</b>(对应 baseM×baseN 上限的累加结果——各核的部分和先写 GM workspace再由归约阶段累加后半 20MB 是基类默认 RPC 区(注释 <i>"20MB workspace for RPC"</i>,核间通信/同步标志位。fixpipe 选择 <code>GetL0C2OutFlag()</code><code>n&gt;64 且 n%16≠0 且 m&gt;2 且 m×n≥256</code> → ND_FIXPIPE_1_2N 不对齐且输出块足够大时L0C→GM 直通会成为瓶颈,改走 1 AIC : 2 AIV 的 ND fixpipe 通路分担搬出)。</p>
<h4>⑤ 为什么这么做StreamK 方法学背景</h4>
<p>StreamK 思想源于 GPU 端的 Stream-K 工作中心分解Osama 等arXiv:2301.035982023传统 data-parallel GEMM 按输出块切核,当输出块数 &lt; 核数时算力浪费StreamK 把总工作量输出块×K 迭代)按"工作量流"均分给所有核核间通过部分和归约汇合。CANN 公开文档中<b>没有 StreamK 的说明</b>(知识库检索仅命中 catlass/ops-nn 源码),官方文档的切 K 建议是 EnableMultiCoreSplitK + GM 清零 + AtomicAdd206篇案例 K=1024 切 2 核后 19.60us→13.70us。BMM 这里的实现与官方简易切 K 的差异在于:<b>用 workspace 归约而非 AtomicAdd</b>fp32 部分和写 workspace 再规约,避免 GM 原子加的顺序随机性——这也解释了条件 1 的确定性等级管控),并用 <code>aivNum==2×aicNum</code> 的 MIX 模式让 AIV 承担归约/搬出。进入条件的两条 K 阈值≥max(8192, aic×256B/dtype) 才切、fp32 ≤200 万)分别守住"切 K 收益下限"与"fp32 精度上限"。</p>
<div class="figbox">
<svg viewBox="0 0 980 250" xmlns="http://www.w3.org/2000/svg" font-family="PingFang SC,Microsoft YaHei,sans-serif" font-size="12">
<defs><marker id="ar3" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" fill="#6d28a8"/></marker></defs>
<text x="20" y="22" font-weight="bold" fill="#4c1d95">StreamK一个输出块的 K 维被切给 kCnt 个核</text>
<rect x="20" y="40" width="200" height="60" rx="6" fill="#f3e8fd" stroke="#6d28a8"/>
<text x="120" y="60" text-anchor="middle">输出块 C[m,n]</text><text x="120" y="78" text-anchor="middle" font-size="11" fill="#5f6b7a">K = singleCoreK × kCnt</text>
<line x1="220" y1="55" x2="280" y2="40" stroke="#6d28a8" stroke-width="1.3" marker-end="url(#ar3)"/>
<line x1="220" y1="70" x2="280" y2="85" stroke="#6d28a8" stroke-width="1.3" marker-end="url(#ar3)"/>
<line x1="220" y1="85" x2="280" y2="130" stroke="#6d28a8" stroke-width="1.3" marker-end="url(#ar3)"/>
<rect x="285" y="30" width="150" height="36" rx="5" fill="#eaf2fb" stroke="#0b6bcb"/><text x="360" y="52" text-anchor="middle">核 iK 段 0 部分和</text>
<rect x="285" y="75" width="150" height="36" rx="5" fill="#eaf2fb" stroke="#0b6bcb"/><text x="360" y="97" text-anchor="middle">核 i+1K 段 1 部分和</text>
<rect x="285" y="120" width="150" height="36" rx="5" fill="#eaf2fb" stroke="#0b6bcb"/><text x="360" y="142" text-anchor="middle">…核 i+kCnt1</text>
<line x1="435" y1="48" x2="500" y2="80" stroke="#6d28a8" stroke-width="1.3" marker-end="url(#ar3)"/>
<line x1="435" y1="93" x2="500" y2="93" stroke="#6d28a8" stroke-width="1.3" marker-end="url(#ar3)"/>
<line x1="435" y1="138" x2="500" y2="106" stroke="#6d28a8" stroke-width="1.3" marker-end="url(#ar3)"/>
<rect x="505" y="66" width="190" height="55" rx="6" fill="#fdeee2" stroke="#b45309"/>
<text x="600" y="86" text-anchor="middle">GM workspacefp32 部分和)</text>
<text x="600" y="104" text-anchor="middle" font-size="11" fill="#5f6b7a">aicNum × 256×256 × 4B + 20MB RPC</text>
<line x1="695" y1="93" x2="755" y2="93" stroke="#6d28a8" stroke-width="1.3" marker-end="url(#ar3)"/>
<rect x="760" y="66" width="200" height="55" rx="6" fill="#e6f7ee" stroke="#0e9f6e"/>
<text x="860" y="86" text-anchor="middle">归约AIV1:2 配比)+ fixpipe</text>
<text x="860" y="104" text-anchor="middle" font-size="11" fill="#5f6b7a">确定性等级≥2 时此分支被禁用</text>
<text x="20" y="180" font-size="11" fill="#5f6b7a">进入前提batchC×mCnt×nCnt ≤ aicNum/2MN 并行度不足一半核数)且 K ≥ max(8192, aicNum×256B/dtype)(每核 K 段足够长)</text>
<text x="20" y="200" font-size="11" fill="#5f6b7a">kCnt = blocksPerBatch / (mCnt×nCnt),由判据保证 kCnt ≥ 2fp32 且 K&gt;200 万时因 binary accumulation 精度要求回退基础模板</text>
</svg>
<div class="figcap">图 5-3 StreamK 数据流K 向切分 → 各核部分和写 workspace → AIV 归约写出</div>
</div>
</div>
<div class="branch">
<h3 id="b3">5.4 MERGE_BATCH_BASICAPI优先级 3—— 小 M/N 大 batch 合并进 L0 <span class="tag">AIC_ONLY</span></h3>
<p class="src">tilingarch35/batch_matmul_v3_mergebatch_basicapi_tiling.cpp145 行kernelbatch_mat_mul_v3_mergebatch_basicapi_cmct.h + common/cmct/kernel/kernel_matmul_merge_batch.h</p>
<h4>① 目标 case</h4>
<p>M、N 很小(要求 M≤N、K≥64、<b>batch 巨大</b>的 BMM如小维度投影层的海量 batch 推理)。单 batch 的基本块太小喂不饱 Cube把多个 batch 在 M/N 方向"拼接"成大块一次算。</p>
<h4>② 进入条件IsCapablecpp L28-77逐条</h4>
<table>
<tr><th>#</th><th>条件(源码表达式)</th><th>取值依据(注释原文 / 分析)</th></tr>
<tr><td>1</td><td>非连续转置 / NZ → 拒绝</td><td>合并 batch 要求物理内存连续(注释:<i>"Non-contiguous transpose does not support MergeBatch."</i></td></tr>
<tr><td>2</td><td><code>hasBias || (fp32 且非 hf32)</code> → 拒绝</td><td>bias 未支持fp32 不转 hf32 时 L0C 内 fp32 累加块容量减半、合并收益差,直接排除</td></tr>
<tr><td>3</td><td>A/B 四级 batch 不等 → 拒绝</td><td>合并要求 A/B batch 完全一致(无广播)</td></tr>
<tr><td>4</td><td><code>batchC &lt; 4 × aicNum</code> → 拒绝</td><td>常量 <code>MIN_BATCH_L0=4</code>.h L46注释原文<i>"each aic should process at least 4 batchs"</i><b>为什么 4</b>:合并的最小粒度是 4 个 batch合并 4 个才有"L0 一块装下"的收益),且每个 AIC 至少要分到一份最小合并块 → 总 batch ≥ 4×aicNum</td></tr>
<tr><td>5</td><td><code>alignK &lt; 64 || m &gt; n</code> → 拒绝</td><td>注释:<i>"shape check, aligned k shape should be at least 64"</i>。K≥64fp16 下 128B是一次 K 内轴搬运的最低收益门槛m≤n 因为合并沿 N 方向展开tempAlignN=4×alignNN 是较大维才有合并收益</td></tr>
<tr><td>6</td><td>预计算 tempAlignM / tempAlignN</td><td><code>tempAlignM = CeilAlign(4m, 16)</code><b>但 isATrans 且 m&gt;1 时 <code>tempAlignM = 4×CeilAlign(m,16)</code></b>——A 不转置时 4 个 batch 的 M 行内存天然连续,先拼成 4m 再整体对齐(损耗 ≤15 元素A 转置时每个 batch 的 fractal 独立、M 维须各自对齐 16 再拼接(损耗 ≤4×15容量校验更保守。tempAlignN = 4×alignNN 向总是逐 batch 对齐后堆叠)</td></tr>
<tr><td>7</td><td><b>L0 三容量校验</b>(最小合并粒度 4 下):<code>tempAlignM×minBaseK×dtype×2 &gt; l0ASize</code><code>tempAlignN×minBaseK×dtype×2 &gt; l0BSize</code><code>tempAlignM×tempAlignN×4B×2 &gt; l0CSize</code> → 拒绝</td><td>注释:<i>"l0 buffer check"</i>。L0A 须装下"4 合并 M × 最小 K"的双缓冲L0B 对称L0C 须装下"4M×4N"的 fp32 累加双缓冲64KB/64KB/256KB950 白皮书表4-2<b>连最小合并4 batch都放不下合并就无可行性</b>。minBaseKK 参与 fractal 对齐A 转置或 B 不转置)时取 16 倍数,否则取 c0Size=32B/dtype</td></tr>
</table>
<h4>③ tiling 实现步骤DoOpTilingcpp L79-113</h4>
<ol class="tight">
<li><code>batchNumPerCore = CeilDiv(batchC, aicNum)</code></li>
<li>由 L0B 反推 baseK 上限:<code>maxBaseK = l0BSize / 4 / alignN / dtype / 2</code>L0B 要同时容纳 4 个 batch 的 B 块、双缓冲),向下 16 对齐、保底 16</li>
<li><code>baseK = min(maxBaseK, 64)</code> —— 注释:<i>"threshold of basek is 64"</i><b>为什么 64</b>mergebatch 的收益来自 batch 合并而非 K 深度baseK=64fp16 下 128B已满足 K 内轴最小搬运粒度056篇把 baseK 压小可腾出 L0B 容量给更多合并 batch下一步 maxBatchL0 与 baseK 成反比——64 是"搬运效率"与"合并度"折中的经验上限(源码经验值);</li>
<li><code>maxBatchL0 = max(l0BSize/baseK/dtype/2 / alignN, 1)</code> —— 已定 baseK 下L0B 双缓冲能容纳的 N 向总元素 ÷ 单 batch 对齐 N = <b>L0B 容量允许的最大合并 batch 数</b></li>
<li><code>mergeBatchL0 = min(CalBatchL0WithPolynomial(l0cElem, m), maxBatchL0, batchNumPerCore)</code> —— 最终合并数取三者最小:<b>L0C 多项式解(容量最优)∩ L0B 上限 ∩ 每核实际 batch 数</b></li>
<li>合并块:<code>baseM = CeilAlign(mergeBatchL0×m, 16)</code>transA 时逐 batch 对齐再累加)、<code>baseN = mergeBatchL0 × alignN</code></li>
<li>L1 步进(注释 <i>"4 buffer for al1_db and bl1_db"</i><code>stepKaMax = min(CeilDiv(batchNumPerCore, mergeBatchL0), l1Size/4/(baseM×baseK×dtype))</code>——L1 均分 4 份A pingpong 2 份 + B pingpong 2 份),单份容量 l1Size/4 除以单步数据量得容量允许的最大步数,再与 batch 轮数、K 总步数取 min</li>
<li>对外写回 <code>baseM = alignM, baseN = alignN</code>(单 batch 对齐值)+ <code>mergeBatchL0</code> 字段 —— 注释:<i>"M和N在L1和L0里不切分"</i><b>切分全部发生在 batch 维</b>,合并倍数由独立字段传给 kernel。</li>
</ol>
<h4>④ CalBatchL0WithPolynomialL0C 容量下最优合并数的多项式求根cpp L115-124</h4>
<p>注释原文:<i>"多项式求解最佳batchL0"</i>。设合并 x 个 batch合并后 L0C 输出块为 <code>CeilAlign(x·m,16) × (x·alignN)</code>fp32。L0C 单缓冲容量 S = l0CSize/2/4 元素,约束:</p>
<pre><code>CeilAlign(x·m, 16) · x·alignN ≤ S
令 a = m/16取整余量 t = 15/16向上取整的最大余量则 CeilAlign(x·m,16) ≈ 16(ax + t)
代入16(ax + t) · x·alignN ≤ S ⟹ ax² + tx ≤ S/(16·alignN)
两边乘 a 构造完全平方:(ax)² + t(ax) ≤ aS/(16·alignN) ≜ p
解正根ax = √(p + t²/4) t/2 ≜ y // 与源码 L121 逐符一致
x = floor( min( p/(ceil(y)·a), ceil(y)/a ) ) // L0C 容量约束 ∩ M 块数自洽,保底 1</code></pre>
<p><b>为什么这个根是最优</b>y 是连续意义下"恰好装满 L0C 单缓冲"的合并块 M 向 16-块数上界——约束取等号时 L0C 利用率 100%x 再大就溢出、再小则合并度不足cube 有效算力占比随合并度单调上升)。取整时用 <code>ceil(y)</code> 作实际 M 块数 Y再同时满足 L0C 容量x ≤ p/(Y·a))与 M 块数自洽x·a ≤ Y两个约束取 minfloor 后保底 1得到不超过 L0C 容量的最大合法整数合并数。</p>
<h4>⑤ kernel 实现</h4>
<p><code>KernelMatMulMergeBatch</code> + <code>BlockSchedulerMergeBatchBuiltIn</code>:构造时做负载均衡——<code>mainBatchNum = batchC/batchAL1</code><code>mainBatchLoop = mainBatchNum/blockNum</code>,余数均摊成"主块/主尾块/最终尾块"三种 tile前几个核多算一个<code>GetIterBatchTuple = {batchAL1, batchBL1, batchL0, batchL0}</code>A/B 在 L1/L0 可载不同 batch 数)。支持 fixpipe 1V2 融合 add/mul epilogue<code>BlockEpilogueMergeBatch</code>。tilingKeyBASIC_LEVEL + <b>BatchModel=MERGE_BATCH_MODEL</b>;另有一个细节:<code>transA = isATrans &amp;&amp; m&gt;1</code>(注释 <i>"trans_a=true &amp;&amp; m==1 equals to trans_a=False"</i>m=1 的转置是退化情形按非转置选 kernel</p>
<div class="figbox">
<svg viewBox="0 0 980 210" xmlns="http://www.w3.org/2000/svg" font-family="PingFang SC,Microsoft YaHei,sans-serif" font-size="12">
<defs><marker id="ar4" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" fill="#0b6bcb"/></marker></defs>
<text x="20" y="22" font-weight="bold" fill="#0b3d73">mergeBatchL0 个 batch 在 M/N 方向拼接,一次 mmad 等价连续算多个 batch</text>
<!-- small blocks -->
<g>
<rect x="30" y="45" width="40" height="26" fill="#dbe7f5" stroke="#0b6bcb"/><text x="50" y="62" text-anchor="middle" font-size="10">b0</text>
<rect x="30" y="75" width="40" height="26" fill="#dbe7f5" stroke="#0b6bcb"/><text x="50" y="92" text-anchor="middle" font-size="10">b1</text>
<rect x="30" y="105" width="40" height="26" fill="#dbe7f5" stroke="#0b6bcb"/><text x="50" y="122" text-anchor="middle" font-size="10">b2</text>
<rect x="30" y="135" width="40" height="26" fill="#dbe7f5" stroke="#0b6bcb"/><text x="50" y="152" text-anchor="middle" font-size="10">b3</text>
<text x="50" y="180" text-anchor="middle" font-size="11" fill="#5f6b7a">单 batch M×N 很小</text>
</g>
<line x1="80" y1="105" x2="150" y2="105" stroke="#0b6bcb" stroke-width="1.5" marker-end="url(#ar4)"/>
<rect x="155" y="45" width="70" height="116" fill="#eaf2fb" stroke="#0b3d73" stroke-width="1.5"/>
<line x1="155" y1="74" x2="225" y2="74" stroke="#0b3d73" stroke-dasharray="3 2"/>
<line x1="155" y1="103" x2="225" y2="103" stroke="#0b3d73" stroke-dasharray="3 2"/>
<line x1="155" y1="132" x2="225" y2="132" stroke="#0b3d73" stroke-dasharray="3 2"/>
<text x="190" y="180" text-anchor="middle" font-size="11" fill="#5f6b7a">L0A合并后 baseM 块</text>
<rect x="245" y="45" width="180" height="40" fill="#eaf2fb" stroke="#0b3d73" stroke-width="1.5"/>
<line x1="290" y1="45" x2="290" y2="85" stroke="#0b3d73" stroke-dasharray="3 2"/>
<line x1="335" y1="45" x2="335" y2="85" stroke="#0b3d73" stroke-dasharray="3 2"/>
<line x1="380" y1="45" x2="380" y2="85" stroke="#0b3d73" stroke-dasharray="3 2"/>
<text x="335" y="105" text-anchor="middle" font-size="11" fill="#5f6b7a">L0B合并后 baseN 块N 向堆叠)</text>
<line x1="425" y1="85" x2="490" y2="100" stroke="#0b6bcb" stroke-width="1.5" marker-end="url(#ar4)"/>
<rect x="495" y="60" width="110" height="80" rx="6" fill="#e3ecf7" stroke="#0b3d73"/>
<text x="550" y="95" text-anchor="middle">Cube 一次计算</text>
<text x="550" y="115" text-anchor="middle" font-size="10" fill="#5f6b7a">fractal 利用率拉满</text>
<line x1="605" y1="100" x2="665" y2="100" stroke="#0b6bcb" stroke-width="1.5" marker-end="url(#ar4)"/>
<rect x="670" y="60" width="150" height="80" rx="6" fill="#eaf2fb" stroke="#0b6bcb"/>
<text x="745" y="88" text-anchor="middle">L0C 256KB</text>
<text x="745" y="106" text-anchor="middle" font-size="10" fill="#5f6b7a">mergeBatchL0 由多项式求根</text>
<text x="745" y="122" text-anchor="middle" font-size="10" fill="#5f6b7a">恰好装满 L0C 单缓冲</text>
<text x="840" y="104" font-size="11" fill="#5f6b7a">前提batchC ≥ 4×aicNumK ≥ 64M ≤ N</text>
</svg>
<div class="figcap">图 5-4 MERGE_BATCH多 batch 合并进 L0 的基本块拼接</div>
</div>
</div>
<div class="branch">
<h3 id="b4">5.5 ITER_BATCH_BROADCAST_BASICAPI优先级 4—— 单边单轴 batch 广播 <span class="tag">AIC_ONLY</span></h3>
<p class="src">tilingarch35/batch_matmul_v3_iterbatch_broadcast_basicapi_tiling.cpp333 行kernelbatch_mat_mul_v3_iterbatch_broadcast.hBlaze BatchMatMulIterBatchBroadcastKernel</p>
<h4>① 目标 case</h4>
<p>A 或 B 的<b>某一级</b> batch 维为 1 的单边广播场景(如一份共享权重对多 batch 激活)。广播语义下被广播算子的单 batch 数据可被对端多个 batch 复用——L1 只需驻留一份广播算子,其余空间全部用来流水加载非广播算子的多个 batch<b>MTE 搬运量按广播倍数减少</b></p>
<h4>② 进入条件IsCapablecpp L232-261 + 子检查,逐条)</h4>
<table>
<tr><th>#</th><th>条件</th><th>取值依据(注释原文 / 分析)</th></tr>
<tr><td>1</td><td>NZ → 拒绝;任一输入 stride 非连续 → 拒绝</td><td><code>IsContiguousStride</code> 自实现检查:收集 view 各维 (stride, size),跳过 stride=0广播维和 size=1 的维,按 stride 降序排序后验证期望 stride 逐维累乘——保证 L1 迭代搬运的地址连续</td></tr>
<tr><td>2</td><td><code>batchBias &gt; 1</code> → 拒绝</td><td>bias 必须单 batch</td></tr>
<tr><td>3</td><td><code>hasBroadcastAxis()</code> 必须为真</td><td><b>4 级 batch 逐一比对</b>cpp L92-124<code>A维==1 且 B维!=1</code> 记 A 广播轴,反之记 B 广播轴。<b>"单边单轴"= 广播轴只在 A 或 B 一侧恰好出现 1 次</b>:双边广播(<i>"Dual-side broadcast is not supported."</i>)、多轴广播(<i>"Multi-axis broadcast is not supported."</i>)、无广播(<i>"No broadcast axis detected, should use ITER_BATCH."</i>)均拒绝。<b>为什么</b>:只有"单边单轴"才能保证 L1 驻留一份广播数据后、迭代轴是单一连续维;双边/多轴广播的复用关系无法用单驻留+单迭代表达,复杂 case 让位给通用 ASW 的 % 广播映射</td></tr>
<tr><td>4</td><td><code>CheckNonBroadcastAxisMatch()</code></td><td>除广播轴外A/B 其余 3 级 batch 维必须逐维相等(允许两侧都为 1</td></tr>
<tr><td>5</td><td><code>batchC ≤ aicNum</code> → 拒绝</td><td>日志:<i>"batchC &lt;= aicNum, no need for iterbatch."</i> batch 不大于核数时 batch 维并行度不足,无 iterbatch 收益</td></tr>
<tr><td>6</td><td><code>CheckL1IterBatch()</code>cpp L143-206</td><td>见下方③的 L1 容量推导</td></tr>
<tr><td>7</td><td><code>CheckL0IterBatch()</code>cpp L208-230</td><td>见下方④</td></tr>
</table>
<h4>③ L1 容量推导与 iterBatchL1 的两种公式CheckL1IterBatch</h4>
<ol class="tight">
<li>单 batch 字节数:<code>sizeAOneBatch = alignM×alignK×dtype</code><code>sizeBOneBatch = alignK×alignN×dtype</code><code>sizeCOneBatch = alignM×alignN×4B</code>L0C 内 fp32</li>
<li>L1 双缓冲门槛:<code>(sizeA + sizeB + bias) × 2 ≤ l1Size</code>A、B、bias 各一份、开 DB 要放得下 512KB L1</li>
<li><b>单 batch 广播判定</b><code>innerDimsProduct = 广播轴之后(更内层)所有 batch 维在广播侧的乘积</code><code>broadcastSingleBatch = (innerDimsProduct == 1)</code>——含义:广播轴之后的内层维在广播侧全是 1即广播侧在整个迭代中只对应<b>同一份单 batch 数据</b>,可以永久驻留 L1循环只在非广播侧上做</li>
<li><b>iterBatchL1 两公式</b>l1Avail = l1Size/2先扣 DB
<ul class="tight">
<li>广播侧单 batch 驻留(如 A 广播):<code>iterBatchL1 = (l1Avail sizeA bias) / sizeB</code> —— L1 半区先扣驻留的 A 单 batch 和 bias<b>余下全部装 B 的多个 batch</b></li>
<li>成对驻留(广播轴内层仍有真实 batch 维):<code>iterBatchL1 = (l1Avail bias) / (sizeA + sizeB)</code> —— A、B 必须按 batch 成对进入;</li></ul></li>
<li>下限与截断:<code>iterBatchL1 ≥ 2</code>(注释:<i>"iterBatchL1 expected to be no less than 2"</i>——广播收益的前提是至少 2 个 batch 复用驻留侧);再 <code>min(iterBatchL1, CeilDiv(batchC, aicNum))</code> 不超过单核平均 batch 数;</li>
<li><b>整除因子收缩</b>:迭代轴长度 = 单batch广播时的非广播侧广播轴维度如 A 广播则 B 的第 bcAxis 维),或成对驻留时的 innerDimsProduct<code>min(iterBatchL1, 迭代轴长)</code> 向下找<b>能整除迭代轴的最大因子</b>——保证 L1 一次搬运的 batch 段在迭代轴上连续且整除避免尾批破坏地址连续性BMM 的 batch 段搬运是 NDDMA 多维整块搬,段边界不对齐迭代轴会产生碎地址)。</li>
</ol>
<h4>④ L0 容量检查与 0.8 均衡率CheckL0IterBatch</h4>
<pre><code>iterBatchL0A = (l0ASize/2) / sizeAOneBatch; // L0A 半区DB可驻留 batch 数L0B/L0C 同理
l0CanLoadBatch = min(L0A, L0B, L0C) ≥ 1 // 标准情形三级半区各放≥1 batch
|| (L0A无DB ≥ 1 &amp;&amp; L0B无DB ≥ 1 &amp;&amp; L0C半区 &gt; 1) // 放宽:牺牲输入 DB换 L0C 多攒 batch
// 源码注释:// try to reduce fixpipe instr
// —— L0C 多攒几个 batch 的结果再一次 fixpipe 搬出,摊薄 fixpipe 指令开销</code></pre>
<p><b>0.8 均衡率</b>(仅当 !l0CanLoadBatch 时执行):</p>
<pre><code>avgIterBatch = batchC / aicNum // 每核理想平均 batch 数
actualMaxIterBatch = CeilDiv(CeilDiv(batchC, iterBatchL1), aicNum) × iterBatchL1 // 最忙核实际负载
balanceRate = avgIterBatch / actualMaxIterBatch &lt; 0.8 → 拒绝</code></pre>
<p>语义batch 先按 iterBatchL1 分组、组再均分到核,最忙核要处理"ceil组数/核数×iterBatchL1"个 batch尾组按满组放大。若 L1 分组导致核间倾斜超过 20%iterbatch 得不偿失,宁可落到通用分支。<b>为什么只在 L0 载不了多 batch 时检查</b>L0 顺畅时 iterbatch 的收益足够覆盖一定的倾斜L0 已打折时再叠加 20% 以上倾斜就不值了。0.8 为源码经验值defaultBalanceOfBatch</p>
<h4>⑤ DoOpTiling 步骤cpp L265-325</h4>
<ol class="tight">
<li>写回 <code>iterBatchL1</code><code>iterBatchL0 = max(min(L0A, L0B, L0C, iterBatchL1), 1)</code>L0 侧迭代不超过 L1 侧);</li>
<li>写广播轴 <code>broadcastAxisA/B</code>(无广播侧保持哨兵 4kernel 据此识别方向);</li>
<li><b>基本块决策</b>l0CanLoadBatch 时 <code>baseM/N/K = alignM/N/K</code>L0 能整 batch 装,一个 batch 一砖M/N/K 都不切);</li>
<li>否则按 M/N/K 大小关系 <b>4-case 收缩基本块</b>(思想:<b>选最小的对齐维固定为整块</b>——保证该维单次装完、至少一个矩阵的一维不被切,再用对应 L0 半区字节数反推另两维):
<table>
<tr><th>case</th><th>条件</th><th>固定</th><th>推导</th></tr>
<tr><td>1</td><td>M&lt;N 且 M&gt;KK 最小)</td><td>baseK=alignK</td><td>baseM=min(l0A/2/dtype/baseK, alignM)baseN=min(l0B/2/dtype/baseK, alignN)</td></tr>
<tr><td>2</td><td>M&lt;N 且 M≤KM 最小)</td><td>baseM=alignM</td><td>baseK=min(l0A/2/dtype/baseM, alignK)baseN=min(l0B/2/dtype/baseK, alignN)</td></tr>
<tr><td>3</td><td>M≥N 且 N&gt;KK 最小)</td><td>baseK=alignK</td><td>baseN=min(l0B/2/dtype/baseK, alignN)baseM=min(l0A/2/dtype/baseK, alignM)</td></tr>
<tr><td>4</td><td>M≥N 且 N≤KN 最小)</td><td>baseN=alignN</td><td>baseK=min(l0B/2/dtype/baseN, alignK)baseM=min(l0A/2/dtype/baseK, alignM)</td></tr>
</table></li>
<li>bias 约束:<code>baseN = min(baseN, btSize/2/4)</code>BT 表半区能容纳的 fp32 元素数);</li>
<li>L0C 约束:较大维收缩至 <code>l0CSize/2/4B ÷ 另一维</code>(保证 baseM×baseN 不超 L0C 半区);</li>
<li>三维 <code>FloorAlign(max(x,16), 16)</code>cube fractal 粒度保底)。</li>
</ol>
<h4>⑥ kernel 实现</h4>
<p>Blaze 栈 <code>BatchMatMulIterBatchBroadcastKernel</code>GemmUniversal + BlockSchedulerIterBatchBroadcast + MatmulIterBatchBroadcast&lt;A_BC,B_BC&gt;scheduler 参数携带 broadcastAxisA/B 与 A/B/C 各 4 维 batchNZ 等不支持 Blaze 的场景回落通用 BatchMatMulAswKernel其 CalcGMOffset 的 <code>batchCiIndex % batchAi / % batchBi</code> 天然支持广播映射,只是失去 L1 单份驻留的搬运优化。tilingKey<b>TENSOR_LEVEL</b> + ITER_BATCH_BROADCAST_A/B_MODEL + 固定 ON_THE_FLY。</p>
</div>
<div class="branch">
<h3 id="b5">5.6 ITER_BATCH_BASICAPI优先级 5—— 普通大 batch基础 API<span class="tag">AIC_ONLY</span> <span class="tag mix">MIX_AIC_1_2</span></h3>
<p class="src">tilingarch35/batch_matmul_v3_iterbatch_basicapi_tiling.cpp241 行kernelbatch_mat_mul_v3_iterbatch_basicapi_cmct.h + common/cmct/kernel/kernel_matmul_iterbatch.h</p>
<h4>① 目标 case</h4>
<p>A/B batch 完全相等、<code>batchC &gt; aicNum</code> 的普通大 batch BMM——每核要顺序算多个 batch让 L1/L0 一次驻留多个 batch 形成流水,摊薄每 batch 的搬运与启动开销。</p>
<h4>② 进入条件IsCapablecpp L74-136</h4>
<ul class="tight">
<li>A 侧非连续转置拒绝(注意:<b>B 侧的 3D 非连续转置在 DoOpTiling 有专门兼容逻辑</b>,见步骤 3NZ 拒绝batchBias ≤ 1四级 batch A/B 完全相等(广播已被分支 4 截胡);<code>batchC &gt; aicNum</code></li>
<li>L1 门槛:<code>((alignM×alignK + alignK×alignN)×dtype + bias) × 2 ≤ l1Size</code>(单 batch A+B+bias 开 DB 放得下。对齐细节源码注释fp16 或 fp32 的 (m,k)/(n,k) 布局 M/N 按 16 对齐fp32 的 (k,m)/(k,n) 布局按 8×2 对齐,<i>"for frac combine in loadtol0a/l0b"</i>L0 装载时的分形合并要求);</li>
<li><code>l0CanLoadBatch = min(L0A半区/sizeA, L0B半区/sizeB, L0C半区/sizeC, L1半区/(sizeA+sizeB)) ≥ 1</code>(带 bias 另需 <code>(btSize/2)/(alignN×4) ≥ 1</code>);不满足时执行与分支 4 相同的 0.8 均衡率检查。</li>
</ul>
<h4>③ DoOpTiling 步骤cpp L140-233与两个经验常数</h4>
<ol class="tight">
<li><b>两个微架构经验常数</b>L142-143注释原文
<pre><code>constexpr uint64_t mmadCount = 8UL; // cube count which will cause issuequene
constexpr uint64_t fullCopySize = 64 * 1024UL; // datasize moving once which can use full of bandwith</code></pre>
<b>依据分析</b>056篇给出"MTE1 和 MMAD 指令队列深度为 32队列满会阻塞其他指令下发、引起流水断流"——mmadCount=8 是"L1 一次驻留的 batch 数使连续 mmad 发射不撑爆 issue queue"的工程经验值(具体数字 8 文档无出处属源码经验值fullCopySize=64KB 是"单次搬运打满带宽"的粒度168篇的官方经验值是 <b>16KB</b>64KB 更保守,同为源码经验值)。</li>
<li><b>iterBatchL1 的 8 截断</b>L144-153<code>if (8 × (单batch A+B 字节数) &gt; 64KB)</code><code>iterBatchL1 = min(iterBatchL1, 8, CeilDiv(batchC, aicNum))</code>、iterBatchL0 同步钳到 8。<b>逻辑</b>:若 8 个 batch 的搬运量已超 64KB说明单 batch 足够大、8 次搬运即可打满带宽——再增加驻留 batch 数只增加 issue queue 压力而无带宽收益;反之(单 batch 很小8 个也填不满 64KB不钳制让 L1 尽量多装以摊薄搬运启动开销;</li>
<li><b>B 侧 3D 非连续转置兼容</b>L155-174注释 <i>"特殊处理3D非连续场景"</i>):非连续转置 view 下只有<b>最内层 batch 维</b>(倒数第 3 维 innerBatch保证物理连续L1 迭代只能沿 innerBatch 且必须整除它——从 <code>min(innerBatch, iterBatchL1)</code> 向下找 innerBatch 的最大因子作为新 iterBatchL1并把 innerBatch 写进 runInfo 供 kernel 算地址(与分支 4 的整除因子逻辑同构);</li>
<li>基本块l0CanLoadBatch 时 baseM/N/K = 完整对齐 M/N/K否则同分支 4 的 4-case 收缩(本分支 A 侧用 aDtypeSize、B 侧用 bDtypeSize分开计算</li>
<li>bias 约束 baseN ≤ btSize/2/4注释 <i>"baseN满足C2大小"</i>C2 即 bias 的 BT 空间L0C 约束较大维;三维 16 对齐;</li>
<li><b>fixpipe 优化开关 GetL0C2OutFlagDav3510</b>L31-39
<pre><code>if (n×bDtype &gt; 256B &amp;&amp; n % (256B/bDtype) != 0 &amp;&amp; aivNum == 2×aicNum)
return ND_FIXPIPE_1_2; else return ON_THE_FLY;</code></pre>
三条件同时满足才启用:① N 单行字节 &gt; 256B② N 非 256B 对齐(直接 fixpipe 写 GM 会产生非对齐写056篇非对齐向上取整补零、效率损失③ 1C:2V 配比vector 侧有足够算力消化 fixpipe 输出。ND_FIXPIPE_1_2 的语义是"1 个 AIC 配 2 个 AIV 做 ND 对齐 fixpipe 后处理"AIC↔AIV 用 MODE_4 CrossCoreFlag 逐 AIV 握手087篇/054篇<b>该枚举无任何 CANN 文档说明,语义以源码为准</b></li>
</ol>
<h4>④ kernel 实现</h4>
<p>Cmct 四层组装 <code>KernelMatMulIterBatch&lt;ProblemShape, BlockMmad, BlockEpilogue, BlockScheduler&gt;</code><code>BlockSchedulerIterBatchBuiltIn</code> 的调度粒度是<b>"一组 batch"</b>——<code>GetTileNum = ceil(b/iterBatchL1)</code><code>GetBlockCoord = {0,0,0, tileIdx×iterBatchL1}</code><code>GetIterBatchTuple = {iterBatchL1, iterBatchL0, 0, 0}</code>L1 一次装 iterBatchL1 个 batch、L0 一次装 iterBatchL0 个);核映射为 batch 组轮询。ND_FIXPIPE_1_2 变体MIX_AIC_1_2<code>BlockEpilogueIterbatch</code> 由 2 个 AIV 配合 1 个 AIC 做后处理(支持 add/mul/relu 融合。tilingKeyBASIC_LEVEL + SINGLE_BIAS_MODEL + l0C2Out 动态位。</p>
</div>
<div class="branch">
<h3 id="b6">5.7 ITER_BATCH优先级 6—— 大 batch 高阶 API 版 <span class="tag">AIC_ONLY</span></h3>
<p class="src">tilingarch35/batch_matmul_v3_iterbatch_tiling.cpp143 行kernelarch35/batch_mat_mul_v3_iterbatch_kernel_advanced.hBatchMatMulMultiBatchKernel</p>
<h4>① 目标 case 与分支 5 的关系</h4>
<p>目标 case 与分支 5 相同(大 batch、batch 相等),区别在于<b>搬移抽象层</b>:分支 5 用基础 API/Cmcthost 手工管理 L1/L0 双缓冲),本分支用高阶 MatmulImpl API 的 <code>IterateBatch</code>batch 维打进 MM 流水线DB 由 API 内部管理。官方文档依据141篇<i>"调用一次 IterateBatch可以计算出多个 singleCoreM × singleCoreN 大小的 C 矩阵"</i><i>"当单次 Matmul 输入 shape 较小时搬运开销占比大IterateBatch 批量处理可有效提升带宽利用率"</i></p>
<h4>② 进入条件的关键差异IsCapablecpp L71-126</h4>
<ul class="tight">
<li>A、B <b>任一侧</b>非连续转置均拒绝(高阶版没有分支 5 的 3D 兼容逻辑,日志 <i>"Non-contiguous transpose does not support high-level IterBatch."</i></li>
<li><code>iterBatch = l1Size / inputSizeOneBatch &gt; 1</code><b>不除 DB_SIZE</b>),再与每核 batch 数取 min。
<b>为什么不除 2</b>(与分支 4/5 的 <code>l1Size/2</code> 对比batch 维的双缓冲由<b>高阶 API 内部</b>实现API 在 L1 内对 batch 段做 ping-ponghost 侧只需给出"理论可装多少 batch"DoOpTiling 里两处注释互证——<i>"L1 mm fullLoad, batch 2 DB"</i>L1 深度 depthA1=stepKa×stepM 不含 batch 因子batch 的 DB 独立处理)和 <i>"need align to 2 for db in api"</i>iterBatch 向下对齐 2保证 API 能拆两份做 DB。而 basicapi 版 kernel 手工管理 L1 双缓冲,所以 host 必须先除 2 预留;</li>
<li>L0 载不了多 batch 时同样 0.8 均衡率检查(此处 min 不含 L1——L1 可装性已由 iterBatch&gt;1 保证)。</li>
</ul>
<h4>③ DoOpTiling 步骤cpp L27-69</h4>
<ol class="tight">
<li>ResetBase + CalL1Tiling 出初始 baseM/baseN<code>singleCoreM/N/K = 完整 m/n/k</code>(单核内 M/N 不切分,<b>并行维度只有 batch</b></li>
<li>多 batch 使能判定:<code>if (baseM ≥ m &amp;&amp; baseN ≥ n) { baseM/N = 对齐整块; isEnableMultiBatch = true; }</code>——L1 基本块能完整覆盖 MN 时才允许多 batch 驻留 L0C</li>
<li><code>baseK = min(l0A/2/baseM/dtype, l0B/2/baseN/dtype)</code> 向下 16 对齐;</li>
<li><b>singleCoreK 减半条件</b>L43-45
<pre><code>singleCoreK = (baseK &lt; singleCoreK || iterBatch ≤ 4) ? singleCoreK / 2 : singleCoreK;
// 注释4 avoid issueque</code></pre>
两个触发条件:① L0 装不下整条 KbaseK&lt;singleCoreKK 本就要分多次,先把单核 K 减半配合 L1 DB 流水;② iterBatch ≤ 4batch 迭代次数太少时cube 在 batch 循环间的指令发射不足以填满 issue queue<b>减半 K 让 K 循环与 batch 循环重叠、保持 issue queue 有指令可发</b>。阈值 4 与分支 5 的 mmadCount=8 同源8÷DB2=4 的直觉均为源码经验值056篇仅有队列深度 32 的机制描述);</li>
<li>step/depth<code>stepKa = stepKb = CeilDiv(singleCoreK, baseK)</code><code>depthA1 = stepKa×stepM</code>(注释 <i>"L1 mm fullLoad, batch 2 DB"</i></li>
<li><code>iterBatch = FloorAlign(iterBatch, 2)</code>API 内部 DB 要求偶数);</li>
<li><b>batchOutNum</b>L0C 一次驻留的输出 batch 数):
<pre><code>batchOutNum = isEnableMultiBatch ? min( l0CSize / (baseM×baseN×dbL0C×4B), iterBatch ) : 1;
if (batchOutNum == iterBatch) batchOutNum = iterBatch &gt;&gt; 1; // 恰好相等时再减半</code></pre>
<b>为什么相等要减半</b>:若 L0C 刚好能把本轮全部 batch 存满,就没有余量做"一半写出、一半继续算"的乒乓;减半后一半容量在计算、一半容量在 fixpipe 搬出,实现输出与计算的流水重叠。这里直接吃到 <b>950 L0C 增大到 256KB 的红利</b>(白皮书 §4.1.1 明言"更大的 L0C Buffer 提供更灵活的 Tiling 策略"——256KB 让更多 batch 的输出能驻留 L0C</li>
</ol>
<h4>④ kernel 实现</h4>
<p><code>BatchMatMulMultiBatchKernel</code> + <code>BatchMatMulMultiBatchBaseBlock</code>:主循环 <code>LoopTimes = ceil(batchC / (iterBatch×useCoreNum))</code>,每核每轮处理 iterBatch 个 batch核心是<b>一次 <code>mm_.IterateBatch(cGlobal, 0,0,0, singleASize, singleBSize)</code> 让 MatmulImpl 内部完成多个 batch 的连续 mmad</b>——batch 维打进 MM 流水线L0C 按 batchOutNum 攒多 batch 再一次写出(<code>SetNBatchOutNum</code>,该接口 CANN 9.0/9.2 文档均未收录,属未文档化高阶接口,语义以源码为准),相对普通分支"一块一 Iterate/GetTensorC"显著减少指令发射与同步开销。<code>GetMultiBatchInfo(loopIndex)</code> 处理尾轮均衡(前几个核多算一个 batch。tilingKey 只设 trans + SINGLE_BIAS_MODELapiLevel 由 key 默认值决定,落 HIGH_LEVEL</p>
</div>
<div class="branch">
<h3 id="b7">5.8 AL1 / BL1_FULL_LOAD_BASIC优先级 7/8—— 单边无 batch 的 L1 全载 <span class="tag">AIC_ONLY</span></h3>
<p class="src">tilingarch35/batch_matmul_v3_asw_al1_full_load_basic_tiling.cpp / ..._bl1_...公共实现mat_mul_v3/arch35/matmul_v3_basic_aswt_tiling.cppDoAL1FullLoad/DoBL1FullLoadkernelarch35/batch_mat_mul_v3_asw_{al1,bl1}_full_load_kernel_advanced.h</p>
<h4>① 目标 case</h4>
<p><b>A 无 batchbatchA=1且 M≤256</b>、B 有 batch 且总量大AL1镜像地 B 无 batch 且 N≤256BL1。典型小 M 激活 / 小 N 权重MoE 投影层)被大 batch 对端乘。"全载full load"的含义:<b>小的一侧矩阵完整常驻 L1整个 kernel 只从 GM 搬一次</b>,之后所有 batch、所有 N或 M块的计算都从 L1 复用,彻底消除该侧的重复 MTE 搬运。</p>
<h4>② 进入条件IsCapableAL1 为例cpp L31-72逐条</h4>
<table>
<tr><th>#</th><th>条件</th><th>取值依据(注释原文 / 分析)</th></tr>
<tr><td>1</td><td>非连续转置 → 拒绝</td><td>全载要求 GM 一次性 Nd2Nz 大整块搬入view 转置 stride 不支持</td></tr>
<tr><td>2</td><td>dtype 限 fp16/bf16A/BC 可 fp16/bf16/fp32非支持类型且 B 为 NZ → 报错</td><td>基础 API 全载模板的类型约束fp32 输入只接受 ND</td></tr>
<tr><td>3</td><td><code>batchA &gt; 1</code> → 拒绝</td><td>注释:<i>"matrix A should not have batch when AL1FullLoad"</i>——A 有 batch 时每核要驻留多份 A"全载"语义不成立。注意batchB==1 且 A 不转置的情形已在更早的 <code>MergeBatchAndMAxis()</code> 被折叠成 MM见 §4走到这里的是 B 有 batch 的真 BMM</td></tr>
<tr><td>4</td><td><code>m &gt; 256</code> → 拒绝</td><td>(源码注释 <i>"m should be larger than 256"</i> 与实际逻辑相反,系注释笔误,实际要求 m≤256<b>为什么 256</b>m≤256 时 A 的 M 向恰好一个 baseM(256) 覆盖stepM=1单核 M 向无循环——A 全载后每核只需加载一次 A 且各核间 A 完全共享,全载收益最大</td></tr>
<tr><td>5</td><td>★ "值得全载"门槛:<code>alignMatBSize &lt; l1Size×aicNum &amp;&amp; batchB×CeilDiv(n,256) &lt; 4×aicNum</code> → 拒绝</td><td>注释:<i>"each core needs to loop at least 4 batch for MatB"</i><i>"总数据量大于2轮 batch大于4轮"</i>。两个子条件:<br><b>① B 总量 &lt; 全核 L1 之和</b>B 小到可摊到所有核各存一份、整体只搬一遍A 全载没有搬运量优势;<br><b>② 每核循环轮数 &lt; 4</b>A 全载的收益是"A 加载一次、B 流水多轮复用",每核 &lt;4 轮则复用收益抵不过 A 驻留占用的 L1 空间B 的 stepK/buffer 会被压小)。<br><b>取反语义</b>:拒绝条件是"①且②同时成立",即进入全载 ⟺ <b>B 足够大(必然被反复搬运)或 轮数足够多每核≥4 轮)</b>,二者满足其一即值得</td></tr>
<tr><td>6</td><td><code>alignMatASize × 2 &gt; l1Size</code> → 拒绝</td><td>全载可行性A 全量 16 对齐数据 ×2L1 上 A 区开 ping-pong 双 buffer 保持流水)≤ 512KB L1否则物理装不下</td></tr>
</table>
<h4>③ DoAL1FullLoad() 完整步骤matmul_v3_basic_aswt_tiling.cpp L138-210</h4>
<ol class="tight">
<li>重置全载负载均衡tailSplitCnt=1 等,注释 <i>"全载模板需重置负载均衡计算"</i></li>
<li><code>remainL1Size = l1Size (aL1Size + biasSize)</code> —— A 全载驻留后剩余的 L1</li>
<li><b>maxBaseN 由两个上限卡出</b><code>maxBaseNWithL1 = remainL1 / (baseK×dtype×2)</code>(剩余 L1 给 B 开 DB每份 baseN×baseK<code>maxBaseNWithL0cDb = l0CSize / (baseM×4B×2)</code>L0C 双缓冲下 fp32 输出块须装得下),取小并 16 对齐;</li>
<li><b>balanceBaseN = maxBaseNBMM 特有)</b>:单 MatMul 下 balanceBaseN = CeilDiv(n, aicNum)(把 N 均分给各核保均衡);<b>BMM 下负载均衡已由 batch 维提供</b>(总任务 = batch×mCore×nCore并行度充足无需再均分 N直接取硬件允许的最大 baseN 最大化单块计算/搬运效率;</li>
<li><code>baseN = min(原baseN, maxBaseN, balanceBaseN)</code>N 为内轴且 baseN&gt;128B/dtype 时向下 128B 对齐(注释 <i>"N内轴时满足128B对齐"</i>056篇内轴对齐粒度</li>
<li><b>stepK 上限</b><code>maxStepK = min(CeilDiv(k,baseK), remainL1/(baseK×baseN×2×dtype), 4)</code>——注释 <i>"stepK最大不超过4"</i>:限制 L1 上 B 的流水级数,级数过多收益递减且挤压 buffer4 为源码经验值);</li>
<li><b>baseK 翻倍重试</b>L169-178当 B 为 K 内轴isBTrans且 stepK 被 buffer 上限卡住、且 <code>maxStepK×baseK</code> 不满足 256B 对齐、且 baseK 翻倍后 L0A 装得下(<code>(baseK×2)×baseM×dtype×2 ≤ l0ASize</code>)时:<code>baseK &lt;&lt;= 1</code> 重算。注释:<i>"B矩阵K为内轴k_bl1不满足256B对齐尝试baseK放大一倍提升B矩阵搬运效率"</i>——K 内轴 256B 对齐才能发挥搬运带宽056篇/950白皮书 §4.3.2 L2 128B Sector</li>
<li><b>stepK 枚举</b>1→maxStepK取第一个同时满足的<code>baseN×baseK×stepK×dtype ≥ 48KB</code>L1_SINGLE_SIZE_LIMIT注释 <i>"L1搬运量约束"</i>——单次 L1 搬运量阈值;<b>注意</b>168篇的官方经验值是"单次搬运 16KB 以上发挥带宽最佳"48KB 为源码更保守的经验值)且 K 内轴时 <code>kL1 % (256B/dtype) == 0</code>(注释 <i>"K内轴时约束kL1 256B对齐发挥带宽能力"</i>);都不满足则放大到 maxStepK 兜底;</li>
<li><b>depth</b><code>depthA1 = stepM × stepKa</code>A 全载:深度 = 整个 A 的 M×K 块数,即 A 的所有块都在 L1 有槽位)、<code>depthB1 = stepKb × 2</code>B 流水 DB</li>
<li><code>singleCoreM = m</code>A 全载单核 M 向全覆盖)、<code>singleCoreN = baseN</code></li>
<li><b>l1BufferNum = 4 或 2</b><code>bL1TensorSize×4 + aL1TensorSize ≤ l1Size</code> 则 B 区开 4 buffer注释 <i>"l1开2db后依然只使用了一半的空间则开启4 db。该字段仅在基础api场景生效"</i>)。<b>4 buffer 依据</b>:文档无"4 buffer"直接依据(高阶 API 框架 db 只取 1/2类似思想见 208篇 NBuffer33 模板的 3 份错峰MTE2 Bound 时错开搬运流水、减少单次搬运量);</li>
<li><code>dbL0C = baseM×baseN×4B×2 ≤ l0CSize ? 2 : 1</code></li>
<li><code>usedCoreNum = min(CeilDiv(n, baseN) × batchC, aicNum)</code> —— 并行任务 = N 向条带数 × batchCbatch 维提供主要并行度;</li>
<li><b>CalcTailBasicBlockAL1Full 尾块切分</b>L105-120一轮任务 = nCnt 个 N 条带,最后一轮只有 <code>tailCnt = nCnt % aicNum</code> 个核有活;把每个尾块沿 N 向再切成 tailInfo.nCnt 份,使尾轮工作核数 <code>tailCnt × nCnt</code> 逼近 aicNum条件 <code>(nCnt+1)×tailCnt ≤ aicNum</code>);若 N 是 B 内轴还要求切分后每份内轴字节 &gt; 128B避免内轴过短搬运效率崩塌</li>
</ol>
<h4>④ 为什么全载能省搬运官方定量例证192篇</h4>
<p>192篇L1 长驻优化给出精确核算L1=512K左矩阵 992K、右矩阵 16Kfp16无法同时全载。<b>反例</b>不长驻A 分 2 块、B 分 2 块,加载顺序"A1→B1→B2→A2→B1→B2",总搬运 <b>6 次</b>A×2 + B×4B 被每个 A 块周期重复搬);<b>正例</b>16K 的 B 全载长驻):循环内只搬 A1、A2<b>3 次</b>B×1 + A×2。BMM 把这一收益再放大 batch 倍——被全载侧的复用次数 ×= batchB这就是为什么条件 5 用"B 总量 vs 全核 L1 之和"和"每核循环 ≥4 轮"两个尺度衡量复用收益。对带宽相对弱的 950PR1.6TB/sDT 为 4TB/s消除重复搬运的价值更高。</p>
<h4>⑤ kernel 实现</h4>
<p><code>BatchMatMulAswAL1FullLoadKernel</code>:关键类型重定义 <code>A_TYPE_NEW = MatmulL1GmType&lt;TPosition::TSCM, ...&gt;</code>——告诉 MatmulImpl 该操作数<b>源在 L1(TSCM) 而非 GM</b>。Process 仅两步:① <code>AswAL1FullLoadKernelCopyInA1()</code> 一次性 Nd2Nz DataCopy 把本核 singleCoreM×Ka 的 A 切片搬入 L1TQue 深度 1、无 DB——只搬一次无需双缓冲② 主循环与 ASW 相同(复用同一套滑窗 swizzle<code>mm_.SetTensorA(al1Local, ...)</code> 直接给 L1 地址,并 <code>SetOrgShape(singleCoreM, N, Ka)</code>。与普通 ASW 的本质差异:<b>A 的 GM→L1 搬移次数从"每个基本块一次"降为"整个 kernel 一次"</b>。BL1 完全镜像B 全载、CalCopyBL1 Nd2Nz/Nz2Nz 区分 GM 格式。tilingKeyModel=BASIC + <b>FullLoad=A_FULL_LOAD / B_FULL_LOAD</b></p>
<div class="figbox">
<svg viewBox="0 0 980 240" xmlns="http://www.w3.org/2000/svg" font-family="PingFang SC,Microsoft YaHei,sans-serif" font-size="12">
<defs><marker id="ar5" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" fill="#0b6bcb"/></marker></defs>
<text x="20" y="22" font-weight="bold" fill="#0b3d73">AL1 全载A 一次性搬入 L1 常驻B 按块流水A 的 GM 搬运次数 = 1</text>
<rect x="20" y="45" width="110" height="60" rx="6" fill="#fdeee2" stroke="#b45309"/>
<text x="75" y="70" text-anchor="middle">GMA</text><text x="75" y="88" text-anchor="middle" font-size="10" fill="#5f6b7a">batchA=1M≤256</text>
<rect x="20" y="130" width="110" height="60" rx="6" fill="#fdeee2" stroke="#b45309"/>
<text x="75" y="155" text-anchor="middle">GMB</text><text x="75" y="173" text-anchor="middle" font-size="10" fill="#5f6b7a">batchB 大、N 大</text>
<rect x="200" y="40" width="200" height="160" rx="8" fill="#eaf2fb" stroke="#0b6bcb" stroke-width="1.5"/>
<text x="300" y="60" text-anchor="middle" font-weight="bold">L1 512KB</text>
<rect x="215" y="70" width="170" height="55" rx="5" fill="#e3ecf7" stroke="#0b3d73"/>
<text x="300" y="90" text-anchor="middle">A 全载区×2 DB</text>
<text x="300" y="107" text-anchor="middle" font-size="10" fill="#5f6b7a">depthA1 = stepM×stepKa整个 A</text>
<rect x="215" y="135" width="170" height="55" rx="5" fill="#dbe7f5" stroke="#0b6bcb"/>
<text x="300" y="155" text-anchor="middle">B 流水区2/4 buffer</text>
<text x="300" y="172" text-anchor="middle" font-size="10" fill="#5f6b7a">stepKb ≤ 4单次 ≥48KBK内轴256B对齐</text>
<line x1="130" y1="75" x2="196" y2="90" stroke="#0b6bcb" stroke-width="1.6" marker-end="url(#ar5)"/>
<text x="140" y="70" font-size="10" fill="#c0392b">仅 1 次</text>
<line x1="130" y1="160" x2="196" y2="162" stroke="#0b6bcb" stroke-width="1.6" marker-end="url(#ar5)"/>
<text x="135" y="150" font-size="10" fill="#5f6b7a">逐块流水N 向 × batchB 轮)</text>
<rect x="470" y="70" width="120" height="100" rx="6" fill="#eaf2fb" stroke="#0b6bcb"/>
<text x="530" y="105" text-anchor="middle">L0A/L0B 64KB</text>
<text x="530" y="125" text-anchor="middle" font-size="10" fill="#5f6b7a">从 L1 重载(非 GM</text>
<line x1="400" y1="120" x2="466" y2="120" stroke="#0b6bcb" stroke-width="1.6" marker-end="url(#ar5)"/>
<rect x="630" y="70" width="100" height="100" rx="6" fill="#e3ecf7" stroke="#0b3d73"/>
<text x="680" y="115" text-anchor="middle">Cube</text>
<text x="680" y="135" text-anchor="middle" font-size="10" fill="#5f6b7a">mmad 流水</text>
<line x1="590" y1="120" x2="626" y2="120" stroke="#0b6bcb" stroke-width="1.6" marker-end="url(#ar5)"/>
<rect x="770" y="70" width="110" height="100" rx="6" fill="#eaf2fb" stroke="#0b6bcb"/>
<text x="825" y="105" text-anchor="middle">L0C 256KB</text>
<text x="825" y="125" text-anchor="middle" font-size="10" fill="#5f6b7a">maxBaseN 受</text>
<text x="825" y="140" text-anchor="middle" font-size="10" fill="#5f6b7a">l0C/(baseM×4×2) 约束</text>
<line x1="730" y1="120" x2="766" y2="120" stroke="#0b6bcb" stroke-width="1.6" marker-end="url(#ar5)"/>
<text x="20" y="225" font-size="11" fill="#5f6b7a">usedCoreNum = min(N向条带数 × batchC, aicNum);尾轮 N 条带沿 N 再切分逼近满载CalcTailBasicBlockAL1Full</text>
</svg>
<div class="figcap">图 5-5 AL1 全载数据通路BL1 镜像B 常驻、A 流水)</div>
</div>
</div>
<div class="branch">
<h3 id="b8">5.9 ASW_BASIC优先级 9与 BASE999—— 通用路径cubeBound 寻优 + 自适应滑窗 <span class="tag">AIC_ONLY</span></h3>
<p class="src">tilingarch35/batch_matmul_v3_asw_basic_tiling.cpp + batch_matmul_v3_asw_tiling.cpp公共mat_mul_v3/arch35/matmul_v3_tiling_helper.cppResetBase/GetRebalanceBlock/CalL1TilingDefault/GetAswWindowLenkernelbatch_mat_mul_v3_asw_{kernel,block}_advanced.h</p>
<h4>① 目标 case</h4>
<p>前面 9 个特判都不命中的<b>通用 case</b>batch 量适中、M/N 够大、无广播或复杂广播。ASW_BASIC 是带约束的通用路径(基础/张量 API + cubeBound 模型寻优BASE(999) 是无条件兜底(高阶 API 默认参数。ASW = <b>Adaptive Sliding Window自适应滑窗</b>,命名佐证见 ops-nn 仓 dual_level_quant_batch_matmul 的 <code>DualLevelQuantBatchMatmulTilingASW</code>adaptive_sliding_window滑窗指 kernel 侧块到核映射的"滑窗+蛇形"swizzle详见第 6 章)。</p>
<h4>② ASW_BASIC 进入条件cpp L31-57</h4>
<ul class="tight">
<li><b>A、B 的非连续转置状态必须一致</b>(都连续或都非连续;混合则拒绝——注释 <i>"ASW Basic only supports AB non-contiguous transpose"</i>,混合场景只留给 BASE 的高阶 API</li>
<li>batch 完全相等(不等即广播——广播由 BASE 的 BROADCAST_BATCH_MODEL 或分支 4 承接batchBias ≤ 1dtype 同分支 7。</li>
</ul>
<h4>③ DoOpTiling 三步走ResetBase → GetRebalanceBlock → CalL1Tiling</h4>
<p><b>第 1 步 ResetBaseDAV_3510 默认值及依据)</b>baseM=256、baseN=256注释 <i>"256 is better base"</i>——DAV_3510 cube 以 256×256×(128B/dtype) 为最优基本块、baseK=128B/dtypeL0A/L0B 单次载入内轴的最小高效粒度056篇 128B、stepM=stepN=1、iterateOrder=列优先、dbL0C=1、singleCoreK=全 K。</p>
<p><b>第 2 步 GetRebalanceBlockcubeBound 解析模型寻优 baseM/baseN</b>helper.cpp L387-492——这是通用路径的"基本块寻优器"</p>
<ol class="tight">
<li><b>平台指标</b>hbmBW/l2BW 由频率×核数×平台速率算出;<code>computePower = 单核算力 × aicNum</code>fp32 时 ÷16</li>
<li><b>cubeBoundEdge 公式</b>L418-419
<pre><code>cubeBoundEdge = l2BW/computePower // ① 理论阈值L2 供数速率 ÷ cube 耗数速率
+ l2CacheUsage × (1 l2BW/hbmBW) × cmr // ② L2 装不下工作集时的访存惩罚
(1 + l2BW/hbmBW) / kValue // ③ K 向复用修正
cmr = (m+n)/(m×n) l2CacheUsage = max(batch×(m+n)×k×dtype / l2Size, 1.0)</code></pre>
逐项含义cube bound 的判据是"每输出块的相对搬运量 1/baseM+1/baseN ≤ edge";①是纯 L2 供数能力下的理论 edge②当工作集超 L2l2CacheUsage&gt;1时按 HBM 与 L2 带宽差抬高 edge更容易判成 memory bound倾向更大基本块③K 越大 A/B 复用越充分edge 越小(更容易 compute bound。进入枚举前 edge 先乘 <b>CUBE_BOUND_RATIO=0.85</b> 预留 15% 余量(防实测达不到理论带宽);</li>
<li><b>候选空间与对齐单位</b>:候选上界由 L0 bufferl0A/2/minKL0 等、bias table、K 内轴对齐BMM 固定内轴 512B 对齐,注释 <i>"目前batchmatmul固定内轴512B对齐"</i>memory bound 且非 batch 为 256B卡出<code>fixpBoundEdge = m×n×hbmBW / ((m+n)×l2BW)</code>k &lt; fixpBoundEdgefixpipe bound时 baseN 强制 256B 对齐(注释 <i>"fixpipe bound场景下要求baseN是256B对齐发挥搬出带宽"</i></li>
<li><b>枚举与剪枝</b>curBaseM 从上界按对齐单位递减、内层 curBaseN 递减;剪枝① balanceRate ≥ 0.9balanceRateEdge注释 <i>"用于判断是否取得最优解,进行减枝"</i>)且候选计算访存比既差于当前最优又大于 edge → 跳过;剪枝② fp32 cubeBound 且多轮时 baseM/N &lt; 64FP32_MIN_BASE_BLOCK跳过fp32 小块多轮效率差);</li>
<li><b>评分</b>cubeBoundCond满足 cubebound 且更均衡)优先;否则 balanceCond 以 <code>cubeBoundParam/balanceRate</code>(单位负载均衡率的搬运代价)为综合分,比值差 &lt; EPSILON(1e-9) 时取更均衡者;命中 cubeBoundCond 后 <b>edge 收紧为当前 param</b>,形成单调收敛;</li>
<li><b>尾块感知的 balanceRate</b>GetBalanceRateWithTailL223-246<code>totalRound = batch×mCnt×nCnt</code>,主轮数 mainRound = ⌈totalRound/核数⌉1尾轮按二维 √ 拆分估计totalTailSplit 按 √ 开方分两维);尾块拆分后 <code>baseM×baseN/split &lt; 4096</code>MIN_TATL_BLOCK_SIZE则无拆分收益<b>BMMbatchInfo≠直接用均值/最忙核的简单比率</b>——batch 维已摊薄尾块效应;</li>
<li>收尾 GetBaseKk ≤ maxBaseKl0A/2/dtype/max(baseM,baseN))则 K 全载;否则按 256B/128B/64B/32B/16 递减选最大可行对齐;<code>usedCoreNum = min(batch×mCore×nCore, aicNum)</code>dbL0C/ubDB 按容量置 2/1。</li>
</ol>
<p><b>第 3 步 CalL1TilingCalL1TilingDefaultL45-80</b><code>maxStepK = min(CeilDiv(k,baseK), 8)</code>(注释 <i>"Shape约束 &amp;&amp; issue queue约束"</i>——stepK≤8 又是 issue queue 经验值K 内轴对齐单位 512B/dtypeBMM 固定stepK 从 1 枚举,容量约束 <code>(aL1+bL1)×2 ≤ totalL1</code><code>max(aL1,bL1)×4 ≤ l1Size</code>(单边开 DB 不得超 L1 一半);更新条件优先级:<b>首个可行解 &gt; 256B 对齐 &gt; 单次搬运 ≥48KB</b>"优先对齐,对齐不了就把单次搬运量做大");得 stepKa=stepKb、depthA1=depthB1=stepK×2。</p>
<h4>④ 收尾l1BufferNum 与 apiLevel</h4>
<ul class="tight">
<li><code>l1BufferNum = abL1TensorSize×4 ≤ l1Size ? 4 : 2</code>(注释 <i>"l1开2db后依然只使用了一半的空间则开启4 db"</i></li>
<li><b>CheckTensorApiSupport</b>fp32 非 hf32、双 ND、连续、非全载且 <code>k &gt; 阈值</code>K&gt;2^28 时阈值 8192否则 1024FP32_K_SWITCH_BASE/FP32_SPLIT_K_BASE1/2判为 splitK 走 BASIC_LEVEL纯 BatchMatMulV3 节点、连续、非 splitK、未禁用 tensor api → TENSOR_LEVEL注释 <i>"非切K且连续场景下才允许切换tensor api实现"</i>。fp32 大 K 单核切 K 的依据fp32 累加对 K 长度敏感,单核内把 K 分段累加splitKRound可降低长 K 的累加误差与 L0C 压力。</li>
</ul>
<h4>⑤ BASE(999) 兜底</h4>
<p>IsCapable 恒 trueDoOpTiling 仅 ResetBase + CalL1Tiling + ubDB 判定——<b>不做 GetRebalanceBlock</b>(用默认 256/256/128B 基块最保守apiLevel 兜底为 HIGH_LEVEL非 BASIC<code>batchModel = (apiLevel==TENSOR_LEVEL) ? BROADCAST_BATCH_MODEL : BATCH_MODEL</code>——TENSOR_LEVEL 时高阶 API 内建任意 batch 广播支持(枚举注释 <i>"只用于BatchMatMul广播场景"</i>),这保证<b>任意广播形态的 case 都不会漏接</b>(分支 4 只接"单边单轴",其余广播全部汇到这里)。</p>
<h4>⑥ ASW 滑窗窗长GetAswWindowLenmatmul_v3_base_tiling_advanced.h L316-325</h4>
<pre><code>sqrtNum = sqrt(aicNum);
for (factor = sqrtNum; factor ≥ 1; --factor)
if (aicNum % factor == 0) return factor; // 不超过 √aicNum 的最大因子</code></pre>
<p><b>为什么这样取</b>:滑窗是 M 向的分组宽度kernel 侧 mainWindow = min(aswWindowLen, mCnt),窗内 N 向蛇形扫描,见 §6.1)。窗口越接近正方形,同窗口内 A 行块与 B 列块的 L2 足迹越小、并发核访问局部性越好——√aicNum 是"方形"的直觉;取 aicNum 的因子保证整窗被核数均分、窗口边界不碎。例aicNum=32 → 窗长 425 → 524 → 4质数核数退化为 1退化为普通行优先。窗长写入 tilingData.aswWindowLen 供 kernel 使用。</p>
</div>
<h3>5.10 参数取值依据总表(含依据来源分级)</h3>
<table>
<tr><th>参数 / 阈值</th><th>出现位置</th><th>取值</th><th>依据来源</th></tr>
<tr><td>fractal 16 对齐baseM/N/K</td><td>全部分支</td><td>16</td><td><b>文档</b>054篇 cube 一拍 16×16×16064篇 一个分形恰好 512B</td></tr>
<tr><td>L0A/L0B 512B 对齐</td><td>全部分支</td><td>512B</td><td><b>文档</b>054篇对齐要求064篇 分形=512B</td></tr>
<tr><td>内轴 128B / 256B / 512B 对齐</td><td>baseK、stepK、fixpipe 判定</td><td>128/256/512B</td><td><b>文档</b>056篇"搬运拆成 128B/256B/512B非对齐向上取整"950白皮书 §4.3.2 L2 512B CacheLine/128B Sector源码注释"BMM 固定内轴 512B 对齐"</td></tr>
<tr><td>DB ×2</td><td>所有容量公式</td><td>2</td><td><b>文档</b>067篇 InitBuffer 块数=2 的乒乓机制624篇 db 只取 1/2</td></tr>
<tr><td>batchC ≥ 128TO_MUL</td><td>分支1</td><td>2×aivNum</td><td><b>源码注释</b>"batch数大于等于128(AIV CoreNum *2) 才能开pingpong"</td></tr>
<tr><td>N&gt;256B/dtypeTO_MUL</td><td>分支1</td><td>256B</td><td><b>源码注释</b> + 054篇 Vector 每拍 256B</td></tr>
<tr><td>K ≥ max(8192, aic×256B/dtype)StreamK</td><td>分支2</td><td>8192 / 256B</td><td><b>逻辑推断</b>:每核 K 段至少一个 256B 内轴搬运块 + 绝对收益门槛</td></tr>
<tr><td>fp32 K ≤ 200 万StreamK</td><td>分支2</td><td>2×10⁶</td><td><b>源码注释</b>binary accumulation 精度),具体数值无文档依据</td></tr>
<tr><td>batchC×mCnt×nCnt ≤ aicNum/2StreamK</td><td>分支2</td><td>1/2</td><td><b>逻辑推断</b>MN 并行度不足一半核数才切 K同时保证 kCnt≥2</td></tr>
<tr><td>MIN_BATCH_L0 = 4mergebatch</td><td>分支3</td><td>4</td><td><b>源码注释</b>"each aic should process at least 4 batchs"</td></tr>
<tr><td>baseK ≤ 64mergebatch</td><td>分支3</td><td>64</td><td><b>源码注释</b>"threshold of basek is 64"+ 推断:压 K 深度换 L0B 合并度</td></tr>
<tr><td>iterBatchL1 ≥ 2iterbatch 系)</td><td>分支4/5/6</td><td>2</td><td><b>源码注释</b>:广播/驻留收益的前提是至少 2 batch 复用</td></tr>
<tr><td>mmadCount = 8 / iterBatch ≤ 4</td><td>分支5/6</td><td>8 / 4</td><td><b>源码经验值</b>:注释 "cube count which will cause issuequene";文档仅有 056篇"MTE1/MMAD 队列深度 32"的机制描述8/4 无官方出处</td></tr>
<tr><td>fullCopySize = 64KB / L1_SINGLE_SIZE_LIMIT = 48KB</td><td>分支5、全载、CalL1Tiling</td><td>64KB / 48KB</td><td><b>源码经验值</b>:注释 "moving once which can use full of bandwith"168篇官方经验值为 16KB更宽松</td></tr>
<tr><td>均衡率 0.8 / balanceRateEdge 0.9 / CUBE_BOUND_RATIO 0.85</td><td>分支4/5/6、GetRebalanceBlock</td><td>0.8 / 0.9 / 0.85</td><td><b>源码经验值</b>(注释仅说明用途,无推导)</td></tr>
<tr><td>m/n ≤ 256全载</td><td>分支7/8</td><td>256 = baseM/baseN</td><td><b>逻辑</b>:单 base 块覆盖全载轴stepM/stepN=1</td></tr>
<tr><td>"值得全载":总量 ≥ L1×aic 或 轮数 ≥ 4/核</td><td>分支7/8</td><td>4 轮</td><td><b>源码注释</b> + 192篇 6→3 次搬运的复用收益模型</td></tr>
<tr><td>全载数据 ×2 ≤ L1</td><td>分支7/8</td><td>×2</td><td><b>逻辑</b>L1 全载区开 ping-pong 双 buffer</td></tr>
<tr><td>stepK ≤ 4全载/ ≤ 8通用</td><td>分支7/8/9</td><td>4 / 8</td><td><b>源码注释</b>"stepK最大不超过4"、"issue queue约束"84 无进一步文档依据</td></tr>
<tr><td>ASW 窗长 = aicNum 的 ≤√aicNum 最大因子</td><td>分支9/BASE</td><td>√aicNum</td><td><b>逻辑推断</b>:方形窗 L2 足迹最小 + 因子保证整窗均分</td></tr>
<tr><td>workspace 20MB RPC</td><td>基类</td><td>20MB</td><td><b>源码注释</b>"20MB workspace for RPC"(核间通信区)</td></tr>
<tr><td>ND_FIXPIPE_1_1/1_2 启用条件</td><td>分支5、StreamK、helper</td><td>N 非对齐 + 1C:2V</td><td><b>源码</b>文档无该枚举说明机制支撑054/087篇 fixpipe 随路 NZ2ND、CrossCoreFlag MODE_4</td></tr>
</table>
<div class="warn"><b>依据分级说明</b>:标<b>文档</b>的参数可在知识库 CANN 文档/950 白皮书中找到直接依据;标<b>源码注释</b>的参数源码注释给出了设计意图但无官方文档佐证;标<b>源码经验值/逻辑推断</b>的参数是调优得出的工程阈值(如 8、4、0.8、0.85、48KB/64KB对 950PR 满配/降配不同档位未必是最优点,做具体 case 的最优方案决策时建议对阈值两侧的边界 case 实测复核。</div>
<!-- ============================== 6 ============================== -->
<h2 id="sec6">6. Swizzle 专题:核间分块执行顺序的三种实现机制</h2>
<p>BMM v3 中"分块执行顺序 / 哪个分块在哪个核上做"由三套机制实现,分别服务于不同的分支族:</p>
<h3>6.1 ASW 自适应滑窗 + 蛇形ASW 系 / 全载系 kernel</h3>
<p>核心代码在 <code>BatchMatMulAswBlock::UpdateBasicIndex()</code>arch35/batch_mat_mul_v3_asw_block_advanced.h</p>
<pre><code>params_.index = newBlockIdx + roundIdx * usedCoreNum; // 块→核:每轮核号错开
uint64_t matIndex = params_.index % (params_.mCnt * params_.nCnt); // batch 内 m×n 平面下标
uint64_t rowIdx = matIndex / params_.nCnt / params_.mainWindow; // 第几个划窗行
if (rowIdx &lt; params_.mainRow) { // 主划窗区
params_.mCntIndex = rowIdx * params_.mainWindow + matIndex % params_.mainWindow;
params_.nCntIndex = (matIndex / params_.mainWindow) % params_.nCnt;
} else { // 尾划窗区tailWindow
...
}
if (rowIdx % 2 != 0) { // 奇数划窗行反向扫描(蛇形)
params_.nCntIndex = params_.nCnt - 1 - params_.nCntIndex;
}</code></pre>
<p>机制解读:</p>
<ol class="tight">
<li><b>块到核映射</b><code>index = blockIdx + round×usedCoreNum</code>,第 r 轮核 c 处理第 c + r×usedCoreNum 个基本块,天然错轮;</li>
<li><b>划窗</b>M 方向每 <code>mainWindow = min(aswWindowLen, mCnt)</code> 个 m 块作为一行窗口,遍历顺序是"窗口内先 m 后 n"——同一 A 行窗口内的所有 n 块连续被计算A 的 GM/L2 数据在一个窗口内被 nCnt 次复用,最大化 L2 命中。窗长由 host 侧 <code>GetAswWindowLen()</code> 计算:<b>取 aicNum 的不超过 sqrt(aicNum) 的最大因子</b>(如 32 核 → 窗长 425 核 → 5写入 tilingData.aswWindowLen。这个选择的直觉是窗口越接近正方形同窗口内 A 块与 B 块的 L2 足迹越小、并发核的访问局部性越好;取 aicNum 的因子保证整窗能被核数均分;</li>
<li><b>蛇形</b>:奇数窗口行 n 方向反向扫描,相邻行间 n 首尾相接,避免长距离跳址,让上一行窗口末尾的 B 列块与下一行开头的 B 列块相邻,提高 B 的 L2 复用;</li>
<li><b>尾窗</b>mCnt 不能被窗长整除时剩余部分走 tailWindow 分支单独处理。</li>
</ol>
<h3>6.2 对角线错位分核(老路径通用 kernel</h3>
<p><code>BatchMatMulCommonBaseBlock::UpdateBasicIndex()</code>batch_mat_mul_v3_com_base_block.h</p>
<pre><code>uint64_t newBlockIdx = (GetBlockIdx() + usedCoreNum - params_.blockIdxStart) % usedCoreNum
+ roundIdx * usedCoreNum;
uint64_t mIdx = newBlockIdx % params_.mCntUse;
uint64_t nIdx = (newBlockIdx + newBlockIdx / MMLcm(params_.mCntUse, params_.nCntUse)) % params_.nCntUse;
params_.index = mIdx * params_.nCntUse + nIdx;</code></pre>
<p>线性 id 先取 m再用 <code>newBlockIdx / lcm(mCnt,nCnt)</code> 给 n 加相位偏移,使同一时刻各核落在 m×n 平面的<b>不同对角线</b>上,避免多核同时抢同一行 A / 同一列 B 的 GM 流量;配合每个 L2 tile 边界轮转起始核号blockIdxStart与 mTile 间蛇形reverse 翻转 nTile 遍历方向)。</p>
<h3>6.3 Host 侧 L2 cache tilingcalOrder + mTileBlock/nTileBlock老路径</h3>
<p>Host 侧 <code>BatchMatmulV3BaseTiling::DoL2CacheAndCalOrderTiling()</code>batch_mat_mul_v3_base_tiling.cpp决定核间执行的宏观顺序</p>
<ul class="tight">
<li>冲突度量 <code>transConflict = max(CeilDiv(usedCoreNum,mCnt), CeilDiv(usedCoreNum,nCnt))</code>——同一时刻并发核访问同一 A/B 块的最大冲突数;</li>
<li><code>nCnt &lt; usedCoreNum &lt; mCnt</code> 时,从 <code>CeilDiv(usedCoreNum,nCnt)×nCnt</code> 递减搜索 newMcnt直到尾块占比 ≤ 0.5TAIL_CONFLICT_RATIO——让第一轮 M 方向无冲突且尾部浪费不超过一半;</li>
<li>启用 L2 分块条件:<code>isBigSize总数据量 &gt; l2Ratio×100MB且 cBatchDimAll &lt; usedCoreNum 且 transConflict ≤ 6</code>,命中后写 mTileCntL2/mTileBlock/nTileBlock</li>
<li><code>CheckandSetDiagonalConflict()</code>:不做 L2 分块时,比较"错位分核"与"行优先"的冲突数,错位更优才改写分组;</li>
<li><code>calOrder</code>(父类 matmul_v3_base_tiling.cpp 注释明确):<b>0 = M 优先行优先1 = N 优先(列优先)</b>,由 isNKMsmallK 判定kernel 侧 <code>params_.rowOrder = tilingL2.calOrder</code> 控制块派发方向。L1 全载模板直接 mTileCntL2=nTileCntL2=1不做核间 swizzle全载后 A/B 已在 L1无 L2 复用问题)。</li>
</ul>
<h3>6.4 batch 组轮询iterbatch/mergebatch/matmul2mul</h3>
<p>这些分支的调度粒度是"一组 batch"而非 m×n 基本块,核映射为简单轮询 <code>for (tileIdx = curBlockIdx; tileIdx &lt; tileNum; tileIdx += blockNum)</code>,坐标 <code>{0,0,0, tileIdx×iterBatchL1}</code>mergebatch 额外按主块/主尾块/最终尾块做负载均衡。因为并行维度只有 batch不存在 m×n 平面上的访存冲突问题,无需滑窗/错位。</p>
<table>
<tr><th>机制</th><th>所在分支</th><th>解决的问题</th><th>核心参数</th></tr>
<tr><td>滑窗+蛇形ASW</td><td>ASW_BASIC / BASE / 全载系arch35</td><td>m×n 基本块遍历的 L2 复用</td><td>aswWindowLen = aicNum 的 ≤√aicNum 最大因子</td></tr>
<tr><td>对角线错位</td><td>老路径通用 kernel</td><td>并发核抢同一行 A / 列 B 的 GM 冲突</td><td>lcm(mCnt,nCnt) 相位偏移 + 起始核轮转</td></tr>
<tr><td>calOrder 行/列优先 + L2 tile 分组</td><td>老路径 host tiling</td><td>大 shape 下 L2 容量约束的宏观遍历方向</td><td>mTileBlock/nTileBlock/transConflict ≤ 6</td></tr>
<tr><td>batch 组轮询</td><td>iterbatch / mergebatch / matmul2mul</td><td>batch 为主并行维度时的均摊</td><td>iterBatchL1 / batchAL1 / 主尾块均摊</td></tr>
</table>
<!-- ============================== 7 ============================== -->
<h2 id="sec7">7. tiling_key 编码7 位段如何锁定一条 kernel 路径</h2>
<p>arch35 路径的 tilingKey 由 <code>BatchMatMulV3TilingKey::GetTilingKey()</code> 生成7 个位段(声明于 <code>op_kernel/arch35/batch_mat_mul_v3_tiling_key.h</code>,低位到高位):</p>
<table>
<tr><th>位段</th><th>位宽</th><th>取值</th><th>决定什么</th></tr>
<tr><td>BATCH_API_LEVEL</td><td>4 bit</td><td>0=HIGH_LEVELMatmulImpl 高阶 API/ 1=BASIC_LEVELBasicAPI/Cmct/ 2=TENSOR_LEVEL张量级/Blaze</td><td>用哪一层搬移计算抽象</td></tr>
<tr><td>BATCH_A_TRANS / BATCH_B_TRANS</td><td>2+2 bit</td><td>0/1</td><td>layout/分形方向</td></tr>
<tr><td>BATCH_ITER_MODEL</td><td>4 bit</td><td>0=FOR_BATCH / 1=ITER_BATCH_SINGLE_BIAS / 2=BATCH_MATMUL_TO_MUL / 3=MERGE_BATCH / 4=FOR_FUSED_BATCH / 5=BROADCAST_BATCH / 6=ITER_BATCH_BROADCAST_A / 7=ITER_BATCH_BROADCAST_B</td><td>batch 处理模式(≈ 分支 id</td></tr>
<tr><td>BMODEL</td><td>4 bit</td><td>0=BASIC / 1=STREAM_K / 2=K_EQUAL_ZERO</td><td>计算模型特判</td></tr>
<tr><td>BATCH_FULL_LOAD</td><td>4 bit</td><td>0=NONE / 1=A_FULL_LOAD / 2=B_FULL_LOAD / 3=AB_FULL_LOAD</td><td>L1 全载模式</td></tr>
<tr><td>BATCH_L0C2OUT_MODEL</td><td>4 bit</td><td>0=ON_THE_FLY / 1=1V1_ND_ALIG_FIXPIPE / 2=1V2_ND_ALIG_FIXPIPE</td><td>L0C→GM 输出通路fixpipe</td></tr>
</table>
<p>每个合法位段组合通过 <code>ASCENDC_TPL_SEL</code> 宏同时绑定三样东西:<b>kernel 类型</b>AIC_ONLY / AIV_ONLY / MIX_AIC_1_2<b>tiling 结构体</b>BatchMatMulV3TilingData / ...IterBatchBasicTilingData / ...MergeBatchBasicTilingData / ...ToMulBasicTilingData / MatMulV3KEqZeroBasicTilingData 等)、<b>kernel 模板实例化</b>。编译期为每个 tilingKey 生成确定的 kernel运行时零分发开销。</p>
<p>kernel 入口是一张编译期 <code>if constexpr</code> 分发树arch35/batch_mat_mul_v3.cpp节选主干</p>
<pre><code>// tilingKey 位段 → kernel 类(节选)
HIGH_LEVEL + BASIC + NO_FULL_LOAD + FOR_BATCH → BatchMatMulAswKernel + BatchMatMulAswBlockASW 主分支)
BASIC_LEVEL + BASIC + A_FULL_LOAD + FOR_BATCH → MatMulActKernel&lt;..., A_FULL_LOAD_MODE&gt;
TENSOR_LEVEL+ BASIC + B_FULL_LOAD + FOR_BATCH → Blaze: MatMulBL1FullLoadKernel
TENSOR/BASIC+ STREAM_K + FOR_BATCH → MatMulStreamKKernelMIX_AIC_1_2
BASIC_LEVEL + BASIC + ITER_BATCH_SINGLE_BIAS → BatchMatMulActIterBatchKernelCmct可带 ND_FIXPIPE_1_2
HIGH_LEVEL + BASIC + ITER_BATCH_SINGLE_BIAS → BatchMatMulMultiBatchKernelIterateBatch 高阶版)
BASIC_LEVEL + BASIC + MERGE_BATCH → BatchMatMulActMergeBatchKernelCmct
BASIC_LEVEL + BATCH_MATMUL_TO_MULAIV_ONLY → BatchMatMulToMulActKernelK=1
BASIC_LEVEL + K_EQUAL_ZEROAIV_ONLY → MatMulInputKEqZeroClearOutputK=0
TENSOR_LEVEL+ BROADCAST_BATCH → Blaze: BatchMatMulBroadcastKernel
TENSOR_LEVEL+ ITER_BATCH_BROADCAST_A/B → Blaze: BatchMatMulIterBatchBroadcastKernel</code></pre>
<div class="note">注意 <b>IS_BLAZE 分叉</b>TENSOR_LEVEL 组合在 ASC_DEVKIT ≥ 9.1Blaze GEMM 框架可用)时走 Blaze 的 GemmUniversal 系 kernel否则回落到 MatMulActKernel/AswKernel。这是同一位段组合在不同编译环境下的第二维分叉不改变分支语义。</div>
<!-- ============================== 8 ============================== -->
<h2 id="sec8">8. 分支完备性论证与批判性讨论</h2>
<h3>8.1 完备性论证</h3>
<ol class="tight">
<li><b>覆盖完备</b>:优先级表尾部 BASE(999) 的 IsCapable 恒 true、ASW_BASIC 仅校验 dtype/batch 一致性,任何通过 Validate 的合法输入必然命中某分支TENSOR_LEVEL 的 BROADCAST_BATCH_MODEL 由高阶 API 内建支持任意广播形态,广播 case 无遗漏。</li>
<li><b>特判 ⊂ 通用</b>:前 10 个分支覆盖的 case 用通用 cube 模板也能正确计算(只是更慢),即"任何 case 的最优实现必然落在 {特判分支} {通用模板} 内",满足"性能最优分支集是完备全集子集"的方法论要求。排除逻辑体现在 IsCapable 的每个拒绝条件上——例如 StreamK 的 <code>batchC×mCnt×nCnt ≤ aicNum/2</code> 保证了只有切 K 收益明确的 case 才分流出去,其余留在通用路径。</li>
<li><b>互斥性与优先级无冲突</b>多数特判条件天然互斥K=0 与 K=1 互斥;单边广播与 batch 完全相等互斥mergebatch 要求 batch≥4×核数而 StreamK 要求并行度 ≤ 核数一半……);少数有交集的(如 iterbatch basicapi 与 iterbatch 高阶版)靠优先级顺序取先命中者,语义等价只搬移路径不同。</li>
</ol>
<h3>8.2 批判性观察(源码未必最优,值得审视的点)</h3>
<div class="crit">
<ul class="tight">
<li><b>经验常数硬编码</b>mmadCount=8issue queue、fullCopySize=64KB单次搬满带宽、48KBL1_SINGLE_SIZE_LIMIT、均衡率 0.8、transConflict ≤ 6、TAIL_CONFLICT_RATIO=0.5 等都是拍死的经验阈值,与 950PR 满配 32 核/降配 28 核、不同 HBM 档位的实际曲线未必吻合。给定具体 case 做最优方案决策时,这些阈值两侧的边界 case 值得用实测复核。</li>
<li><b>cubeBound 解析模型依赖平台带宽参数</b>GetRebalanceBlock 的 cubeBoundEdge 公式使用 l2BW/hbmBW/computePower 等静态配置,若平台库中 PR 降配版1.4TB/s参数不准确baseM/baseN 寻优结果会系统性偏移。</li>
<li><b>ASW 窗长取"aicNum 的 ≤√aicNum 最大因子"是启发式</b>:对 mCnt 很小或 nCnt≫mCnt 的细长 shape方形窗假设不成立此时滑窗退化为普通行优先mainWindow=min(aswWindowLen,mCnt)L2 复用收益可能不如按 shape 长宽比自适应的窗形。</li>
<li><b>TO_MUL 的 N 区间裁剪比较"碎"</b>n∈(32B,256B]/dtype 排除、n%256B≠0、n≠1……说明该分支是按已知 case 调出来的,对区间外 N 的 K=1 case 会回落到 cube 路径——存在"K=1 但 N 对齐"的 case 被迫走 mmad 的潜在劣化点。</li>
<li><b>StreamK 的 fp32 K≤200 万阈值</b>源于 binary accumulation 精度,属保守设定;对精度不敏感的场景本可放宽,但 tiling 层没有暴露该旋钮。</li>
<li><b>iterbatch basicapi 与 iterbatch 高阶版职责重叠</b>:两者目标 case 几乎相同仅搬移抽象层不同BasicAPI/Cmct vs MatmulImpl优先级 5 先于 6意味着高阶 API 版只在 basicapi 的 L1/L0 条件不满足时才有机会——若某些 case 下高阶 API 实际更优,当前顺序无法表达。</li>
</ul>
</div>
<!-- ============================== 9 ============================== -->
<h2 id="sec9">9. 附:非 arch35 老路径分支910B 等平台)简述</h2>
<p>非 DAV_3510/RESV 平台走 <code>BatchMatmulV3BaseTiling::DoLibApiTiling()</code>batch_mat_mul_v3_base_tiling.cpp不是短路遍历而是<b>固定函数序列 + flag 覆盖</b></p>
<div class="flow">DoLibApiTiling父类基础 tiling
→ SetBatchDimInfo / CalcBatchDimAll
→ CheckNd2NzOnTheFlyLimit()(内轴 &gt; 65535→ DoUnAlignCommonTilingmixNd2Nz直接返回
→ DoCommonTiling()TuneBaseMKN 选 baseM/N/K复位全部 TilingEnable flag
→ CheckVectorComputationCondition() → DoVectorTilingAIV/UB 模式 GEMV直接返回
→ DoL1FullLoadTiling()AL1/BL1 全载判定)
→ DoL2CacheAndCalOrderTiling()(核间执行顺序/swizzle见 §6.3
→ DoMultiBatchAndL1FullLoadTiling()(多 batch命中则把 loadMode 从 AL1/BL1 改写回 BASE——多 batch 优先级实际高于 L1 全载)
→ DoTilingKeyCustom()5 个 4-bit flag 编码 tilingKey
= {MultiBatchL1FullLoad, MultiBatch, LoadMode(0=BASE/1=AL1/2=BL1/3=VECTOR_FULLLOAD), IsMultiBatchOut, MixNd2Nz}</div>
<p>对应 kernelop_kernel/batch_mat_mul_v3.cpp 分发Common对角错位 swizzle、UnAlignCommonAIV 做 ND2NZ + AIC cube 的 CV 协同CrossCoreFlag 双缓冲、AL1/BL1_FULLLOADMatmulCallBackFunc 回调式全载、MultiBatch / MultiBatchOut、MultiBatchL1FullLoad多 batch Nd2Nz 一次搬 L1 内层复用、VECTOR_FULLLOADBatchMatmulVectorKernelUB 流水 Mul+ReduceSum 的 GEMV</p>
<!-- ============================== 10 ============================== -->
<h2 id="sec10">10. 参考来源清单</h2>
<h4>源码昇腾NPU知识库/代码仓/ops-nn</h4>
<ul class="tight">
<li>matmul/batch_mat_mul_v3/op_host/op_tiling/batch_mat_mul_v3_tiling.cpp入口、batch_mat_mul_v3_base_tiling.cpp/.h老路径基类DoLibApiTiling、DoL1FullLoadTiling、DoL2CacheAndCalOrderTiling、CheckandSetDiagonalConflict、DoTilingKeyCustom</li>
<li>matmul/batch_mat_mul_v3/op_host/op_tiling/arch35/batch_matmul_v3_tiling_strategy.h分支优先级表、batch_matmul_v3_tiling_advanced.cpp/.hbatch 提取/校验/MergeBatchAndMAxis、batch_matmul_v3_tiling_key.cpp/.h以及 12 个策略 tiling 文件k_equal_zero / matmul2mul / basic_streamk / mergebatch_basicapi / iterbatch_broadcast_basicapi / iterbatch_basicapi / iterbatch / asw_al1_full_load_basic / asw_bl1_full_load_basic / asw_basic / asw</li>
<li>matmul/batch_mat_mul_v3/op_kernel/arch35/batch_mat_mul_v3.cppif constexpr 分发树、arch35/batch_mat_mul_v3_tiling_key.h位段+组合表、arch35/batch_mat_mul_v3_asw_{kernel,block}_advanced.hASW 主 kernel 与滑窗 swizzle、arch35/batch_mat_mul_v3_asw_{al1,bl1}_full_load_kernel_advanced.h、arch35/batch_mat_mul_v3_iterbatch_{kernel,block}_advanced.h、arch35/batch_mat_mul_v3_{iterbatch,mergebatch}_basicapi_{cmct,block_scheduler}.h、arch35/batch_mat_mul_v3_matmul2mul_*.h、batch_mat_mul_v3.h / batch_mat_mul_v3_com_base_block.h / batch_mat_mul_v3_block.h / batch_mat_mul_v3_vector.h老路径 kernel</li>
<li>matmul/mat_mul_v3/(公共基类与 kernel 复用arch35/matmul_v3_tiling_advanced.cpp主流程、arch35/matmul_base_tiling.h、arch35/matmul_tiling_registry.h短路遍历、arch35/matmul_v3_basic_aswt_tiling.cppDoAL1/BL1FullLoad、mat_mul_pingpong_basic.h / mat_mul_streamk.h / mat_mul_{al1,bl1}_full_load.h / mat_mul_input_k_eq_zero_clear_output.h / mat_mul_v3_full_load_kernel_helper.h</li>
<li>matmul/common/cmct/Cmct GEMM 框架kernel/kernel_matmul_{iterbatch,merge_batch}.h、kernel/kernel_batch_matmul_to_mul.h、block/block_mmad_{builder,iterbatch,mergebatch,to_mul}.h、epilogue/block_epilogue_{iterbatch,mergebatch}.h</li>
<li>ASW 命名佐证dual_level_quant_batch_matmul/op_host/op_tiling/dual_level_quant_batch_matmul_adaptive_sliding_window_tiling.cpp</li>
</ul>
<h4>硬件与编程文档昇腾NPU知识库</h4>
<ul class="tight">
<li>00_硬件/昇腾950_NPU架构白皮书.pdf§3 表3-1 核数/算力表4-2 存储层级容量 L1 512KB / L0A/B 64KB / L0C 256KB / UB 512KB / L2 128MB§4.1 微架构特性)</li>
<li>00_硬件/Ascend_950PR_产品页.txt、Ascend_950DT_产品页.txt、昇腾产品形态说明.md</li>
<li>CANN商用版9.0.0/01_AscendC算子开发/054_架构规格_NPU架构版本351x.mdAIC/AIV 分离、数据通路变化、分形格式、126_矩阵编程高阶API基础知识.md、191/192_矩阵计算性能优化.mdL0C 累加、L1 长驻、067_DoubleBuffer.md</li>
</ul>
<p class="src">说明本文所有分支条件、阈值、代码片段均直接引用自上述源码文件硬件数值均标注来源。资料未覆盖的规格项MTE 各通路带宽、SSBuffer 容量、芯片频率等)未做编造。</p>
</div>
</body>
</html>