已合并
矩阵乘模板总结文档 #383
sunhao_hw创建于 2025年12月3日
矩阵乘模板总结文档 #383
已合并
sunhao_hw创建于 2025年12月3日
3 个文件变更+545-1
Mdocs/advanced_collections.md+1-0
@@ -3,5 +3,6 @@
3本文档按照由浅入深的次序,组织CATLASS模板库中的进阶材料,读者可按照个人兴趣查阅具体内容。3本文档按照由浅入深的次序,组织CATLASS模板库中的进阶材料,读者可按照个人兴趣查阅具体内容。
4 4 
5- [catlass_optimize_guidance](./contents/advanced/catlass_optimize_guidance.md) - 介绍模板库下的基础调优方式,包括如何通过Tiling调参、应用不同的Dispatch策略的方式,快速获得性能提升。5- [catlass_optimize_guidance](./contents/advanced/catlass_optimize_guidance.md) - 介绍模板库下的基础调优方式,包括如何通过Tiling调参、应用不同的Dispatch策略的方式,快速获得性能提升。
6+- [matmul_template_summary](./contents/advanced/matmul_template_summary.md) - 对模板库的`examples`目录内已有的`matmul`模板设计进行介绍,包含样例模板清单、理论模板清单、工程优化清单、模板应用浅述,可用于matmul性能调优时参考。
6- [swizzle_explanation](./contents/advanced/swizzle_explanation.md) - 对模板库中`Swizzle`策略的基本介绍,这影响了AI Core上计算基本块间的顺序。7- [swizzle_explanation](./contents/advanced/swizzle_explanation.md) - 对模板库中`Swizzle`策略的基本介绍,这影响了AI Core上计算基本块间的顺序。
7- [dispatch_policies](./contents/advanced/dispatch_policies.md) - 对模板库在`Block`层面上`BlockMmad`中的一个重要模板参数`DispatchPolicy`的介绍。8- [dispatch_policies](./contents/advanced/dispatch_policies.md) - 对模板库在`Block`层面上`BlockMmad`中的一个重要模板参数`DispatchPolicy`的介绍。
Mdocs/contents/advanced/catlass_optimize_guidance.md+1-1
@@ -77,7 +77,7 @@ L0C实际占用 = L0::M * L0::N * 4(Byte)
77 77 
78### 样例覆盖和选择78### 样例覆盖和选择
79 79 
80-当前库上基础Matmul算子80+当前库上基础Matmul算子如下,更详细介绍请见[矩阵乘模板清单](./matmul_template_summary.md)
81 81 
82- [00_basic_matmul](../examples/00_basic_matmul/basic_matmul.cpp),采用`MmadAtlasA2Pingpong`的dispatchPolicy,使能pingpong策略82- [00_basic_matmul](../examples/00_basic_matmul/basic_matmul.cpp),采用`MmadAtlasA2Pingpong`的dispatchPolicy,使能pingpong策略
83 83 
Adocs/contents/advanced/matmul_template_summary.md+543-0
@@ -0,0 +1,543 @@
1+# 矩阵乘模板总结
2+ 
3+当前库上`examples`内包含多种矩阵乘的`样例模板`,其来源是不同的matmul`理论模板`与工程实践中发现的`工程优化`点的组合。在充分理解了各个`理论模板``工程优化`后,开发者可以基于问题场景选择适合的`样例模板`、甚至进一步自行组合出库上没有的新的`样例模板`,来达到矩阵乘的性能极致优化。
4+ 
5+注意,本文档仅总结矩阵乘方案相关的样例,其他涉及量化、groupMatmul、后处理等的矩阵乘不在此处总结。
6+ 
7+## 样例模板清单
CheaterAbec
CheaterAbecCheaterAbec2025年12月4日

样例模板清单是否缺少30样例和32样例,是否需要预留位置后续补充,建议简要说明不放在该文档目录下的原因

likedislike
8+<details>
9+<summary><strong><font size="4">00_basic_matmul</font></strong></summary>
L
Lli_wei212025年12月4日

可以添加pingpong这种模板的说明,简要说明一下double buffer性能提升方法

likedislike
10+ 
11+- 理论模板:`Common模板`
12+- 工程优化:`流水优化(Multi Buffer)`
13+- 关键交付件
14+ - host:[00_basic_matmul](../../../examples/00_basic_matmul/basic_matmul.cpp)
15+ - kernel:[basic_matmul.hpp](../../../include/catlass/gemm/kernel/basic_matmul.hpp)
16+ - blockMmad:[block_mmad_pingpong.hpp](../../../include/catlass/gemm/block/block_mmad_pingpong.hpp)
17+- dispatchPolicy:`MmadAtlasA2Pingpong`
CheaterAbec
CheaterAbecCheaterAbec2025年12月4日

建议附带相关的链接,下同

likedislike
sunhao_hw
2025年12月4日 评论:
18+</details>
19+ 
20+<details>
21+<summary><strong><font size="4">04_padding_matmul</font></strong></summary>
22+ 
23+- 理论模板:`Common模板`
24+- 工程优化:
25+ - `流水优化(Multi Buffer)`
26+ - `读取带宽优化(padding)- PaddingMatrixND`
27+- 关键交付件
28+ - host:[04_padding_matmul](../../../examples/04_padding_matmul/padding_matmul.cpp)
29+ - kernel:[padding_matmul.hpp](../../../include/catlass/gemm/kernel/padding_matmul.hpp)
30+ - blockMmad:[block_mmad_pingpong.hpp](../../../include/catlass/gemm/block/block_mmad_pingpong.hpp)
31+- dispatchPolicy:`MmadAtlasA2Pingpong`
32+</details>
33+ 
34+<details>
35+<summary><strong><font size="4">06_optimized_matmul</font></strong></summary>
36+ 
37+- 理论模板:`Common模板`
38+- 工程优化:
39+ - `流水优化(Multi Buffer)`
40+ - `流水优化(Preload)`
41+ - `读取带宽优化(Padding)- PaddingMatrixNZ`
42+ - `读取带宽优化(ShuffleK)`
43+ - `读取带宽优化(小M下指令替换)`(需要修改样例使能)
44+- 关键交付件
45+ - host:[06_optimized_matmul](../../../examples/06_optimized_matmul/optimized_matmul.cpp)
46+ - kernel:[optimized_matmul.hpp](../../../include/catlass/gemm/kernel/optimized_matmul.hpp)
47+ - Padding前处理组件:[padding_matmul.hpp](../../../include/catlass/gemm/kernel/padding_matmul.hpp)
48+ - blockMmad:[block_mmad_preload.hpp](../../../include/catlass/gemm/block/block_mmad_preload.hpp)
49+- dispatchPolicy:`MmadAtlasA2Preload`
50+- ⚠️ 注意:即使没有使用`PaddingMatrixNZ`前处理,依然会产生MIX算子编译和CV1:0启动的开销(比仅AIC启动的开销大)
51+</details>
52+ 
53+<details>
54+<summary><strong><font size="4">09_splitk_matmul</font></strong></summary>
55+ 
56+- 理论模板:`多核切K模板 MultiCoreSplitK`
57+- 工程优化:`流水优化(Multi Buffer)`
58+- 关键交付件
59+ - host:[09_splitk_matmul](../../../examples/09_splitk_matmul/optimized_matmul.cpp)
60+ - kernel:[splitk_matmul.hpp](../../../include/catlass/gemm/kernel/splitk_matmul.hpp)
61+ - blockMmad:[block_mmad_pingpong.hpp](../../../include/catlass/gemm/block/block_mmad_pingpong.hpp)
62+- dispatchPolicy:`MmadAtlasA2Pingpong`
63+</details>
64+ 
65+<details>
66+<summary><strong><font size="4">21_basic_matmul_preload_zN</font></strong></summary>
67+ 
68+(此样例主要承载NZ排布输入的适配方法,也可换成ND排布输入,无MIX算子编译启动的开销)
longjihui
longjihuilongjihui2025年12月7日

可以和06_optimized_matmul保持一致,放在注意里面

likedislike
69+- 理论模板:`Common模板`
70+- 工程优化:
71+ - `流水优化(Multi Buffer)`
72+ - `流水优化(Preload)`
73+ - `读取带宽优化(ShuffleK)`
74+- 关键交付件
75+ - host:[21_basic_matmul_preload_zN](../../../examples/09_splitk_matmul/basic_matmul_preload_zN.cpp)
76+ - kernel:[basic_matmul_preload.hpp](../../../include/catlass/gemm/kernel/basic_matmul_preload.hpp)
77+ - blockMmad:[block_mmad_preload.hpp](../../../include/catlass/gemm/block/block_mmad_preload.hpp)
78+- dispatchPolicy:`MmadAtlasA2Preload`
79+</details>
80+ 
81+<details>
82+<summary><strong><font size="4">22_padding_splitk_matmul</font></strong></summary>
83+ 
84+- 理论模板:`多核切K模板 MultiCoreSplitK`
85+- 工程优化:
86+ - `流水优化(Multi Buffer)`
87+ - `读取带宽优化(padding)- PaddingMatrixND`
88+- 关键交付件
89+ - host:[22_padding_splitk_matmul](../../../examples/22_padding_splitk_matmul/padding_splitk_matmul.cpp)
90+ - kernel:[padding_splitk_matmul.hpp](../../../include/catlass/gemm/kernel/padding_splitk_matmul.hpp)
91+ - Padding前处理组件:[padding_matmul.hpp](../../../include/catlass/gemm/kernel/padding_matmul.hpp)
92+ - SplitkReduceAdd后处理组件:[splitk_matmul.hpp](../../../include/catlass/gemm/kernel/splitk_matmul.hpp)
93+ - blockMmad:[block_mmad_pingpong.hpp](../../../include/catlass/gemm/block/block_mmad_pingpong.hpp)
94+- dispatchPolicy:`MmadAtlasA2Pingpong`
longjihui
longjihuilongjihui2025年12月7日

前面几个的BlockScheduler也可以考虑补充上(虽然是通用的zn),后面好像单独补充了特殊的几个

likedislike
95+</details>
96+ 
97+<details>
98+<summary><strong><font size="4">25_matmul_full_loadA</font></strong></summary>
99+ 
100+(此样例及相关组件仅适配了A矩阵全载实现,需要实现B矩阵全载可参考关键交付件自行开发)
longjihui
longjihuilongjihui2025年12月7日

同21,可以和06_optimized_matmul保持一致,放在注意里面

likedislike
101+- 理论模板:`Common模板`
102+- 工程优化:
103+ - `流水优化(Multi Buffer)`(全载的A矩阵在L1上不使用多buffer)
104+ - `读取带宽优化(L1常驻)`
105+- 关键交付件
106+ - host:[25_matmul_full_loadA](../../../examples/09_splitk_matmul/25_matmul_full_loadA.cpp)
107+ - kernel:[matmul_full_loadA.hpp](../../../include/catlass/gemm/kernel/matmul_full_loadA.hpp)
108+ - blockMmad:[block_mmad_pingpong_full_loadA.hpp](../../../include/catlass/gemm/block/block_mmad_pingpong_full_loadA.hpp)
109+- dispatchPolicy:`MmadAtlasA2FullLoadA`
110+- BlockScheduler:`GemmIdentityBlockSwizzleL1FullLoad`
111+</details>
112+ 
113+<details>
114+<summary><strong><font size="4">31_small_matmul</font></strong></summary>
115+ 
116+- 理论模板:`Common模板`
117+- 工程优化:
118+ - `流水优化(Multi Buffer)`
119+ - `Scalar开销消减`
120+- 关键交付件
121+ - host:[31_small_matmul](../../../examples/31_small_matmul/small_matmul.cpp)
122+ - kernel:[small_matmul.hpp](../../../include/catlass/gemm/kernel/small_matmul.hpp)
123+ - blockMmad:[block_mmad_small.hpp](../../../include/catlass/gemm/block/block_mmad_small.hpp)
124+- dispatchPolicy:`MmadAtlasA2Small`
125+- BlockScheduler:kernel内实际不使用
126+</details>
127+ 
128+<details>
129+<summary><strong><font size="4">34_single_core_splitk_matmul</font></strong></summary>
130+ 
H
Hhuangxin3612025年12月4日

这里可以补充streamK和aivmatmul的相应实现样例,以及对应的说明

likedislike
131+- 理论模板:`单核切K模板 SingleCoreSplitK`
132+- 工程优化:
133+ - `流水优化(Multi Buffer)`
134+ - `读取带宽优化(Padding)- PaddingMatrixNZ`
135+ - `写出带宽优化`
136+- 关键交付件
137+ - host:[34_single_core_splitk_matmul](../../../examples/34_single_core_splitk_matmul/single_core_splitk.cpp)
138+ - kernel:[single_core_slicek_matmul.hpp](../../../include/catlass/gemm/kernel/single_core_slicek_matmul.hpp)
139+ - Padding前处理组件和RemovePaddingNDAndCast后处理组件:[padding_matmul.hpp](../../../include/catlass/gemm/kernel/padding_matmul.hpp)
140+ - blockMmad:[block_mmad_single_core_splitk.hpp](../../../include/catlass/gemm/block/block_mmad_single_core_splitk.hpp)
141+- dispatchPolicy:`MmadAtlasA2SingleCoreSplitk`
142+- BlockScheduler:`SingleCoreSplitkGemmIdentityBlockSwizzle`
143+</details>
144+ 
145+## 理论模板清单
CheaterAbec
CheaterAbecCheaterAbec2025年12月4日

建议调换理论模板清单和样例模板清单的位置,可读性更好;否则阅读样例模板清单时候出现了理论模板清单的相关内容,不符合一般阅读顺序

likedislike
146+ 
147+<details>
148+<summary><strong><font size="4">Common模板</font></strong></summary>
149+ 
150+### Tiling建模
151+ 
152+<img src="https://raw.gitcode.com/user-images/assets/7801479/b1cb21ac-af83-4736-8582-4ed7392d766b/1common.png" width="80%">
153+ 
tianxinghui
tianxinghuitianxinghui2025年12月4日

tiling过程从完整MKN到核内L1上m1,少了分核过程?

likedislike
154+如图展示一个常规的fp16的矩阵运算(L0C上按照fp32累加),定义相关参数:
155+- 问题shape:$M$,$N$,$K$
156+- 搬入L1Cache时的TileShape:$m_1$,$n_1$,$k_1$
157+- 搬入L0A/LOB、搬出L0C时的TileShape:$m_0$,$n_0$,$k_0$
158+ 
159+采用 $M$、$N$ 方向分核,按照$m_1$、$n_1$切分,产生$\frac{MN}{m_1n_1}$个基本任务块、分配给AIC核完成搬运和计算,每个基本任务块需要搬运$m_1K+Kn_1$的数据、计算得到$m_1n_1$的结果并搬出。由此产生约束:
160+- $m_1k_1*L1Stage_A + n_1k_1*L1Stage_B <= L1Size / 2Byte$
161+- $m_0k_0*L0AStage <= L0ASize / 2Byte$
162+- $n_0k_0*L0BStage <= L0BSize / 2Byte$
163+- $m_0n_0*L0CStage <= L0CSize / 4Byte$
164+- $m_0 = m_1$
165+- $n_0 = n_1$
166+ 
167+### 读取数据量
168+每个基本任务块需要搬运$m_1K+Kn_1$的数据,总读取量为:
169+ 
170+$2Byte * [m_1K+Kn_1] * \frac{MN}{m_1n_1} = 2Byte * MNK * [\frac{1}{m_1}+\frac{1}{n_1}]$
171+ 
172+### 写出数据量
173+每个基本任务块计算得到$m_1n_1$的结果并搬出,总写出量为:
174+ 
175+$2Byte * MN$
176+ 
177+### 计算量
178+输出矩阵C的每个数据点需要$K$次乘加,总计算量固定为:
179+ 
180+$2MNK$
181+ 
182+计算耗时多数情况下为刚性时间,仅与参与计算的AIC核数相关,在各理论模板中都一样,后续不再赘述。
183+</details>
184+ 
185+<details>
186+<summary><strong><font size="4">多核切K模板 MultiCoreSplitK</font></strong></summary>
187+ 
188+### Tiling建模
189+ 
190+<img src="https://raw.gitcode.com/user-images/assets/7801479/d4b0e2d2-4333-4df4-9af2-44654cc37e54/2multiCoreSplitK.png" width="80%">
191+ 
192+如图展示一个常规的fp16的矩阵运算(L0C上按照fp32累加),$MN$方向共切分12个基本任务块,假设有24个AIC物理核,此时负载不均衡,故引入$K$轴切分成2个$k$、产生24个基本任务块,在AIC上负载均衡。定义相关参数:
193+- 问题shape:$M$,$N$,$K$
194+- 搬入L1Cache时的TileShape:$m_1$,$n_1$,$k_1$
195+- 搬入L0A/LOB、搬出L0C时的TileShape:$m_0$,$n_0$,$k_0$
196+- <font color="red">相较Common模板</font>,新增$K$方向切分长度$k$
197+ 
198+相比`Common模板`,为了减少读取数据量,在较大的$m_1$、$n_1$下,可能存在负载均衡问题,即$MN$方向切分的任务块远少于AIC核数,导致读取带宽不高(核数不够),所以加入$K$方向分核。采用 $M$、$N$、$K$ 方向分核,产生$\frac{MNK}{m_1n_1k}$个基本任务块、分配给AIC核完成搬运和计算,每个基本任务块需要搬运$m_1k+kn_1$的数据、计算得到$m_1n_1$的结果并搬出。硬件上约束与Common相同。
199+ 
200+### 读取数据量
201+每个基本任务块需要搬运$m_1K+Kn_1$的数据,总读取量与`Common模板`一致:
202+ 
203+$2Byte * [m_1k+kn_1] * \frac{MNK}{m_1n_1k} = 2Byte * MNK * [\frac{1}{m_1}+\frac{1}{n_1}]$
204+ 
205+### 写出数据量
206+每个基本任务块计算得到$m_1n_1$的结果并搬出,需要$\frac{K}{k}$个基本块累加来得到输出矩阵C的$m_1n_1$块的最终输出,总写出量为:
207+ 
208+$2Byte * MNK / k$
209+ 
210+### 定性分析
211+ 
212+相较`Common模板`,搬入数据量不变,写出数据量增加,并产生后处理ReduceAdd的开销(包含MIX算子编译启动的开销),但切分基本块更多、更易负载均衡。
213+ 
214+</details>
215+ 
216+<details>
217+<summary><strong><font size="4">单核切K模板 SingleCoreSplitK</font></strong></summary>
218+ 
219+### Tiling建模
220+ 
221+<img src="https://raw.gitcode.com/user-images/assets/7801479/e16f5a39-2f7b-4a72-9d79-502cc8682e75/3singleCoreSplitK.png" width="80%">
222+ 
223+如图展示一个常规的fp16的矩阵运算(L0C上按照fp32累加),定义相关参数:
224+- 问题shape:$M$,$N$,$K$
225+- 搬入L1Cache时的TileShape:$m_1$,$n_1$,$k_1$
226+- 搬入L0A/LOB、搬出L0C时的TileShape:$m_0$,$n_0$,$k_0$
227+ 
228+相比`Common模板`,为了减少读取数据量,进一步增大抽象上的$m_1$、$n_1$,考虑将$m_1k_1$的tile块直接与对应的所有$k_1n_1$的tile块完成计算(等同于将$n_1$放大到$N$),此时输出$m_0n_0$的tile块没法在$L0C$常驻累加,需要及时搬出,通过`atomicAdd`在`GM`上累加。硬件上约束如下:
229+- $m_1k_1*L1Stage_A + n_1k_1*L1Stage_B <= L1Size / 2Byte$
230+- $m_0k_0*L0AStage <= L0ASize / 2Byte$
231+- $n_0k_0*L0BStage <= L0BSize / 2Byte$
232+- $m_0n_0*L0CStage <= L0CSize / 4Byte$
233+- $m_0 <= m_1$
234+- $n_0 <= n_1$
235+ 
236+### 读取数据量
237+`Common模板`的读取数据量公式上,将$n_1$放大到$N$;或者从A矩阵分基本任务块来理解,切分$\frac{MK}{m_1k_1}$个基本块,每个基本块完成搬入此块A矩阵tile块以及对应全部的B矩阵tile块、即搬入$m_1k_1+k_1N$的数据:
238+ 
239+$2Byte * [m_1k_1+k_1N] * \frac{MK}{m_1k_1} = 2Byte * MNK * [\frac{1}{m_1}+\frac{1}{N}]$
240+ 
241+### 写出数据量
242+切分A矩阵分基本任务块,共$\frac{MK}{m_1k_1}$个基本块,每个基本任务块计算得到$m_1N$的结果并搬出,总写出量为:
243+ 
244+$2Byte * MNK / k_1$
245+ 
246+### 定性分析
247+ 
248+相较`Common模板`,搬入数据量减少,写出数据量增加,与AIV无关。
249+ 
250+</details>
251+ 
252+## 工程优化清单
253+ 
254+<details>
255+<summary><strong><font size="4">流水优化(Multi Buffer)</font></strong></summary>
256+ 
257+### 现象分析
258+ 
259+如下图构造一个`Common`模板下的简单场景,对于单个AIC处理一个基本任务块C,需要的A/B矩阵Tile块较小,可以直接全部放入L1,且A/B矩阵从L1搬入L0时需要切4次搬入。
260+ 
261+<img src="https://raw.gitcode.com/user-images/assets/7801479/a62726da-f3c2-4c37-8a32-711046cfd239/8pingpong0.png" width="80%">
262+ 
263+各pipe的指令流水图示例如下:
264+ 
265+<img src="https://raw.gitcode.com/user-images/assets/7801479/1dca48b1-2b5d-450c-9f66-18dbc1f7e2d1/8pingpong1.png" width="100%">
266+ 
267+如果在AIC的L1/L0A/L0B/L0C上,每次载入数据tile块时都尽量塞满所有空间,会导致各PIPE的流水串行,整体效率低下
268+ 
269+### 优化方案
270+ 
271+使用常规优化手段Multi Buffer,即在L1/L0A/L0B/L0C上启用多buffer,使得流水尽可能并行,提升效率。如下图在L1/L0A/L0B上采用double buffer:
272+ 
273+ 
274+<img src="https://raw.gitcode.com/user-images/assets/7801479/40315511-85cc-44ac-be3f-9efb6b5c0194/8pingpong2.png" width="80%">
275+ 
276+各pipe的指令流水图示例如下,MTE1上指令的0、1表示pingpong流水:
277+ 
278+<img src="https://raw.gitcode.com/user-images/assets/7801479/5917e1ca-dea0-4e00-8322-ef5ba2f32f46/8pingpong3.png" width="100%">
279+ 
280+⚠️ 需要注意的是,与`L1常驻`优化结合时,常驻的A/B矩阵的tile块,不启用多buffer。
281+ 
282+### 特性承载代码
283+ 
284+由于是常规优化手段,所有blockMmad组件均使能。
285+ 
286+</details>
287+ 
288+<details>
289+<summary><strong><font size="4">流水优化(Preload)</font></strong></summary>
290+ 
291+### 现象分析
292+ 
293+通过仿真流水发现pingpong策略的blockMmad存在问题:
294+- MTE2流水上,“当前C矩阵基本块计算的最后一个A矩阵(B矩阵)的tile块”和“下一个C矩阵基本块计算的第一个A矩阵(B矩阵)的tile块”之间加载的空泡。
295+### 优化方案
296+ 
297+针对GM->L1过程,读取当前轮次的$m_1k_1$($k_1n_1$)时,计算上一轮读取的数据(假设Preload一轮,PRELOAD_STAGES = 1),步骤伪代码如下:
298+```cpp
299+for ... {
300+ # 搬入当前轮次的数据
301+ copyGM2L1A
302+ copyGM2L1B
303+ preload_count++
304+ for (preload_count == PRELOAD_STAGES) {
305+ # 计算前PRELOAD_STAGES轮次的数据
306+ copyL12L0A
307+ copyL12L0B
308+ Mmad
309+ }
310+}
311+```
312+ 
313+如下图构造一个`Common`模板下的简单场景,对于单个AIC处理两个基本任务块C1和C2,需要的A/B矩阵Tile块放入L1需要切2次,且A/B矩阵L1Tile块从L1搬入L0时需要切4次搬入(可以先学习前文`流水优化(Multi Buffer)`来增强理解)。这里给出`MmadAtlasA2Pingpong``MmadAtlasA2Preload``MmadAtlasA2PreloadAsync`的指令对比:
314+- `MmadAtlasA2Pingpong`中,调用两次blockMmad分别完成C1和C2的计算
315+- `MmadAtlasA2Preload`中,两次blockMmad也是分别完成C1和C2的计算,但A3/B3的GmToL1搬运提前到了第一次blockMmad中执行
316+- `MmadAtlasA2PreloadAsync`中,调用两次blockMmad和一次收尾的SynchronizeBlock。A2/B2的L1ToL0搬运、tileMmad以及C1的搬出从第一次blockMmad中推迟到第二次blockMmad中;A4/B4的L1ToL0搬运、tileMmad以及C2的搬出从第二次blockMmad中推迟到SynchronizeBlock中
317+ 
318+<img src="https://raw.gitcode.com/user-images/assets/7801479/eca1e06a-7c9a-40d6-934b-6843f9229d7c/9preload0.png" width="100%">
319+ 
320+各pipe的指令流水图示例如下,最终达成了A3、B3块的GmToL1搬运提前,减缓了MTE2上的搬运空泡:
321+ 
322+<img src="https://raw.gitcode.com/user-images/assets/7801479/1edf8fb8-c8e2-4b84-b742-0e6c507efb64/9preload1.png" width="100%">
323+ 
324+### 特性承载代码
325+- [block_mmad_preload.hpp](../../../include/catlass/gemm/block/block_mmad_preload.hpp),对应dispatchPolicy:`MmadAtlasA2Preload`,需要在kernel内手动计算传入下一块预载数据的信息。
326+- [block_mmad_preload_async.hpp](../../../include/catlass/gemm/block/block_mmad_preload_async.hpp),对应dispatchPolicy:`MmadAtlasA2PreloadAsync`,通过异步控制,无需手动计算下一块预载数据信息,并支持mmad计算完成后的`Callback`传入。
327+- [block_mmad_preload_async_with_callback.hpp](../../../include/catlass/gemm/block/block_mmad_preload_async_with_callback.hpp),对应dispatchPolicy:`MmadAtlasA2PreloadAsyncWithCallback`,通过异步控制,无需手动计算下一块预载数据信息,并支持blockMmad计算前后的`Callback`传入。
328+</details>
329+ 
330+<details>
331+<summary><strong><font size="4">读取带宽优化(Padding)</font></strong></summary>
332+ 
333+### 现象分析
334+当数据读取为主流水时,优化读取带宽能带来性能受益,以fp16的A矩阵为例,目前有以下几种低带宽场景:
335+- **Stride非512B对齐导致的低带宽**。搬运参数srcDValue(详见[昇腾文档:DataCopy-随路转换ND2NZ搬运](https://www.hiascend.com/document/detail/zh/canncommercial/83RC1/API/ascendcopapi/atlasascendc_api_07_00127.html))非512B对齐时,带宽会有明显下降。
336+- **搬运指令限制导致的低带宽**。ND2NZ的搬运指令,参数srcDValue是uint16类型,最大值65535。当K>65535时,只能通过取ndNum= 1,在m方向循环调用搬运指令,降低了读取带宽。
337+- 相比ND2ND不转换排布,**ND2NZ随路转换有带宽损失**
I
Iinit__zhb__2025年12月11日

针对上述带宽损失情形,是否可以链接昇腾社区的相关文档。如果暂无关联性强的外链,可以在后续文档更新中通过图、表等方式展现不同条件的带宽利用情况。

likedislike
338+### 优化方案
339+针对上述情况,可以使用AIV对数据格式进行重排(预处理动作),当重排开销低于带宽损失时,会有性能收益。从复杂度和能应对的场景出发,有下列三种不同的重排方式。
340+#### PaddingMatrixND
341+<img src="https://raw.gitcode.com/user-images/assets/7801479/80501346-2ea2-42ba-8cc0-b6f614630606/4paddingND.png" width="100%">
342+ 
343+将Stride方向按照512B对齐,实现复杂度最低,可以处理Stride非对齐导致的带宽下降。
344+#### PaddingMatrixBlockND
345+<img src="https://raw.gitcode.com/user-images/assets/7801479/9e5e0d37-ef6a-41e0-b54a-1f6d079e1179/4paddingBlockND.png" width="100%">
346+ 
347+按$m_1*k_1$作为“block”粒度重排,block内行优先、block间行优先,且$k_1$为512B对齐,实现复杂度适中,可以处理Stride非对齐和Stride超过65535导致的带宽下降。
348+ 
349+#### PaddingMatrixNZ
350+<img src="https://raw.gitcode.com/user-images/assets/7801479/11728de5-073d-481b-a030-b262f425161f/4paddingNZ.png" width="100%">
351+ 
352+重排为zN格式,实现复杂度最高,因为和L1上数据排布一致,搬运带宽也最高,可以处理Stride非对齐、Stride超过65535、ND2NZ随路转换导致的带宽下降。
353+ 
354+<font color="red">实际应用中,不同case适合的padding方式不同,暂无全局最优Padding选择。</font>
355+ 
356+### 特性承载代码
357+- [padding_matmul.hpp](../../../include/catlass/gemm/kernel/padding_matmul.hpp)中包含Padding前处理组件。
358+- 实际适配可参考[06_optimized_matmul](../../../examples/06_optimized_matmul/optimized_matmul.cpp),通过`PaddingTag``PaddingBuilder`组装出A矩阵/B矩阵的Padding前处理。
359+</details>
360+ 
361+<details>
362+<summary><strong><font size="4">读取带宽优化(ShuffleK)</font></strong></summary>
363+ 
364+### 现象分析
365+通常,所有AIC核心都从$K$方向的第一个分块开始搬运计算,会存在多个核心同时读取同一片GM上数据的情况,产生数据读取冲突,导致读取带宽降低。
366+### 优化方案
367+<img src="https://raw.gitcode.com/user-images/assets/7801479/3b79fdb8-1154-46fa-bde8-057950d16b86/5shuffleK.png" width="100%">
368+ 
369+`Common模板`为例,如图:
longjihui
longjihuilongjihui2025年12月4日

图示建议补充swizzle信息,初学者会对coreid的排布产生误解

likedislike
370+- $matC$中的$CoreX$表示该基本块分配给第$X$个AIC进行计算
371+- $Aj$表示A矩阵该$m_1$下沿$K$轴切分的第$j$个L1Tile基本块
372+- $Bij$表示B矩阵该$n_1$下沿$K$轴切分的第$j$个L1Tile基本块
373+- 图中matC基本块分核采用Swizzle<2, 1>,详见[swizzle_explanation](./swizzle_explanation.md)
374+ 
375+如图左原始方案,$Core2$和$Core3$搬运A矩阵时均按照“$A0$->$A1$->$A2$->$A3$”的顺序,产生了数据读取冲突。
376+ 
377+ 
378+如图右$ShuffleK$方案,根据$CoreIdx$来偏移起始起始搬运序号$j$,$Core2$搬运A矩阵时按照“$A2$->$A3$->$A0$->$A1$”的顺序,对应B矩阵按照“$B02$->$B03$->$B00$->$B01$”的顺序;$Core3$搬运A矩阵均按照“$A3$->$A0$->$A1$->$A2$”的顺序,对应B矩阵按照“$B13$->$B10$->$B11$->$B12$”的顺序。从时间上错开,避免同地址访问冲突。
379+### 特性承载代码
380+- [block_mmad_preload.hpp](../../../include/catlass/gemm/block/block_mmad_preload.hpp)
381+- [block_mmad_preload_async.hpp](../../../include/catlass/gemm/block/block_mmad_preload_async.hpp)
382+- [block_mmad_preload_async_with_callback.hpp](../../../include/catlass/gemm/block/block_mmad_preload_async_with_callback.hpp)
383+ 
384+上述BlockMmad内均采用设置起始L1序号为$CoreIdx/kTileCount$的方式来实现错位:
385+```cpp
386+kTileCount = CeilDiv<L1TileShape::K>(actualShape.k());
387+startTileIdx = AscendC::GetBlockIdx();
388+firstTileIdx = startTileIdx % kTileCount;
389+```
390+ 
391+</details>
392+ 
393+<details>
394+<summary><strong><font size="4">读取带宽优化(小M下指令替换)</font></strong></summary>
395+ 
396+### 现象分析
397+<img src="https://raw.gitcode.com/user-images/assets/7801479/e3a90904-677a-4973-b3b5-93e93ea072e2/6smallM.png" width="80%">
398+ 
399+矩阵计算中,当$M$很小时(例如$M$ < 8),采用随路ND2NZ的DataCopy(详见[昇腾文档:DataCopy-随路转换ND2NZ搬运](https://www.hiascend.com/document/detail/zh/canncommercial/83RC1/API/ascendcopapi/atlasascendc_api_07_00127.html))效率不高。
400+ 
401+### 优化方案
402+可以采用普通间隔搬运的DataCopy,for循环每次搬运一行,每一行调用DataCopy多次搬运。
403+### 特性承载代码
404+- [CopyGmToL1IntervalDataCopy](../../../include/catlass/gemm/tile/copy_gm_to_l1.hpp)
405+- 实际适配可参考[06_optimized_matmul](../../../examples/06_optimized_matmul/optimized_matmul.cpp),在`struct TileCopyOpt`中手动替换`using CopyGmToL1A = Gemm::Tile::CopyGmToL1IntervalDataCopy<ArchTag, AType>;`,而不是默认的`using CopyGmToL1A = typename Base::CopyGmToL1A;`
406+ 
407+```diff
408+struct TileCopyOpt : public Catlass::Gemm::Tile::TileCopy<ArchTag, AType, BType, CType, BiasType> {
409+ ...
410+ 
411+- // using CopyGmToL1A = Gemm::Tile::CopyGmToL1IntervalDataCopy<ArchTag, AType>;
412++ using CopyGmToL1A = Gemm::Tile::CopyGmToL1IntervalDataCopy<ArchTag, AType>;
413+ 
414+- using CopyGmToL1A = typename Base::CopyGmToL1A;
415++ // using CopyGmToL1A = typename Base::CopyGmToL1A;
416+ ...
417+};
418+```
419+ 
420+</details>
421+ 
422+<details>
423+<summary><strong><font size="4">读取带宽优化(L1常驻)</font></strong></summary>
424+ 
425+### 优化方案
426+实际开发时,可采用tile块常驻L1的方式,减少tile块数据的重复读取,等效提升了读取带宽,该特性需要结合不同理论模板来考率实现方法。
427+### 特性承载代码
428+- `Common`模板可参考[25_matmul_full_loadA](../../../examples/09_splitk_matmul/25_matmul_full_loadA.cpp)及相关交付件,该样例通过$M$轴上的单核全载或多核全载来在特定场景下优化性能,并配合专门的swizzle策略来提高L1上全载的A矩阵块的复用频率。
429+ - kernel:[matmul_full_loadA.hpp](../../../include/catlass/gemm/kernel/matmul_full_loadA.hpp)
430+ - blockMmad:[block_mmad_pingpong_full_loadA.hpp](../../../include/catlass/gemm/block/block_mmad_pingpong_full_loadA.hpp)
431+ - dispatchPolicy:`MmadAtlasA2FullLoadA`
432+ - BlockScheduler:`GemmIdentityBlockSwizzleL1FullLoad`
433+- `单核切K模板`[34_single_core_splitk_matmul](../../../examples/34_single_core_splitk_matmul/single_core_splitk.cpp)在理论设计上就考虑了L1Tile块常驻的优化点。
H
Hhuangxin3612025年12月4日

可以参考或引用单核切k的流水示意图。说明使用单核切k的计算模板之后的数据搬运和计算的流程

likedislike
434+ - kernel:[single_core_slicek_matmul.hpp](../../../include/catlass/gemm/kernel/single_core_slicek_matmul.hpp)
435+ - blockMmad:[block_mmad_single_core_splitk.hpp](../../../include/catlass/gemm/block/block_mmad_single_core_splitk.hpp)
436+ - dispatchPolicy:`MmadAtlasA2SingleCoreSplitk`
437+ - BlockScheduler:`SingleCoreSplitkGemmIdentityBlockSwizzle`
438+ 
439+</details>
440+ 
441+<details>
442+<summary><strong><font size="4">Scalar开销消减</font></strong></summary>
443+ 
444+### 现象分析
445+对于小Shape场景,如`Common模板`中:
446+- $M$、$N$方向分核数小于实际物理核数,每个AIC物理核最多仅处理一个基本任务块
447+- $k_1$ >= $K$,$K$方向无需切分后从GM搬入L1
448+ 
449+此时kernel总耗时较小,scalar开销对性能影响显著。
450+### 优化方案
451+消减冗余的scalar计算
452+- kernel内不使用 BlockScheduler 来将任务块分配给物理核,手动计算每个物理核对应的任务块
453+- kernel内消除基本块循环(每个AIC仅处理1个任务块)
454+- kernel内简化offset相关计算
455+- blockMmad内消减L1层面$m$、$n$的循环
456+- blockMmad内简化offset相关计算
457+### 特性承载代码
458+- 参考[31_small_matmul](../../../examples/31_small_matmul/small_matmul.cpp),可以和[00_basic_matmul](../../../examples/00_basic_matmul/basic_matmul.cpp)的相关交付件对比来加深理解
459+ - kernel:[small_matmul.hpp](../../../include/catlass/gemm/kernel/small_matmul.hpp)
460+ - blockMmad:[block_mmad_small.hpp](../../../include/catlass/gemm/block/block_mmad_small.hpp)
461+ 
462+</details>
463+ 
464+<details>
465+<summary><strong><font size="4">写出带宽优化</font></strong></summary>
466+ 
467+### 现象分析
468+当数据写出为主流水时,优化写出带宽能够带来性能受益。
469+- 当写出时dstStride非512B对齐时,带宽有明显下降
470+- 搬出时用NZ2ND随路格式转换,会产生带宽损失
471+### 优化方案
472+针对上述情况,可使用AIV对数据格式进行重排,在重排开销低于带宽损失时,会有性能受益。以下提供四种重排方式。
473+<img src="https://raw.gitcode.com/user-images/assets/7801479/89afcf74-a193-431b-aea3-a8de2abcb4f9/7rmPadding1.png" width="80%">
474+ 
475+(↑)**方式一**:使用局部workSpace,ND写出到GM时512B对齐,再按block块粒度在UB上重排,再写出到GM上。
476+ 
477+<img src="https://raw.gitcode.com/user-images/assets/7801479/12665171-d86f-4208-8c11-3bef2359cfa1/7rmPadding2.png" width="80%">
478+ 
479+(↑)**方式二**:使用全量workSpace,ND写出到GM时512B对齐,等全量结果写完后,再启动UB上数据重排,写出到GM上。
480+ 
481+<img src="https://raw.gitcode.com/user-images/assets/7801479/13bd80b2-d6ab-4520-839c-cf5e700db0d5/7rmPadding3.png" width="80%">
482+ 
483+(↑)**方式三**:使用局部workSpace,NZ写出到GM时512B对齐,再按block块粒度在UB上重排,ND写出到GM上。
484+ 
485+<img src="https://raw.gitcode.com/user-images/assets/7801479/204d24e9-5dc8-4318-bfe1-7f958598c514/7rmPadding4.png" width="80%">
486+ 
487+(↑)**方式四**:使用全量workSpace,NZ写出到GM时512B对齐,等全量结果写完后,再启动UB上数据重排,ND写出到GM上。
488+ 
489+### 特性承载代码
490+- [padding_matmul.hpp](../../../include/catlass/gemm/kernel/padding_matmul.hpp)中实现了包含**方式二**`RemovePaddingNDAndCast`后处理组件
491+- 实际适配可参考[34_single_core_splitk_matmul](../../../examples/34_single_core_splitk_matmul/single_core_splitk.cpp)
492+</details>
493+ 
494+## 模板应用浅述
495+ 
496+参考[102_dynamic_optimized_matmul的select_kernel策略](../../../examples/102_dynamic_optimized_matmul/include/select_kernel_b16.h)
497+ 
498+ 
499+<details>
500+<summary><strong><font size="4">模板选择</font></strong></summary>
501+ 
502+先尝试基于[00_basic_matmul](../../../examples/00_basic_matmul/basic_matmul.cpp)进行TileShape调优并获得**性能基线**,可参考[模板库优化指引
503+](./catlass_optimize_guidance.md)
504+ 
505+ 
506+依次识别是否满足各模板适合场景,并和性能基线作比较:
507+- [31_small_matmul](../../../examples/31_small_matmul/small_matmul.cpp):
508+ - 计算当前基本任务块 taskBlocks
509+ ```cpp
510+ taskBlocks = CeilDiv(M, m1) * CeilDiv(N, n1);
511+ ```
512+ - 基本任务块小于AIC数: $taskBlocks < aicCoreNum$
513+ - $K$轴较小,$K <= k_1$
514+- [09_splitk_matmul](../../../examples/09_splitk_matmul/optimized_matmul.cpp)或[22_padding_splitk_matmul](../../../examples/22_padding_splitk_matmul/padding_splitk_matmul.cpp)(带Padding前处理)
515+ - 选择$m_1$、$n_1$、$k_1$
516+ - 先设置$m_1 = 128$、$n_1 = 256$、$k_1 = 256$
517+ - 满足下列场景其一,修改$m_1 = 256$、$n_1 = 128$
518+ - A矩阵和B矩阵均为ColumnMajor
519+ - A矩阵为ColumnMajor,B矩阵为RowMajor,且$M > N$
520+ - 计算当前基本任务块 taskBlocks
521+ ```cpp
522+ taskBlocks = CeilDiv(M, m1) * CeilDiv(N, n1);
523+ ```
524+ - 满足下列两种场景:
525+ - 基本任务块小于一半AIC数,且$K$轴够大:$taskBlocks < aicCoreNum / 2, K > 5120$
526+ - 基本任务块小于3块,且$K$轴不会小:$taskBlocks <= 2, K > 1024$
527+- [06_optimized_matmul](../../../examples/06_optimized_matmul/optimized_matmul.cpp)(带Padding前处理)和[21_basic_matmul_preload_zN](../../../examples/09_splitk_matmul/basic_matmul_preload_zN.cpp)(手动改为ND输入)
528+ - 泛化性更强,适用于剩余场景
529+ - 不需要使用Padding时,为了节约MIX算子编译启动的开销,建议使用[21_basic_matmul_preload_zN](../../../examples/09_splitk_matmul/basic_matmul_preload_zN.cpp)模板
530+ 
531+⚠️ 全载特性的使用[25_matmul_full_loadA](../../../examples/09_splitk_matmul/25_matmul_full_loadA.cpp) 和 单核切K方案[34_single_core_splitk_matmul](../../../examples/34_single_core_splitk_matmul/single_core_splitk.cpp)的适用场景待完善
532+ 
533+ 
534+</details>
535+ 
536+<details>
537+<summary><strong><font size="4">Padding选择</font></strong></summary>
538+ 
539+当Stride非512B对齐时可以考虑使用Padding前处理,但需要考虑Padding带来的开销以及MIX算子编译启动的开销(小shape[31_small_matmul](../../../examples/31_small_matmul/small_matmul.cpp)方法不推荐额外适配Padding)
540+ 
541+`PaddingMatrixND``PaddingMatrixBlockND``PaddingMatrixNZ`各自的适用场景待完善,泛化上`PaddingMatrixNZ`更具有优势。
542+ 
543+</details>