已合并
【社区任务】aclsparseSpMM(950) 算子设计文档 #1073
【社区任务】aclsparseSpMM(950) 算子设计文档 #1073
已合并
Jessicaruan创建于 8月17日
共 1 个文件变更+281-0
@@ -0,0 +1,281 @@
1+# 【社区任务】aclsparseSpMM(950)算子设计文档
2+ 
3+> 参赛账号:Jessicaruan(@gcw_DmEcQNNx)
4+> 任务:8月社区任务 - aclsparseSpMM 算子开发(950)
5+> 任务书:https://www.hiascend.com/activities/task-center/details/c62841e450064e198c34599c2b31bee5
6+> 活动页:https://www.hiascend.com/activities/task-center/details/c62841e450064e198c34599c2b31bee5
7+> 合入仓:https://gitcode.com/cann/ops-sparse (`master`,沿用现有 `aclsparseSpMM*`,不新增同名接口)
8+> 硬件:Ascend 950PR / 950DT(arch35)
9+ 
10+# 一、需求背景(required)
11+ 
12+## 1.1 需求来源
13+ 
14+本需求来源于 CANN 社区任务「8月社区任务-aclsparseSpMM 算子开发(950)」。要求参考 PyTorch 2.7+ 的 `torch.sparse.addmm` / `aten::_sparse_addmm`,在 950 上完成 Python/ATen 适配,并**复用、扩展** `ops-sparse` 已有 aclsparse SpMM C++ 接口及 Ascend C Kernel。
15+ 
16+- 公式:`out = β · input + α · (mat1 × mat2)`,`mat1` 为 CSR 稀疏矩阵,`mat2`/`input`/`out` 为稠密矩阵
17+- 必选 dtype:`float16` / `bfloat16` / `float32` / `complex64`(complex64 不是选做)
18+- 禁止 CPU fallback 代替 NPU 计算
19+- 与 A2/A3 同题并行:公共 Host 逻辑必须和硬件差异解耦,保证同一主干可共存
20+ 
21+## 1.2 背景介绍
22+ 
23+### 1.2.1 功能说明
24+ 
25+Python 公开接口:
26+ 
27+```python
28+torch.sparse.addmm(input, mat1, mat2, *, beta=1, alpha=1) -> Tensor
29+```
30+ 
31+ATen Schema:
32+ 
33+```text
34+aten::_sparse_addmm(Tensor self, Tensor mat1, Tensor mat2, *, Scalar beta=1, Scalar alpha=1) -> Tensor
35+```
36+ 
37+C++ 三步流程(已有,必须保持源码兼容):
38+ 
39+1. `aclsparseSpMMGetBufferSize`
40+2. `aclsparseSpMMPreprocess`(CSR 行重排 + 分桶)
41+3. `aclsparseSpMM`
42+ 
43+### 1.2.2 现有实现现状(ops-sparse `sparse/spmm/arch35`)
44+ 
45+仓库已有 950 路径,且 README 写明 **950 支持、A2/A3 不支持**。当前能力与任务书差距如下:
46+ 
47+| 维度 | 现有 arch35 | 本任务要求 |
48+| --- | --- | --- |
49+| 产品 | 950PR/DT | 950PR(本任务) |
50+| 计算 dtype | `ACL_FLOAT` / `ACL_INT32` | `ACL_FLOAT16` / `ACL_BF16` / `ACL_FLOAT` / `ACL_COMPLEX64` |
51+| Kernel 分发 | `SPMM_DTYPE_FP32/FP16/INT8` | 增 `BF16`、`COMPLEX64`;INT8 保留但不作为本任务验收项 |
52+| 组合 | fp32;fp16 compute fp32;int8→int32 | 四类实数/复数全链路;组合对齐 cuSPARSE 13.3 SpMM 官方表 |
53+| 索引基址 | 仅 `INDEX_BASE_ZERO` | 0 和 1 |
54+| opA | 仅 NON_TRANSPOSE | 按 cuSPARSE 表;CSR_ALG3 仅 NON_TRANSPOSE,且不支持 opB 共轭转置 |
55+| opB | NON_TRANSPOSE / TRANSPOSE,无共轭 | 声明支持的转置/共轭转置;未声明组合明确报错 |
56+| 算法 | DEFAULT / CSR_ALG1 / FP32_HIGH_PRECISION | CSR_ALG1 偏列主、CSR_ALG2 偏行主、CSR_ALG3 受限 CSR |
57+| TilingData 标量 | `float alpha_host/beta_host` | 需容纳 complex64 的 α/β |
58+| Python/ATen | 未见 `aten::_sparse_addmm` NPU 注册 | 必须注册,行为对齐 PyTorch 2.7+ |
59+| 性能 | 无 A100 对标门槛 | fp16/bf16/fp32 ≥ 1.0× A100;complex64 ≥ 0.8× A100 |
60+ 
61+结论:**不新写一套 SpMM 接口**,在 `arch35` 上补齐 dtype/布局/算法/ATen,并保证与 `arch22`(A2 任务)通过编译期/运行期分流共存。
62+ 
63+# 二、需求分析(required)
64+ 
65+## 2.1 需求描述
66+ 
67+在 Ascend 950 上打通:
68+ 
69+`torch.sparse.addmm` → `aten::_sparse_addmm` → `aclsparseSpMM*` → `sparse/spmm/arch35` Kernel
70+ 
71+核心计算必须在 NPU 完成。C++ 语义对标 CUDA Toolkit 13.3 Update 1 的 cuSPARSE SpMM;Python 语义对标 PyTorch 2.7+。
72+ 
73+## 2.2 需求拆解
74+ 
75+1. **描述符补齐**:`CreateCsr/CreateConstCsr/CreateDnMat` 支持 fp16/bf16/fp32/complex64;索引 int32;`idxBase` 0/1;非法参数返回确定错误码。
76+2. **Workspace / Preprocess / Compute**:950 上四类 dtype、Row/Col 布局、CSR_ALG1/2/3 的 workspace 计算、行重排分桶、Kernel 执行。
77+3. **ATen 适配**:NPU 注册;dtype/device/shape/广播/stride/异常对齐 PyTorch;`beta=0` 不读取 input 中 NaN/Inf;实数 dtype 拒绝虚部非零的复数标量。
78+4. **泛化**:动态 M/K/N/nnz;空 nnz、空行、长尾行、非连续稠密 Tensor(按任务书支持范围)。
79+5. **与 A2 解耦**:硬件相关代码只放 `arch35/`;公共校验/枚举/错误码放 `sparse/common` 或现有 Host 层,避免和 A2 PR 互踩。
80+6. **精度**:混合容差单标杆,CPU Golden(fp16/bf16→fp32,fp32→fp64,c64→c128)。
81+7. **性能**:P-01/P-02/P-03 及任务附带 50 条性能用例;NPU Profiler Kernel 总耗时 vs A100 NCU。
82+ 
83+## 2.3 支持硬件与软件
84+ 
85+| 项目 | 要求 |
86+| --- | --- |
87+| 硬件 | Ascend 950PR |
88+| CANN | ops-sparse 指定版本 |
89+| PyTorch | ≥ 2.7 |
90+| torch_npu | ≥ 26.0.0 |
91+| 开发环境 | hidevlab WebIDE / 950 算力 |
92+ 
93+# 三、详细设计(required)
94+ 
95+## 3.1 算子分析
96+ 
97+### 3.1.1 数学公式
98+ 
99+$$
100+\mathrm{out} = \beta \cdot \mathrm{input} + \alpha \cdot \mathrm{op}(A_{\mathrm{CSR}}) \cdot \mathrm{op}(B)
101+$$
102+ 
103+`input` 按 PyTorch 广播到 `[M,N]`;`mat1` 为 `[M,K]` CSR;`mat2` 为 `[K,N]` dense。
104+ 
105+### 3.1.2 支持数据类型
106+ 
107+| 层 | dtype |
108+| --- | --- |
109+| Python / ATen | float16, bfloat16, float32, complex64;三者必须相同,不做提升 |
110+| aclsparse | `ACL_FLOAT16`, `ACL_BF16`, `ACL_FLOAT`, `ACL_COMPLEX64` |
111+| 索引 | 仅 `ACL_SPARSE_INDEX_32I` |
112+ 
113+complex64:实部/虚部分别按 fp32 混合容差验收;须支持复数 α/β 及声明范围内的共轭转置。
114+ 
115+### 3.1.3 支持形状与约束
116+ 
117+- 动态 shape:规格内 M/K/N/nnz
118+- `mat1.size(1) == mat2.size(0)`
119+- 仅 `input` 广播;`mat1`/`mat2` 不做矩阵维广播
120+- 同设备 NPU;跨设备报错
121+- B/C:`ACL_SPARSE_ORDER_ROW`(`ld >= cols`)与 `ACL_SPARSE_ORDER_COL`(`ld >= rows`)
122+- 未在 cuSPARSE 13.3 官方表中的「布局 × opA × opB × dtype × alg」组合:返回明确错误,不静默降级
123+ 
124+### 3.1.4 算法与布局策略
125+ 
126+| 算法 | 优先路径 | 备注 |
127+| --- | --- | --- |
128+| CSR_ALG2 | 行主 | 对齐任务书 |
129+| CSR_ALG1 | 列主 | 现有 arch35 已有 CSR_ALG1 骨架,优先复用 |
130+| CSR_ALG3 | CSR 且 opA=NON_TRANSPOSE | 不支持 opB=CONJUGATE_TRANSPOSE |
131+| DEFAULT | 映射到任务允许的默认 ALG | 文档中写死映射,避免和 A2 不一致 |
132+ 
133+## 3.2 算子实现
134+ 
135+### 3.2.1 分层
136+ 
137+```
138+torch.sparse.addmm
139+ │
140+ ▼
141+aten::_sparse_addmm (ops-sparse Python/ATen 适配)
142+ │ 校验 dtype/device/shape/stride/αβ
143+ │ CSR 元数据 → aclsparse 描述符
144+ ▼
145+aclsparseSpMMGetBufferSize / Preprocess / SpMM
146+ │ 公共 Host:参数校验、错误码、workspace 布局
147+ ▼
148+sparse/spmm/arch35/{spmm_host,spmm_kernel,spmm_csr_mat}
149+ │ 950 tiling / 行重排分桶 / Cube+Vector Kernel
150+ ▼
151+NPU 950
152+```
153+ 
154+不新增第二套 `aclsparseSpMM*` 名字。Python 层只做注册与转换,计算走现有 C++ 入口。
155+ 
156+### 3.2.2 Host 侧(arch35)
157+ 
158+复用现有 workspace 布局并扩展:
159+ 
160+```
161+[64B header]
162+[SpmmTilingData] // 扩展:dtype、alg、idxBase、complex α/β、nnz
163+[int32 reorder[m]] // 行重排
164+[int32 bin_edge[bin+1]] // 分桶
165+[可选:complex 标量/共轭工作缓冲]
166+```
167+ 
168+改动点:
169+ 
170+1. `SpmmDataTypeFromAcl` 增加 `ACL_BF16`、`ACL_COMPLEX64`。
171+2. `SpmmTilingData` 的 `alpha_host/beta_host` 改为按 dtype 存储(fp32 路径保持 float;c64 存两个 float 或 `aclFloatComplex`)。
172+3. Preprocess:idxBase=1 时在 Host 将列索引统一到 0-base 视图(或 Kernel 内减 1,二选一,文档锁定一种并测非法索引)。
173+4. 未支持组合在 GetBufferSize 即失败,避免 Preprocess/SpMM 走到半截。
174+5. `beta=0`:ATen 层仍校验 input 的 shape/dtype/device,但不把 input 数值传入 Kernel(或 Kernel 跳过 β·C 读)。
175+ 
176+公共逻辑(A2/A5 共用)只放:
177+ 
178+- 描述符字段、错误码枚举、CSR 合法性检查
179+- 算法/布局/dtype 组合表(按 soc 查询)
180+ 
181+950 特有 tiling、Kernel launch、workspace 公式只放 `arch35/`。
182+ 
183+### 3.2.3 Kernel 侧(arch35)
184+ 
185+现有 `spmm_kernel.cpp` 已按 fp32/fp16/int8 分发。本任务:
186+ 
187+1. **fp32**:复用现有 CSR_ALG1 路径,补 ALG2/ALG3 与行主/列主。
188+2. **fp16 / bf16**:fp16 已有 compute=fp32 路径;bf16 按同样提升到 fp32 累加再写回,满足混合容差。
189+3. **complex64**:拆成实部/虚部四次实数乘加,或一条复数 Kernel;优先与现有分桶调度兼容,避免为 c64 另搞一套 tiling。共轭转置只在声明支持的组合启用。
190+4. 空 nnz / 空行:分桶后空桶直接写 `β·C`(β=0 则填 0)。
191+5. 确定性:同一 alg 重复执行 bit-wise(任务书对确定性算法的要求);非确定性 alg 走混合容差。
192+ 
193+禁止 Host for 循环逐元素算 SpMM。
194+ 
195+### 3.2.4 Python / ATen 适配
196+ 
197+1. 在 ops-sparse 现有 PyTorch 适配目录注册 `aten::_sparse_addmm` 的 NPU impl。
198+2. 校验:
199+ - 三输入同 dtype、同 NPU device
200+ - `mat1` 为 2D CSR,`csrRowOffsets/csrColInd` 为 int32
201+ - `mat2` 2D;`input` 可广播到 `[M,N]`
202+ - 非连续 dense:能按 stride 描述则直接用,否则 `.contiguous()` 副本
203+3. 标量:转成共同 compute dtype;实数拒绝 `imag != 0` 的复数。
204+4. 输出:`[M,N]`,dtype 与输入相同,device 相同。
205+5. Profiler:kernel 名必须出现在 `torch_npu.profiler` / `op_statistic.csv`,作为无 CPU fallback 证据。
206+ 
207+## 3.3 精度与自测方案
208+ 
209+### 3.3.1 任务附带用例(已有)
210+ 
211+目录:`aclsparseSpMM_testCase/`
212+ 
213+| 集合 | 数量 | dtype |
214+| --- | --- | --- |
215+| 精度 JSON | 200 | **仅 fp32 100 + c64 100** |
216+| 性能 JSON | 50 | **仅 fp32 25 + c64 25** |
217+ 
218+附带用例**不覆盖 fp16/bf16**。自测报告必须额外补:
219+ 
220+- fp16 / bf16 精度抽样(shape/nnz/空行/αβ 各若干)
221+- 性能表 P-02、P-03 的 fp16/bf16(及 P-03 的 c64)
222+ 
223+否则会出现「附带脚本全绿、验收仍失败」。
224+ 
225+### 3.3.2 精度标准
226+ 
227+混合容差单标杆,匹配率 ≥ 0.99,且 `|err| ≤ max(A, 32×ULP(golden))`:
228+ 
229+| dtype | rtol | atol | A | Golden |
230+| --- | --- | --- | --- | --- |
231+| float16 | 2^-9 | 2^-9 | 1e-1 | fp32 |
232+| bfloat16 | 2^-6 | 2^-6 | 1e0 | fp32 |
233+| float32 | 2^-10 | 2^-16 | 1e-2 | fp64 |
234+| complex64 | 实/虚各按 fp32 | 同左 | 同左 | complex128 |
235+ 
236+### 3.3.3 性能标准(任务书硬门槛)
237+ 
238+预热 ≥10,正式 ≥30,报中位数与 P90。正式采样复用描述符/workspace/preprocess。倍率 = GPU `kernel_total_us` / NPU `kernel_total_us`。
239+ 
240+| ID | 规模 | 目标 |
241+| --- | --- | --- |
242+| P-01 | 2708³ 量级,nnz=10556,fp32,α=1 β=0 | ≥ 1.0× A100(87.040 μs) |
243+| P-02 | 169343²×128,nnz=1.16e6,fp16/bf16/fp32,α=β=1 | ≥ 1.0× |
244+| P-03 | 2449029²×256,nnz=6.19e7,四 dtype,α=1 β=0 | 实数 ≥1.0×;c64 ≥0.8×(c64 GPU 38806.400 μs) |
245+ 
246+P-03 是最大风险项:内存、workspace、分桶与 Kernel 访存都可能爆。开发顺序:**fp32 小 shape 打通 → 附带 200/50 → fp16/bf16 → c64 → P-01 → P-02 → P-03**。
247+ 
248+### 3.3.4 C++ UT 必测
249+ 
250+GetBufferSize → Preprocess → SpMM;workspace 不足;维度/dtype 不匹配;非法索引;非法布局/alg;泄漏(反复 create/destroy)。
251+ 
252+# 四、约束与风险
253+ 
254+1. 不能 CPU fallback;验收看 Profiler。
255+2. 不能新增同名 C++ 接口;签名变更必须源码兼容。
256+3. A2 任务 25 人、本任务 14 人,都会改 `ops-sparse`。后合入方处理冲突;950 代码禁止写进 `arch22/`。
257+4. 现有 TilingData 用 `float` 存 α/β,c64 必须改结构,属于兼容性风险,需在实现时保持旧 fp32 二进制布局或加 version 字段。
258+5. cuSPARSE 13.3 组合表以外一律报错,避免「能跑但验收判未声明」。
259+6. 100 小时云端时长:不使用时关闭;大 case 先在小 shape 验证再上 P-03。
260+ 
261+# 五、交付与合入计划
262+ 
263+| 阶段 | 内容 | 状态 |
264+| --- | --- | --- |
265+| 报名 | 任务广场审核通过 | 已完成 |
266+| 设计文档 | 本文件 PR 至 `cann-ops-competitions` `tasklist/08-13-aclsparseSpMM-950/Jessicaruan/docs/design.md` | 进行中 |
267+| 代码仓 | Fork `ops-sparse`,邀请 `Ascend-CANN` 为开发者 | 待办 |
268+| 功能 | arch35 补齐四 dtype + ATen 注册 + 附带 200 条精度 | 待办 |
269+| 性能 | P-01/02/03 与 50 条;Profiler 证据 | 待办 |
270+| 验收 zip | 自测报告 + README + 仓/分支/目录 | 待办 |
271+| 合入 | PR 到 `ops-sparse` master,处理与 A2 的 Host 冲突 | 待办 |
272+ 
273+# 六、参考资料
274+ 
275+1. 任务书与自测包(本地 `0817复杂任务/aclsparseSpMM 算子开发(950)任务书/`)
276+2. ops-sparse SpMM README:https://gitcode.com/cann/ops-sparse/blob/master/sparse/spmm/README.md
277+3. arch35:`sparse/spmm/arch35/{spmm.h,spmm_host.cpp,spmm_kernel.cpp,spmm_csr_mat.*}`
278+4. PyTorch `torch.sparse.addmm`:https://docs.pytorch.org/docs/stable/generated/torch.sparse.addmm.html
279+5. cuSPARSE SpMM:https://docs.nvidia.com/cuda/cusparse/index.html#cusparseSpMM
280+6. 精度标准:https://gitcode.com/cann/opbase/blob/master/docs/zh/ops_precision_standard/experimental_standard.md
281+7. 社区任务流程:https://gitcode.com/org/cann/discussions/39