已合并
[社区任务] aclsparseSpMM A2/A3 算子设计文档 #1113
[社区任务] aclsparseSpMM A2/A3 算子设计文档 #1113
已合并
nwww创建于 19 天前
1 个文件变更+385-0
@@ -0,0 +1,385 @@
1+# 需求背景(required)
2+ 
3+## 需求来源
4+ 
5+本设计面向 CANN 社区任务“08-12-aclsparseSpMM 算子开发(A2/A3)”。任务要求在 Ascend A2、Ascend A3 上补齐 `ops-sparse` 仓库已有 `aclsparseSpMM` 能力,并打通 Python/PyTorch、ATen NPU、aclsparse C++ 接口、Host 与 Ascend C Kernel 全链路。
6+ 
7+代码基于 `ops-sparse` 现有目录演进,不重复新增同名接口,主要涉及:
8+ 
9+- `include/cann_ops_sparse.h`
10+- `sparse/spmm/arch22/`
11+- `test/spmm/arch22/`
12+- `python_adapter/`
13+ 
14+## 背景介绍
15+ 
16+SpMM(Sparse Matrix-Matrix Multiplication)用于计算 CSR 稀疏矩阵与稠密矩阵的乘积:
17+ 
18+```text
19+C = alpha * op(A) * op(B) + beta * C
20+```
21+ 
22+其中:
23+ 
24+- `A` 为 CSR 格式稀疏矩阵;
25+- `B``C` 为稠密矩阵;
26+- `op(A)``op(B)` 可为不转置、转置或共轭转置;
27+- `alpha``beta` 为标量;
28+- Python 侧对应 `torch.sparse.addmm(input, mat1, mat2, beta, alpha)`
29+ 
30+SpMM 广泛用于图计算、推荐系统、稀疏神经网络和科学计算。任务重点不是新增另一套孤立实现,而是在已有 aclsparse SpMM 三阶段接口上补齐 A2/A3 数据类型、布局、转置、边界、Python/ATen 接入及测试能力。
31+ 
32+## 现状与问题
33+ 
34+原 arch22 实现的能力较窄,主要面向 CSR、FP32、非转置和紧凑行主序场景,无法满足任务书要求。需要解决的问题包括:
35+ 
36+1. 数据类型需覆盖 `float16``bfloat16``float32``complex64`
37+2. CSR 索引基址需支持 base-0 和 base-1。
38+3. 稠密矩阵需支持行主序、列主序以及合法 leading dimension。
39+4. `opA``opB` 需支持转置;复数场景需正确处理共轭转置。
40+5. 需要覆盖空稀疏矩阵、空行、中间空行、长尾行和合法边界输入。
41+6. Python/ATen 路径必须真正调度到 NPU,不允许回退 CPU。
42+7. 需要提供可复现的 C++ UT、端到端测试与 ATK 精度验证。
43+ 
44+# 需求分析(required)
45+ 
46+## 需求描述
47+ 
48+在 A2/A3 上实现并验证完整的 aclsparse SpMM 链路,保持现有公开接口:
49+ 
50+```cpp
51+aclsparseSpMMGetBufferSize(...);
52+aclsparseSpMMPreprocess(...);
53+aclsparseSpMM(...);
54+```
55+ 
56+同时通过独立 ATen 扩展注册 `SparseCsrPrivateUse1`,将 NPU 上的 `aten::_sparse_addmm` 转发到 `aclsparseSpMM`
57+ 
58+## 功能拆解
59+ 
60+| 能力 | 设计要求 |
61+| --- | --- |
62+| 稀疏格式 | CSR |
63+| CSR 索引 | int32 行偏移与列索引 |
64+| 索引基址 | base-0、base-1 |
65+| 数据类型 | float16、bfloat16、float32、complex64 |
66+| 计算类型 | 实数输入使用 FP32 累加;complex64 使用复数计算 |
67+| 稠密布局 | B/C 行主序、列主序,支持合法 `ld` |
68+| `opA` | NON_TRANSPOSE、TRANSPOSE、CONJUGATE_TRANSPOSE |
69+| `opB` | NON_TRANSPOSE、TRANSPOSE、CONJUGATE_TRANSPOSE |
70+| 算法 | DEFAULT、CSR_ALG1、CSR_ALG2、CSR_ALG3(受约束场景) |
71+| 标量 | 支持实数及 complex64 的 `alpha``beta` |
72+| 边界 | `nnz=0`、空行、中间空行、不同 shape/稀疏度、padding |
73+| Python | `torch.sparse.addmm` NPU 调度,不回退 CPU |
74+ 
75+## 非功能需求
76+ 
77+1. 对不支持的格式、索引类型、数据类型、布局、维度和算法组合返回明确错误码。
78+2. 输入描述符与输入数据只读,输出只写入合法矩阵区域,不覆盖 padding。
79+3. Kernel 异步执行,遵守调用方 stream;测试计时前后显式同步。
80+4. 代码沿用仓库既有描述符、Handle、CMake 和安装机制。
81+5. 精度按生态算子开源精度标准的混合容差单标杆方法验收。
82+ 
83+# 详细设计(required)
84+ 
85+## 算子分析
86+ 
87+### 数学定义
88+ 
89+设原始 CSR 矩阵 `A` 形状为 `m x k`,稠密矩阵 `B` 与操作类型共同决定输出列数 `n`,则:
90+ 
91+```text
92+C(i,j) = alpha * sum_t(op(A)(i,t) * op(B)(t,j)) + beta * C(i,j)
93+```
94+ 
95+对 complex64:
96+ 
97+- `TRANSPOSE` 仅交换行列;
98+- `CONJUGATE_TRANSPOSE` 在交换行列的同时对元素取共轭;
99+- `alpha``beta` 的实部与虚部均参与计算。
100+ 
101+### 形状推导
102+ 
103+Host 侧根据 `opA``opB` 推导逻辑矩阵尺寸:
104+ 
105+- `opA == NON_TRANSPOSE`:输出行数为 `A.rows`,归约维为 `A.cols`
106+- 否则:输出行数为 `A.cols`,归约维为 `A.rows`
107+- `opB == NON_TRANSPOSE``B.rows` 必须等于归约维,输出列数为 `B.cols`
108+- 否则:`B.cols` 必须等于归约维,输出列数为 `B.rows`
109+- `C` 的逻辑形状必须等于推导出的输出形状。
110+ 
111+## 总体架构
112+ 
113+```text
114+torch.sparse.addmm
115+ |
116+ v
117+ATen SparseCsrPrivateUse1 adapter
118+ |
119+ v
120+aclsparseSpMMGetBufferSize
121+ |
122+ v
123+aclsparseSpMMPreprocess
124+ |
125+ v
126+aclsparseSpMM
127+ |
128+ v
129+arch22 Host validation / tiling / launch
130+ |
131+ v
132+Ascend C Kernel (FP16/BF16/FP32/Complex64)
133+```
134+ 
135+核心库不强制依赖 PyTorch。ATen 适配器以独立动态库构建,仅在用户显式加载或测试进程通过 `sitecustomize.py` 启用时注册。
136+ 
137+## Host 侧设计
138+ 
139+### 参数校验
140+ 
141+三个公开入口共用统一校验逻辑,校验内容包括:
142+ 
143+1. 描述符和输出参数指针非空。
144+2. `A` 必须为 CSR 格式。
145+3. CSR 行偏移与列索引必须为支持的 int32 类型。
146+4. `indexBase` 仅接受 ZERO 或 ONE。
147+5. A/B/C 元素数据类型必须一致。
148+6. 实数类型使用 `ACL_FLOAT` 作为 computeType;complex64 使用 `ACL_COMPLEX64`
149+7. `opA``opB` 必须属于支持枚举。
150+8. 根据转置状态校验 A/B/C 逻辑维度。
151+9. 行主序要求 `ld >= cols`,列主序要求 `ld >= rows`
152+10. 算法必须为 DEFAULT、CSR_ALG1、CSR_ALG2 或 CSR_ALG3。
153+11. CSR_ALG3 限定 `opA` 为 NON_TRANSPOSE,且 `opB` 不能为 CONJUGATE_TRANSPOSE。
154+ 
155+非法组合在 Kernel 启动前返回 `INVALID_VALUE``NOT_SUPPORTED` 或矩阵格式相关错误,防止静默错算和越界访问。
156+ 
157+### Workspace 布局
158+ 
159+workspace 按 64 字节边界组织,包含:
160+ 
161+```text
162+| header | tiling data | row reorder table | bin edge table |
163+```
164+ 
165+大小由 `SpmmArch22WorkspaceSize(M, blockDim)` 计算:
166+ 
167+- header:固定 64 字节;
168+- tiling data:`SpmmArch22TilingData` 向上对齐;
169+- reorder table:按输出/源行数存放 int32 行号;
170+- bin edge table:按 block 数存放边界。
171+ 
172+`GetBufferSize` 返回总字节数;`Preprocess` 构建并写入 tiling 和预处理数据;`SpMM` 复用同一 buffer 启动计算。
173+ 
174+### Tiling 数据
175+ 
176+Host 向 Kernel 传递的主要字段包括:
177+ 
178+- `M/K/N/nnz`
179+- `blockDim``nChunks`
180+- `dataType`
181+- `indexBase`
182+- `orderB/orderC`
183+- `opA/opB`
184+- `sourceRows`
185+- `ldb/ldc`
186+- `alpha/beta` 实部和虚部
187+- workspace 中 reorder/bin 数据偏移
188+ 
189+### 分核与 N 维分块
190+ 
191+arch22 最大使用 24 个计算核。输出行或源行按核切分;N 维根据 UB 容量切成多个 chunk,避免大 N 场景超过 UB。
192+ 
193+实数 Kernel 预留输入、计算和输出 tile 空间,并基于约 192 KiB UB、预留开销和 8 元素对齐计算 `nChunks`。尾块单独按有效长度处理。
194+ 
195+## Kernel 侧设计
196+ 
197+### 实数路径
198+ 
199+FP16、BF16、FP32 共用模板化执行框架:
200+ 
201+1. 从 CSR rowOffsets 获取当前行 `[rowStart, rowEnd)`
202+2. 对 base-1 索引统一减去 `indexBase`,转换为零基逻辑下标。
203+3. 按 N chunk 加载或访问 B。
204+4. 使用 FP32 进行乘加累积。
205+5. 计算 `alpha * accumulator + beta * oldC`
206+6. 根据目标数据类型写回 FP16、BF16 或 FP32。
207+ 
208+`beta == 0` 时不读取原 C,避免输入中的 NaN/Inf 对结果产生不符合接口语义的污染。
209+ 
210+### complex64 路径
211+ 
212+complex64 使用显式实部/虚部运算:
213+ 
214+```text
215+(ar + i*ai) * (br + i*bi)
216+= (ar*br - ai*bi) + i*(ar*bi + ai*br)
217+```
218+ 
219+累加后再与复数 `alpha``beta` 组合。对于 `CONJUGATE_TRANSPOSE`,读取 A 或 B 时将虚部取反。
220+ 
221+### 转置 A
222+ 
223+CSR 天然按原始行组织。`opA == NON_TRANSPOSE` 时按输出行直接遍历 CSR 行;`opA` 为 TRANSPOSE 或 CONJUGATE_TRANSPOSE 时,按源行遍历非零元素并将原列索引映射为输出行。复数共轭转置额外对 A 值取共轭。
224+ 
225+### B/C 布局与 leading dimension
226+ 
227+Kernel 根据 `orderB/orderC``ldb/ldc` 计算物理地址:
228+ 
229+- 行主序:`offset = row * ld + col`
230+- 列主序:`offset = col * ld + row`
231+ 
232+`opB` 决定逻辑 `(row, col)` 到物理 B 坐标的映射;共轭转置同时翻转虚部。C 仅写逻辑矩阵区域,padding 保持不变。
233+ 
234+### 边界处理
235+ 
236+- `nnz == 0`:乘积项为零,仅按 `beta` 处理 C。
237+- 空行:累加值为零,不访问 values/indices。
238+- 中间空行:每行独立读取 rowOffsets,不假设行内一定存在非零元素。
239+- N 尾块:仅处理有效列数。
240+- base-1:rowOffsets 和 colIndices 在使用时统一扣除基址。
241+ 
242+## 算法设计
243+ 
244+| 算法 | 设计 |
245+| --- | --- |
246+| DEFAULT | 默认确定性 CSR 路径 |
247+| CSR_ALG1 | 与默认 SIMT/AIV 路径一致 |
248+| CSR_ALG2 | 行导向确定性路径 |
249+| CSR_ALG3 | 使用预处理数据的 CSR 路径;限制 opA/opB 组合 |
250+ 
251+算法选择由 Host 校验并写入相应预处理/执行流程。不支持的组合明确返回错误,而不是隐式降级。
252+ 
253+## ATen NPU 适配设计
254+ 
255+`python_adapter/sparse_addmm_npu.cpp` 注册 `SparseCsrPrivateUse1` Kernel,将 `aten::_sparse_addmm` 转发到 aclsparse:
256+ 
257+1. 校验输入为单设备 CSR NPU Tensor。
258+2. 支持 int32 CSR 索引及 FP16/BF16/FP32/Complex64 values。
259+3. 将广播的 dense input 展开为输出形状。
260+4. 将非连续 dense input/materialized view 转换为连续 NPU Tensor。
261+5. 创建 aclsparse Handle、CSR 描述符和稠密矩阵描述符。
262+6. 依次执行 GetBufferSize、Preprocess、SpMM。
263+7. 返回 NPU Tensor,不执行 CPU 搬运或 CPU 计算回退。
264+ 
265+扩展以独立 `.so` 构建,避免核心 `ops-sparse` 引入强制 PyTorch 依赖。测试工作进程可通过以下环境变量自动加载:
266+ 
267+```bash
268+export PYTHONPATH=$PWD/python_adapter:${PYTHONPATH}
269+export OPS_SPARSE_TORCH_LIBRARY=$PWD/python_adapter/build/ops_sparse_torch_npu.so
270+```
271+ 
272+未设置变量时 `sitecustomize.py` 不产生副作用;设置后若动态库不存在或注册失败,则显式报错,避免静默回退。
273+ 
274+## 支持硬件
275+ 
276+| 支持的芯片版本 | 支持情况 |
277+| --- | --- |
278+| Atlas A2(arch22 / DAV-2201) | 支持 |
279+| Atlas A3(arch22 / DAV-2201) | 支持 |
280+ 
281+## 约束限制
282+ 
283+1. 稀疏矩阵格式仅支持 CSR。
284+2. CSR 行偏移与列索引当前支持 int32。
285+3. A/B/C 元素类型必须一致。
286+4. 实数类型使用 FP32 computeType;complex64 使用 complex64 computeType。
287+5. CSR_ALG3 不支持转置 A,也不支持 B 的共轭转置。
288+6. 输入必须位于同一 NPU 设备。
289+7. Kernel 为异步执行,调用方如需读取结果或计时必须同步 stream。
290+ 
291+# 可维可测分析
292+ 
293+## 精度标准
294+ 
295+精度按《生态算子开源精度标准》中的混合容差单标杆方法执行。测试覆盖:
296+ 
297+- float16、bfloat16、float32、complex64;
298+- base-0、base-1;
299+- 行主序、列主序、带 padding 的 leading dimension;
300+- opA/opB 转置及复数共轭转置;
301+- alpha/beta 为 0、1、负数和复数;
302+- 不同 M/K/N、nnz、稀疏度、空行、长尾行;
303+- NaN/Inf 规则及 `beta == 0` 不读取输入 C 的语义;
304+- 非连续 PyTorch Tensor;
305+- 输入只读和输出 padding 边界。
306+ 
307+A3 使用 ATK 执行 200 个精度用例,结果为 200/200 通过,准确率 100%。A2 完整报告随最终自测报告补充。
308+ 
309+## 性能标准与测量方案
310+ 
311+性能测试仅在 A3 提交,采用任务书规定的同范围调用与基线:
312+ 
313+1. 预热至少 10 次;
314+2. 正式采样至少 30 次;
315+3. 每轮调用后同步设备;
316+4. 报告 Kernel 总耗时中位数和 P90;
317+5. 描述符、workspace 和 preprocess 结果在正式采样期间复用;
318+6. 不计首次编译、数据生成、Host 到 Device 搬运及无关初始化;
319+7. Python 端到端耗时和 C++ 执行耗时作为补充数据;
320+8. 使用 Profiler 证明核心计算调度至 NPU,未回退 CPU。
321+ 
322+性能数值和 Profiler 截图在完成 A3 性能采集后写入自测报告,不在设计阶段虚构结论。
323+ 
324+## 可维护性
325+ 
326+1. 三个公开入口复用统一参数校验。
327+2. 数据类型通过模板实例化,减少重复控制逻辑。
328+3. Host 与 Kernel 通过明确 tiling 结构通信。
329+4. ATen 适配器与核心库解耦。
330+5. 不支持场景返回明确状态码。
331+6. C++ smoke、选择性 case、ATen 端到端和 ATK 全量测试形成分层回归体系。
332+ 
333+## 兼容性分析
334+ 
335+本设计复用现有公开 `aclsparseSpMM*` 接口并扩展原 arch22 能力,不新增同名接口。已有受支持场景继续走默认路径;新增数据类型、布局、转置和算法组合通过描述符与枚举表达。对非法或尚不支持的组合采用 fail-fast,不改变合法调用的返回语义。
336+ 
337+# 测试设计
338+ 
339+## C++ 测试
340+ 
341+`test/spmm/arch22/spmm_test.cpp` 提供:
342+ 
343+- 200 个基础 shape/nnz/alpha/beta case;
344+- 环境变量 `SPMM_CASE_INDEX` 选择单例运行;
345+- FP16/BF16 dtype smoke;
346+- layout/base/algorithm smoke;
347+- complex64 和转置 smoke;
348+- `nnz=0`、中间空行等边界 smoke。
349+ 
350+## Python/ATen 测试
351+ 
352+`python_adapter/test_sparse_addmm_npu.py` 覆盖 FP16、BF16、FP32、Complex64、广播、`beta == 0`、非连续输入,并检查 `SparseCsrPrivateUse1` 已注册。
353+ 
354+## ATK 测试
355+ 
356+ATK 测试工程提供:
357+ 
358+- 200 个精度用例 JSON;
359+- 混合容差比较器;
360+- CPU Golden 与 NPU Under Test;
361+- A3 最终结果:`Total Task: 200, success 200, failed 0``acc_pass_result:Pass`
362+ 
363+# 风险与应对
364+ 
365+| 风险 | 应对措施 |
366+| --- | --- |
367+| 大 N 导致 UB 超限 | 按 UB 容量计算 N chunk,并处理尾块 |
368+| base-1 造成越界 | Host 校验枚举,Kernel 统一减基址 |
369+| 转置维度错配 | Host 根据 opA/opB 推导并校验逻辑形状 |
370+| 列主序或 padding 写坏 | 统一布局寻址函数,测试 padding 不被覆盖 |
371+| complex64 共轭语义错误 | A/B 分别按 op 类型处理虚部符号,专项用例验证 |
372+| beta=0 仍读取 C | beta 为零时跳过旧 C 读取 |
373+| Python 静默回退 CPU | 显式注册与 dispatch table 检查,加载失败直接报错 |
374+| CPU Golden 对特殊非连续复数视图行为异常 | Golden 使用语义等价的连续输入,NPU 侧保留非连续输入覆盖,并记录诊断证据 |
375+ 
376+# 版本与交付信息
377+ 
378+| 项目 | 信息 |
379+| --- | --- |
380+| 个人代码仓 | `https://gitcode.com/nannan-2026/ops-sparse` |
381+| 开发分支 | `feature/aclsparse-spmm-a2a3` |
382+| 验证提交 | `f385698d7c0b25890f7c3ef6acf24d5b3b933b86` |
383+| CANN | 9.0.0 |
384+| A3 精度结果 | ATK 200/200 Pass,100% |
385+| 测试报告 | 随验收交付件提交 |