Files
matmul-analysis/Matmul/UnitFlag与L0C_DoubleBuffer分析.md

14 KiB
Raw Blame History

UnitFlag 与 L0C Double Buffer 的适用边界分析 —— 昇腾 950PRDAV_3510

关注点L0C Buffer 的 Double BufferdbL0C与 Fixpipe 的 UnitFlag 都能用来「掩盖 Fixpipe 搬出耗时」两者是否等价、是否可以互相替代mat_mul_v3 中各自的实际使用场景与边界是什么? 参考:昇腾950PR/昇腾950PR架构解读.md、官方文档《Matmul高阶API开启UnitFlag》《MatmulConfig》、源码 ops-nn/matmul/mat_mul_v3/op_host/op_tiling + op_kernel/arch35。 结论先行:两者掩盖的对象不同、不互为替代mat_mul_v3 中 L0C DB 与 UnitFlag 独立存在、可叠加,且 UnitFlag 在该算子里只在 StreamK 的自定义搬出回调里显式开启


0. 问题重述与先验共识

昇腾 950PR 上Matmul 的标准数据流是:

GM ──MTE2──► L1 ──MTE1──► L0A/L0B ──MMAD──► L0C ──Fixpipe──► GM(C)

其中 L0C 的输出Fixpipe 搬出)与 Cube 的计算MMAD之间存在「谁先谁后」的问题。用户观察到的两条掩盖路径

  1. L0C Double BufferdbL0C=2L0C 分两份,当前一份在 MMAD 计算,另一份在 Fixpipe 搬出,用「计算-搬出」重叠掩盖 Fixpipe。
  2. UnitFlag细粒度同步MMAD 每算完 512B 就立即触发 Fixpipe 搬出该 512B用「指令级流水」掩盖 Fixpipe。

问题:既然 UnitFlag 能这么细粒度地掩盖,L0C 还需要开 Double Buffer 吗? 两者的适用边界在哪?


1. 两条掩盖路径的原理性拆解

1.1 L0C Double Buffer 掩盖的是什么

L0C DB 的目标是:当前 base 块的 Fixpipe 搬出 与 下一个 base 块的 MMAD 计算 并行。它的掩盖对象是「base 块与 base 块之间的 Fixpipe 串行等待」。

  • 时序dbL0C=2方块为 base 块):
L0C 块0:  MMAD ──► Fixpipe 搬出
L0C 块1:           MMAD ──► Fixpipe 搬出
L0C 块2:                     MMAD ──► Fixpipe 搬出
时间轴:  ────────┬───────┬───────┬──►
                 块0搬出与块1计算重叠   块1搬出与块2计算重叠
  • 关键点:没有 L0C DB 时,当前 base 块必须等 Fixpipe 完全搬出才能开始下一个 base 块的 MMADL0C 只有一份,搬出期间不能写入);有 L0C DB 时,下一个 base 块的 MMAD 可以立即在另一份 L0C 上开始,当前块的 Fixpipe 与之并行。

1.2 UnitFlag 掩盖的是什么

UnitFlag 的目标是:单个 base 块内部MMAD 计算 与 Fixpipe 搬出 的细粒度并行。它的掩盖对象是「单个 base 块内部的 MMAD-Fixpipe 串行等待」。

  • 官方定义《Matmul高阶API开启UnitFlag》UnitFlag 为 MMAD 计算指令和 FIXPIPE 搬运指令提供基于内存访问的 512B 细粒度同步使计算与搬运流水并行。未开启时FIXPIPE 要等整条 MMAD 指令算完才搬出开启后MMAD 每算完 512B 数据FIXPIPE 立即搬出该 512B。

  • 时序(单个 base 块内):

未开 UnitFlag:  MMAD(整个base块) ──────► Fixpipe(整个base块)
开 UnitFlag:    MMAD[512B] ─► Fixpipe[512B]
                  MMAD[512B] ─► Fixpipe[512B]   ...
                512B 粒度交错,计算与搬出重叠)
  • 用户观察「最多剩一个小小的 16×16 尾巴 Fixpipe 输出不可掩盖」是对的:最后一个 512B 块算完后,它的 Fixpipe 没有后续 MMAD 可重叠,是尾开销。但相比整个 base 块的 Fixpipe 串行等待,这个尾开销很小。

1.3 两者的关系:掩盖对象不同,可叠加

维度 L0C Double Buffer UnitFlag
掩盖对象 base 块之间的 Fixpipe 串行 base 块内部的 MMAD-Fixpipe 串行
粒度 base 块级(一份 L0C 搬出时另一份在算) 512B 指令级
触发方式 Host tiling 决策dbL0C=1/2 Device kernel 里 enUnitFlag=true
资源成本 L0C 容量翻倍占用 无额外 Buffer但受模板/格式约束

关键结论:两者不互为替代,而是掩盖不同层级的串行。一个 base 块的完整耗时可以拆解为:

T_base_block ≈ max(T_MMAD_base, T_MTE1_base, T_MTE2_base) + T_Fixpipe_base_tail
  • T_MMAD_base:该 base 块的 MMAD 计算时间;
  • T_Fixpipe_base_tailFixpipe 搬出该 base 块的时间中无法被掩盖的部分。

开 L0C DB 的效果:下一个 base 块的 MMAD 可以并行启动,把「当前块 Fixpipe」的时间藏进下一个块的 MMAD 里。但如果当前块的 MMAD 本身就比 Fixpipe 短(计算快、搬出慢),下一个块的 MMAD 很快算完,又要等当前块的 Fixpipe → L0C DB 掩盖不住「计算远快于搬出」的场景

开 UnitFlag 的效果:把当前块内部的 MMAD 与 Fixpipe 交错,Fixpipe 不再等整个 base 块算完,而是边算边搬。即使当前块 MMAD 很快Fixpipe 也能跟着 MMAD 的进度流水走,把大部分搬出时间藏进 MMAD 里。

所以:L0C DB 掩盖「块间」串行UnitFlag 掩盖「块内」串行;在「单块 MMAD 短、Fixpipe 长」的场景L0C DB 掩盖不住,必须靠 UnitFlag在「单块 MMAD 长、Fixpipe 短」的场景L0C DB 已足够UnitFlag 收益小。


2. mat_mul_v3 中两者的实际使用场景

2.1 L0C Double Buffer 的使用场景Host tiling 决策)

mat_mul_v3 的 L0C DB 由 Host tiling 层计算 runInfo.dbL0C,写入 tiling data 的 dbL0C/l0cDB 字段Device 侧消费。源码中 dbL0C 的设置点:

设置点 文件 条件 结果
① 初始默认 matmul_v3_tiling_helper.cpp:134ResetBaseDefault dbL0C = DB_OFF_SIZE = 1(默认关)
② 主搜索后 matmul_v3_tiling_helper.cpp:490GetRebalanceBlock 末尾) baseM·baseN·4B·2 ≤ L0C(256KB) 满足则 dbL0C=2,否则 1
③ A 全载 matmul_v3_basic_aswt_tiling.cpp:203DoAL1FullLoad 同上 同上
④ B 全载 matmul_v3_basic_aswt_tiling.cpp:274DoBL1FullLoad 同上 同上

核心条件统一为:

dbL0C = (baseM · baseN · DATA_SIZE_FP32 · DB_SIZE ≤ L0C容量) ? 2 : 1

代入 950PR 数值L0C=256KB=262144BDATA_SIZE_FP32=4BDB_SIZE=2

baseM·baseN·4·2 ≤ 262144  →  baseM·baseN ≤ 32768 = 2^15

baseM=baseN=256 时:256·256 = 65536 > 32768dbL0C=1。 baseM=256、baseN=128 时:256·128 = 32768dbL0C=2

物理含义baseM=baseN=256 的 FP32 累加块恰好填满整个 L0C256KB开不了 DB只有把 baseN或 baseM砍到一半L0C 才能容纳两份,才能开 DB。这与「baseM=baseN=256 是重复读最少的最优解」存在张力:base 块取 256×256 时L0C 被占满,无法用 DB 掩盖 Fixpipe

使用场景总结mat_mul_v3 中L0C DB 在绝大多数分支都可能开启(普通 ASWT、A/B 全载、StreamK 复用同一引擎),但前提都是 baseM·baseN ≤ 32768。当最优 base 块是 256×256 时dbL0C 强制为 1。

2.2 UnitFlag 的使用场景Device kernel 开启)

mat_mul_v3 中 UnitFlag 由 kernel 侧的 MatmulConfig.enUnitFlag 或自定义搬出回调的 fixpipeParams.unitFlag 控制。检索结果:

  • 全局默认配置mat_mul_v3_common.h 定义 MM_CFG_NO_PRELOAD = GetMDLConfig(..., true)(最后一个参数即 enUnitFlag。多个 kernelMatMulBasicKernel 等)默认 MM_CFG = MM_CFG_NO_PRELOAD。但 GetMDLConfig 是 MDL 模板,官方文档明确「MDL 下 enUnitFlag 默认不使能」,且 GetMDLConfig 的具体默认参数需查 API 文档——MM_CFG_NO_PRELOAD 的 enUnitFlag 实际生效值取决于 GetMDLConfig 的实现,不能直接断言 mat_mul_v3 所有路径都开了 UnitFlag。

  • 唯一显式开启点mat_mul_stream_k_kernel.h:36-37StreamK 的自定义搬出回调 CustomDataCopyOut

if (params->enUnitFlag) {
    fixpipeParams.unitFlag = MM_FIX_PIPE_UNIT_FLAG; // 使能unitflag的参数 3U
}

这里 params->enUnitFlag 来自上层传入的搬出参数,unitFlag = 3U 直接写进 FixpipeParamsC310

使用场景总结mat_mul_v3 中 UnitFlag 的显式、确定开启只发生在 StreamK 分支的自定义搬出回调StreamK 的部分和需要从 L0C 搬到 GM workspace再由 AIV 累加,搬出路径特殊)。普通 ASWT/A/B 全载分支是否开 UnitFlag取决于 GetMDLConfig 的默认值官方口径MDL 模板默认不使能),源码没有显式强制开启。


3. 适用条件边界:什么时候用哪个

3.1 L0C Double Buffer 的适用边界

条件 说明
baseM·baseN ≤ 32768FP32 累加) 硬约束L0C 必须能容纳两份 base 块
存在「块间 Fixpipe 串行」 即 base 块数 > 1多 base 块循环)
Fixpipe 耗时与 MMAD 相当或更短 若 Fixpipe 远快于 MMAD开不开 DB 都掩盖得住;若 Fixpipe 远慢DB 掩盖不住(需 UnitFlag
无 L0C 累加冲突 若 base 块需要在 L0C 上做 K 方向累加split-K 等DB 语义复杂

典型受益场景baseM=256、baseN=128 的矩形 base 块,多 base 块循环MMAD 与 Fixpipe 耗时相当。

3.2 UnitFlag 的适用边界(官方文档明确约束)

约束 内容
模板限制 仅支持 Norm、IBShare、MDL 三个模板(MatmulConfig.enUnitFlag 字段说明)
流水互斥 使能 UnitFlag 时,不支持同时存在 CO1(L0C)→GM 和 A1(L1)→GM 两种搬出流水
累加限制 使能 UnitFlag + L0C 累加时,不支持多次 Iterate 计算、一次 GetTensorC 输出
收益前提 仅当 MMAD 与 FIXPIPE 串行且未被其他流水掩盖时收益大;若已被 MTE2 Bound 掩盖,收益很小
默认状态 Norm/IBShare 默认使能,MDL 默认不使能(需显式 enUnitFlag=true

典型受益场景:单个 base 块内 MMAD 很快、Fixpipe 慢(如小 K、大 MN且模板是 Norm/IBShare/MDL、无 L1→GM 混合搬出。

3.3 两者的叠加与互斥判断

从官方约束与源码实现看:

  • 可叠加L0C DB 是 Host 层的 buffer 划分UnitFlag 是 Device 层的指令同步,两者作用于不同层级,不冲突。isA2B2Shared 字段说明里甚至建议「开启 A2B2 共享时同时设 enUnitFlag=true」。
  • 不互斥,但有先后baseM=baseN=256 时 L0C 开不了 DB容量不够此时若 Fixpipe 是瓶颈,只能靠 UnitFlag 在块内掩盖baseM·baseN≤32768 时 DB 可开,块间掩盖由 DB 负责,块内细粒度掩盖仍可由 UnitFlag 补充。
  • StreamK 的特殊性StreamK 的搬出是「L0C→workspace→AIV 累加→GM」不是直接 L0C→GM且用自定义回调显式开了 UnitFlag——因为 StreamK 的部分和搬出频繁、且与计算强相关,细粒度同步收益大。

4. 对用户问题的直接回答

Q1mat_mul_v3 中是否存在 L0C 上开 double buffer 的场景?

存在。Host 层 dbL0CbaseM·baseN·4B·2 ≤ L0C(256KB)(即 baseM·baseN ≤ 32768时置 2普通 ASWT、A/B 全载、StreamK复用同一引擎都会走到。但当最优 base 块是 256×256 时dbL0C=1开不了

Q2是否存在开 UnitFlag 特性的场景?

存在,且显式开启点唯一StreamK 分支的自定义搬出回调 CustomDataCopyOutmat_mul_stream_k_kernel.h),在 params->enUnitFlag 为真时设 fixpipeParams.unitFlag = 3U。普通 ASWT 分支的 UnitFlag 状态取决于 MM_CFG_NO_PRELOADGetMDLConfig)的默认值——官方口径 MDL 默认不使能,因此普通 ASWT 路径上 UnitFlag 大概率是关的,需对照 GetMDLConfig 实际参数确认。

Q3L0C DB 和 UnitFlag 的适用边界?

  • L0C DB掩盖「base 块之间」的 Fixpipe 串行,要求 L0C 能容两份 base 块baseM·baseN≤32768适合多 base 块、MMAD 与 Fixpipe 耗时相当的场景。
  • UnitFlag掩盖「base 块内部」的 MMAD-Fixpipe 串行512B 细粒度,适合单块 MMAD 快、Fixpipe 慢的场景但受模板Norm/IBShare/MDL、流水互斥不能同时 CO1→GM 和 A1→GM、累加方式不能多次 Iterate 一次输出约束MDL 默认关。

Q4UnitFlag 这么好,是不是没必要开 L0C DB 了?

不是。两者掩盖对象不同:L0C DB 掩盖块间、UnitFlag 掩盖块内。在「单块 MMAD 长、多块循环」的场景L0C DB 已能很好掩盖UnitFlag 收益小;在「单块 MMAD 短、Fixpipe 长」的场景L0C DB 掩盖不住(下一块 MMAD 很快算完又要等当前块搬出),必须靠 UnitFlag。两者是正交互补不是替代关系。


5. 对 mat_mul_v3 的观察与后续建议

  1. UnitFlag 在普通 ASWT 路径的默认状态需确认MM_CFG_NO_PRELOAD = GetMDLConfig(..., true) 的最后一个参数是否真是 enUnitFlag、MDL 默认值是否生效,建议对照 GetMDLConfig API 文档核实。若普通 ASWT 默认未开 UnitFlag而 StreamK 开了,两者在 Fixpipe 掩盖上的行为不一致,值得在性能模型里区分。
  2. dbL0C 与 baseM/baseN 的耦合dbL0C 直接由 baseM·baseN 决定,但 baseM/baseN 的搜索(GetRebalanceBlock)目标是 CubeBound + 负载均衡没有把「dbL0C 能否开」纳入目标函数。当 256×256开不了 DB与 256×128能开 DB性能接近时模型可能选前者而牺牲 DB 收益——这是一个潜在的 tiling 改进点。
  3. 性能模型需区分两种掩盖机制:后续 Phase 1 的 T_total = max(...) 模型里Fixpipe 项应区分「块间掩盖DB」和「块内掩盖UnitFlag否则对 Fixpipe Bound 场景会高估或低估。

附录:关键证据索引

证据 位置
UnitFlag 定义与约束 昇腾NPU知识库/CANN社区版9.2.0-beta.1/01_AscendC算子开发/292_..._Matmul高阶API开启UnitFlag.md、商用版 9.0.0 203_..._使能UnitFlag.md
enUnitFlag 字段与默认值 昇腾NPU知识库/CANN商用版9.0.0/02_API参考/AscendC_API/626_..._MatmulConfig.md
dbL0C 设置条件 ops-nn/matmul/mat_mul_v3/op_host/op_tiling/arch35/matmul_v3_tiling_helper.cpp:490
dbL0C 在全载分支 .../matmul_v3_basic_aswt_tiling.cpp:203,274
UnitFlag 显式开启 ops-nn/matmul/mat_mul_v3/op_kernel/arch35/mat_mul_stream_k_kernel.h:36-37
MM_CFG_NO_PRELOAD 定义 ops-nn/matmul/mat_mul_v3/op_kernel/mat_mul_v3_common.h:72