diff --git a/Matmul/UnitFlag能否取代L0C_DoubleBuffer.html b/Matmul/UnitFlag能否取代L0C_DoubleBuffer.html new file mode 100644 index 0000000..cde7af5 --- /dev/null +++ b/Matmul/UnitFlag能否取代L0C_DoubleBuffer.html @@ -0,0 +1,136 @@ +UnitFlag 能否完全取代 L0C Double Buffer —— 昇腾 950PR(DAV_3510)时序与数据流分析 + +

UnitFlag 能否完全取代 L0C Double Buffer —— 昇腾 950PR(DAV_3510)时序与数据流分析

+ +
+

0. 先修正并锁定一个前提事实

+ +
// 官方 GetMDLConfig 签名(第 7 个位置参数即 enUnitFlag)
+GetMDLConfig(intrinsicsLimit, batchLoop, doMTE2Preload, isVecND2NZ,
+             isPerTensor, hasAntiQuantOffset, enUnitFlag=false, ...)
+
+// mat_mul_v3_common.h
+constexpr MatmulConfig MM_CFG_NO_PRELOAD =
+    GetMDLConfig(false, false, 0, false, false, false, /*enUnitFlag=*/true);
+

MM_CFG_NO_PRELOAD 的第 7 个实参是 trueenUnitFlag 被显式开启。而 mat_mul_v3 的普通 ASWT、A/B 全载、SplitK 等模板 kernel 的默认模板参数就是 MM_CFG = MM_CFG_NO_PRELOAD;StreamK 又在自定义搬出回调 CustomDataCopyOut 里再显式 fixpipeParams.unitFlag = MM_FIX_PIPE_UNIT_FLAG(=3)

+

结论:mat_mul_v3 在 950PR 的主路径上,UnitFlag 默认是开启的(普通模板经 MM_CFG_NO_PRELOAD,StreamK 经回调)。这与"MDL 模板默认不使能"并不矛盾——MDL 默认关,但 mat_mul_v3 用 GetMDLConfig 把这一位显式置真了。

+
+

1. 两条掩盖路径的时序本质(先把"流水"画清楚)

+

设 MTE1(L1→L0)与 MTE2(GM→L1)已由 L1/L0 的 Double Buffer 掩盖(这是 tiling 的常规动作,与本文讨论的 L0C 无关),则单个 base 块的耗时只需关注 MMAD 计算Fixpipe 搬出 两级:

+
t_mm = baseM·baseN·baseK / R_cube        (MMAD 计算时间,R_cube=8192 FLOP/cycle)
+t_fp = baseM·baseN·dtypeC / BW_fp        (Fixpipe 搬出时间,dtypeC=输出dtype字节数)
+

1.1 无 DB 无 UnitFlag —— 完全串行

+
块n:   [==== MMAD ====]··等待··[---- Fixpipe ----]
+块n+1:                          [==== MMAD ====]··等待··[---- Fixpipe ----]
+周期 T = t_mm + t_fp
+

L0C 只有一份,Fixpipe 搬出期间 Cube 不能写 → 块内串行;且当前块 Fixpipe 不结束,下一块 MMAD 不能开始 → 块间也串行。这是最差形态。

+

1.2 只开 L0C Double Buffer —— 块间掩盖

+

L0C 分两份(A/B),块 n 在 L0C.A 搬出的同时,块 n+1 在 L0C.B 上算:

+
L0C.A(块n):   [==== MMAD ====]········[---- Fixpipe ----]
+L0C.B(块n+1):            [==== MMAD ====]········[---- Fixpipe ----]
+L0C.A(块n+2):                                   [==== MMAD ====]···
+周期 T = max(t_mm, t_fp)
+

关键:DB 让"块 n 的 Fixpipe"与"块 n+1 的 MMAD"重叠,把块间等待消掉。但注意两点:

+ +

1.3 只开 UnitFlag —— 块内 512B 细粒度流水

+

UnitFlag 把单个 base 块按 512B 切成若干 micro-tile,MMAD 每算完一个 512B 就立即触发对应 Fixpipe,两者在同一块内交错:

+
块n(L0C): [M][M][M]...[M]      ← MMAD 按 512B 推进
+           [F][F][F]...[F]    ← Fixpipe 跟随 512B,逐段搬出
+周期 T ≈ max(t_mm, t_fp) + tail
+        (tail = 最后一个 512B micro-tile 的 Fixpipe 尾开销,可忽略)
+

关键差异:UnitFlag 不依赖"下一块的 MMAD"来掩盖,它在当前块内部就让 Fixpipe 跟着 MMAD 的进度流水走。因此:

+ +
+

2. 块内能 max 了,块间还需不需要考虑?—— 叠加性论证

+

这是你追问的核心。我们严格区分两种情形。

+

2.1 情形 A:K 不在单块内累加(一次 Iterate 算完一整块 K)

+

这是 mat_mul_v3 最常见的形态:一个 base 块的 singleCoreK 完整累加进 L0C,算完一次性 Fixpipe 搬出到 GM。

+ +

所以在这个情形下,DB 的块间掩盖与 UnitFlag 的块内掩盖争夺的是同一段时间(同一块的 Fixpipe),被 UnitFlag 掩盖后,DB 几乎无的放矢 → 两者收益不叠加。 你的判断"开了 UnitFlag,上一块的 cube 计算已经和上一块的 fixpipe 重叠了,就没法再和下一块的 cube 重叠"在 K 内一次累加场景下成立

+

2.2 情形 B:K 在单块内分片累加(多次 Iterate 累加进同一 L0C)

+

singleCoreK 很大、被切成多个 baseK 分片时,同一 base 块要做多轮 MMAD→(部分结果留在 L0C)Fixpipe 只在最后一次累加完成后才整体搬出

+ +

所以 UnitFlag 的使用边界里明确排除了「L0C K 累加」这一类场景,而这恰恰是多分片大 K 的常见形态 → 这类场景 UnitFlag 无法取代 DB。

+

2.3 叠加性结论

+ + + + + +
场景UnitFlagL0C DB能否叠加主导掩盖
K 一次累加,t_mm≥t_fp,能开DB块内掩盖块间掩盖收益不叠加(同一段 Fixpipe)任一即可,UnitFlag 更省 L0C
K 一次累加,t_mm<t_fp块内掩盖(Cube不停)掩盖不足(退化 t_fp)DB 无效,UnitFlag 主导UnitFlag
K 一次累加,baseM·baseN>32768块内掩盖开不了DB 不可用,UnitFlag 主导UnitFlag
K 分片累加(大 K)不支持(多次Iterate一次输出)块间掩盖UnitFlag 不可用,DB 主导L0C DB
+
+

3. UnitFlag 能否完全取代 L0C DB?

+

不能完全取代。 结合上面论证与 NPU 架构约束,分三层说清:

+

3.1 从掩盖能力看:UnitFlag 掩盖域 ⊇ DB(在它能用的场景内)

+

在「K 一次累加、C 输出 GM、Norm/IBShare/MDL 模板」这个 UnitFlag 的合法域内:

+ +

所以在 UnitFlag 合法域内,DB 相对它没有独立价值,可以被取代。你的直觉在这一层是对的。

+

3.2 从合法域看:UnitFlag 有硬约束,DB 是兜底

+

UnitFlag 的官方约束(缺一不可):

+
    +
  1. 模板限制:仅 Norm / IBShare / MDL 三模板;
  2. +
  3. 流水互斥:使能后不允许同时存在 CO1(L0C)→GM 与 A1(L1)→GM 两种搬出流水
  4. +
  5. 累加限制:使能 + L0C 累加时,不支持多次 Iterate、一次 GetTensorC 输出
  6. +
  7. 收益前提:仅当 MMAD 与 Fixpipe 串行且未被 MTE2 等其他流水掩盖时才有收益。
  8. +
+

凡是落在这些约束之外的场景(最典型就是 §2.2 的多分片 K 在 L0C 累加),UnitFlag 直接不可用,只能退回到 L0C DB 做块间掩盖。

+

3.3 从 mat_mul_v3 实际看:默认已开 UnitFlag,DB 是「补位」而非「主选」

+ +

最终判断:UnitFlag 在其合法域内可以取代 L0C DB(且更优),但因「模板/流水互斥/累加」三重硬约束,它无法覆盖全部场景;L0C DB 作为不依赖这些约束的块间掩盖手段,是 UnitFlag 失效场景下的必要补充,两者是主从互补关系,而非简单的"新替旧"。

+
+

4. 对本项目性能建模与最优方案的直接推论

+
    +
  1. 建模时 Fixpipe 项要分两种掩盖:在性能模型 T_total = max(T_MMAD, T_MTE2, T_MTE1, T_FIXPIPE) 中,T_FIXPIPE 的实际暴露量应写成
  2. +
+ +
    +
  1. 最优 tiling 的取舍被简化:既然 UnitFlag 默认开,baseM/baseN 的搜索不必再为"能否开 L0C DB"让步,可以更激进地逼近 256×256(重复读最少)。原先"为开 DB 而把 baseN 砍半"的权衡在 UnitFlag 合法域内不成立。这是对 §3.2 GetRebalanceBlock 的一处实质修正点。
  2. +
  3. StreamK 的 K 分片场景要单独建模:StreamK/大 K 分片累加命中 UnitFlag 的"多次 Iterate 一次输出"约束,其 Fixpipe 掩盖机制与普通 ASWT 不同,性能模型需为这类 case 单列。
  4. +
+
+

附录:关键证据索引

+ + + + + + + + +
证据位置
GetMDLConfig 签名(第7参=enUnitFlag)昇腾NPU知识库/CANN商用版9.0.0/02_API参考/AscendC_API/631_..._GetMDLConfig.md
enUnitFlag 默认值与约束昇腾NPU知识库/CANN商用版9.0.0/02_API参考/AscendC_API/626_..._MatmulConfig.md
UnitFlag 512B 细粒度同步定义昇腾NPU知识库/CANN社区版9.2.0-beta.1/01_AscendC算子开发/292_..._开启UnitFlag.md
MM_CFG_NO_PRELOAD 显式开 enUnitFlagops-nn/matmul/mat_mul_v3/op_kernel/mat_mul_v3_common.h:72
StreamK 回调显式 unitFlag=3ops-nn/matmul/mat_mul_v3/op_kernel/arch35/mat_mul_stream_k_kernel.h:36-37
StreamK K 分片累加(L0C→workspace→AIV)ops-nn/matmul/mat_mul_v3/op_kernel/arch35/mat_mul_stream_k_block.hmat_mul_stream_k_kernel.h:188-202
dbL0C 判据 baseM·baseN≤32768ops-nn/matmul/mat_mul_v3/op_host/op_tiling/arch35/matmul_v3_tiling_helper.cpp:490
\ No newline at end of file