Files
matmul-analysis/BMM算子优化分析_Release/MergeBatch_vs_IterBatch分析_v1.0.html
2026-08-26 11:56:10 +00:00

250 lines
18 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

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

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MergeBatch vs IterBatch 分析</title>
<script>
MathJax = {
tex: {
inlineMath: [['$','$']],
displayMath: [['$$','$$']],
tags: 'ams',
processEscapes: true
}
};
</script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<style>
:root{--ink:#1f2933;--muted:#5f6b7a;--accent:#0b6bcb;--accent2:#0e9f6e;--line:#d9e2ec;--code-bg:#f4f6f9}
*{box-sizing:border-box}
body{font-family:"PingFang SC","Microsoft YaHei","Helvetica Neue",Arial,sans-serif;color:var(--ink);background:#eef2f6;margin:0;line-height:1.8}
.page{max-width:1020px;margin:0 auto;padding:32px 44px 80px;background:#fff;box-shadow:0 0 24px rgba(0,0,0,.06)}
h1{font-size:28px;border-bottom:3px solid var(--accent);padding-bottom:12px;margin-top:8px}
h2{font-size:22px;margin-top:44px;border-left:6px solid var(--accent);padding-left:12px;color:#0b3d73}
h3{font-size:18px;margin-top:30px;color:#0b3d73;border-bottom:1px dashed var(--line);padding-bottom:6px}
h4{font-size:16px;margin-top:20px;color:#123}
table{border-collapse:collapse;width:100%;margin:14px 0;font-size:14px}
th,td{border:1px solid var(--line);padding:7px 10px;text-align:left;vertical-align:top}
th{background:#eaf2fb;color:#0b3d73}
tr:nth-child(even) td{background:#f8fafc}
code,pre{font-family:"JetBrains Mono",Consolas,Menlo,monospace;font-size:13px}
code{background:var(--code-bg);padding:1px 5px;border-radius:4px;color:#9d2c5e}
pre{background:var(--code-bg);border:1px solid var(--line);border-radius:8px;padding:14px;overflow-x:auto;line-height:1.55}
pre code{background:none;color:#243447;padding:0}
blockquote{background:#eafaf3;border-left:5px solid var(--accent2);padding:10px 16px;border-radius:0 8px 8px 0;margin:14px 0}
.math{background:#fafbfc;border:1.5px solid #c3d6ee;border-radius:10px;padding:10px 22px;margin:14px 0;overflow-x:auto}
ul.tight li,ol.tight li{margin:3px 0}
hr{border:none;border-top:1px solid var(--line);margin:24px 0}
</style>
</head>
<body>
<div class="page">
<h1>MergeBatch vs IterBatchBMM 小 tile 场景的两种实现路径分析</h1>
<blockquote>目标芯片:昇腾 950PRDAV_3510。理论分析基于《BMM 算子优化分析 v0.96》,源码参考 [cann-ops-nn](https://gitcode.com/cann/ops-nn/tree/master/matmul/batch_mat_mul_v3) 实现。</blockquote>
<h2>摘要</h2>
<p>BMM 中当 B ≥ C 时核间切 B 免费,核内有 IterBatch逐 batch和 MergeBatch合并 $b_0$ 个 batch两种路径。本文从 GM→L1 搬移粒度差异出发,证明 MergeBatch 的核心优势是<b>合并搬移减少 DMA 命令数</b>(每次搬移有固定开销 $T_{cmd}$),核心劣势是 **drain 暴露放大 $b_0$ 倍<b>。分界条件MergeBatch 仅在 </b>K 截断**$k_{L1} = K$,小 M/N且 $b_{core}$ 足够大时优于 IterBatch**L1 绑定情形($k_{L1} &lt; K$MergeBatch 恒劣于 IterBatch**。与源码对比发现:源码 MergeBatch 进入条件偏宽(缺搬移量/tile/算存比约束IterBatch 只覆盖 L1 形态 b缺 a/c/d</p>
<hr>
<h2>一、问题定义</h2>
<p>BMMBatchMatMul当 B ≥ Cbatch 数 ≥ AIC 核数)时,核间切 B 是免费的——每核独立处理若干 batch无共享无依赖。核内的两种实现路径</p>
<ul class="tight">
<li><b>IterBatch</b>:逐 batch 处理——每 batch 独立搬入 L1、独立计算、独立写回</li>
<li><b>MergeBatch</b>:合并处理——把 $b_0$ 个 batch 的 $A[M,K]@B[K,N]$ 合并为 $[b_0M, K]@[K, b_0N]$ 的大矩阵乘,一次搬入 L1、一次计算、一次写回</li>
</ul>
<p>核心问题:<b>什么条件下 MergeBatch 优于 IterBatch</b></p>
<hr>
<h2>二、IterBatch 理论最优实现</h2>
<h3>2.1 执行模型</h3>
<p>核间切 B每核 $b_{core} = B/C$ 个 batch。核内逐 batch 做标准 Matmul</p>
<pre><code>对每 batch i ∈ [0, b_core):
GM→L1: 搬入 A_i[M, K] + B_i[K, N](或按 kL1 分块)
L1→L0→Cube: 计算 C_i[M, N]
Fixpipe: L0C→GM 写回 C_i</code></pre>
<h3>2.2 L1 驻留形态</h3>
<p>IterBatch 按 L1 容量分四种形态v0.96 §六):</p>
<table><tr><th>形态</th><th>条件</th><th>特征</th></tr>
<tr><td>a) 单 batch 全驻留</td><td>$b_{core}=1$$(MK+KN) \cdot dtype \le L1$</td><td>零重复读</td></tr>
<tr><td>b) 双 batch 乒乓</td><td>$b_{core}&gt;1$$2(MK+KN) \cdot dtype \le L1$</td><td>batch 间流水掩盖</td></tr>
<tr><td>c) 一侧驻留+对侧切 K</td><td>$MK \cdot dtype \le L1/2$(驻留侧)</td><td>驻留侧零重复读</td></tr>
<tr><td>d) 两侧都切 K</td><td>兜底</td><td>两侧都有 K 段级重复读</td></tr></table>
<h3>2.3 实现方案</h3>
<ul class="tight">
<li><b>L0 tile</b>BaseM × BaseN 尽量用满 L0C32768 元素双缓冲baseK 由 L0A/L0B 容量决定</li>
<li><b>L1 tile</b>:按形态选择 kL1须满足 $k_{L1} \cdot dtype \ge 256$BdValue</li>
<li><b>流水</b>fixpipe 开 unitflagbatch 间由 L0C 双缓冲自动交叠</li>
</ul>
<hr>
<h2>三、MergeBatch 理论最优实现</h2>
<h3>3.1 执行模型</h3>
<p>核间切 B同 IterBatch核内把 $b_0$ 个 batch 合并计算:</p>
<pre><code>对每合并组 g ∈ [0, b_core/b0):
GM→L1: 搬入 A'[b0·M, K] + B'[K, b0·N](单次 DMAndNum=1
L1→L0→Cube: 计算 C'[b0·M, b0·N]
Fixpipe: L0C→GM 写回(只取对角块,丢弃交叉项)</code></pre>
<p>交叉项 $(b_0^2 - b_0) \cdot MN$ 被算出但丢弃——浪费比例 $(b_0-1)/b_0$。进入该分支的 case 必然访存 Bound条件 5 保证),浪费的算力被搬移时延掩盖。</p>
<h3>3.2 进入条件</h3>
<p>同时满足($b_0$ = 最小合并数,取 2</p>
<ol class="tight">
<li>$BatchA = BatchB$ 且 $b_{core} = B/C \ge 2b_0$</li>
<li>$2(b_0 M)(b_0 N) \cdot 4B \le L0C$(合并后输出放得下 L0C 双缓冲)</li>
<li>$b_{core}(MK+KN) \cdot dtype \ge min\_DatamountPerCore$(单核搬移量足够)</li>
<li>$\max(MK, KN) \cdot dtype \ge min\_TileSize$(搬移 tile 不太小)</li>
<li>$2MN/(M+N) &lt; R_{16}/b_0$(合并后仍访存 Bound</li>
</ol>
<h3>3.3 实现方案</h3>
<p><b>Step 1合并数 b₀L0C + 算存比双上限)</b></p>
<div class="math">$$
b_0 = \min\Big(\sqrt{\frac{L0C}{2 \cdot MN \cdot 4B}},\; \frac{R_{16}(M+N)}{2MN},\; b_{core}\Big)
$$</div>
<p>**Step 2L0 级 K 粒度 $k_{L0}$(由 b₀ 和 L0A/L0B 决定)**</p>
<div class="math">$$
k_{L0} = \min\Big(\frac{L0A}{2 b_0 M \cdot dtype},\; \frac{L0B}{2 b_0 N \cdot dtype}\Big) \text{ 向下 16 对齐}
$$</div>
<p>**Step 3L1 级 $k_{L1}$(先反推再截断)**</p>
<p>先令 $b_{L1}^* = b_0$,由 L1 容量反推 $k_{L1}^*$</p>
<div class="math">$$
k_{L1}^* = \frac{L1}{2 \cdot b_0 \cdot (M+N) \cdot dtype}
$$</div>
<p>然后取 $k_{L1} = \min(k_{L1}^*,\; K,\; 512B/dtype)$——不超过 KK 截断),也不超过 dValue 推荐值 512B。</p>
<p>**Step 4$b_{L1}$ 最大化**</p>
<div class="math">$$
b_{L1} = \min\Big(\frac{L1}{2 \cdot k_{L1} \cdot (M+N) \cdot dtype},\; b_{core}\Big)
$$</div>
<p>若 $k_{L1}^* &gt; 512B/dtype$$k_{L1}$ 被 512B 截断,省出的 L1 空间可容纳更多 batch$b_{L1} &gt; b_0$),提升 batch 间流水深度。</p>
<hr>
<h2>四、MergeBatch vs IterBatch 分界分析</h2>
<h3>4.1 执行模型与符号定义</h3>
<p>两分支的核间切分相同——B 维切分到 C 核,每核 $b_{core} = B/C$ 个 batch核内不切 M/N。差异在核内 K 维处理和 GM→L1 搬移粒度:</p>
<ul class="tight">
<li><b>IterBatch</b>:逐 batch 处理。每 batch 的 $A[M,K]+B[K,N]$ 作为独立 DMA 操作搬入 L1源码 <code>ndNum = curIterBatchL1</code>,多块独立寻址),然后在 L1 内逐 batch 计算</li>
<li><b>MergeBatch</b>:合并处理。$b_0$ 个 batch 的 $A'[b_0 M, K]+B'[K, b_0 N]$ 合并为一个大矩阵,作为<b>单次 DMA 操作</b>搬入 L1源码 <code>ndNum = 1</code>,单块连续搬移),然后合并计算</li>
</ul>
<p>*符号表*</p>
<table><tr><th>符号</th><th>含义</th><th>表达式/取值</th></tr>
<tr><td>$C$</td><td>AIC 核数</td><td>32</td></tr>
<tr><td>$b_{core}$</td><td>每核 batch 数</td><td>$B/C$</td></tr>
<tr><td>$b_0$</td><td>合并因子(单次合并的 batch 数)</td><td>由 L0C + 算存比双上限确定</td></tr>
<tr><td>$k_{L1}$</td><td>L1 级 K 分块粒度</td><td>$\min(K,\; L1/(2(M{+}N) \cdot dtype))$</td></tr>
<tr><td>$n_K$</td><td>K 分块数</td><td>$\lceil K/k_{L1} \rceil$</td></tr>
<tr><td>$T_{load}$</td><td>每 K 分块搬移时延</td><td>$k_{L1}(M{+}N) \cdot dtype / BW_{pc}$</td></tr>
<tr><td>$T_{comp}$</td><td>每 K 分块计算时延</td><td>$2MN \cdot k_{L1} / Q_{16}$</td></tr>
<tr><td>$T_{write}$</td><td>单 batch 输出写回时延</td><td>$MN \cdot outB / W_{GM}$</td></tr>
<tr><td>$T_{cmd}$</td><td>单次 GM→L1 DMA 搬移固定开销</td><td>描述符配置 + 地址生成 + 突发启动</td></tr>
<tr><td>$BW_{pc}$</td><td>单核 GM 带宽份额</td><td>$W_{GM}/C = 50$ GB/s</td></tr>
<tr><td>$Q_{16}$</td><td>单核 Cube BF16 峰值算力</td><td>486/32 ≈ 15.2 TFLOPS</td></tr>
<tr><td>$W_{GM}$</td><td>GM 带宽</td><td>1.6 TB/s</td></tr>
<tr><td>$outB$</td><td>输出元素字节数</td><td>BF16 → 2B</td></tr></table>
<h3>4.2 端到端时延建模</h3>
<p>L0C 双缓冲使 fixpipe 与 Cube 完全交叠,<b>batch 边界无额外同步开销</b>。两分支的差异仅来自 GM→L1 搬移次数和 drain 暴露。</p>
<p><b>IterBatch 时延</b>(每核处理 $b_{core}$ 个 batch每 batch $n_K$ 个 K 分块):</p>
<div class="math">$$
T_{iter} = \underbrace{b_{core} \cdot n_K \cdot (T_{load} + T_{cmd})}_{\text{搬移(逐 batch每次含 } T_{cmd}\text{}} + \underbrace{T_{comp} + T_{write}}_{\text{末 batch drain}}
$$</div>
<p><b>MergeBatch 时延</b>(每核处理 $b_{core}/b_0$ 个合并 batch每合并 batch $n_K^m$ 个 K 分块):</p>
<div class="math">$$
T_{mb} = \underbrace{\frac{b_{core}}{b_0} \cdot n_K^m \cdot (T_{load}^m + T_{cmd})}_{\text{搬移(合并,每次含 } T_{cmd}\text{}} + \underbrace{b_0 \cdot (T_{comp} + T_{write})}_{\text{末合并 batch drain}}
$$</div>
<p>其中 $T_{load}^m$ 是合并后每 K 分块的搬移时延,$n_K^m$ 是合并后的 K 分块数——两者的取值取决于 $k_{L1}$ 是否截断(见下)。</p>
<p><b>合并后 K 分块粒度</b>MergeBatch 合并 $b_0$ 个 batch 后L1 中的 tile 变为 $[b_0 M, k_{L1}^m] + [k_{L1}^m, b_0 N]$。L1 容量约束:</p>
<div class="math">$$
2 \cdot k_{L1}^m \cdot b_0 \cdot (M+N) \cdot dtype \le L1 \Rightarrow k_{L1}^m = \min\Big(K,\; \frac{k_{L1}}{b_0}\Big)
$$</div>
<ul class="tight">
<li><b>K 截断</b>$k_{L1} = K$$k_{L1}^m = K$ 不减半,$n_K^m = 1$——整个 K 装入 L1 一块</li>
<li><b>L1 绑定</b>$k_{L1} &lt; K$$k_{L1}^m = k_{L1}/b_0$ 减半,$n_K^m = b_0 \cdot n_K$——K 分块数 $b_0$ 倍</li>
</ul>
<h3>4.3 K 截断情形($k_{L1} = K$</h3>
<p>$n_K = n_K^m = 1$$T_{load}^m = b_0 \cdot T_{load}$(合并后单次搬移数据量 $b_0$ 倍):</p>
<div class="math">$$
T_{iter} = b_{core}(T_{load} + T_{cmd}) + T_{comp} + T_{write}
$$</div>
<div class="math">$$
T_{mb} = \frac{b_{core}}{b_0}(b_0 \cdot T_{load} + T_{cmd}) + b_0(T_{comp} + T_{write})
$$</div>
<p>两式相减:</p>
<div class="math">$$
\Delta_K = T_{mb} - T_{iter} = \underbrace{(b_0-1)(T_{comp} + T_{write})}_{\text{drain 惩罚}} - \underbrace{b_{core}\Big(1-\frac{1}{b_0}\Big) T_{cmd}}_{\text{搬移命令节省}}
$$</div>
<p><b>分界条件</b>MergeBatch 优于 IterBatch 当且仅当 $\Delta_K &lt; 0$</p>
<div class="math">$$
b_{core} > \frac{b_0 \cdot (T_{comp} + T_{write})}{T_{cmd}}
$$</div>
<p>小 MN → $T_{comp}$ 小 → 惩罚小 → MergeBatch 更容易赢;大 B → $b_{core}$ 大 → 搬移节省多 → MergeBatch 更容易赢。</p>
<h3>4.4 L1 绑定情形($k_{L1} &lt; K$</h3>
<p>$k_{L1}^m = k_{L1}/b_0$$n_K^m = b_0 \cdot n_K$$T_{load}^m = T_{load}$(每块搬移量相同,因为 $k_{L1}^m \cdot b_0 = k_{L1}$</p>
<div class="math">$$
T_{iter} = b_{core} \cdot n_K \cdot (T_{load} + T_{cmd}) + T_{comp} + T_{write}
$$</div>
<div class="math">$$
T_{mb} = \frac{b_{core}}{b_0} \cdot b_0 n_K \cdot (T_{load} + T_{cmd}) + b_0(T_{comp} + T_{write}) = b_{core} \cdot n_K \cdot (T_{load} + T_{cmd}) + b_0(T_{comp} + T_{write})
$$</div>
<p>搬移次数相同($b_{core} \cdot n_K$),每次搬移量相同——<b>MergeBatch 无搬移优势</b></p>
<div class="math">$$
\Delta_L = T_{mb} - T_{iter} = (b_0-1)(T_{comp} + T_{write}) > 0
$$</div>
<p><b>L1 绑定情形 MergeBatch 恒劣于 IterBatch</b>——合并只放大了 drain 暴露,没有换来搬移命令节省。</p>
<h3>4.5 统一分界条件</h3>
<div class="math">$$
\text{MergeBatch 最优} \iff \underbrace{k_{L1} = K}_{\text{K 截断}} \;\land\; \underbrace{b_{core} > \frac{b_0 \cdot (T_{comp} + T_{write})}{T_{cmd}}}_{\text{搬移节省} > \text{drain 惩罚}}
$$</div>
<p>K 截断的物理含义:$L1/(2(M{+}N) \cdot dtype) \ge K$——小 M/N 使 L1 一次装下整个 K 维。</p>
<table><tr><th>维度</th><th>IterBatch</th><th>MergeBatch</th><th>差异</th></tr>
<tr><td>稳态搬移吞吐</td><td>相同</td><td>相同</td><td>总搬移量相同</td></tr>
<tr><td>GM→L1 搬移命令数</td><td>$b_{core}$ 次</td><td>$b_{core}/b_0$ 次K 截断时)</td><td>**MergeBatch 少 $b_0$ 倍** ← 核心优势</td></tr>
<tr><td>drain 暴露</td><td>$T_{comp} + T_{write}$</td><td>$b_0(T_{comp} + T_{write})$</td><td>IterBatch 少 $b_0$ 倍 ← 核心劣势</td></tr>
<tr><td>L0C 利用率</td><td></td><td></td><td>访存 Bound 下不影响时延</td></tr></table>
<h3>4.6 $T_{cmd}$ 的物理成因</h3>
<p>每次 GM→L1 DMA 搬移的固定开销包括Nd2Nz 描述符配置ndNum/nValue/dValue/srcStride 等 7 个字段写入 DMA 寄存器)、地址生成与突发启动、与 L1 buffer 的同步握手(<code>SetFlag&lt;MTE2_MTE1&gt;</code> / <code>WaitFlag&lt;MTE1_MTE2&gt;</code>)。量级估计为数十 ns精确值需实测标定。</p>
<hr>
<h2>五、与源码实现的对比</h2>
<h3>5.1 MergeBatch 源码(<code>batch_matmul_v3_mergebatch_basicapi_tiling.cpp</code></h3>
<p><b>IsCapable 条件</b></p>
<pre><code>// 源码https://gitcode.com/cann/ops-nn/tree/master/matmul/batch_mat_mul_v3
if (batchC &lt; MIN_BATCH_L0 * aicNum) return false; // B &lt; 128即 b_core &lt; 4
if (alignK &lt; 64 || M &gt; N) return false; // K ≥ 64M ≤ N
if (L0 buffer check with b0=4) return false; // L0C 容量</code></pre>
<p><b>对比分析</b></p>
<table><tr><th>维度</th><th>理论</th><th>源码</th><th>差异</th></tr>
<tr><td>合并数 b₀</td><td>min(L0C 上限, 算存比上限, b_core)</td><td>MIN_BATCH_L0 = 4固定实际由多项式动态求解</td><td>源码 b₀=4 是硬编码下限,实际值由 L0C 容量动态计算</td></tr>
<tr><td>b_core 下限</td><td>≥ 2b₀ = 4</td><td>≥ 4batchC ≥ 128</td><td>一致</td></tr>
<tr><td>L0C 约束</td><td>2(b₀M)(b₀N)·4B ≤ L0C</td><td>tempAlignM × tempAlignN × 4B × 2 ≤ L0C</td><td>一致</td></tr>
<tr><td>搬移量约束</td><td>b_core(MK+KN)·dtype ≥ 480KB</td><td><b></b></td><td>源码缺少</td></tr>
<tr><td>tile 大小约束</td><td>max(MK,KN)·dtype ≥ 16KB</td><td><b></b></td><td>源码缺少</td></tr>
<tr><td>算存比约束</td><td>2MN/(M+N) &lt; R₁₆/b₀</td><td><b></b></td><td>源码缺少</td></tr>
<tr><td>额外约束</td><td></td><td>K ≥ 64M ≤ N</td><td>源码独有</td></tr></table>
<p><b>结论</b>:源码的 MergeBatch 进入条件<b>偏宽</b>——缺少搬移量、tile 大小、算存比三条约束。可能导致不应进入的 case 被误捕获(如计算 Bound 的 case 进入 MergeBatch 后冗余计算成为瓶颈)。</p>
<h3>5.2 IterBatch 源码(<code>batch_matmul_v3_iterbatch_basicapi_tiling.cpp</code></h3>
<p><b>IsCapable 条件</b></p>
<pre><code>// 源码https://gitcode.com/cann/ops-nn/tree/master/matmul/batch_mat_mul_v3
if (batchC &lt;= aicNum) return false; // B ≤ 32 不进 IterBatch
if ((alignM*alignK + alignK*alignN)*dtype*2 &gt; L1) return false; // L1 双缓冲
if (!l0CanLoadBatch_ &amp;&amp; balanceRate &lt; 0.8) return false; // 负载均衡</code></pre>
<p><b>对比分析</b></p>
<table><tr><th>维度</th><th>理论</th><th>源码</th><th>差异</th></tr>
<tr><td>L1 形态</td><td>四种a/b/c/d</td><td>只覆盖形态 b</td><td>源码缺 a/c/d</td></tr>
<tr><td>B 下限</td><td>b_core ≥ 1</td><td>B &gt; 32b_core ≥ 2</td><td>源码排除了 b_core=1 的形态 a</td></tr>
<tr><td>负载均衡</td><td>B mod C ≥ minCoreNum</td><td>balanceRate ≥ 0.8</td><td>实现方式不同,目的一致</td></tr></table>
<p><b>结论</b>:源码的 IterBatch <b>只覆盖形态 b</b>(双 batch 乒乓),且要求 B &gt; C。形态 a/c/d 的 case 由 ASW/AL1/BL1 分支承接。</p>
<h3>5.3 综合评价</h3>
<p>理论分析的价值在于:</p>
<ol class="tight">
<li><b>分界条件</b>——源码无显式 MergeBatch vs IterBatch 分界,理论给出了 $k_{L1} = K \land b_{core} &gt; b_0(T_{comp}+T_{write})/T_{cmd}$ 的闭式条件</li>
<li><b>L1 绑定情形</b>——理论证明 MergeBatch 在 L1 绑定时恒劣于 IterBatch源码未区分</li>
<li><b>T_cmd 的物理意义</b>——GM→L1 DMA 命令固定开销,是 MergeBatch 优势的本质来源</li>
</ol>
<p>源码实现的价值在于:</p>
<ol class="tight">
<li><b>工程完备性</b>——负载均衡、格式检查、bias 处理等</li>
<li><b>cubeBound 模型</b>——经实测调优的 baseM/baseN 寻优</li>
<li><b>L2 Cache 管理</b>——100MB 阈值 + transConflict 控制</li>
</ol>
<hr>
<h2>参考文献</h2>
<ol class="tight">
<li>[昇腾 950 NPU 架构白皮书](https://public-download.obs.cn-east-2.myhuaweicloud.com/ascend/%E6%98%87%E8%85%BE950%20NPU%E6%9E%B6%E6%9E%84%E7%99%BD%E7%9A%AE%E4%B9%A6.pdf)华为技术有限公司2026</li>
<li>[CANN 9.2.0-beta.1 Ascend C API 参考](https://www.hiascend.com/document/detail/zh/CANNCommunityEdition/920beta1/API/ascendcopapi/atlasascendc_api_07_0003.html)——UnitFlag 机制详见"矩阵计算ISASI/Mmad 计算关键特性说明/UnitFlag"章节</li>
<li>[cann-ops-nn 源码仓](https://gitcode.com/cann/ops-nn/tree/master/matmul/batch_mat_mul_v3)</li>
</ol>
</div>
</body>
</html>