补充 UnitFlag 三条官方约束的逐条考据(含义/出处/950PR实际生效性)

This commit is contained in:
2026-08-28 07:52:41 +00:00
parent 1412964a0e
commit afb91797b3

View File

@@ -111,6 +111,56 @@ L0C.A(块n+2): [==== MMAD ====]···
</ul>
<p><strong>最终判断</strong>UnitFlag 在其合法域内可以取代 L0C DB且更优但因「模板/流水互斥/累加」三重硬约束,它<strong>无法覆盖全部场景</strong>L0C DB 作为不依赖这些约束的块间掩盖手段,是 UnitFlag 失效场景下的必要补充,两者是<strong>主从互补</strong>关系,而非简单的&quot;新替旧&quot;</p>
<hr/>
<h3>3.4 对官方三条约束的逐条考据(答疑)</h3>
<p>针对三条容易被误读的官方约束,逐一给出原文出处、准确含义、以及在 mat_mul_v3 / 950PR 上的实际适用性。</p>
<h4>3.4.1 「UnitFlag + L0C 累加时,不支持多次 Iterate 计算、一次 GetTensorC 输出」——具体是什么约束?</h4>
<p><strong>官方原文出处</strong>(两处,措辞略异,商用版更完整):</p>
<ul>
<li>昇腾社区官网 CANN 社区版 → 算子实践参考 → 优秀实践 → Matmul 性能调优案例 → <strong>《Matmul 高阶 API 开启 UnitFlag》</strong>,约束条件第 3 条:</li>
</ul>
</blockquote>
<ul>
<li>昇腾社区官网 CANN 商用版 → 同名章节 <strong>《Matmul 高阶 API 使能 UnitFlag》</strong>,约束条件第 3 条(完整表述):</li>
</ul>
</blockquote>
<p><strong>准确含义拆解</strong>:这条约束针对的是 <strong>「同一个 base 块K 方向分多片,在 L0C 上驻留并多次累加,最后才一次性 GetTensorC 搬出」</strong> 的编程形态。即:</p>
<pre><code>// 被禁止的形态K 在 L0C 内分片累加,最后一次才搬出
for (kSlice = 0; kSlice &lt; kSliceNum; kSlice++) { // 多次 Iterate
mm.Iterate(); // 每片 K 的部分结果累加进同一块 L0CL0C 累加)
}
mm.GetTensorC(c); // 只在最后一次性搬出(一次输出)</code></pre>
<p><strong>为什么 UnitFlag 与这种形态冲突</strong>UnitFlag 的硬件机制是「MMAD 每产出 512B 成品,就立即触发 Fixpipe 把这段搬走」。但 L0C 累加形态下,前面若干次 Iterate 的结果是<strong>部分和,还必须留在 L0C 里等后续 K 片继续累加</strong>,不能被搬走。一旦开 UnitFlagMMAD 算出 512B 就被 Fixpipe 立即搬出L0C 里就保不住部分和、后续累加无从谈起——两者在&quot;这 512B 到底是驻留还是立即搬走&quot;上直接矛盾,故硬件/API 不允许叠加。</p>
<p><strong>mat_mul_v3 是否踩这条约束?——不踩(常规路径)</strong>。看 <code>mat_mul_asw_kernel.h:140-141</code></p>
<pre><code>mm_.Iterate();
mm_.GetTensorC(cGlobal_[...], enAtomic || kIndex != 0); // 每次 Iterate 紧跟一次 GetTensorC</code></pre>
<p>mat_mul_v3 的普通 ASWT 是 <strong>「一次 Iterate → 一次 GetTensorC」一一配对</strong>(即使在 splitKRound 循环里,也是 Iterate 与 GetTensorC 成对出现,用 <code>enAtomic || kIndex!=0</code> 在 GM 侧做原子加,而非在 L0C 里驻留累加)。它<strong>不是</strong>&quot;多次 Iterate、一次 GetTensorC&quot;因此常规路径不触碰这条约束UnitFlag 可用。</p>
<p><strong>真正会踩这条的场景</strong>:把 split-K 的<strong>核内</strong>部分和放在 L0C 里驻留累加L0C 累加),或 <code>intraBlockPartSum</code>(两 AIV 结果在 L0C 累加)这类显式 L0C 累加特性。StreamK 之所以要绕道 workspace 由 AIV 累加、而不是在 L0C 里累加,正是为了避开这条。</p>
<h4>3.4.2 一次 Iterate 最大支持的 K 是多少?</h4>
<p><strong>先澄清概念</strong>:官方约束里没有任何一条把 &quot;一次 Iterate 的 K 上限&quot; 与 UnitFlag 挂钩。<strong>UnitFlag 约束的不是 K 的大小,而是&quot;累加形态&quot;</strong>(见上一条)。一次 Iterate 能算多大的 K<strong>L0C 之外的资源L1/L0A/L0B 容量、stepKa/stepKb 流水深度、baseK</strong> 决定,与 UnitFlag 无关:</p>
<ul>
<li>单次 Iterate 的 K 由 <code>singleCoreK</code> 决定,而 <code>singleCoreK</code> 在 L1 内按 <code>stepKa·baseK</code>A 侧)/ <code>stepKb·baseK</code>B 侧)分多片流水喂给 Cube<strong>每片的中间结果在同一 L0C 上原地累加</strong>(这是 MMAD 的固有累加语义,不是&quot;多次 Iterate 一次输出&quot;,也不违反 UnitFlag——因为整个 singleCoreK 是一次 Iterate 内部完成的)。</li>
<li>换句话说:一次 Iterate 内部Cube 会在 L0C 上做 <code>singleCoreK/baseK</code> 次累加,这在 UnitFlag 下是允许的UnitFlag 禁止的是&quot;<strong>跨多次 Iterate</strong> 的累加 + 只在最后一次输出&quot;</li>
</ul>
<p><strong>结论</strong><strong>没有&quot;一次 Iterate 最大 K 受 UnitFlag 限制&quot;这回事</strong>。一次 Iterate 的 K 上限由 L1/L0 容量与 stepK 流水决定(例如 950PR 上 <code>maxStepK ≤ 8</code><code>baseK ≈ 128B/dtype</code>UnitFlag 只管&quot;算出的 512B 何时搬出&quot;,不管 K 多大。把这条约束理解成&quot;K 不能超过某值&quot;是误读——它限的是&quot;累加跨几次 Iterate&quot;,不是&quot;K 的绝对大小&quot;</p>
<h4>3.4.3 「不允许同时存在 CO1(L0C)→GM 与 A1(L1)→GM 两种搬出流水」——是什么意思L1 能直接写回 GM 吗?</h4>
<p><strong>官方原文出处</strong>同上两篇《Matmul 高阶 API使能/开启UnitFlag》约束条件第 2 条:</p>
</blockquote>
<p><strong>这条的字面意思</strong>UnitFlag 开启时,同一个算子里不能<strong>同时</strong>出现两类搬出流水——一类是 L0C→GM结果搬出走 Fixpipe另一类是 L1(A1)→GM从 L1 直接把数据写回 GM。即搬出通路的&quot;源头&quot;必须统一为 L0C不能 L0C 和 L1 混着当搬出源。</p>
<p><strong>「L1 能直接写回 GM 吗?」——分架构看,这正是关键点</strong></p>
<ul>
<li><strong>在 950PR351x 架构)上:不能。</strong> 950 白皮书明确351x 相对上代<strong>删除了 <code>L1→GM</code> 的回写通路</strong>(同时也删除了 <code>GM→L0A/L0B</code> 的直达通路),一切矩阵数据的搬出都必须经 <code>L0C→Fixpipe→GM</code>。所以<strong>在 950PR 上根本不存在&quot;L1→GM&quot;这条物理通路</strong>,这条约束对 950PR 而言<strong>自然满足、不会触发</strong>——它更像是从支持 L1→GM 的老架构(如部分 A2/A3 代际)沿用过来的兼容性约束。</li>
<li><strong>mat_mul_v3 在 950PR 上的搬出源</strong>:普通 ASWT 的结果一律走 <code>L0C→Fixpipe→GM(C)</code><code>mm_.GetTensorC(cGlobal...)</code>),没有任何 L1→GM 的直接搬出 → 符合该约束。</li>
</ul>
<p><strong>小结</strong>:这条约束的语义是&quot;搬出源必须唯一(只能是 L0C&quot;,防止 UnitFlag 的细粒度同步在&quot;两种不同源头的搬出&quot;上产生时序混乱。在 950PR 上由于 L1→GM 通路已被硬件删除,这条约束<strong>自动成立</strong>,不会成为限制 mat_mul_v3 开 UnitFlag 的因素。</p>
<h4>3.4.4 三条约束在 mat_mul_v3 / 950PR 上的实际生效性汇总</h4>
<table><thead><tr><th>官方约束</th><th>准确含义</th><th>950PR 上是否实质限制 mat_mul_v3</th></tr></thead><tbody>
<tr><td>仅 Norm/IBShare/MDL 模板</td><td>UnitFlag 只在三个高阶模板下有效</td><td>mat_mul_v3 用 MDLGetMDLConfig<strong>满足</strong></td></tr>
<tr><td>不能同时有 CO1→GM 与 A1→GM 两种搬出</td><td>搬出源必须唯一为 L0C</td><td>351x 已删 L1→GM 通路 → <strong>自动满足,无实质限制</strong></td></tr>
<tr><td>L0C 累加时不支持多次 Iterate 一次输出</td><td>禁止跨 Iterate 的 L0C 驻留累加 + 末次才搬出</td><td>mat_mul_v3 常规路径是 Iterate/GetTensorC 一一配对 → <strong>不触碰UnitFlag 可用</strong>;仅显式 L0C 累加(核内 split-K 驻留 / intraBlockPartSum才触碰</td></tr>
<tr><td>MTE2 Bound 已掩盖时收益小</td><td>Fixpipe 已被搬运流水掩盖时无需再开</td><td>这是<strong>收益</strong>问题不是<strong>正确性</strong>问题,不影响可用性</td></tr>
</tbody></table>
<p><strong>由此可得更精确的判断</strong>mat_mul_v3 在 950PR 上常规路径(含普通 ASWT、A/B 全载、以及把 K 分片放到 GM workspace 由 AIV 累加的 StreamK<strong>都满足 UnitFlag 的可用条件且默认已开启</strong>;真正被 UnitFlag 排除的是&quot;<strong>在 L0C 里做跨 Iterate 驻留累加</strong>&quot;这一类形态——而这种形态在 mat_mul_v3 里被刻意用&quot;GM 原子加 / workspace + AIV 累加&quot;规避了。这进一步支撑了 §3.3 的结论:<strong>在 950PR 上 UnitFlag 的合法域覆盖了 mat_mul_v3 的全部主路径L0C DB 的补位空间被压缩得很小</strong></p>
<hr/>
<h2>4. 对本项目性能建模与最优方案的直接推论</h2>
<ol>
<li><strong>建模时 Fixpipe 项要分两种掩盖</strong>:在性能模型 <code>T_total = max(T_MMAD, T_MTE2, T_MTE1, T_FIXPIPE)</code> 中,<code>T_FIXPIPE</code> 的实际暴露量应写成</li>
@@ -121,16 +171,20 @@ L0C.A(块n+2): [==== MMAD ====]···
</ul>
<ol>
<li><strong>最优 tiling 的取舍被简化</strong>:既然 UnitFlag 默认开baseM/baseN 的搜索<strong>不必再为&quot;能否开 L0C DB&quot;让步</strong>,可以更激进地逼近 256×256重复读最少。原先&quot;为开 DB 而把 baseN 砍半&quot;的权衡在 UnitFlag 合法域内不成立。这是对 §3.2 <code>GetRebalanceBlock</code> 的一处实质修正点。</li>
<li><strong>StreamK 的 K 分片场景要单独建模</strong>StreamK/大 K 分片累加命中 UnitFlag 的&quot;多次 Iterate 一次输出&quot;约束,其 Fixpipe 掩盖机制与普通 ASWT 不同,性能模型需为这类 case 单列</li>
<li><strong>真正需要单独建模的 K 累加形态</strong>:需要警惕的不是 mat_mul_v3 的主路径,而是&quot;<strong>在 L0C 里做跨 Iterate 驻留累加</strong>&quot;这一类形态(核内 split-K 驻留 / intraBlockPartSum 等)。这类形态被 UnitFlag 约束排除,只能靠 L0C DB 做块间掩盖。mat_mul_v3 的 StreamK 用 workspace + AIV 累加规避了它,因此主路径不受影响;但若未来引入 L0C 驻留累加的新模板,性能模型需为其单列 Fixpipe 掩盖方式(回到 DB 主导)</li>
</ol>
<hr/>
<h2>附录:关键证据索引</h2>
<table><thead><tr><th>证据</th><th>位置</th></tr></thead><tbody>
<tr><td>UnitFlag 三条约束官方原文(在线)</td><td>昇腾社区官网 → CANN → 算子实践参考 → 优秀实践 → Matmul 性能调优案例 → 《Matmul 高阶 API 开启 UnitFlag》社区版/ 《Matmul 高阶 API 使能 UnitFlag》商用版。社区版直达链接见任务输入所给 UnitFlag.md 页面</td></tr>
<tr><td>UnitFlag 三条约束官方原文(本地)</td><td><code>昇腾NPU知识库/CANN社区版9.2.0-beta.1/01_AscendC算子开发/292_..._开启UnitFlag.md</code>第19/21行<code>昇腾NPU知识库/CANN商用版9.0.0/01_AscendC算子开发/203_..._使能UnitFlag.md</code>第21/23行表述最完整</td></tr>
<tr><td>GetMDLConfig 签名第7参=enUnitFlag</td><td><code>昇腾NPU知识库/CANN商用版9.0.0/02_API参考/AscendC_API/631_..._GetMDLConfig.md</code></td></tr>
<tr><td>enUnitFlag 默认值与约束</td><td><code>昇腾NPU知识库/CANN商用版9.0.0/02_API参考/AscendC_API/626_..._MatmulConfig.md</code></td></tr>
<tr><td>UnitFlag 512B 细粒度同步定义</td><td><code>昇腾NPU知识库/CANN社区版9.2.0-beta.1/01_AscendC算子开发/292_..._开启UnitFlag.md</code></td></tr>
<tr><td>351x 删除 L1→GM / GM→L0 直达通路</td><td><code>昇腾NPU知识库/00_硬件/昇腾950_NPU架构白皮书.txt</code>CV融合/数据通路章节);同仓 <code>昇腾950PR/昇腾950PR架构解读.md</code> §5.1</td></tr>
<tr><td>MM_CFG_NO_PRELOAD 显式开 enUnitFlag</td><td><code>ops-nn/matmul/mat_mul_v3/op_kernel/mat_mul_v3_common.h:72</code></td></tr>
<tr><td>普通 ASWT Iterate/GetTensorC 一一配对</td><td><code>ops-nn/matmul/mat_mul_v3/op_kernel/arch35/mat_mul_asw_kernel.h:140-141</code></td></tr>
<tr><td>StreamK 回调显式 unitFlag=3</td><td><code>ops-nn/matmul/mat_mul_v3/op_kernel/arch35/mat_mul_stream_k_kernel.h:36-37</code></td></tr>
<tr><td>StreamK K 分片累加L0C→workspace→AIV</td><td><code>ops-nn/matmul/mat_mul_v3/op_kernel/arch35/mat_mul_stream_k_block.h</code><code>mat_mul_stream_k_kernel.h:188-202</code></td></tr>
<tr><td>StreamK K 分片workspace + AIV 累加</td><td><code>ops-nn/matmul/mat_mul_v3/op_kernel/arch35/mat_mul_stream_k_kernel.h:188-259</code><code>mat_mul_stream_k_block.h</code></td></tr>
<tr><td>dbL0C 判据 baseM·baseN≤32768</td><td><code>ops-nn/matmul/mat_mul_v3/op_host/op_tiling/arch35/matmul_v3_tiling_helper.cpp:490</code></td></tr>
</tbody></table></body></html>