Files
matmul-analysis/BMM/BatchMatMulV3算子分支实现分析.html

924 lines
124 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>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>