已合并
【特性】新增fusedSgd #6276
majiajian_hw创建于 6月18日
【特性】新增fusedSgd #6276
已合并
majiajian_hw创建于 6月18日
32 个文件变更+4281-0
Mclassify_rule.yaml+10-0
@@ -2023,6 +2023,14 @@ loss-c@ops-nn:
2023 - ops/ops-nn/optim/apply_adam_w_v2/op_kernel/apply_adam_w_v2_fp.h2023 - ops/ops-nn/optim/apply_adam_w_v2/op_kernel/apply_adam_w_v2_fp.h
2024 - ops/ops-nn/optim/apply_adam_w_v2/op_kernel/apply_adam_w_v2_mix_dtype.h2024 - ops/ops-nn/optim/apply_adam_w_v2/op_kernel/apply_adam_w_v2_mix_dtype.h
2025 - ops/ops-nn/optim/apply_adam_w_v2/op_kernel/apply_adam_w_v2.cpp2025 - ops/ops-nn/optim/apply_adam_w_v2/op_kernel/apply_adam_w_v2.cpp
2026 - ops/ops-nn/optim/fused_sgd/docs/
2027 - ops/ops-nn/optim/fused_sgd/README.md
2028 - ops/ops-nn/optim/fused_sgd/op_host/
2029 - ops/ops-nn/optim/fused_sgd/op_kernel/fused_sgd_base.h
2030 - ops/ops-nn/optim/fused_sgd/op_kernel/fused_sgd_f16_bf16.h
2031 - ops/ops-nn/optim/fused_sgd/op_kernel/fused_sgd_f32.h
2032 - ops/ops-nn/optim/fused_sgd/op_kernel/fused_sgd_tiling_data.h
2033 - ops/ops-nn/optim/fused_sgd/op_kernel/fused_sgd.cpp
2026 - ops/ops-nn/optim/apply_fused_ema_adam/2034 - ops/ops-nn/optim/apply_fused_ema_adam/
2027 opensource_style: null2035 opensource_style: null
2028 kernel_style: null2036 kernel_style: null
@@ -2089,6 +2097,8 @@ loss-c@ops-nn:
2089 - ops/ops-nn/optim/apply_adam_w_quant/tests/2097 - ops/ops-nn/optim/apply_adam_w_quant/tests/
2090 - ops/ops-nn/optim/apply_adam_w_v2/examples/2098 - ops/ops-nn/optim/apply_adam_w_v2/examples/
2091 - ops/ops-nn/optim/apply_adam_w_v2/tests/2099 - ops/ops-nn/optim/apply_adam_w_v2/tests/
2100 - ops/ops-nn/optim/fused_sgd/examples/
2101 - ops/ops-nn/optim/fused_sgd/tests/
2092 - ops/ops-nn/optim/apply_fused_ema_adam/examples/2102 - ops/ops-nn/optim/apply_fused_ema_adam/examples/
2093 - ops/ops-nn/optim/apply_fused_ema_adam/tests/2103 - ops/ops-nn/optim/apply_fused_ema_adam/tests/
2094 non_delivery: null2104 non_delivery: null
Mdocs/zh/op_list.md+10-0
@@ -3289,6 +3289,16 @@
3289 <td>AI Core</td>3289 <td>AI Core</td>
3290 <td>结合Adagrad自适应学习率与FOBOS(Forward-Backward Splitting)Proximal近端算法的优化器,基于梯度平方累加器自适应调整学习率,并通过软阈值(L1正则化)与缩放(L2正则化)对模型参数进行原地更新。</td>3290 <td>结合Adagrad自适应学习率与FOBOS(Forward-Backward Splitting)Proximal近端算法的优化器,基于梯度平方累加器自适应调整学习率,并通过软阈值(L1正则化)与缩放(L2正则化)对模型参数进行原地更新。</td>
3291 </tr>3291 </tr>
3292 <tr>
3293 <td>optim</td>
3294 <td><a href="../../optim/fused_sgd/README.md">fused_sgd</a></td>
atomgit-bot
atomgit-botatomgit-bot6月18日

🟠 High Priority

docs/zh/op_list.md 第3294行,href 路径为 ../../optim/fusedSgd/README.md,但实际目录名为 optim/fused_sgd/(全小写,下划线分割)。在区分大小写的文件系统上,该链接将无法访问,返回404。

变更:第 3294 行新增 <a href="../../optim/fusedSgd/README.md">fusedSgd</a>

实际路径应为 ../../optim/fused_sgd/README.md

建议:将 href 中的 fusedSgd 改为 fused_sgd,使其与实际目录名一致。

改动建议
3294
- <td><a href="../../optim/fused_sgd/README.md">fused_sgd</a></td>
3294
+ <td><a href="../../optim/fused_sgd/README.md">fusedSgd</a></td>
应用建议
likedislike
3295 <td>✓</td>
3296 <td>✓</td>
3297 <td>✓</td>
3298 <td>✗</td>
3299 <td>AI Core</td>
3300 <td>实现FusedSgd融合优化器功能。</td>
3301 </tr>
3292 <tr>3302 <tr>
3293 <td>pooling</td>3303 <td>pooling</td>
3294 <td><a href="../../pooling/adaptive_avg_pool3d/README.md">adaptive_avg_pool3d</a></td>3304 <td><a href="../../pooling/adaptive_avg_pool3d/README.md">adaptive_avg_pool3d</a></td>
Aoptim/fused_sgd/CMakeLists.txt+11-0
@@ -0,0 +1,11 @@
1# ----------------------------------------------------------------------------
2# Copyright (c) 2026 Huawei Technologies Co., Ltd.
3# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4# CANN Open Software License Agreement Version 2.0 (the "License").
5# Please refer to the License for details. You may not use this file except in compliance with the License.
6# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8# See LICENSE in the root of the software repository for the full text of the License.
9# ----------------------------------------------------------------------------
10message(STATUS "=== Debug: start fused_sgd.CMakeLists.txt ")
11add_modules_sources(HOSTNAME ${OPHOST_NAME} MODE PRIVATE DIR ${CMAKE_CURRENT_SOURCE_DIR} OPTYPE fused_sgd ACLNNTYPE aclnn_exclude)
Aoptim/fused_sgd/README.md+167-0
@@ -0,0 +1,167 @@
1# FusedSgd
2 
3## 产品支持情况
4 
5|产品 | 是否支持 |
6|:-------------------------|:----------:|
7| <term>Ascend 950PR/Ascend 950DT</term> | × |
8| <term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term> | √ |
9| <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term> | √ |
10| <term>Atlas 200I/500 A2 推理产品</term> | × |
11| <term>Atlas 推理系列产品</term> | × |
12| <term>Atlas 训练系列产品</term> | × |
13 
14## 功能说明
15 
16- **算子功能**:实现fusedSgd算子。将传统 SGD 更新过程中原本分散的多个细粒度操作(如梯度缩放、权重衰减、动量更新、参数赋值等)融合为单个NPU Kernel执行。
17- **计算公式**
18 
19 $$
20 \begin{aligned}
21 &\tilde{g}_t = \begin{cases}
22 g_t / s & s \neq \text{None} \\
23 g_t & \text{otherwise}
24 \end{cases} \\
25 
26 &\hat{g}_t = \begin{cases}
27 -\tilde{g}_t & \text{maximize} \\
28 \tilde{g}_t & \text{otherwise}
29 \end{cases} \\
30 
31 &\bar{g}_t = \hat{g}_t + weightDecay \cdot \theta_t \\
32 
33 &v_{t+1} = \begin{cases}
34 \bar{g}_t & \text{first step} \\
35 \mu v_t + (1-dampening)\bar{g}_t & \text{otherwise}
36 \end{cases} \\
37 
38 &g_t^{\text{final}} = \begin{cases}
39 \bar{g}_t + \mu v_{t+1} & \text{nesterov} \\
40 v_{t+1} & v_t \neq \text{None} \\
41 \bar{g}_t & \text{otherwise}
42 \end{cases} \\
43 
44 &\theta_{t+1} = \theta_t - lr \cdot g_t^{\text{final}} \\
45 &g_{t+1} = \tilde{g}_t
46 
47 \end{aligned}
48 $$
49 
50## 参数说明
51 
52<table style="undefined;table-layout: fixed; width: 1080px"><colgroup>
53<col style="width: 155px">
54<col style="width: 162px">
55<col style="width: 380px">
56<col style="width: 276px">
57<col style="width: 107px">
58</colgroup>
59<thead>
60 <tr>
61 <th>参数名</th>
62 <th>输入/输出/属性</th>
63 <th>描述</th>
64 <th>数据类型</th>
65 <th>数据格式</th>
66 </tr></thead>
67<tbody>
68 <tr>
69 <td>params</td>
70 <td>输入/输出</td>
71 <td>待更新参数,对应公式中的θ。</td>
72 <td>BFLOAT16、FLOAT16、FLOAT</td>
73 <td>ND</td>
74 </tr>
75 <tr>
76 <td>grads</td>
77 <td>输入/输出</td>
78 <td>待更新参数对应的梯度,对应公式中的g。</td>
79 <td>BFLOAT16、FLOAT16、FLOAT</td>
80 <td>ND</td>
81 </tr>
82 <tr>
83 <td>x</td>
84 <td>输入</td>
85 <td>待更新参数对应的动量,对应公式中的v。</td>
86 <td>BFLOAT16、FLOAT16、FLOAT</td>
87 <td>ND</td>
88 </tr>
89 <tr>
90 <td>gradScale</td>
91 <td>输入</td>
92 <td>梯度缩放大小,对应公式中的s。</td>
93 <td>FLOAT</td>
94 <td>ND</td>
95 </tr>
96 <tr>
97 <td>y</td>
98 <td>输出</td>
99 <td>待更新参数对应的动量,对应公式中的v。</td>
100 <td>BFLOAT16、FLOAT16、FLOAT</td>
101 <td>ND</td>
102 </tr>
103 <tr>
104 <td>weightDecay</td>
105 <td>属性</td>
106 <td>权重衰减值,对应公式中的weightDecay,默认为0。</td>
107 <td>FLOAT</td>
108 <td>-</td>
109 </tr>
110 <tr>
111 <td>momentum</td>
112 <td>属性</td>
113 <td>动量值,对应公式中的μ,默认为0。</td>
114 <td>FLOAT</td>
115 <td>-</td>
116 </tr>
117 <tr>
118 <td>lr</td>
119 <td>属性</td>
120 <td>学习率,对应公式中的lr,默认为1e-3。</td>
121 <td>FLOAT</td>
122 <td>-</td>
123 </tr>
124 <tr>
125 <td>dampening</td>
126 <td>属性</td>
127 <td>动量阻尼系数,对应公式中的dampening,默认为0。</td>
128 <td>FLOAT</td>
129 <td>-</td>
130 </tr>
131 <tr>
132 <td>nesterov</td>
133 <td>属性</td>
134 <td>是否启用Nesterov动量,对应公式中的nesterov,默认为False。</td>
135 <td>BOOL</td>
136 <td>-</td>
137 </tr>
138 <tr>
139 <td>maximize</td>
140 <td>属性</td>
141 <td>是否为最大化目标函数,对应公式中的maximize,默认为False。</td>
142 <td>BOOL</td>
143 <td>-</td>
144 </tr>
145 <tr>
146 <td>isFirstStep</td>
147 <td>属性</td>
148 <td>是否第一步更新,对应公式中的FirstStep,默认为True。</td>
149 <td>BOOL</td>
150 <td>-</td>
151 </tr>
152</tbody></table>
153 
154## 约束说明
155 
156- params、grads、x、gradScale的数据类型在支持的范围之内。
157- params、grads与x及其中各个tensor具有相同的数据类型。
158- params、grads与x中tensor的shape维度小于等于8,gradScale的shape为[1]。
159- lr、momentum、weightDecay、dampening的值大于等于0。
160- params、grads与x(x不为空时)中相同索引tensor的shape相同。
161- params、grads与x(x不为空时)中不能有空指针。
162 
163## 调用说明
164 
165| 调用方式 | 样例代码 | 说明 |
166| --------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
167| aclnn接口 | [test_aclnn_fused_sgd](./examples/test_aclnn_fused_sgd.cpp) | 通过[aclnnFusedSgd](docs/aclnnFusedSgd.md)接口方式调用FusedSgd算子。 |
Aoptim/fused_sgd/docs/aclnnFusedSgd.md+555-0
@@ -0,0 +1,555 @@
1# aclnnFusedSgd
2 
3## 产品支持情况
4 
5|产品 | 是否支持 |
6|:-------------------------|:----------:|
7| <term>Ascend 950PR/Ascend 950DT</term> | × |
8| <term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term> | √ |
9| <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term> | √ |
10| <term>Atlas 200I/500 A2 推理产品</term> | × |
11| <term>Atlas 推理系列产品</term> | × |
12| <term>Atlas 训练系列产品</term> | × |
13 
14## 功能说明
15 
16- 算子功能:实现fusedSgd算子。将传统 SGD 更新过程中原本分散的多个细粒度操作(如梯度缩放、权重衰减、动量更新、参数赋值等)融合为单个NPU Kernel执行。
17- 计算公式:
18 
19 $$
20 \begin{aligned}
21 &\tilde{g}_t = \begin{cases}
22 g_t / s & s \neq \text{None} \\
23 g_t & \text{otherwise}
24 \end{cases} \\
25 
26 &\hat{g}_t = \begin{cases}
27 -\tilde{g}_t & \text{maximize} \\
28 \tilde{g}_t & \text{otherwise}
29 \end{cases} \\
30 
31 &\bar{g}_t = \hat{g}_t + weightDecay \cdot \theta_t \\
32 
33 &v_{t+1} = \begin{cases}
34 \bar{g}_t & \text{first step} \\
35 \mu v_t + (1-dampening)\bar{g}_t & \text{otherwise}
36 \end{cases} \\
37 
38 &g_t^{\text{final}} = \begin{cases}
39 \bar{g}_t + \mu v_{t+1} & \text{nesterov} \\
40 v_{t+1} & v_t \neq \text{None} \\
41 \bar{g}_t & \text{otherwise}
42 \end{cases} \\
43 
44 &\theta_{t+1} = \theta_t - lr \cdot g_t^{\text{final}} \\
45 &g_{t+1} = \tilde{g}_t
46 
47 \end{aligned}
48 $$
49 
50## 函数原型
51 
52算子执行接口为[两段式接口](../../../docs/zh/context/两段式接口.md),必须先调用“aclnnFusedSgdGetWorkspaceSize”接口获取入参并根据计算流程计算所需workspace大小,再调用“aclnnFusedSgd”接口执行计算。
53 
54```cpp
55aclnnStatus aclnnFusedSgdGetWorkspaceSize(
56 const aclTensorList *paramsRef,
57 const aclTensorList *gradsRef,
58 const aclTensorList *momentumBufferListOptionalRef,
59 const aclTensor *gradScaleOptional,
60 float weightDecay,
61 float momentum,
62 float lr,
63 float dampening,
64 bool nesterov,
65 bool maximize,
66 bool isFirstStep,
67 uint64_t *workspaceSize,
68 aclOpExecutor **executor);
69```
70 
71```cpp
72aclnnStatus aclnnFusedSgd(
73 void *workspace,
74 uint64_t workspace_size,
75 aclOpExecutor *executor,
76 const aclrtStream stream)
77```
78 
79## aclnnFusedSgdGetWorkspaceSize
80 
81- **参数说明**
82
83 <div style="overflow-x: auto;">
84 <table style="undefined;table-layout: fixed; width: 1497px"><colgroup>
85 <col style="width: 150px">
86 <col style="width: 120px">
87 <col style="width: 300px">
88 <col style="width: 330px">
89 <col style="width: 212px">
90 <col style="width: 100px">
91 <col style="width: 140px">
92 <col style="width: 145px">
93 </colgroup>
94 <thead>
95 <tr>
96 <th>参数名</th>
97 <th>输入/输出</th>
98 <th>描述</th>
99 <th>使用说明</th>
100 <th>数据类型</th>
101 <th>数据格式</th>
102 <th>维度(shape)</th>
103 <th>非连续Tensor</th>
104 </tr></thead>
105 <tbody>
106 <tr>
107 <td>paramsRef</td>
108 <td>输入/输出</td>
109 <td>更新参数。</td>
110 <td>不支持空Tensor。</td>
111 <td>BFLOAT16、FLOAT16、FLOAT32</td>
112 <td>ND</td>
113 <td>小于等于8维</td>
114 <td>√</td>
115 </tr>
116 <tr>
117 <td>gradsRef</td>
118 <td>输入/输出</td>
119 <td>更新梯度</td>
120 <td>不支持空Tensor。</td>
121 <td>BFLOAT16、FLOAT16、FLOAT32</td>
122 <td>ND</td>
123 <td>小于等于8维</td>
124 <td>√</td>
125 </tr>
126 <tr>
127 <td>momentumBufferListOptionalRef</td>
128 <td>输入/输出</td>
129 <td>更新动量</td>
130 <td>支持空Tensor。</td>
131 <td>BFLOAT16、FLOAT16、FLOAT32</td>
132 <td>ND</td>
133 <td>小于等于8维</td>
134 <td>√</td>
135 </tr>
136 <tr>
137 <td>gradScaleOptional</td>
138 <td>输入</td>
139 <td>梯度缩放值</td>
140 <td>支持空Tensor。</td>
141 <td>BFLOAT16、FLOAT16、FLOAT32</td>
142 <td>ND</td>
143 <td>shape等于[1]</td>
144 <td>√</td>
145 </tr>
146 <tr>
147 <td>weightDecay</td>
148 <td>属性</td>
149 <td>权重衰减值。</td>
150 <td>-</td>
151 <td>FLOAT</td>
152 <td>-</td>
153 <td>-</td>
154 <td>-</td>
155 </tr>
156 <tr>
157 <td>momentum</td>
158 <td>属性</td>
159 <td>动量值。</td>
160 <td>-</td>
161 <td>FLOAT</td>
162 <td>-</td>
163 <td>-</td>
164 <td>-</td>
165 </tr>
166 <tr>
167 <td>lr</td>
168 <td>属性</td>
169 <td>学习率。</td>
170 <td>-</td>
171 <td>FLOAT</td>
172 <td>-</td>
173 <td>-</td>
174 <td>-</td>
175 </tr>
176 <tr>
177 <td>dampening</td>
178 <td>属性</td>
179 <td>动量的阻尼系数。</td>
180 <td>-</td>
181 <td>FLOAT</td>
182 <td>-</td>
183 <td>-</td>
184 <td>-</td>
185 </tr>
186 <tr>
187 <td>nesterov</td>
188 <td>属性</td>
189 <td>是否启用 Nesterov 动量。</td>
190 <td>-</td>
191 <td>BOOL</td>
192 <td>-</td>
193 <td>-</td>
194 <td>-</td>
195 </tr>
196 <tr>
197 <td>maximize</td>
198 <td>属性</td>
199 <td>是否为最大化目标函数。</td>
200 <td>-</td>
201 <td>BOOL</td>
202 <td>-</td>
203 <td>-</td>
204 <td>-</td>
205 </tr>
206 <tr>
207 <td>isFirstStep</td>
208 <td>属性</td>
209 <td>是否第一步更新。</td>
210 <td>-</td>
211 <td>BOOL</td>
212 <td>-</td>
213 <td>-</td>
214 <td>-</td>
215 </tr>
216 <tr>
217 <td>workspaceSize</td>
218 <td>输出</td>
219 <td>返回用户需要在Device侧申请的workspace大小。</td>
220 <td>-</td>
221 <td>-</td>
222 <td>-</td>
223 <td>1</td>
224 <td>-</td>
225 </tr>
226 <tr>
227 <td>executor</td>
228 <td>输出</td>
229 <td>返回op执行器,包含了算子计算流程。</td>
230 <td>-</td>
231 <td>-</td>
232 <td>-</td>
233 <td>1</td>
234 <td>-</td>
235 </tr>
236 </tbody></table>
237 </div>
238- **返回值**
239
240 返回aclnnStatus状态码,具体参见[aclnn返回码](../../../docs/zh/context/aclnn返回码.md)。
241
242 第一段接口完成入参校验,出现以下场景时报错:
243
244 <div style="overflow-x: auto;">
245 <table style="undefined;table-layout: fixed; width: 1000px"> <colgroup>
246 <col style="width: 250px">
247 <col style="width: 100px">
248 <col style="width: 650px">
249 </colgroup>
250 <thead>
251 <tr>
252 <th>返回值</th>
253 <th>错误码</th>
254 <th>描述</th>
255 </tr></thead>
256 <tbody>
257 <tr>
258 <td>ACLNN_ERR_PARAM_NULLPTR</td>
259 <td>161001</td>
260 <td>传入的paramsRef、gradsRef、momentumBufferListOptionalRef、grad_scale是空指针。</td>
261 </tr>
262 <tr>
263 <td>ACLNN_ERR_PARAM_INVALID</td>
264 <td>161002</td>
265 <td><ul><li>paramsRef、gradsRef、momentumBufferListOptionalRef、grad_scale的数据类型不在支持的范围之内。</li>
266 <li>paramsRef、gradsRef与momentumBufferListOptionalRef及其中各个tensor的数据类型不同。</li>
267 <li>paramsRef、gradsRef与momentumBufferListOptionalRef中tensor的shape维度小于等于8,grad_scale的shape不为[1]。</li>
268 <li>paramsRef、gradsRef与momentumBufferListOptionalRef(momentumBufferListOptionalRef不为空时)中相同索引tensor的shape不同。</li>
269 <li>paramsRef、gradsRef与momentumBufferListOptionalRef(momentumBufferListOptionalRef不为空时)中有空指针。</li>
270 <li>lr、momentum、weightDecay、dampening的值小于0。</li>
271 <li>gradScale等于0。</li>
272 </ul></td>
273 </tr>
274 </tbody>
275 </table>
276 </div>
277 
278## aclnnFusedSgd
279 
280***参数说明**​:
281 
282<div style="overflow-x: auto;">
283 <table style="undefined;table-layout: fixed; width: 900px"><colgroup>
284 <col style="width: 150px">
285 <col style="width: 100px">
286 <col style="width: 650px">
287 </colgroup>
288 <thead>
289 <tr>
290 <th>参数名</th>
291 <th>输入/输出</th>
292 <th>描述</th>
293 </tr></thead>
294 <tbody>
295 <tr>
296 <td>workspace</td>
297 <td>输入</td>
298 <td>在Device侧申请的workspace内存地址。</td>
299 </tr>
300 <tr>
301 <td>workspaceSize</td>
302 <td>输入</td>
303 <td>在Device侧申请的workspace大小,由第一段接口aclnnFusedSgd获取。</td>
304 </tr>
305 <tr>
306 <td>executor</td>
307 <td>输入</td>
308 <td>op执行器,包含了算子计算流程。</td>
309 </tr>
310 <tr>
311 <td>stream</td>
312 <td>输入</td>
313 <td>指定执行任务的Stream。</td>
314 </tr>
315 </tbody></table>
316 </div>
317 
318***返回值**​:
319 返回aclnnStatus状态码,具体参见[aclnn返回码](../../../docs/zh/context/aclnn返回码.md)。
320 
321## 约束说明
322 
3231. paramsRef、gradsRef、momentumBufferListOptionalRef、grad_scale的数据类型在支持的范围之内。
3242. paramsRef、gradsRef与momentumBufferListOptionalRef及其中各个tensor具有相同的数据类型。
3253. paramsRef、gradsRef与momentumBufferListOptionalRef中tensor的shape维度小于等于8,grad_scale的shape为[1]。
3264. paramsRef、gradsRef与momentumBufferListOptionalRef(momentumBufferListOptionalRef不为空时)中相同索引tensor的shape相同。
3275. paramsRef、gradsRef与momentumBufferListOptionalRef(momentumBufferListOptionalRef不为空时)中不能有空指针。
3286. lr、momentum、weightDecay、dampening的值大于等于0。
3297. gradScale不等于0。
330 
331## 调用示例
332 
333示例代码如下,仅供参考,具体编译和执行过程请参考[编译与运行样例](../../../docs/zh/context/编译与运行样例.md)。
334 
335```Cpp
336#include "acl/acl.h"
337#include "aclnnop/aclnn_fused_sgd.h"
338#include <iostream>
339#include <vector>
340 
341#define CHECK_RET(cond, return_expr) \
342 do { \
343 if (!(cond)) { \
344 return_expr; \
345 } \
346 } while (0)
347 
348#define LOG_PRINT(message, ...) \
349 do { \
350 printf(message, ##__VA_ARGS__); \
351 } while (0)
352 
353int64_t GetShapeSize(const std::vector<int64_t> &shape) {
354 int64_t shapeSize = 1;
355 for (auto i : shape) {
356 shapeSize *= i;
357 }
358 return shapeSize;
359}
360 
361void PrintOutResult(std::vector<int64_t> &shape, void **deviceAddr) {
362 auto size = GetShapeSize(shape);
363 std::vector<float> resultData(size, 0);
364 auto ret = aclrtMemcpy(
365 resultData.data(), resultData.size() * sizeof(resultData[0]), *deviceAddr,
366 size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST);
367 CHECK_RET(
368 ret == ACL_SUCCESS,
369 LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret);
370 return );
371 for (int64_t i = 0; i < size; i++) {
372 LOG_PRINT("result[%ld] is: %f\n", i, resultData[i]);
373 }
374}
375 
376int Init(int32_t deviceId, aclrtStream *stream) {
377 auto ret = aclInit(nullptr);
378 CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret);
379 return ret);
380 ret = aclrtSetDevice(deviceId);
381 CHECK_RET(ret == ACL_SUCCESS,
382 LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret);
383 return ret);
384 ret = aclrtCreateStream(stream);
385 CHECK_RET(ret == ACL_SUCCESS,
386 LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret);
387 return ret);
388 return 0;
389}
390 
391template <typename T>
392int CreateAclTensor(const std::vector<T> &hostData,
393 const std::vector<int64_t> &shape, void **deviceAddr,
394 aclDataType dataType, aclTensor **tensor) {
395 auto size = GetShapeSize(shape) * sizeof(T);
396 auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
397 CHECK_RET(ret == ACL_SUCCESS,
398 LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret);
399 return ret);
400 ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size,
401 ACL_MEMCPY_HOST_TO_DEVICE);
402 CHECK_RET(ret == ACL_SUCCESS,
403 LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret);
404 return ret);
405 
406 std::vector<int64_t> strides(shape.size(), 1);
407 for (int64_t i = shape.size() - 2; i >= 0; i--) {
408 strides[i] = shape[i + 1] * strides[i + 1];
409 }
410 
411 *tensor = aclCreateTensor(shape.data(), shape.size(), dataType,
412 strides.data(), 0, aclFormat::ACL_FORMAT_ND,
413 shape.data(), shape.size(), *deviceAddr);
414 return 0;
415}
416 
417int main() {
418 int32_t deviceId = 0;
419 aclrtStream stream;
420 auto ret = Init(deviceId, &stream);
421 CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret);
422 return ret);
423 
424 std::vector<float> paramsRefHostData1 = {1, 2, 3, 4, 5, 6, 7, 8};
425 std::vector<float> gradsRefHostData1 = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8};
426 std::vector<float> momentumHostData1 = {0, 0, 0, 0, 0, 0, 0, 0};
427 std::vector<float> paramsRefHostData2 = {9, 10, 11, 12};
428 std::vector<float> gradsRefHostData2 = {0.9, 1.0, 1.1, 1.2};
429 std::vector<float> momentumHostData2 = {0, 0, 0, 0};
430 std::vector<float> gradScaleOptionalHostData = {1.0};
431 std::vector<int64_t> inputShape1 = {2, 2, 2};
432 std::vector<int64_t> inputShape2 = {2, 2};
433 std::vector<int64_t> scalarShape = {1};
434 
435 void *paramsRef1DeviceAddr = nullptr;
436 void *gradsRef1DeviceAddr = nullptr;
437 void *momentum1DeviceAddr = nullptr;
438 void *paramsRef2DeviceAddr = nullptr;
439 void *gradsRef2DeviceAddr = nullptr;
440 void *momentum2DeviceAddr = nullptr;
441 void *gradScaleOptionalDeviceAddr = nullptr;
442 
443 aclTensor *paramsRef1 = nullptr;
444 aclTensor *gradsRef1 = nullptr;
445 aclTensor *momentum1 = nullptr;
446 aclTensor *paramsRef2 = nullptr;
447 aclTensor *gradsRef2 = nullptr;
448 aclTensor *momentum2 = nullptr;
449 aclTensor *gradScaleOptional = nullptr;
450 
451 ret = CreateAclTensor(paramsRefHostData1, inputShape1, &paramsRef1DeviceAddr, aclDataType::ACL_FLOAT, &paramsRef1);
452 CHECK_RET(ret == ACL_SUCCESS, return ret);
453 ret = CreateAclTensor(gradsRefHostData1, inputShape1, &gradsRef1DeviceAddr, aclDataType::ACL_FLOAT, &gradsRef1);
454 CHECK_RET(ret == ACL_SUCCESS, return ret);
455 ret = CreateAclTensor(momentumHostData1, inputShape1, &momentum1DeviceAddr, aclDataType::ACL_FLOAT, &momentum1);
456 CHECK_RET(ret == ACL_SUCCESS, return ret);
457 ret = CreateAclTensor(paramsRefHostData2, inputShape2, &paramsRef2DeviceAddr, aclDataType::ACL_FLOAT, &paramsRef2);
458 CHECK_RET(ret == ACL_SUCCESS, return ret);
459 ret = CreateAclTensor(gradsRefHostData2, inputShape2, &gradsRef2DeviceAddr, aclDataType::ACL_FLOAT, &gradsRef2);
460 CHECK_RET(ret == ACL_SUCCESS, return ret);
461 ret = CreateAclTensor(momentumHostData2, inputShape2, &momentum2DeviceAddr, aclDataType::ACL_FLOAT, &momentum2);
462 CHECK_RET(ret == ACL_SUCCESS, return ret);
463 ret = CreateAclTensor(gradScaleOptionalHostData, scalarShape, &gradScaleOptionalDeviceAddr, aclDataType::ACL_FLOAT, &gradScaleOptional);
464 CHECK_RET(ret == ACL_SUCCESS, return ret);
465 
466 std::vector<aclTensor*> paramsRefListData = {paramsRef1, paramsRef2};
467 std::vector<aclTensor*> gradsRefListData = {gradsRef1, gradsRef2};
468 std::vector<aclTensor*> momentumListData = {momentum1, momentum2};
469 aclTensorList* paramsRefList = aclCreateTensorList(paramsRefListData.data(), paramsRefListData.size());
470 aclTensorList* gradsRefList = aclCreateTensorList(gradsRefListData.data(), gradsRefListData.size());
471 aclTensorList* momentumList = aclCreateTensorList(momentumListData.data(), momentumListData.size());
472 
473 float weightDecay = 0.01f;
474 float momentumVal = 0.9f;
475 float lr = 0.001f;
476 float dampening = 0.0f;
477 bool nesterov = false;
478 bool maximize = false;
479 bool isFirstStep = true;
480 
481 uint64_t workspaceSize = 0;
482 aclOpExecutor *executor;
483 
484 ret = aclnnFusedSgdGetWorkspaceSize(paramsRefList, gradsRefList, momentumList, gradScaleOptional,
485 weightDecay, momentumVal, lr, dampening,
486 nesterov, maximize, isFirstStep,
487 &workspaceSize, &executor);
488 CHECK_RET(
489 ret == ACL_SUCCESS,
490 LOG_PRINT("aclnnFusedSgdGetWorkspaceSize failed. ERROR: %d\n", ret);
491 return ret);
492 
493 void *workspaceAddr = nullptr;
494 if (workspaceSize > 0) {
495 ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
496 CHECK_RET(ret == ACL_SUCCESS,
497 LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret);
498 return ret);
499 }
500 
501 ret = aclnnFusedSgd(workspaceAddr, workspaceSize, executor, stream);
502 CHECK_RET(ret == ACL_SUCCESS,
503 LOG_PRINT("aclnnFusedSgd failed. ERROR: %d\n", ret);
504 return ret);
505 
506 ret = aclrtSynchronizeStream(stream);
507 CHECK_RET(ret == ACL_SUCCESS,
508 LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret);
509 return ret);
510 
511 LOG_PRINT("====== Tensor 1 paramsRef results ======\n");
512 PrintOutResult(inputShape1, &paramsRef1DeviceAddr);
513 LOG_PRINT("====== Tensor 1 gradsRef results ======\n");
514 PrintOutResult(inputShape1, &gradsRef1DeviceAddr);
515 LOG_PRINT("------ Momentum buffer 1 ------\n");
516 PrintOutResult(inputShape1, &momentum1DeviceAddr);
517 LOG_PRINT("====== Tensor 2 paramsRef results ======\n");
518 PrintOutResult(inputShape2, &paramsRef2DeviceAddr);
519 LOG_PRINT("====== Tensor 2 gradsRef results ======\n");
520 PrintOutResult(inputShape2, &gradsRef2DeviceAddr);
521 LOG_PRINT("------ Momentum buffer 2 ------\n");
522 PrintOutResult(inputShape2, &momentum2DeviceAddr);
523 
524 aclDestroyTensorList(paramsRefList);
525 aclDestroyTensorList(gradsRefList);
526 aclDestroyTensorList(momentumList);
527 aclDestroyTensor(gradScaleOptional);
528 
529 aclrtFree(paramsRef1DeviceAddr);
530 aclrtFree(gradsRef1DeviceAddr);
531 aclrtFree(momentum1DeviceAddr);
532 aclrtFree(paramsRef2DeviceAddr);
533 aclrtFree(gradsRef2DeviceAddr);
534 aclrtFree(momentum2DeviceAddr);
535 aclrtFree(gradScaleOptionalDeviceAddr);
536 if (workspaceSize > 0) {
537 aclrtFree(workspaceAddr);
538 }
539 
540 ret = aclrtDestroyStream(stream);
541 CHECK_RET(ret == ACL_SUCCESS,
542 LOG_PRINT("destroy stream failed. ERROR: %d\n", ret);
543 return ret);
544 ret = aclrtResetDevice(deviceId);
545 CHECK_RET(ret == ACL_SUCCESS,
546 LOG_PRINT("reset device failed. ERROR: %d\n", ret);
547 return ret);
548 ret = aclFinalize();
549 CHECK_RET(ret == ACL_SUCCESS,
550 LOG_PRINT("finalize acl failed. ERROR: %d\n", ret);
551 return ret);
552 return 0;
553}
554 
555```
Aoptim/fused_sgd/examples/test_aclnn_fused_sgd.cpp+228-0
@@ -0,0 +1,228 @@
1/**
2 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 * CANN Open Software License Agreement Version 2.0 (the "License").
5 * Please refer to the License for details. You may not use this file except in compliance with the License.
6 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 * See LICENSE in the root of the software repository for the full text of the License.
9 */
10 
11#include "acl/acl.h"
12#include "aclnnop/aclnn_fused_sgd.h"
13#include <iostream>
14#include <vector>
15 
16#define CHECK_RET(cond, return_expr) \
17 do { \
18 if (!(cond)) { \
19 return_expr; \
20 } \
21 } while (0)
22 
23#define LOG_PRINT(message, ...) \
24 do { \
25 printf(message, ##__VA_ARGS__); \
26 } while (0)
27 
28int64_t GetShapeSize(const std::vector<int64_t> &shape) {
29 int64_t shapeSize = 1;
30 for (auto i : shape) {
31 shapeSize *= i;
32 }
33 return shapeSize;
34}
35 
36void PrintOutResult(std::vector<int64_t> &shape, void **deviceAddr) {
37 auto size = GetShapeSize(shape);
38 std::vector<float> resultData(size, 0);
39 auto ret = aclrtMemcpy(
40 resultData.data(), resultData.size() * sizeof(resultData[0]), *deviceAddr,
41 size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST);
42 CHECK_RET(
43 ret == ACL_SUCCESS,
44 LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret);
45 return );
46 for (int64_t i = 0; i < size; i++) {
47 LOG_PRINT("result[%ld] is: %f\n", i, resultData[i]);
48 }
49}
50 
51int Init(int32_t deviceId, aclrtStream *stream) {
52 auto ret = aclInit(nullptr);
53 CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret);
54 return ret);
55 ret = aclrtSetDevice(deviceId);
56 CHECK_RET(ret == ACL_SUCCESS,
57 LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret);
58 return ret);
59 ret = aclrtCreateStream(stream);
60 CHECK_RET(ret == ACL_SUCCESS,
61 LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret);
62 return ret);
63 return 0;
64}
65 
66template <typename T>
67int CreateAclTensor(const std::vector<T> &hostData,
68 const std::vector<int64_t> &shape, void **deviceAddr,
69 aclDataType dataType, aclTensor **tensor) {
70 auto size = GetShapeSize(shape) * sizeof(T);
71 auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
72 CHECK_RET(ret == ACL_SUCCESS,
73 LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret);
74 return ret);
75 ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size,
76 ACL_MEMCPY_HOST_TO_DEVICE);
77 CHECK_RET(ret == ACL_SUCCESS,
78 LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret);
79 return ret);
80 
81 std::vector<int64_t> strides(shape.size(), 1);
82 for (int64_t i = shape.size() - 2; i >= 0; i--) {
83 strides[i] = shape[i + 1] * strides[i + 1];
84 }
85 
86 *tensor = aclCreateTensor(shape.data(), shape.size(), dataType,
87 strides.data(), 0, aclFormat::ACL_FORMAT_ND,
88 shape.data(), shape.size(), *deviceAddr);
89 return 0;
90}
91 
92int main() {
93 int32_t deviceId = 0;
94 aclrtStream stream;
95 auto ret = Init(deviceId, &stream);
96 CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret);
97 return ret);
98 
99 std::vector<float> paramsRefHostData1 = {1, 2, 3, 4, 5, 6, 7, 8};
100 std::vector<float> gradsRefHostData1 = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8};
101 std::vector<float> momentumHostData1 = {0, 0, 0, 0, 0, 0, 0, 0};
102 std::vector<float> paramsRefHostData2 = {9, 10, 11, 12};
103 std::vector<float> gradsRefHostData2 = {0.9, 1.0, 1.1, 1.2};
104 std::vector<float> momentumHostData2 = {0, 0, 0, 0};
105 std::vector<float> gradScaleOptionalHostData = {1.0};
106 std::vector<int64_t> inputShape1 = {2, 2, 2};
107 std::vector<int64_t> inputShape2 = {2, 2};
108 std::vector<int64_t> scalarShape = {1};
109 
110 void *paramsRef1DeviceAddr = nullptr;
111 void *gradsRef1DeviceAddr = nullptr;
112 void *momentum1DeviceAddr = nullptr;
113 void *paramsRef2DeviceAddr = nullptr;
114 void *gradsRef2DeviceAddr = nullptr;
115 void *momentum2DeviceAddr = nullptr;
116 void *gradScaleOptionalDeviceAddr = nullptr;
117 
118 aclTensor *paramsRef1 = nullptr;
119 aclTensor *gradsRef1 = nullptr;
120 aclTensor *momentum1 = nullptr;
121 aclTensor *paramsRef2 = nullptr;
122 aclTensor *gradsRef2 = nullptr;
123 aclTensor *momentum2 = nullptr;
124 aclTensor *gradScaleOptional = nullptr;
125 
126 ret = CreateAclTensor(paramsRefHostData1, inputShape1, &paramsRef1DeviceAddr, aclDataType::ACL_FLOAT, &paramsRef1);
127 CHECK_RET(ret == ACL_SUCCESS, return ret);
128 ret = CreateAclTensor(gradsRefHostData1, inputShape1, &gradsRef1DeviceAddr, aclDataType::ACL_FLOAT, &gradsRef1);
129 CHECK_RET(ret == ACL_SUCCESS, return ret);
130 ret = CreateAclTensor(momentumHostData1, inputShape1, &momentum1DeviceAddr, aclDataType::ACL_FLOAT, &momentum1);
131 CHECK_RET(ret == ACL_SUCCESS, return ret);
132 ret = CreateAclTensor(paramsRefHostData2, inputShape2, &paramsRef2DeviceAddr, aclDataType::ACL_FLOAT, &paramsRef2);
133 CHECK_RET(ret == ACL_SUCCESS, return ret);
134 ret = CreateAclTensor(gradsRefHostData2, inputShape2, &gradsRef2DeviceAddr, aclDataType::ACL_FLOAT, &gradsRef2);
135 CHECK_RET(ret == ACL_SUCCESS, return ret);
136 ret = CreateAclTensor(momentumHostData2, inputShape2, &momentum2DeviceAddr, aclDataType::ACL_FLOAT, &momentum2);
137 CHECK_RET(ret == ACL_SUCCESS, return ret);
138 ret = CreateAclTensor(gradScaleOptionalHostData, scalarShape, &gradScaleOptionalDeviceAddr, aclDataType::ACL_FLOAT, &gradScaleOptional);
139 CHECK_RET(ret == ACL_SUCCESS, return ret);
140 
141 std::vector<aclTensor*> paramsRefListData = {paramsRef1, paramsRef2};
142 std::vector<aclTensor*> gradsRefListData = {gradsRef1, gradsRef2};
143 std::vector<aclTensor*> momentumListData = {momentum1, momentum2};
144 aclTensorList* paramsRefList = aclCreateTensorList(paramsRefListData.data(), paramsRefListData.size());
145 aclTensorList* gradsRefList = aclCreateTensorList(gradsRefListData.data(), gradsRefListData.size());
146 aclTensorList* momentumList = aclCreateTensorList(momentumListData.data(), momentumListData.size());
147 
148 float weightDecay = 0.01f;
149 float momentumVal = 0.9f;
150 float lr = 0.001f;
151 float dampening = 0.0f;
152 bool nesterov = false;
153 bool maximize = false;
154 bool isFirstStep = true;
155 
156 uint64_t workspaceSize = 0;
157 aclOpExecutor *executor;
158 
159 ret = aclnnFusedSgdGetWorkspaceSize(paramsRefList, gradsRefList, momentumList, gradScaleOptional,
160 weightDecay, momentumVal, lr, dampening,
161 nesterov, maximize, isFirstStep,
162 &workspaceSize, &executor);
163 CHECK_RET(
164 ret == ACL_SUCCESS,
165 LOG_PRINT("aclnnFusedSgdGetWorkspaceSize failed. ERROR: %d\n", ret);
166 return ret);
167 
168 void *workspaceAddr = nullptr;
169 if (workspaceSize > 0) {
170 ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
171 CHECK_RET(ret == ACL_SUCCESS,
172 LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret);
173 return ret);
174 }
175 
176 ret = aclnnFusedSgd(workspaceAddr, workspaceSize, executor, stream);
177 CHECK_RET(ret == ACL_SUCCESS,
178 LOG_PRINT("aclnnFusedSgd failed. ERROR: %d\n", ret);
179 return ret);
180 
181 ret = aclrtSynchronizeStream(stream);
182 CHECK_RET(ret == ACL_SUCCESS,
183 LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret);
184 return ret);
185 
186 LOG_PRINT("====== Tensor 1 paramsRef results ======\n");
187 PrintOutResult(inputShape1, &paramsRef1DeviceAddr);
188 LOG_PRINT("====== Tensor 1 gradsRef results ======\n");
189 PrintOutResult(inputShape1, &gradsRef1DeviceAddr);
190 LOG_PRINT("------ Momentum buffer 1 ------\n");
191 PrintOutResult(inputShape1, &momentum1DeviceAddr);
192 LOG_PRINT("====== Tensor 2 paramsRef results ======\n");
193 PrintOutResult(inputShape2, &paramsRef2DeviceAddr);
194 LOG_PRINT("====== Tensor 2 gradsRef results ======\n");
195 PrintOutResult(inputShape2, &gradsRef2DeviceAddr);
196 LOG_PRINT("------ Momentum buffer 2 ------\n");
197 PrintOutResult(inputShape2, &momentum2DeviceAddr);
198 
199 aclDestroyTensorList(paramsRefList);
200 aclDestroyTensorList(gradsRefList);
201 aclDestroyTensorList(momentumList);
202 aclDestroyTensor(gradScaleOptional);
203 
204 aclrtFree(paramsRef1DeviceAddr);
205 aclrtFree(gradsRef1DeviceAddr);
206 aclrtFree(momentum1DeviceAddr);
207 aclrtFree(paramsRef2DeviceAddr);
208 aclrtFree(gradsRef2DeviceAddr);
209 aclrtFree(momentum2DeviceAddr);
210 aclrtFree(gradScaleOptionalDeviceAddr);
211 if (workspaceSize > 0) {
212 aclrtFree(workspaceAddr);
213 }
214 
215 ret = aclrtDestroyStream(stream);
216 CHECK_RET(ret == ACL_SUCCESS,
217 LOG_PRINT("destroy stream failed. ERROR: %d\n", ret);
218 return ret);
219 ret = aclrtResetDevice(deviceId);
220 CHECK_RET(ret == ACL_SUCCESS,
221 LOG_PRINT("reset device failed. ERROR: %d\n", ret);
222 return ret);
223 ret = aclFinalize();
224 CHECK_RET(ret == ACL_SUCCESS,
225 LOG_PRINT("finalize acl failed. ERROR: %d\n", ret);
226 return ret);
227 return 0;
228}
Aoptim/fused_sgd/op_api/aclnn_fused_sgd.cpp+354-0
@@ -0,0 +1,354 @@
1/**
2 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 * CANN Open Software License Agreement Version 2.0 (the "License").
5 * Please refer to the License for details. You may not use this file except in compliance with the License.
6 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 * See LICENSE in the root of the software repository for the full text of the License.
9 */
10 
11#include "aclnn_fused_sgd.h"
12#include "aclnn_kernels/contiguous.h"
13#include "fused_sgd.h"
14#include "aclnn_kernels/common/op_error_check.h"
15#include "aclnn_kernels/cast.h"
16#include "opdev/common_types.h"
17#include "opdev/data_type_utils.h"
18#include "opdev/format_utils.h"
19#include "opdev/op_dfx.h"
20#include "opdev/op_executor.h"
21#include "opdev/make_op_executor.h"
22#include "opdev/op_log.h"
23#include "opdev/shape_utils.h"
24#include "opdev/tensor_view_utils.h"
25#include "opdev/platform.h"
26#include "op_api/aclnn_util.h"
27 
28using namespace op;
29#ifdef __cplusplus
30extern "C" {
31#endif
32 
33static const std::initializer_list<op::DataType> ASCEND910_DTYPE_SUPPORT_LIST = {
34 op::DataType::DT_FLOAT, op::DataType::DT_FLOAT16};
35 
36static const std::initializer_list<op::DataType> ASCEND910B_DTYPE_SUPPORT_LIST = {
37 op::DataType::DT_FLOAT, op::DataType::DT_FLOAT16, op::DataType::DT_BF16};
38 
39static const std::initializer_list<op::DataType> ASCEND950_DTYPE_SUPPORT_LIST = {
40 op::DataType::DT_FLOAT, op::DataType::DT_FLOAT16, op::DataType::DT_BF16};
41 
42static inline const std::initializer_list<op::DataType>& GetDtypeSupportListFromSocVersion()
43{
44 auto curArch = GetCurrentPlatformInfo().GetCurNpuArch();
45 switch (curArch) {
46 case NpuArch::DAV_2201: {
47 return ASCEND910B_DTYPE_SUPPORT_LIST;
48 }
49 case NpuArch::DAV_3510: {
50 return ASCEND950_DTYPE_SUPPORT_LIST;
51 }
52 case NpuArch::DAV_1001: {
53 return ASCEND910_DTYPE_SUPPORT_LIST;
54 }
55 default: {
56 return ASCEND910_DTYPE_SUPPORT_LIST;
57 }
58 }
59}
60 
61static bool CheckNotNull(
62 const aclTensorList *paramsRef,
63 const aclTensorList *gradsRef,
64 const aclTensorList *momentumBufferListOptionalRef)
65{
66 OP_CHECK_NULL(paramsRef, return false);
67 for (uint64_t i = 0; i < paramsRef->Size(); i++) {
68 OP_CHECK_NULL((*paramsRef)[i], return false);
69 }
70 OP_CHECK_NULL(gradsRef, return false);
71 for (uint64_t i = 0; i < gradsRef->Size(); i++) {
72 OP_CHECK_NULL((*gradsRef)[i], return false);
73 }
74 if(momentumBufferListOptionalRef != nullptr) {
75 for (uint64_t i = 0; i < momentumBufferListOptionalRef->Size(); i++) {
76 OP_CHECK_NULL((*momentumBufferListOptionalRef)[i], return false);
77 }
78 }
79 return true;
80}
81 
82static bool CheckTensorListCount(
83 const aclTensorList *paramsRef,
84 const aclTensorList *gradsRef,
85 const aclTensorList *momentumBufferListOptionalRef)
86{
87 auto tensorCount = paramsRef->Size();
88 if (tensorCount == 0) {
89 OP_LOGE(ACLNN_ERR_PARAM_INVALID, "paramsRef tensor list is empty.");
90 return false;
91 }
92 if (gradsRef->Size() != tensorCount) {
93 OP_LOGE(ACLNN_ERR_PARAM_INVALID, "gradsRef tensor count does not match paramsRef.");
94 return false;
95 }
96 if (momentumBufferListOptionalRef != nullptr && momentumBufferListOptionalRef->Size() != tensorCount) {
97 OP_LOGE(ACLNN_ERR_PARAM_INVALID, "momentumBufferListOptionalRef tensor count does not match paramsRef.");
C
Cchenqi3176月22日

OP_LOGE_FOR_INVALID_DTYPE_WITH_REASON

likedislike
98 return false;
99 }
100 return true;
101}
102 
103static bool CheckDtype(
104 const aclTensorList *paramsRef,
105 const aclTensorList *gradsRef,
106 const aclTensorList *momentumBufferListOptionalRef)
107{
108 const std::initializer_list<op::DataType> dtypeSupportList = GetDtypeSupportListFromSocVersion();
109 auto paramsTensor = (*paramsRef)[0];
110 
111 OP_CHECK_DTYPE_NOT_SUPPORT(paramsTensor, dtypeSupportList, return false);
112 op::DataType inputType = paramsTensor->GetDataType();
113 for (uint64_t i = 0; i < paramsRef->Size(); i++) {
114 if ((*paramsRef)[i]->GetDataType() != inputType || (*gradsRef)[i]->GetDataType() != inputType) {
115 OP_LOGE(ACLNN_ERR_PARAM_INVALID, "expects all input tensors with the same dtype.");
116 return false;
117 }
118 }
119 if(momentumBufferListOptionalRef != nullptr) {
120 auto momentumTensor = (*momentumBufferListOptionalRef)[0];
121 OP_CHECK_DTYPE_NOT_SUPPORT(momentumTensor, dtypeSupportList, return false);
122 OP_CHECK_DTYPE_NOT_SAME(paramsTensor, momentumTensor, return false);
123 for (uint64_t i = 0; i < momentumBufferListOptionalRef->Size(); i++) {
124 if ((*momentumBufferListOptionalRef)[i]->GetDataType() != inputType) {
125 OP_LOGE(ACLNN_ERR_PARAM_INVALID, "expects all input tensors with the same dtype.");
126 return false;
127 }
128 }
129 }
130 return true;
131}
132 
133static void CheckOptionalTensorListEmpty(const aclTensorList *&tensorList)
134{
135 if (tensorList == nullptr) {
136 OP_LOGI("momentumBufferListOptionalRef is nullptr");
137 return;
138 }
139 if (tensorList->Size() == 0) {
140 OP_LOGI("momentumBufferListOptionalRef is set nullptr because len(momentumBufferListOptionalRef) is 0.");
141 tensorList = nullptr;
142 }
143}
144 
145static void CheckIsFirstStep(bool isFirstStep)
146{
147 if (isFirstStep) {
148 OP_LOGW("isFirstStep argument has no effect when momentumBufferListOptionalRef is empty");
149 }
150}
atomgit-bot
atomgit-botatomgit-bot6月18日

🔵 Low Priority

CheckIsFirstStep 函数(第169-173行)在 isFirstStep 为 true 时总是打印警告 "isFirstStep argument has no effect when momentumBufferListOptionalRef is empty",但它不检查 momentumBufferListOptionalRef 的实际状态。

该函数在 aclnnFusedSgdGetWorkspaceSize 第312行被无条件调用,无论 momentum buffer 是否为空。当 momentum buffer 非空且 isFirstStep=true 时,警告信息是误导性的——实际上 isFirstStep=true 在 momentum buffer 不为空时是有意义的(表示第一步动量初始化)。

变更:第169-174行新增的 CheckIsFirstStep 函数。

建议:将 CheckIsFirstStep 改为接收 momentumBufferListOptionalRef 参数,仅在 momentumBufferListOptionalRef 确实为空(nullptr 或 empty)且 isFirstStep=true 时才输出警告。

likedislike
151 
152static bool CheckAttr(
153 const aclTensorList *momentumBufferListOptionalRef,
154 float weightDecay, float momentum, float lr,
155 float dampening, bool nesterov)
156{
157 if (weightDecay < 0) {
158 OP_LOGE(ACLNN_ERR_PARAM_INVALID, "weightDecay[%f] shoule be greater or equal than 0", weightDecay);
159 return false;
160 }
161 if (momentum < 0) {
162 OP_LOGE(ACLNN_ERR_PARAM_INVALID, "momentum[%f] shoule be greater or equal than 0", momentum);
163 return false;
164 }
165 if (lr < 0) {
166 OP_LOGE(ACLNN_ERR_PARAM_INVALID, "lr[%f] shoule be greater or equal than 0", lr);
167 return false;
168 }
169 const float EPS = 1e-6f;
170 if (nesterov && (momentum <= 0 || std::abs(dampening) >= EPS)) {
171 OP_LOGE(ACLNN_ERR_PARAM_INVALID, "nesterov[%d] momentum requires a momentum[%f] and zero dampening[%f].", static_cast<int32_t>(nesterov), momentum, dampening);
172 return false;
173 }
174 if ((momentumBufferListOptionalRef == nullptr && std::abs(momentum) >= EPS)) {
175 OP_LOGE(ACLNN_ERR_PARAM_INVALID, "momentum[%f] is invalid. momentum shoube be 0 when momentumBufferListOptionalRef is nullptr.", momentum);
176 return false;
177 }
178 if ((momentumBufferListOptionalRef != nullptr && (momentum < 0.0 || std::abs(momentum) < EPS))) {
179 OP_LOGE(ACLNN_ERR_PARAM_INVALID, "momentum[%f] is invalid. momentum shoube be greater than 0 when momentumBufferListOptionalRef is not nullptr.", momentum);
180 return false;
atomgit-bot
atomgit-botatomgit-bot6月18日

🔵 Low Priority

aclnn_fused_sgd.cppCheckAttr 函数中,多处错误日志信息将 "should" 拼写为 "shoule":

  • 第182行:"weightDecay[%f] shoule be greater or equal than 0"
  • 第186行:"momentum[%f] shoule be greater or equal than 0"
  • 第190行:"lr[%f] shoule be greater or equal than 0"
  • 第199行:"nesterov[%d] momentum requires a momentum[%f] and zero dampening[%f]."
  • 第203行:"momentum[%f] is invalid. momentum shoube be greater than 0 ..."("shoube" 也是 typo)

此外第199行句子中 "requires a momentum" 语义冗余。这些拼写错误会影响日志可读性,增加排查问题的难度。

变更:第182、186、190、199、203行新增的错误日志字符串。

建议:将所有 "shoule" 修正为 "should",将 "shoube" 修正为 "should be"。第199行建议改为 "nesterov requires momentum[%f] > 0 and dampening[%f] == 0."

likedislike
majiajian_hw
6月22日 评论:
likedislike
181 }
182 return true;
183}
184 
185static bool CheckShape(
186 const aclTensorList *paramsRef,
187 const aclTensorList *gradsRef,
188 const aclTensorList *momentumBufferListOptionalRef,
189 const aclTensor *gradScaleOptional)
190{
191 for (uint64_t i = 0; i < paramsRef->Size(); i++) {
192 op::Shape expectShape = (*paramsRef)[i]->GetViewShape();
193 if((*gradsRef)[i]->GetViewShape() != expectShape ||
194 (momentumBufferListOptionalRef != nullptr &&
195 (*momentumBufferListOptionalRef)[i]->GetViewShape() != expectShape)) {
196 OP_LOGE(ACLNN_ERR_PARAM_INVALID, "expects all input tensors with the same shape.");
197 return false;
198 }
199 }
200 if (gradScaleOptional != nullptr && !(gradScaleOptional->IsEmpty())) {
201 op::Shape shape = gradScaleOptional->GetViewShape();
202 if (shape.GetDimNum() != 1 || shape.GetDim(0) != 1) {
203 return false;
204 }
205 }
206 return true;
207}
208 
209static aclnnStatus CheckParams(
210 const aclTensorList *paramsRef,
211 const aclTensorList *gradsRef,
212 const aclTensorList *momentumBufferListOptionalRef,
213 const aclTensor *gradScaleOptional,
214 float weightDecay, float momentum, float lr, float dampening, bool nesterov)
215{
216 CHECK_RET(CheckAttr(momentumBufferListOptionalRef, weightDecay, momentum, lr, dampening, nesterov), ACLNN_ERR_PARAM_INVALID);
217 CHECK_RET(CheckNotNull(paramsRef, gradsRef, momentumBufferListOptionalRef), ACLNN_ERR_PARAM_NULLPTR);
218 CHECK_RET(CheckTensorListCount(paramsRef, gradsRef, momentumBufferListOptionalRef), ACLNN_ERR_PARAM_INVALID);
219 CHECK_RET(CheckDtype(paramsRef, gradsRef, momentumBufferListOptionalRef), ACLNN_ERR_PARAM_INVALID);
220 CHECK_RET(CheckShape(paramsRef, gradsRef, momentumBufferListOptionalRef, gradScaleOptional), ACLNN_ERR_PARAM_INVALID);
221 return ACLNN_SUCCESS;
222}
223 
224const aclTensor* FlattenDims(const aclTensor* tensor, aclOpExecutor* executor)
225{
226 op::Shape shapeTensor = tensor->GetViewShape();
227 int64_t dimNum = shapeTensor.GetDimNum();
228
229 op::Shape newShape;
230 int64_t catdimSize = 1;
231 for (int64_t i = 0; i < dimNum; i++) {
232 catdimSize *= shapeTensor.GetDim(i);
233 }
234 newShape.AppendDim(catdimSize);
235 auto reshapeTensor = executor->CreateView(tensor, tensor->GetViewShape(), tensor->GetViewOffset());
236 reshapeTensor->SetViewShape(newShape);
237 reshapeTensor->SetOriginalShape(newShape);
238 reshapeTensor->SetStorageShape(newShape);
239 return reshapeTensor;
240}
241 
242static const aclTensorList* MakeContiguousTensorList(
243 const aclTensorList* tensorList, aclOpExecutor* executor)
244{
245 op::FVector<const aclTensor*> contiguousTensors;
246 for (uint64_t i = 0; i < tensorList->Size(); i++) {
247 if ((*tensorList)[i]->IsEmpty()) {
248 continue;
249 }
250 auto contiguous = l0op::Contiguous((*tensorList)[i], executor);
251 CHECK_RET(contiguous != nullptr, nullptr);
252 contiguous = FlattenDims(contiguous, executor);
253 contiguousTensors.emplace_back(contiguous);
254 }
255 return executor->AllocTensorList(contiguousTensors.data(), contiguousTensors.size());
256}
257 
258static void ViewCopyTensorList(
259 const aclTensorList* src, const aclTensorList* dst, aclOpExecutor* executor)
260{
261 uint64_t cnt = 0;
262 for (uint64_t i = 0; i < dst->Size(); i++) {
263 if ((*dst)[i]->IsEmpty()) {
264 continue;
265 }
266 l0op::ViewCopy((*src)[cnt], (*dst)[i], executor);
267 cnt += 1;
268 }
269}
270 
271aclnnStatus aclnnFusedSgdGetWorkspaceSize(
272 const aclTensorList *paramsRef,
273 const aclTensorList *gradsRef,
274 const aclTensorList *momentumBufferListOptionalRef,
275 const aclTensor *gradScaleOptional,
276 float weightDecay,
277 float momentum,
278 float lr,
279 float dampening,
280 bool nesterov,
281 bool maximize,
282 bool isFirstStep,
283 uint64_t *workspaceSize,
284 aclOpExecutor **executor)
285{
286 L2_DFX_PHASE_1(aclnnFusedSgd,
287 DFX_IN(paramsRef, gradsRef, momentumBufferListOptionalRef, gradScaleOptional,
288 weightDecay, momentum, lr, dampening, nesterov, maximize, isFirstStep),
289 DFX_OUT(paramsRef, gradsRef, momentumBufferListOptionalRef));
290 
291 auto uniqueExecutor = CREATE_EXECUTOR();
292 CHECK_RET(uniqueExecutor.get() != nullptr, ACLNN_ERR_INNER_CREATE_EXECUTOR);
293 
294 if (paramsRef->Size() == 0) {
295 uniqueExecutor.ReleaseTo(executor);
296 return ACLNN_SUCCESS;
297 }
298 
299 CheckOptionalTensorListEmpty(momentumBufferListOptionalRef);
300 
301 CheckIsFirstStep(isFirstStep);
302 
303 auto ret = CheckParams(paramsRef, gradsRef, momentumBufferListOptionalRef, gradScaleOptional, weightDecay, momentum, lr, dampening, nesterov);
304 CHECK_RET(ret == ACLNN_SUCCESS, ret);
305 
306 if (gradScaleOptional != nullptr && !(gradScaleOptional->IsEmpty())) {
307 gradScaleOptional = l0op::Cast(gradScaleOptional, DataType::DT_FLOAT, uniqueExecutor.get());
308 }
309 
310 auto paramsContiguous = MakeContiguousTensorList(paramsRef, uniqueExecutor.get());
311 CHECK_RET(paramsContiguous != nullptr, ACLNN_ERR_INNER_NULLPTR);
312 
313 auto gradsContiguous = MakeContiguousTensorList(gradsRef, uniqueExecutor.get());
314 CHECK_RET(gradsContiguous != nullptr, ACLNN_ERR_INNER_NULLPTR);
315 
316 const aclTensorList* momentumContiguous = nullptr;
317 if(momentumBufferListOptionalRef != nullptr) {
318 momentumContiguous = MakeContiguousTensorList(momentumBufferListOptionalRef, uniqueExecutor.get());
319 }
320 
321 auto [paramsOut, gradsOut, momentumOut] = l0op::FusedSgd(
322 paramsContiguous, gradsContiguous, momentumContiguous,
323 gradScaleOptional,
324 weightDecay, momentum, lr, dampening, nesterov, maximize, isFirstStep,
325 uniqueExecutor.get());
326 CHECK_RET(paramsOut != nullptr, ACLNN_ERR_INNER_NULLPTR);
327 CHECK_RET(gradsOut != nullptr, ACLNN_ERR_INNER_NULLPTR);
328 CHECK_RET(momentumContiguous == nullptr || momentumOut != nullptr, ACLNN_ERR_INNER_NULLPTR);
329 
330 ViewCopyTensorList(paramsOut, paramsRef, uniqueExecutor.get());
331 ViewCopyTensorList(gradsOut, gradsRef, uniqueExecutor.get());
332 if(momentumBufferListOptionalRef != nullptr) {
333 ViewCopyTensorList(momentumOut, momentumBufferListOptionalRef, uniqueExecutor.get());
334 }
335 
336 *workspaceSize = uniqueExecutor->GetWorkspaceSize();
337 uniqueExecutor.ReleaseTo(executor);
338 
339 return ACLNN_SUCCESS;
340}
341 
342aclnnStatus aclnnFusedSgd(
343 void *workspace,
344 uint64_t workspaceSize,
345 aclOpExecutor *executor,
346 aclrtStream stream)
347{
348 L2_DFX_PHASE_2(aclnnFusedSgd);
349 return CommonOpExecutorRun(workspace, workspaceSize, executor, stream);
350}
351 
352#ifdef __cplusplus
353}
354#endif
Aoptim/fused_sgd/op_api/aclnn_fused_sgd.h+79-0
@@ -0,0 +1,79 @@
1/**
2 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 * CANN Open Software License Agreement Version 2.0 (the "License").
5 * Please refer to the License for details. You may not use this file except in compliance with the License.
6 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 * See LICENSE in the root of the software repository for the full text of the License.
9 */
10 
11#ifndef ACLNN_FUSED_SGD_H_
12#define ACLNN_FUSED_SGD_H_
13 
14#include "aclnn/aclnn_base.h"
15#include "aclnn_util.h"
16 
17#ifdef __cplusplus
18extern "C" {
19#endif
20 
21/**
22 * @brief aclnnFusedSgd的第一段接口,根据具体的计算流程,计算workspace大小。
23 * @domain aclnn_ops_train
24 *
25 * 算子功能:融合SGD优化器,支持momentum、weight_decay、nesterov、maximize,
26 * 将参数更新、动量更新、梯度缩放等操作融合为单个kernel。
27 *
28 * @param [in] paramsRef: device侧的aclTensorList,需要更新的参数列表。
29 * 数据类型支持FLOAT、FLOAT16、BFLOAT16。数据格式支持ND。
30 * @param [in] gradsRef: device侧的aclTensorList,梯度列表。数据类型、shape需要与params一致。
31 * @param [in] momentumBufferListOptionalRef: device侧的aclTensorList,动量缓冲列表。数据类型、shape需要与params一致。
32 * @param [in] gradScaleOptional: device侧的aclTensor(可选),梯度缩放因子。数据类型支持FLOAT。
33 * @param [in] weightDecay: 权重衰减系数,数据类型FLOAT。
34 * @param [in] momentum: 动量因子,数据类型FLOAT。
35 * @param [in] lr: 学习率,数据类型FLOAT。
36 * @param [in] dampening: 动量阻尼系数,数据类型FLOAT。
37 * @param [in] nesterov: 是否启用Nesterov动量,数据类型BOOL。
38 * @param [in] maximize: 是否最大化目标函数,数据类型BOOL。
39 * @param [in] isFirstStep: 是否为第一个优化步,数据类型BOOL。
40 * @param [out] workspaceSize: 返回用户在device侧申请的workspace大小。
41 * @param [out] executor: 返回op执行器。
42 * @return aclnnStatus: 返回状态码。
43 */
44ACLNN_API aclnnStatus aclnnFusedSgdGetWorkspaceSize(
45 const aclTensorList *paramsRef,
46 const aclTensorList *gradsRef,
47 const aclTensorList *momentumBufferListOptionalRef,
48 const aclTensor *gradScaleOptional,
49 float weightDecay,
50 float momentum,
51 float lr,
52 float dampening,
53 bool nesterov,
54 bool maximize,
55 bool isFirstStep,
56 uint64_t *workspaceSize,
57 aclOpExecutor **executor);
58 
59/**
60 * @brief aclnnFusedSgd的第二段接口,用于执行计算。
61 *
62 * 算子功能:执行融合SGD优化器。
63 * @param [in] workspace: 在device侧申请的workspace内存起址。
64 * @param [in] workspaceSize: workspace大小,由aclnnFusedSgdGetWorkspaceSize获取。
65 * @param [in] executor: op执行器。
66 * @param [in] stream: acl stream流。
67 * @return aclnnStatus: 返回状态码。
68 */
69ACLNN_API aclnnStatus aclnnFusedSgd(
70 void *workspace,
71 uint64_t workspaceSize,
72 aclOpExecutor *executor,
73 aclrtStream stream);
74 
75#ifdef __cplusplus
76}
77#endif
78 
79#endif
Aoptim/fused_sgd/op_api/fused_sgd.cpp+93-0
@@ -0,0 +1,93 @@
1/**
2 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 * CANN Open Software License Agreement Version 2.0 (the "License").
5 * Please refer to the License for details. You may not use this file except in compliance with the License.
6 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 * See LICENSE in the root of the software repository for the full text of the License.
9 */
10 
11/*!
12 * \file fused_sgd.cpp
13 * \brief
14 */
15#include "fused_sgd.h"
16#include "opdev/make_op_executor.h"
17#include "opdev/op_def.h"
18#include "opdev/op_dfx.h"
19#include "opdev/op_executor.h"
20#include "opdev/op_log.h"
21#include "opdev/shape_utils.h"
22#include "opdev/platform.h"
23#include "opdev/format_utils.h"
24#include "aclnn_kernels/common/op_error_check.h"
25 
26using namespace op;
27 
28namespace l0op {
29OP_TYPE_REGISTER(FusedSgd);
30 
31std::tuple<const aclTensorList*, const aclTensorList*, const aclTensorList*> FusedSgd(
32 const aclTensorList* paramsRef,
33 const aclTensorList* gradsRef,
34 const aclTensorList* momentumBufferListOptionalRef,
35 const aclTensor* gradScaleOptional,
36 float weightDecay,
37 float momentum,
38 float lr,
39 float dampening,
40 bool nesterov,
41 bool maximize,
42 bool isFirstStep,
43 aclOpExecutor *executor)
44{
45 L0_DFX(FusedSgd, paramsRef, gradsRef, momentumBufferListOptionalRef, gradScaleOptional,
46 weightDecay, momentum, lr, dampening, nesterov, maximize, isFirstStep);
47 
48 const aclTensorList* momentumBufferListOptionalRefOut = nullptr;
49 if (momentumBufferListOptionalRef == nullptr) {
50 const op::Shape momentumBufferListOptionalRefOutShape = {1};
51 const aclTensor* tmpTensor = executor->AllocTensor(momentumBufferListOptionalRefOutShape, (*paramsRef)[0]->GetDataType(), (*paramsRef)[0]->GetStorageFormat());
52 op::FVector<const aclTensor*> tensorListA;
53 tensorListA.emplace_back(tmpTensor);
54 momentumBufferListOptionalRefOut = executor->AllocTensorList(tensorListA.data(), tensorListA.size());
55 if(momentumBufferListOptionalRefOut == nullptr) {
56 return std::tuple<const aclTensorList*, const aclTensorList*, const aclTensorList*>(
57 nullptr, nullptr, nullptr);
58 }
59 } else {
60 momentumBufferListOptionalRefOut = momentumBufferListOptionalRef;
61 }
62 
63 const aclTensorList* momentumBufferListOptionalRefInput = momentumBufferListOptionalRef;
64 if (momentumBufferListOptionalRef == nullptr) {
65 const op::Shape momentumBufferListOptionalRefInputShape = {0};
66 const aclTensor* tmpTensor = executor->AllocTensor(momentumBufferListOptionalRefInputShape, (*paramsRef)[0]->GetDataType(), (*paramsRef)[0]->GetStorageFormat());
67 op::FVector<const aclTensor*> tensorListB;
68 tensorListB.emplace_back(tmpTensor);
69 momentumBufferListOptionalRefInput = executor->AllocTensorList(tensorListB.data(), tensorListB.size());
70 if(momentumBufferListOptionalRefInput == nullptr) {
71 return std::tuple<const aclTensorList*, const aclTensorList*, const aclTensorList*>(
72 nullptr, nullptr, nullptr);
73 }
74 }
75 
76 auto retAicore = ADD_TO_LAUNCHER_LIST_AICORE(FusedSgd,
77 OP_INPUT(paramsRef, gradsRef, momentumBufferListOptionalRefInput, gradScaleOptional),
78 OP_OUTPUT(paramsRef, gradsRef, momentumBufferListOptionalRefOut),
79 OP_ATTR(weightDecay, momentum, lr, dampening, nesterov, maximize, isFirstStep));
80 if (retAicore != ACLNN_SUCCESS) {
81 OP_LOGE(ACLNN_ERR_INNER_NULLPTR, "FusedSgd ADD_TO_LAUNCHER_LIST_AICORE failed.");
82 return std::tuple<const aclTensorList*, const aclTensorList*, const aclTensorList*>(nullptr, nullptr, nullptr);
83 }
84 if (momentumBufferListOptionalRef == nullptr) {
85 return std::tuple<const aclTensorList*, const aclTensorList*, const aclTensorList*>(
86 paramsRef, gradsRef, nullptr);
87 } else {
88 return std::tuple<const aclTensorList*, const aclTensorList*, const aclTensorList*>(
89 paramsRef, gradsRef, momentumBufferListOptionalRefOut);
90 }
91}
92 
93} // namespace l0op
Aoptim/fused_sgd/op_api/fused_sgd.h+36-0
@@ -0,0 +1,36 @@
1/**
2 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 * CANN Open Software License Agreement Version 2.0 (the "License").
5 * Please refer to the License for details. You may not use this file except in compliance with the License.
6 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 * See LICENSE in the root of the software repository for the full text of the License.
9 */
10 
11/*!
12 * \file fused_sgd.h
13 * \brief
14 */
15#ifndef OP_API_INC_LEVEL0_OP_FUSED_SGD_H_
16#define OP_API_INC_LEVEL0_OP_FUSED_SGD_H_
17 
18#include "opdev/op_executor.h"
19 
20namespace l0op {
21std::tuple<const aclTensorList*, const aclTensorList*, const aclTensorList*> FusedSgd(
22 const aclTensorList* paramsRef,
23 const aclTensorList* gradsRef,
24 const aclTensorList* momentumBufferListOptionalRef,
25 const aclTensor* gradScaleOptional,
26 float weightDecay,
27 float momentum,
28 float lr,
29 float dampening,
30 bool nesterov,
31 bool maximize,
32 bool isFirstStep,
33 aclOpExecutor *executor);
34}
35 
36#endif
Aoptim/fused_sgd/op_graph/CMakeLists.txt+12-0
@@ -0,0 +1,12 @@
1# ----------------------------------------------------------------------------
2# Copyright (c) 2026 Huawei Technologies Co., Ltd.
3# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4# CANN Open Software License Agreement Version 2.0 (the "License").
5# Please refer to the License for details. You may not use this file except in compliance with the License.
6# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8# See LICENSE in the root of the software repository for the full text of the License.
9# ----------------------------------------------------------------------------
10 
11message(STATUS "=== Debug: start ops.optim.fused_sgd.graph_plugin.CMakeLists.txt ")
12add_graph_plugin_sources()
Aoptim/fused_sgd/op_graph/fusion_pass/.gitkeep+0-0
The file is empty
Aoptim/fused_sgd/op_host/config/ascend910_93/fused_sgd_binary.json+341-0
@@ -0,0 +1,341 @@
1{
2 "op_type": "FusedSgd",
3 "op_list": [
4 {
5 "bin_filename": "FusedSgd_0",
6 "inputs": [
7 [
8 {
9 "name": "params",
10 "index": 0,
11 "dtype": "bfloat16",
12 "format": "ND",
13 "paramType": "dynamic",
14 "shape": [-2]
15 }
16 ],
17 [
18 {
19 "name": "grads",
20 "index": 1,
21 "dtype": "bfloat16",
22 "format": "ND",
23 "paramType": "dynamic",
24 "shape": [-2]
25 }
26 ],
27 [
28 {
29 "name": "momentum_buffer_list",
30 "index": 2,
31 "dtype": "bfloat16",
32 "format": "ND",
33 "paramType": "dynamic",
34 "shape": [-2]
35 }
36 ],
37 {
38 "name": "grad_scale",
39 "index": 3,
40 "dtype": "float32",
41 "format": "ND",
42 "paramType": "optional",
43 "shape": [-2]
44 }
45 ],
46 "outputs": [
47 [
48 {
49 "name": "params",
50 "index": 0,
51 "dtype": "bfloat16",
52 "format": "ND",
53 "paramType": "dynamic",
54 "shape": [-2]
55 }
56 ],
57 [
58 {
59 "name": "grads",
60 "index": 1,
61 "dtype": "bfloat16",
62 "format": "ND",
63 "paramType": "dynamic",
64 "shape": [-2]
65 }
66 ],
67 [
68 {
69 "name": "momentum_buffer_list_out",
70 "index": 2,
71 "dtype": "bfloat16",
72 "format": "ND",
73 "paramType": "dynamic",
74 "shape": [-2]
75 }
76 ]
77 ],
78 "attrs": [
79 {
80 "name": "weight_decay",
81 "dtype": "float",
82 "value": 0.0
83 },
84 {
85 "name": "momentum",
86 "dtype": "float",
87 "value": 0.0
88 },
89 {
90 "name": "lr",
91 "dtype": "float",
92 "value": 0.01
93 },
94 {
95 "name": "dampening",
96 "dtype": "float",
97 "value": 0.0
98 },
99 {
100 "name": "nesterov",
101 "dtype": "bool",
102 "value": false
103 },
104 {
105 "name": "maximize",
106 "dtype": "bool",
107 "value": false
108 },
109 {
110 "name": "is_first_step",
111 "dtype": "bool",
112 "value": true
113 }
114 ]
115 },
116 {
117 "bin_filename": "FusedSgd_1",
118 "inputs": [
119 [
120 {
121 "name": "params",
122 "index": 0,
123 "dtype": "float16",
124 "format": "ND",
125 "paramType": "dynamic",
126 "shape": [-2]
127 }
128 ],
129 [
130 {
131 "name": "grads",
132 "index": 1,
133 "dtype": "float16",
134 "format": "ND",
135 "paramType": "dynamic",
136 "shape": [-2]
137 }
138 ],
139 [
140 {
141 "name": "momentum_buffer_list",
142 "index": 2,
143 "dtype": "float16",
144 "format": "ND",
145 "paramType": "dynamic",
146 "shape": [-2]
147 }
148 ],
149 {
150 "name": "grad_scale",
151 "index": 3,
152 "dtype": "float32",
153 "format": "ND",
154 "paramType": "optional",
155 "shape": [-2]
156 }
157 ],
158 "outputs": [
159 [
160 {
161 "name": "params",
162 "index": 0,
163 "dtype": "float16",
164 "format": "ND",
165 "paramType": "dynamic",
166 "shape": [-2]
167 }
168 ],
169 [
170 {
171 "name": "grads",
172 "index": 1,
173 "dtype": "float16",
174 "format": "ND",
175 "paramType": "dynamic",
176 "shape": [-2]
177 }
178 ],
179 [
180 {
181 "name": "momentum_buffer_list_out",
182 "index": 2,
183 "dtype": "float16",
184 "format": "ND",
185 "paramType": "dynamic",
186 "shape": [-2]
187 }
188 ]
189 ],
190 "attrs": [
191 {
192 "name": "weight_decay",
193 "dtype": "float",
194 "value": 0.0
195 },
196 {
197 "name": "momentum",
198 "dtype": "float",
199 "value": 0.0
200 },
201 {
202 "name": "lr",
203 "dtype": "float",
204 "value": 0.01
205 },
206 {
207 "name": "dampening",
208 "dtype": "float",
209 "value": 0.0
210 },
211 {
212 "name": "nesterov",
213 "dtype": "bool",
214 "value": false
215 },
216 {
217 "name": "maximize",
218 "dtype": "bool",
219 "value": false
220 },
221 {
222 "name": "is_first_step",
223 "dtype": "bool",
224 "value": true
225 }
226 ]
227 },
228 {
229 "bin_filename": "FusedSgd_2",
230 "inputs": [
231 [
232 {
233 "name": "params",
234 "index": 0,
235 "dtype": "float32",
236 "format": "ND",
237 "paramType": "dynamic",
238 "shape": [-2]
239 }
240 ],
241 [
242 {
243 "name": "grads",
244 "index": 1,
245 "dtype": "float32",
246 "format": "ND",
247 "paramType": "dynamic",
248 "shape": [-2]
249 }
250 ],
251 [
252 {
253 "name": "momentum_buffer_list",
254 "index": 2,
255 "dtype": "float32",
256 "format": "ND",
257 "paramType": "dynamic",
258 "shape": [-2]
259 }
260 ],
261 {
262 "name": "grad_scale",
263 "index": 3,
264 "dtype": "float32",
265 "format": "ND",
266 "paramType": "optional",
267 "shape": [-2]
268 }
269 ],
270 "outputs": [
271 [
272 {
273 "name": "params",
274 "index": 0,
275 "dtype": "float32",
276 "format": "ND",
277 "paramType": "dynamic",
278 "shape": [-2]
279 }
280 ],
281 [
282 {
283 "name": "grads",
284 "index": 1,
285 "dtype": "float32",
286 "format": "ND",
287 "paramType": "dynamic",
288 "shape": [-2]
289 }
290 ],
291 [
292 {
293 "name": "momentum_buffer_list_out",
294 "index": 2,
295 "dtype": "float32",
296 "format": "ND",
297 "paramType": "dynamic",
298 "shape": [-2]
299 }
300 ]
301 ],
302 "attrs": [
303 {
304 "name": "weight_decay",
305 "dtype": "float",
306 "value": 0.0
307 },
308 {
309 "name": "momentum",
310 "dtype": "float",
311 "value": 0.0
312 },
313 {
314 "name": "lr",
315 "dtype": "float",
316 "value": 0.01
317 },
318 {
319 "name": "dampening",
320 "dtype": "float",
321 "value": 0.0
322 },
323 {
324 "name": "nesterov",
325 "dtype": "bool",
326 "value": false
327 },
328 {
329 "name": "maximize",
330 "dtype": "bool",
331 "value": false
332 },
333 {
334 "name": "is_first_step",
335 "dtype": "bool",
336 "value": true
337 }
338 ]
339 }
340 ]
341}
Aoptim/fused_sgd/op_host/config/ascend910b/fused_sgd_binary.json+341-0
@@ -0,0 +1,341 @@
1{
2 "op_type": "FusedSgd",
3 "op_list": [
4 {
5 "bin_filename": "FusedSgd_0",
6 "inputs": [
7 [
8 {
9 "name": "params",
10 "index": 0,
11 "dtype": "bfloat16",
12 "format": "ND",
13 "paramType": "dynamic",
14 "shape": [-2]
15 }
16 ],
17 [
18 {
19 "name": "grads",
20 "index": 1,
21 "dtype": "bfloat16",
22 "format": "ND",
23 "paramType": "dynamic",
24 "shape": [-2]
25 }
26 ],
27 [
28 {
29 "name": "momentum_buffer_list",
30 "index": 2,
31 "dtype": "bfloat16",
32 "format": "ND",
33 "paramType": "dynamic",
34 "shape": [-2]
35 }
36 ],
37 {
38 "name": "grad_scale",
39 "index": 3,
40 "dtype": "float32",
41 "format": "ND",
42 "paramType": "optional",
43 "shape": [-2]
44 }
45 ],
46 "outputs": [
47 [
48 {
49 "name": "params",
50 "index": 0,
51 "dtype": "bfloat16",
52 "format": "ND",
53 "paramType": "dynamic",
54 "shape": [-2]
55 }
56 ],
57 [
58 {
59 "name": "grads",
60 "index": 1,
61 "dtype": "bfloat16",
62 "format": "ND",
63 "paramType": "dynamic",
64 "shape": [-2]
65 }
66 ],
67 [
68 {
69 "name": "momentum_buffer_list_out",
70 "index": 2,
71 "dtype": "bfloat16",
72 "format": "ND",
73 "paramType": "dynamic",
74 "shape": [-2]
75 }
76 ]
77 ],
78 "attrs": [
79 {
80 "name": "weight_decay",
81 "dtype": "float",
82 "value": 0.0
83 },
84 {
85 "name": "momentum",
86 "dtype": "float",
87 "value": 0.0
88 },
89 {
90 "name": "lr",
91 "dtype": "float",
92 "value": 0.01
93 },
94 {
95 "name": "dampening",
96 "dtype": "float",
97 "value": 0.0
98 },
99 {
100 "name": "nesterov",
101 "dtype": "bool",
102 "value": false
103 },
104 {
105 "name": "maximize",
106 "dtype": "bool",
107 "value": false
108 },
109 {
110 "name": "is_first_step",
111 "dtype": "bool",
112 "value": true
113 }
114 ]
115 },
116 {
117 "bin_filename": "FusedSgd_1",
118 "inputs": [
119 [
120 {
121 "name": "params",
122 "index": 0,
123 "dtype": "float16",
124 "format": "ND",
125 "paramType": "dynamic",
126 "shape": [-2]
127 }
128 ],
129 [
130 {
131 "name": "grads",
132 "index": 1,
133 "dtype": "float16",
134 "format": "ND",
135 "paramType": "dynamic",
136 "shape": [-2]
137 }
138 ],
139 [
140 {
141 "name": "momentum_buffer_list",
142 "index": 2,
143 "dtype": "float16",
144 "format": "ND",
145 "paramType": "dynamic",
146 "shape": [-2]
147 }
148 ],
149 {
150 "name": "grad_scale",
151 "index": 3,
152 "dtype": "float32",
153 "format": "ND",
154 "paramType": "optional",
155 "shape": [-2]
156 }
157 ],
158 "outputs": [
159 [
160 {
161 "name": "params",
162 "index": 0,
163 "dtype": "float16",
164 "format": "ND",
165 "paramType": "dynamic",
166 "shape": [-2]
167 }
168 ],
169 [
170 {
171 "name": "grads",
172 "index": 1,
173 "dtype": "float16",
174 "format": "ND",
175 "paramType": "dynamic",
176 "shape": [-2]
177 }
178 ],
179 [
180 {
181 "name": "momentum_buffer_list_out",
182 "index": 2,
183 "dtype": "float16",
184 "format": "ND",
185 "paramType": "dynamic",
186 "shape": [-2]
187 }
188 ]
189 ],
190 "attrs": [
191 {
192 "name": "weight_decay",
193 "dtype": "float",
194 "value": 0.0
195 },
196 {
197 "name": "momentum",
198 "dtype": "float",
199 "value": 0.0
200 },
201 {
202 "name": "lr",
203 "dtype": "float",
204 "value": 0.01
205 },
206 {
207 "name": "dampening",
208 "dtype": "float",
209 "value": 0.0
210 },
211 {
212 "name": "nesterov",
213 "dtype": "bool",
214 "value": false
215 },
216 {
217 "name": "maximize",
218 "dtype": "bool",
219 "value": false
220 },
221 {
222 "name": "is_first_step",
223 "dtype": "bool",
224 "value": true
225 }
226 ]
227 },
228 {
229 "bin_filename": "FusedSgd_2",
230 "inputs": [
231 [
232 {
233 "name": "params",
234 "index": 0,
235 "dtype": "float32",
236 "format": "ND",
237 "paramType": "dynamic",
238 "shape": [-2]
239 }
240 ],
241 [
242 {
243 "name": "grads",
244 "index": 1,
245 "dtype": "float32",
246 "format": "ND",
247 "paramType": "dynamic",
248 "shape": [-2]
249 }
250 ],
251 [
252 {
253 "name": "momentum_buffer_list",
254 "index": 2,
255 "dtype": "float32",
256 "format": "ND",
257 "paramType": "dynamic",
258 "shape": [-2]
259 }
260 ],
261 {
262 "name": "grad_scale",
263 "index": 3,
264 "dtype": "float32",
265 "format": "ND",
266 "paramType": "optional",
267 "shape": [-2]
268 }
269 ],
270 "outputs": [
271 [
272 {
273 "name": "params",
274 "index": 0,
275 "dtype": "float32",
276 "format": "ND",
277 "paramType": "dynamic",
278 "shape": [-2]
279 }
280 ],
281 [
282 {
283 "name": "grads",
284 "index": 1,
285 "dtype": "float32",
286 "format": "ND",
287 "paramType": "dynamic",
288 "shape": [-2]
289 }
290 ],
291 [
292 {
293 "name": "momentum_buffer_list_out",
294 "index": 2,
295 "dtype": "float32",
296 "format": "ND",
297 "paramType": "dynamic",
298 "shape": [-2]
299 }
300 ]
301 ],
302 "attrs": [
303 {
304 "name": "weight_decay",
305 "dtype": "float",
306 "value": 0.0
307 },
308 {
309 "name": "momentum",
310 "dtype": "float",
311 "value": 0.0
312 },
313 {
314 "name": "lr",
315 "dtype": "float",
316 "value": 0.01
317 },
318 {
319 "name": "dampening",
320 "dtype": "float",
321 "value": 0.0
322 },
323 {
324 "name": "nesterov",
325 "dtype": "bool",
326 "value": false
327 },
328 {
329 "name": "maximize",
330 "dtype": "bool",
331 "value": false
332 },
333 {
334 "name": "is_first_step",
335 "dtype": "bool",
336 "value": true
337 }
338 ]
339 }
340 ]
341}
Aoptim/fused_sgd/op_host/fused_sgd_def.cpp+80-0
@@ -0,0 +1,80 @@
1/**
2 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 * CANN Open Software License Agreement Version 2.0 (the "License").
5 * Please refer to the License for details. You may not use this file except in compliance with the License.
6 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 * See LICENSE in the root of the software repository for the full text of the License.
9 */
10 
11/*!
12 * \file fused_sgd_def.cpp
13 * \brief
14 */
15#include "register/op_def_registry.h"
16 
17namespace ops {
18static constexpr float LR_DEFAULT = 0.001;
19static constexpr float WEIGHT_DECAY_DEFAULT = 0.0;
20static constexpr float MOMENTUM_DEFAULT = 0.0;
21static constexpr float DAMPENING_DEFAULT = 0.0;
22 
23class FusedSgd : public OpDef
24{
25public:
26 explicit FusedSgd(const char* name) : OpDef(name)
27 {
28 this->Input("params")
29 .ParamType(DYNAMIC)
30 .DataType({ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16})
31 .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
32 .UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
33 .AutoContiguous();
34 this->Input("grads")
35 .ParamType(DYNAMIC)
36 .DataType({ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16})
37 .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
38 .UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
39 .AutoContiguous();
40 this->Input("x")
41 .ParamType(DYNAMIC)
42 .DataType({ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16})
43 .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
44 .UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
45 .AutoContiguous();
46 this->Input("grad_scale")
47 .ParamType(OPTIONAL)
48 .DataType({ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_FLOAT})
49 .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
50 .UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
51 .AutoContiguous();
52 this->Output("params")
53 .ParamType(DYNAMIC)
54 .DataType({ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16})
55 .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
56 .UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND});
57 this->Output("grads")
58 .ParamType(DYNAMIC)
59 .DataType({ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16})
60 .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
61 .UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND});
62 this->Output("y")
63 .ParamType(DYNAMIC)
64 .DataType({ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16})
65 .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
66 .UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND});
67 this->Attr("weight_decay").AttrType(OPTIONAL).Float(WEIGHT_DECAY_DEFAULT);
68 this->Attr("momentum").AttrType(OPTIONAL).Float(MOMENTUM_DEFAULT);
69 this->Attr("lr").AttrType(OPTIONAL).Float(LR_DEFAULT);
70 this->Attr("dampening").AttrType(OPTIONAL).Float(DAMPENING_DEFAULT);
71 this->Attr("nesterov").AttrType(OPTIONAL).Bool(false);
72 this->Attr("maximize").AttrType(OPTIONAL).Bool(false);
73 this->Attr("is_first_step").AttrType(OPTIONAL).Bool(true);
74 this->AICore().AddConfig("ascend910b");
75 this->AICore().AddConfig("ascend910_93");
76 }
77};
78 
79OP_ADD(FusedSgd);
80} // namespace ops
Aoptim/fused_sgd/op_host/fused_sgd_infershape.cpp+132-0
@@ -0,0 +1,132 @@
1/**
2 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 * CANN Open Software License Agreement Version 2.0 (the "License").
5 * Please refer to the License for details. You may not use this file except in compliance with the License.
6 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 * See LICENSE in the root of the software repository for the full text of the License.
9 */
10 
11/*!
12 * \file fused_sgd_infershape.cpp
13 * \brief
14 */
15#include "log/log.h"
16#include "register/op_impl_registry.h"
17 
18using namespace ge;
19static constexpr size_t INPUT_PARAMS_INDEX = 0;
20static constexpr size_t INPUT_GRADS_INDEX = 1;
21static constexpr size_t INPUT_MOMENTUM_BUFFER_INDEX = 2;
22static constexpr size_t OUTPUT_PARAMS_INDEX = 0;
23static constexpr size_t OUTPUT_GRADS_INDEX = 1;
24static constexpr size_t OUTPUT_MOMENTUM_BUFFER_INDEX = 2;
25 
26namespace ops {
27static ge::graphStatus InferShapeForFusedSgd(gert::InferShapeContext* context)
28{
29 OP_LOGD(context, "Begin to do InferShapeForFusedSgd.");
30 
31 auto computeNodeInfo = context->GetComputeNodeInfo();
32 OP_CHECK_NULL_WITH_CONTEXT(context, computeNodeInfo);
33 
34 auto paramsInstanceInfo = computeNodeInfo->GetInputInstanceInfo(INPUT_PARAMS_INDEX);
35 OP_CHECK_NULL_WITH_CONTEXT(context, paramsInstanceInfo);
36 auto inputNum = paramsInstanceInfo->GetInstanceNum();
37 if (inputNum == 0) {
38 OP_LOGE(context, "input num must be greater than 0");
39 return ge::GRAPH_FAILED;
40 }
41 
42 // 判断tensorlist是否为空
43 auto momentumBufferListInput = context->GetDynamicInputShape(INPUT_MOMENTUM_BUFFER_INDEX, 0);
44 if (momentumBufferListInput != nullptr) {
45 uint32_t momentumBufferListDims = momentumBufferListInput->GetDimNum();
46 bool flag = true;
47 if (momentumBufferListDims < 1) {
48 flag = false;
49 }
50 if (flag) {
51 for(uint32_t i = 0; i < momentumBufferListDims; i++) {
52 int64_t dimValue = momentumBufferListInput->GetDim(i);
53 if (dimValue == 0) {
54 flag = false;
55 break;
56 }
57 }
58 }
59 if (!flag) {
60 momentumBufferListInput = nullptr;
61 }
62 }
63 
64 auto paramsOutInstanceInfo = context->GetIrOutputInstanceInfo(OUTPUT_PARAMS_INDEX);
65 auto gradsOutInstanceInfo = context->GetIrOutputInstanceInfo(OUTPUT_GRADS_INDEX);
66 auto momentumOutInstanceInfo = context->GetIrOutputInstanceInfo(OUTPUT_MOMENTUM_BUFFER_INDEX);
67 
68 for (uint32_t i = 0; i < inputNum; i++) {
69 const gert::Shape* paramsShape = context->GetDynamicInputShape(INPUT_PARAMS_INDEX, i);
70 OP_CHECK_NULL_WITH_CONTEXT(context, paramsShape);
71 const gert::Shape* gradsShape = context->GetDynamicInputShape(INPUT_GRADS_INDEX, i);
72 OP_CHECK_NULL_WITH_CONTEXT(context, gradsShape);
73 const gert::Shape* momentumShape = nullptr;
74 if (momentumBufferListInput != nullptr) {
75 momentumShape = context->GetDynamicInputShape(INPUT_MOMENTUM_BUFFER_INDEX, i);
76 OP_CHECK_NULL_WITH_CONTEXT(context, momentumShape);
77 }
78 
79 if (*paramsShape != *gradsShape || (momentumShape != nullptr && *paramsShape != *momentumShape)) {
80 OP_LOGE(context, "params, grads and momentum_buffer_list should have the same shape");
81 return ge::GRAPH_FAILED;
82 }
83 
84 gert::Shape* paramsRefShape = context->GetOutputShape(paramsOutInstanceInfo->GetInstanceStart() + i);
85 OP_CHECK_NULL_WITH_CONTEXT(context, paramsRefShape);
86 gert::Shape* gradsRefShape = context->GetOutputShape(gradsOutInstanceInfo->GetInstanceStart() + i);
87 OP_CHECK_NULL_WITH_CONTEXT(context, gradsRefShape);
88 gert::Shape* momentumRefShape = nullptr;
89 if (momentumBufferListInput != nullptr) {
90 momentumRefShape = context->GetOutputShape(momentumOutInstanceInfo->GetInstanceStart() + i);
91 OP_CHECK_NULL_WITH_CONTEXT(context, momentumRefShape);
92 }
93 
94 *paramsRefShape = *paramsShape;
95 *gradsRefShape = *gradsShape;
96 if (momentumBufferListInput != nullptr) {
97 *momentumRefShape = *momentumShape;
98 }
99 }
100 
101 OP_LOGD(context, "End to do InferShapeForFusedSgd.");
102 return ge::GRAPH_SUCCESS;
103}
104 
105static graphStatus InferDataTypeForFusedSgd(gert::InferDataTypeContext* context)
106{
107 auto computeNodeInfo = context->GetComputeNodeInfo();
108 OP_CHECK_NULL_WITH_CONTEXT(context, computeNodeInfo);
109 
110 auto paramsInstanceInfo = computeNodeInfo->GetInputInstanceInfo(INPUT_PARAMS_INDEX);
111 OP_CHECK_NULL_WITH_CONTEXT(context, paramsInstanceInfo);
112 auto inputNum = paramsInstanceInfo->GetInstanceNum();
113 
114 auto paramsOutInstanceInfo = context->GetIrOutputInstanceInfo(OUTPUT_PARAMS_INDEX);
115 auto gradsOutInstanceInfo = context->GetIrOutputInstanceInfo(OUTPUT_GRADS_INDEX);
116 auto momentumOutInstanceInfo = context->GetIrOutputInstanceInfo(OUTPUT_MOMENTUM_BUFFER_INDEX);
117 
118 for (uint32_t i = 0; i < inputNum; i++) {
119 context->SetOutputDataType(paramsOutInstanceInfo->GetInstanceStart() + i,
120 context->GetDynamicInputDataType(INPUT_PARAMS_INDEX, i));
121 context->SetOutputDataType(gradsOutInstanceInfo->GetInstanceStart() + i,
122 context->GetDynamicInputDataType(INPUT_GRADS_INDEX, i));
123 context->SetOutputDataType(momentumOutInstanceInfo->GetInstanceStart() + i,
124 context->GetDynamicInputDataType(INPUT_MOMENTUM_BUFFER_INDEX, i));
atomgit-bot
atomgit-botatomgit-bot6月18日

🟡 Medium Priority

fused_sgd_infershape.cpp 第123-124行,InferDataTypeForFusedSgd 无条件地对每个 i 调用 context->GetDynamicInputDataType(INPUT_MOMENTUM_BUFFER_INDEX, i),而未像 InferShapeForFusedSgd(第43-62行)那样先判断 momentum buffer 是否为空(dimension 全零/null)。

当 momentum buffer 为空时(tensor 全部无元素),GetDynamicInputDataType(2, i) 可能返回无效值或触发断言,导致数据类型推导阶段报错或产生不一致的推导结果。

同一文件第27行的 InferShapeForFusedSgd 已通过 momentumBufferListInput 的 nullptr 检查正确处理了该情况,但 InferDataTypeForFusedSgd 缺少相同的防护。

变更:第123-124行新增,对 momentum buffer 的无条件访问。

建议:在 InferDataTypeForFusedSgd 中添加与 InferShapeForFusedSgd 相同的 momentum buffer 空检测逻辑,确定 momentum 是否有效后再决定是否为 momentum output 设置数据类型。

likedislike
majiajian_hw
6月22日 评论:
likedislike
125 }
126 return GRAPH_SUCCESS;
127}
128 
129IMPL_OP_INFERSHAPE(FusedSgd)
130 .InferShape(InferShapeForFusedSgd)
131 .InferDataType(InferDataTypeForFusedSgd);
132} // namespace ops
Aoptim/fused_sgd/op_host/fused_sgd_tiling.cpp+342-0
@@ -0,0 +1,342 @@
1/**
2 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 * CANN Open Software License Agreement Version 2.0 (the "License").
5 * Please refer to the License for details. You may not use this file except in compliance with the License.
6 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 * See LICENSE in the root of the software repository for the full text of the License.
9 */
10 
11/*!
12 * \file fused_sgd_tiling.cpp
13 * \brief
14 */
15#include "fused_sgd_tiling.h"
16#include "register/op_impl_registry.h"
17#include "util/math_util.h"
18#include "log/log.h"
19#include "tiling/platform/platform_ascendc.h"
20#include "platform/platform_infos_def.h"
21 
22using namespace std;
23namespace optiling {
24constexpr uint32_t INPUT_PARAMS_IDX = 0;
25constexpr uint32_t INPUT_GRADS_IDX = 1;
26constexpr uint32_t INPUT_MOMENTUM_BUFFER_IDX = 2;
27constexpr uint32_t INPUT_GRAD_SCALE_IDX = 3;
28constexpr uint32_t ATTR_WEIGHT_DECAY_IDX = 0;
29constexpr uint32_t ATTR_MOMENTUM_IDX = 1;
30constexpr uint32_t ATTR_LR_IDX = 2;
31constexpr uint32_t ATTR_DAMPENING_IDX = 3;
32constexpr uint32_t ATTR_NESTEROV_IDX = 4;
33constexpr uint32_t ATTR_MAXIMIZE_IDX = 5;
34constexpr uint32_t ATTR_IS_FIRST_STEP_IDX = 6;
35constexpr uint32_t ONE_BLK_NUM = 16;
36constexpr uint32_t ONE_BLK_NUM_FP32 = 8;
37constexpr uint32_t BYTE_ONE_BLK = 32;
38constexpr uint32_t TBUFFER_NUM = 3;
39constexpr uint32_t BUFFER_NUM = 2;
40constexpr uint32_t FP16_BF16_DTYPE_SIZE = 2;
41constexpr uint32_t FP32_DTYPE_SIZE = 4;
42 
43std::string FusedSgdTiling::TilingDataToString() const
44{
45 return "weightDecay = " + std::to_string(weightDecay_) + \
46 ", momentum = " + std::to_string(momentum_) + \
47 ", lr = " + std::to_string(lr_) + \
48 ", dampening = " + std::to_string(dampening_) + \
49 ", nesterov = " + std::to_string(nesterov_) + \
50 ", maximize = " + std::to_string(maximize_) + \
51 ", isFirstStep = " + std::to_string(isFirstStep_) + \
52 ", useGradScale = " + std::to_string(useGradScale_) + \
53 ", useMomentum = " + std::to_string(useMomentum_) + \
54 ", tensorNum = " + std::to_string(tensorNum_) + \
55 ", tensorsPerCore = " + std::to_string(tensorsPerCore_) + \
56 ", usedCoreNum = " + std::to_string(usedCoreNum_) + \
57 ", coreCalcMax = " + std::to_string(coreCalcMax_);
58}
59 
60// 获取硬件信息
61ge::graphStatus FusedSgdTiling::GetPlatformInfo()
62{
63 auto platformInfo = context_->GetPlatformInfo();
64 OP_CHECK_NULL_WITH_CONTEXT(context_, platformInfo);
65 auto ascendcPlatform = platform_ascendc::PlatformAscendC(platformInfo);
66 coreNum_ = static_cast<uint32_t>(ascendcPlatform.GetCoreNumAiv());
67 OP_CHECK_IF(coreNum_ == 0, OP_LOGE(context_, "coreNum is 0"), return ge::GRAPH_FAILED);
68 ascendcPlatform.GetCoreMemSize(platform_ascendc::CoreMemType::UB, ubSize_);
69 OP_CHECK_IF(ubSize_ == 0, OP_LOGE(context_, "ubSize is 0"), return ge::GRAPH_FAILED);
70 sysWorkspaceSize_ = ascendcPlatform.GetLibApiWorkSpaceSize();
71 return ge::GRAPH_SUCCESS;
72}
73 
74ge::graphStatus FusedSgdTiling::GetAttrInfo()
75{
76 // 获取属性
77 auto* attrs = context_->GetAttrs();
78 OP_CHECK_NULL_WITH_CONTEXT(context_, attrs);
79 const float* attrWeightDecay = attrs->GetAttrPointer<float>(ATTR_WEIGHT_DECAY_IDX);
80 OP_CHECK_NULL_WITH_CONTEXT(context_, attrWeightDecay);
81 weightDecay_ = static_cast<float>(*attrWeightDecay);
82 const float* attrMomentum = attrs->GetAttrPointer<float>(ATTR_MOMENTUM_IDX);
83 OP_CHECK_NULL_WITH_CONTEXT(context_, attrMomentum);
84 momentum_ = static_cast<float>(*attrMomentum);
85 const float* attrLr = attrs->GetAttrPointer<float>(ATTR_LR_IDX);
86 OP_CHECK_NULL_WITH_CONTEXT(context_, attrLr);
87 lr_ = static_cast<float>(*attrLr);
88 const float* attrDampening = attrs->GetAttrPointer<float>(ATTR_DAMPENING_IDX);
89 OP_CHECK_NULL_WITH_CONTEXT(context_, attrDampening);
90 dampening_ = static_cast<float>(*attrDampening);
91 const bool* attrNesterov = attrs->GetAttrPointer<bool>(ATTR_NESTEROV_IDX);
92 OP_CHECK_NULL_WITH_CONTEXT(context_, attrNesterov);
93 nesterov_ = static_cast<uint32_t>(*attrNesterov ? 1 : 0);
94 const bool* attrMaximize = attrs->GetAttrPointer<bool>(ATTR_MAXIMIZE_IDX);
95 OP_CHECK_NULL_WITH_CONTEXT(context_, attrMaximize);
96 maximize_ = static_cast<uint32_t>(*attrMaximize ? 1 : 0);
97 const bool* attrIsFirstStep = attrs->GetAttrPointer<bool>(ATTR_IS_FIRST_STEP_IDX);
98 OP_CHECK_NULL_WITH_CONTEXT(context_, attrIsFirstStep);
99 isFirstStep_ = static_cast<uint32_t>(*attrIsFirstStep ? 1 : 0);
100 
101 return ge::GRAPH_SUCCESS;
102}
103 
104void FusedSgdTiling::CheckOptionalInputs()
105{
106 // 判断gradScale为空
107 auto shapeInput = context_->GetOptionalInputTensor(INPUT_GRAD_SCALE_IDX);
108 if (shapeInput != nullptr) {
109 const gert::Shape& inputShapeGradScale = shapeInput->GetStorageShape();
110 uint32_t gradScaleDims = inputShapeGradScale.GetDimNum();
111 bool flag = true;
112 if (gradScaleDims < 1) {
113 flag = false;
114 }
115 if (flag) {
116 for(uint32_t i = 0; i < gradScaleDims; i++) {
117 int64_t dimValue = inputShapeGradScale.GetDim(i);
118 if (dimValue == 0) {
119 flag = false;
120 break;
121 }
122 }
123 }
124 if (flag) {
125 useGradScale_ = 1;
126 } else {
127 useGradScale_ = 0;
128 }
129 } else {
130 useGradScale_ = 0;
131 }
132
133 // 判断tensorlist为空
134 auto momentumBufferListInput = context_->GetDynamicInputShape(INPUT_MOMENTUM_BUFFER_IDX, 0);
135 if (momentumBufferListInput != nullptr) {
136 const gert::Shape& inputShapeMomentumBufferList = momentumBufferListInput->GetStorageShape();
137 uint32_t momentumBufferListDims = inputShapeMomentumBufferList.GetDimNum();
138 bool flag = true;
139 if (momentumBufferListDims < 1) {
140 flag = false;
141 }
142 if (flag) {
143 for(uint32_t i = 0; i < momentumBufferListDims; i++) {
144 int64_t dimValue = inputShapeMomentumBufferList.GetDim(i);
145 if (dimValue == 0) {
146 flag = false;
147 break;
148 }
149 }
150 }
151 if (flag) {
152 useMomentum_ = 1;
153 } else {
154 useMomentum_ = 0;
155 }
156 } else {
157 useMomentum_ = 0;
158 }
159}
160 
161static ge::graphStatus CheckInputDtype(gert::TilingContext* context, uint32_t useMomentum_)
162{
163 auto dtypeInput = context->GetDynamicInputDesc(INPUT_PARAMS_IDX, 0);
164 OP_CHECK_NULL_WITH_CONTEXT(context, dtypeInput);
165 auto paramsDtype = dtypeInput->GetDataType();
166 
167 dtypeInput = context->GetDynamicInputDesc(INPUT_GRADS_IDX, 0);
168 OP_CHECK_NULL_WITH_CONTEXT(context, dtypeInput);
169 auto gradsDtype = dtypeInput->GetDataType();
170 
171 bool isDiffDtype =
172 (paramsDtype != gradsDtype);
173 ge::DataType momentumDtype;
174 if(useMomentum_) {
175 dtypeInput = context->GetDynamicInputDesc(INPUT_MOMENTUM_BUFFER_IDX, 0);
176 OP_CHECK_NULL_WITH_CONTEXT(context, dtypeInput);
177 momentumDtype = dtypeInput->GetDataType();
178 isDiffDtype = isDiffDtype || (paramsDtype != momentumDtype);
179 }
180 
181 if (isDiffDtype) {
182 std::string dtypeMsg = Ops::Base::ToString(paramsDtype) + ", " +
183 Ops::Base::ToString(gradsDtype);
184 if(!useMomentum_) {
185 OP_LOGE_FOR_INVALID_DTYPES_WITH_REASON(
186 context->GetNodeName(), "params, grads", dtypeMsg.c_str(),
187 "params, grads should have the same dtype");
188 } else {
189 dtypeMsg = dtypeMsg + " and " + Ops::Base::ToString(momentumDtype);
190 OP_LOGE_FOR_INVALID_DTYPES_WITH_REASON(
191 context->GetNodeName(), "params, grads and momentum_buffer_list", dtypeMsg.c_str(),
192 "params, grads and momentum_buffer_list should have the same dtype");
193 }
194 return ge::GRAPH_FAILED;
195 }
196 
197 bool isInvalidType = (paramsDtype != ge::DT_FLOAT) && (paramsDtype != ge::DT_BF16) && (paramsDtype != ge::DT_FLOAT16);
198 if (isInvalidType) {
199 OP_LOGE_FOR_INVALID_DTYPE(context->GetNodeName(), "params/grads/momentum_buffer_list",
200 Ops::Base::ToString(paramsDtype).c_str(), "float16, bfloat16 and float");
201 return ge::GRAPH_FAILED;
202 }
203 
204 return ge::GRAPH_SUCCESS;
205}
206 
207ge::graphStatus FusedSgdTiling::GetInputTensorInfo()
208{
209 auto computeNodeInfo = context_->GetComputeNodeInfo();
210 OP_CHECK_NULL_WITH_CONTEXT(context_, computeNodeInfo);
211 
212 auto anchorInstanceInfo = computeNodeInfo->GetInputInstanceInfo(INPUT_PARAMS_IDX);
213 OP_CHECK_NULL_WITH_CONTEXT(context_, anchorInstanceInfo);
214 tensorNum_ = static_cast<uint64_t>(anchorInstanceInfo->GetInstanceNum());
215 if (tensorNum_ == 0) {
216 OP_LOGE(context_, "tensor num can not be 0");
217 return ge::GRAPH_FAILED;
218 }
219 
220 // 检查可选输入是否为空
221 CheckOptionalInputs();
222 
223 for (uint64_t i = 0; i < tensorNum_; i++) {
224 auto paramsShapePtr = context_->GetDynamicInputShape(INPUT_PARAMS_IDX, i);
225 OP_CHECK_NULL_WITH_CONTEXT(context_, paramsShapePtr);
226 auto gradsShapePtr = context_->GetDynamicInputShape(INPUT_GRADS_IDX, i);
227 OP_CHECK_NULL_WITH_CONTEXT(context_, gradsShapePtr);
228 
229 gert::Shape paramsShape = paramsShapePtr->GetStorageShape();
230 gert::Shape gradsShape = gradsShapePtr->GetStorageShape();
231 bool isDiffSize = paramsShape != gradsShape;
232
233 gert::Shape momentumShape;
234 if(useMomentum_) {
235 auto momentumShapePtr = context_->GetDynamicInputShape(INPUT_MOMENTUM_BUFFER_IDX, i);
236 OP_CHECK_NULL_WITH_CONTEXT(context_, momentumShapePtr);
237 momentumShape = momentumShapePtr->GetStorageShape();
238 isDiffSize = isDiffSize || paramsShape != momentumShape;
239 }
240 if (isDiffSize) {
241 std::string shapesMsg = Ops::Base::ToString(paramsShape) + ", " +
242 Ops::Base::ToString(gradsShape);
243 if(!useMomentum_) {
244 OP_LOGE_FOR_INVALID_SHAPES_WITH_REASON(
245 context_->GetNodeName(), "params, grads", shapesMsg.c_str(),
246 "params, grads should have the same shape");
247 } else {
248 shapesMsg = shapesMsg + " and " + Ops::Base::ToString(momentumShape);
249 OP_LOGE_FOR_INVALID_SHAPES_WITH_REASON(
250 context_->GetNodeName(), "params, grads and momentum_buffer_list", shapesMsg.c_str(),
251 "params, grads and momentum_buffer_list should have the same shape");
252 }
253 return ge::GRAPH_FAILED;
254 }
255 }
256 
257 return CheckInputDtype(context_, useMomentum_);
258}
259 
260ge::graphStatus FusedSgdTiling::CalculateOutputInfo()
261{
262 usedCoreNum_ = tensorNum_ < static_cast<uint64_t>(coreNum_) ? tensorNum_ : static_cast<uint64_t>(coreNum_);
263 tensorsPerCore_ = static_cast<uint32_t>((tensorNum_ + usedCoreNum_ - 1) / usedCoreNum_);
264 
265 dtypeSize_ = context_->GetDynamicInputDesc(INPUT_PARAMS_IDX, 0)->GetDataType() == ge::DT_FLOAT ? FP32_DTYPE_SIZE : FP16_BF16_DTYPE_SIZE;
266 uint64_t tBuffersize = BUFFER_NUM * BYTE_ONE_BLK;
267 uint64_t bufferSize = ubSize_ - tBuffersize;
268 // 计算处理一个元素所需的ub大小
269 uint64_t coreOnesize;
270 if (dtypeSize_ == FP32_DTYPE_SIZE) {
271 coreOnesize = FP32_DTYPE_SIZE * 3 * 2 * BUFFER_NUM;
272 } else {
273 coreOnesize = ((dtypeSize_ + FP32_DTYPE_SIZE) * 3 + FP32_DTYPE_SIZE * 3) * BUFFER_NUM;
274 }
275 uint64_t alignSize = dtypeSize_ == FP32_DTYPE_SIZE ? ONE_BLK_NUM_FP32 : ONE_BLK_NUM;
276 OP_LOGI(context_, "bufferSize = %lu", bufferSize);
277 OP_LOGI(context_, "coreOnesize = %lu", coreOnesize);
278 OP_LOGI(context_, "alignSize = %lu", alignSize);
279 // 计算ub一次最多能处理的数据量
280 coreCalcMax_ = bufferSize / coreOnesize / alignSize * alignSize;
281 OP_LOGI(context_, "coreCalcMax_ = %lu", coreCalcMax_);
282 
283 return ge::GRAPH_SUCCESS;
284}
285 
286void FusedSgdTiling::SetTilingData(FusedSgdTilingData* tilingData) {
287 tilingData->weightDecay = weightDecay_;
288 tilingData->momentum = momentum_;
289 tilingData->lr = lr_;
290 tilingData->dampening = dampening_;
291 tilingData->nesterov = nesterov_;
292 tilingData->maximize = maximize_;
293 tilingData->isFirstStep = isFirstStep_;
294 tilingData->useGradScale = useGradScale_;
295 tilingData->useMomentum = useMomentum_;
296 tilingData->tensorNum = tensorNum_;
297 tilingData->tensorsPerCore = tensorsPerCore_;
298 tilingData->usedCoreNum = usedCoreNum_;
299 tilingData->coreCalcMax = coreCalcMax_;
300 
301 size_t* workspaceSize = context_->GetWorkspaceSizes(1);
302 *workspaceSize = sysWorkspaceSize_;
303 context_->SetTilingKey(0);
304 context_->SetBlockDim(usedCoreNum_);
305}
306 
307ge::graphStatus Tiling4FusedSgd(gert::TilingContext* context)
308{
309 OP_LOGD(context, "Tiling4FusedSgd");
310 FusedSgdTiling tiling(context);
311 OP_CHECK_IF(
312 tiling.GetPlatformInfo() != ge::GRAPH_SUCCESS, OP_LOGE(context, "GetPlatformInfo error"),
313 return ge::GRAPH_FAILED);
314 OP_CHECK_IF(
315 tiling.GetAttrInfo() != ge::GRAPH_SUCCESS, OP_LOGE(context, "GetAttrInfo error"),
316 return ge::GRAPH_FAILED);
317 OP_CHECK_IF(
318 tiling.GetInputTensorInfo() != ge::GRAPH_SUCCESS, OP_LOGE(context, "GetInputTensorInfo error"),
319 return ge::GRAPH_FAILED);
320 OP_CHECK_IF(
321 tiling.CalculateOutputInfo() != ge::GRAPH_SUCCESS, OP_LOGE(context, "CalculateOutputInfo error"),
322 return ge::GRAPH_FAILED);
323
324 FusedSgdTilingData* tilingData = context->GetTilingData<FusedSgdTilingData>();
325 OP_CHECK_NULL_WITH_CONTEXT(context, tilingData);
326 OP_CHECK_IF(
327 memset_s(tilingData, sizeof(FusedSgdTilingData), 0, sizeof(FusedSgdTilingData)) != EOK,
328 OP_LOGE(context, "set tiling data error"), return ge::GRAPH_FAILED);
329 tiling.SetTilingData(tilingData);
330 OP_LOGD(context, "tiling data: %s", tiling.TilingDataToString().c_str());
331 return ge::GRAPH_SUCCESS;
332}
333 
334ge::graphStatus TilingPrepare4FusedSgd([[maybe_unused]] gert::TilingParseContext* context)
335{
336 return ge::GRAPH_SUCCESS;
337}
338 
339IMPL_OP_OPTILING(FusedSgd)
340 .Tiling(Tiling4FusedSgd)
341 .TilingParse<FusedSgdCompileInfo>(TilingPrepare4FusedSgd);
342} // namespace optiling
Aoptim/fused_sgd/op_host/fused_sgd_tiling.h+60-0
@@ -0,0 +1,60 @@
1/**
2 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 * CANN Open Software License Agreement Version 2.0 (the "License").
5 * Please refer to the License for details. You may not use this file except in compliance with the License.
6 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 * See LICENSE in the root of the software repository for the full text of the License.
9 */
10 
11/*!
12 * \file fused_sgd_tiling.h
13 * \brief
14 */
15#ifndef OPS_BUILD_IN_OP_TILING_RUNTIME_FUSED_SGD_TILING_H_
16#define OPS_BUILD_IN_OP_TILING_RUNTIME_FUSED_SGD_TILING_H_
17 
18 
19#include "register/tilingdata_base.h"
20#include "op_host/tiling_base_util.h"
21#include "../op_kernel/fused_sgd_tiling_data.h"
22 
23namespace optiling {
24 
25struct FusedSgdCompileInfo {
26};
27 
28class FusedSgdTiling {
29public:
30 explicit FusedSgdTiling(gert::TilingContext* context) : context_(context) {};
31 ge::graphStatus GetPlatformInfo();
32 ge::graphStatus GetAttrInfo();
33 ge::graphStatus GetInputTensorInfo();
34 ge::graphStatus CalculateOutputInfo();
35 void CheckOptionalInputs();
36 void SetTilingData(FusedSgdTilingData* tilingData);
37 std::string TilingDataToString() const;
38 
39private:
40 gert::TilingContext* context_;
41 uint32_t coreNum_{0};
42 uint64_t ubSize_{0};
43 uint64_t sysWorkspaceSize_{0};
44 uint32_t usedCoreNum_{0};
45 float weightDecay_{0.0f};
46 float momentum_{0.0f};
47 float lr_{0.0f};
48 float dampening_{0.0f};
49 uint32_t nesterov_{0};
50 uint32_t maximize_{0};
51 uint32_t isFirstStep_{0};
52 uint32_t useGradScale_{0};
53 uint32_t useMomentum_{0};
54 uint32_t tensorsPerCore_{0};
55 uint32_t dtypeSize_{0};
56 uint64_t tensorNum_{0};
57 uint64_t coreCalcMax_{0};
58};
59} // namespace optiling
60#endif // OPS_BUILD_IN_OP_TILING_RUNTIME_FUSED_SGD_TILING_H_
Aoptim/fused_sgd/op_kernel/fused_sgd.cpp+86-0
@@ -0,0 +1,86 @@
1/**
2 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 * CANN Open Software License Agreement Version 2.0 (the "License").
5 * Please refer to the License for details. You may not use this file except in compliance with the License.
6 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 * See LICENSE in the root of the software repository for the full text of the License.
9 */
10 
11/*!
12 * \file fused_sgd.cpp
13 * \brief
14 */
15#include "kernel_operator_list_tensor_intf.h"
16#include "fused_sgd_f32.h"
17#include "fused_sgd_f16_bf16.h"
18 
19using namespace AscendC;
20using namespace FusedSgd;
21 
22#ifdef __CCE_UT_TEST__
23extern "C" __global__ __aicore__ void fused_sgd(
24 GM_ADDR params, GM_ADDR grads, GM_ADDR momentum_buffer_list,
25 GM_ADDR grad_scale, GM_ADDR params_ref, GM_ADDR grads_ref,
26 GM_ADDR momentum_buffer_list_out, GM_ADDR workspace,
27 GM_ADDR tiling)
28{
29 KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIV_ONLY);
30 AscendC::TPipe pipe;
31 GET_TILING_DATA_WITH_STRUCT(FusedSgdTilingData, tilingData, tiling);
32 
33 uint32_t blockIdx = GetBlockIdx();
34 uint64_t tensorStart = static_cast<uint64_t>(blockIdx) * tilingData.tensorsPerCore;
35 uint64_t tensorEnd = tensorStart + tilingData.tensorsPerCore;
36 if (tensorEnd > tilingData.tensorNum) {
37 tensorEnd = tilingData.tensorNum;
38 }
39 
40 FusedSgdF32<DTYPE_X> op(&pipe);
41 op.Init(params, grads, momentum_buffer_list, grad_scale,
42 params_ref, grads_ref, momentum_buffer_list_out,
43 tilingData, tensorStart, tensorEnd);
44 op.Process();
45}
46#else
47extern "C" __global__ __aicore__ void fused_sgd(
48 GM_ADDR params, GM_ADDR grads, GM_ADDR momentum_buffer_list,
49 GM_ADDR grad_scale,
50 GM_ADDR params_ref, GM_ADDR grads_ref, GM_ADDR momentum_buffer_list_out,
51 GM_ADDR workspace, GM_ADDR tiling)
52{
53 KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIV_ONLY);
54 AscendC::TPipe pipe;
55 REGISTER_TILING_DEFAULT(FusedSgdTilingData);
56 GET_TILING_DATA_WITH_STRUCT(FusedSgdTilingData, tilingData, tiling);
57 
58 // 计算当前核处理的tensor范围 [tensorStart, tensorEnd)
59 uint32_t blockIdx = GetBlockIdx();
60 uint64_t tensorStart = static_cast<uint64_t>(blockIdx) * tilingData.tensorsPerCore;
61 uint64_t tensorEnd = tensorStart + tilingData.tensorsPerCore;
62 if (tensorEnd > tilingData.tensorNum) {
63 tensorEnd = tilingData.tensorNum;
64 }
65 
66 #if (ORIG_DTYPE_PARAMS == DT_BF16)
67 FusedSgdF16Bf16<bfloat16_t> op(&pipe);
68 op.Init(params, grads, momentum_buffer_list, grad_scale,
69 params_ref, grads_ref, momentum_buffer_list_out,
70 tilingData, tensorStart, tensorEnd);
71 op.Process();
72 #elif (ORIG_DTYPE_PARAMS == DT_FLOAT16)
73 FusedSgdF16Bf16<half> op(&pipe);
74 op.Init(params, grads, momentum_buffer_list, grad_scale,
75 params_ref, grads_ref, momentum_buffer_list_out,
76 tilingData, tensorStart, tensorEnd);
77 op.Process();
78 #elif (ORIG_DTYPE_PARAMS == DT_FLOAT32)
79 FusedSgdF32<float> op(&pipe);
80 op.Init(params, grads, momentum_buffer_list, grad_scale,
81 params_ref, grads_ref, momentum_buffer_list_out,
82 tilingData, tensorStart, tensorEnd);
83 op.Process();
84 #endif
85}
86#endif
Aoptim/fused_sgd/op_kernel/fused_sgd_base.h+82-0
@@ -0,0 +1,82 @@
1/**
2 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 * CANN Open Software License Agreement Version 2.0 (the "License").
5 * Please refer to the License for details. You may not use this file except in compliance with the License.
6 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 * See LICENSE in the root of the software repository for the full text of the License.
9 */
10 
11/*!
12 * \file fused_sgd_base.h
13 * \brief
14 */
15 
16#ifndef FUSED_SGD_BASE_H
17#define FUSED_SGD_BASE_H
18 
19#include "kernel_operator.h"
20#include "fused_sgd_tiling_data.h"
21 
22namespace FusedSgd {
23using namespace AscendC;
24constexpr int32_t BYTE_ONE_BLOCK = 32;
25constexpr int32_t BUFFER_NUM = 2;
26constexpr int32_t INDEX_PARAMS = 0;
27constexpr int32_t INDEX_GRADS = 1;
28constexpr int32_t INDEX_MOMENTUM_BUFFER = 2;
29 
30template <typename T>
31class FusedSgdBase
32{
33public:
34 __aicore__ inline FusedSgdBase(){};
35 __aicore__ inline void InitData(const FusedSgdTilingData& tiling);
36 __aicore__ inline void PipeSync();
37 
38protected:
39 float weightDecay;
40 float momentum;
41 float lr;
42 float dampening;
43 uint64_t nesterov;
44 uint64_t maximize;
45 uint64_t isFirstStep;
46 uint64_t useGradScale;
47 uint64_t useMomentum;
48 uint64_t tensorNum;
49 uint64_t tensorsPerCore;
50 uint64_t usedCoreNum;
51 uint64_t coreCalcMax;
52};
53 
54template <typename T>
55__aicore__ inline void FusedSgdBase<T>::InitData(const FusedSgdTilingData& tiling)
56{
57 weightDecay = tiling.weightDecay;
58 momentum = tiling.momentum;
59 lr = tiling.lr;
60 dampening = tiling.dampening;
61 nesterov = tiling.nesterov;
62 maximize = tiling.maximize;
63 isFirstStep = tiling.isFirstStep;
64 useGradScale = tiling.useGradScale;
65 useMomentum = tiling.useMomentum;
66 tensorNum = tiling.tensorNum;
67 tensorsPerCore = tiling.tensorsPerCore;
68 usedCoreNum = tiling.usedCoreNum;
69 coreCalcMax = tiling.coreCalcMax;
70}
71 
72template <AscendC::HardEvent hardEvent>
73__aicore__ inline void PipeSync()
74{
75 int32_t eventID = static_cast<int32_t>(GetTPipePtr()->FetchEventID(hardEvent));
76 AscendC::SetFlag<hardEvent>(eventID);
77 AscendC::WaitFlag<hardEvent>(eventID);
78}
79 
80} // namespace FusedSgd
81 
82#endif // FUSED_SGD_BASE_H
Aoptim/fused_sgd/op_kernel/fused_sgd_f16_bf16.h+248-0
@@ -0,0 +1,248 @@
1/**
2 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 * CANN Open Software License Agreement Version 2.0 (the "License").
5 * Please refer to the License for details. You may not use this file except in compliance with the License.
6 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 * See LICENSE in the root of the software repository for the full text of the License.
9 */
10 
11/*!
12 * \file fused_sgd_f16_bf16.h
13 * \brief
14 */
15 
16#ifndef FUSED_SGD_F16_BF16_H
17#define FUSED_SGD_F16_BF16_H
18 
19#include "fused_sgd_base.h"
20 
21namespace FusedSgd {
22using namespace AscendC;
23 
24template <typename T>
25class FusedSgdF16Bf16 : public FusedSgdBase<T>
26{
27public:
28 __aicore__ inline FusedSgdF16Bf16(TPipe *pipe) : pipe_(pipe) {};
29 __aicore__ inline void Init(
30 GM_ADDR params, GM_ADDR grads, GM_ADDR momentum_buffer_list,
31 GM_ADDR grad_scale, GM_ADDR params_ref, GM_ADDR grads_ref,
32 GM_ADDR momentum_buffer_list_out, const FusedSgdTilingData& tiling,
33 uint64_t tensorStart, uint64_t tensorEnd);
34 __aicore__ inline void Process();
35 
36protected:
37 __aicore__ inline void Compute(const uint64_t index, const uint64_t dataCount);
38 
39 TQue<QuePosition::VECIN, BUFFER_NUM> inQue;
40 TQue<QuePosition::VECOUT, BUFFER_NUM> outQue;
41 
42 GlobalTensor<T> gmParams;
43 GlobalTensor<T> gmGrads;
44 GlobalTensor<T> gmMomentumBuffer;
45 GlobalTensor<float> gmGradScale;
46 GlobalTensor<float> gmFoundInf;
47 GlobalTensor<T> gmParamsRef;
48 GlobalTensor<T> gmGradsRef;
49 GlobalTensor<T> gmMomentumBufferOut;
50 
51 ListTensorDesc paramsList_;
52 ListTensorDesc gradsList_;
53 ListTensorDesc momentumList_;
54 ListTensorDesc paramsRefList_;
55 ListTensorDesc gradsRefList_;
56 ListTensorDesc momentumOutList_;
57 TensorDesc<uint64_t> desc_;
58 
59 float gradScaleValue;
60 uint64_t hasGradScale;
61 uint64_t tensorStart_;
62 uint64_t tensorEnd_;
63 int64_t paramsOffset;
64 int64_t gradsOffset;
65 int64_t momentumOffset;
66 int64_t paramsOffsetC;
67 int64_t gradsOffsetC;
68 int64_t momentumOffsetC;
69 TPipe *pipe_;
70 const FusedSgdTilingData* tiling_;
71};
72 
73template <typename T>
74__aicore__ inline void FusedSgdF16Bf16<T>::Init(
75 GM_ADDR params, GM_ADDR grads, GM_ADDR momentum_buffer_list,
76 GM_ADDR grad_scale, GM_ADDR params_ref, GM_ADDR grads_ref,
77 GM_ADDR momentum_buffer_list_out, const FusedSgdTilingData& tiling,
78 uint64_t tensorStart, uint64_t tensorEnd)
79{
80 this->InitData(tiling);
81 tiling_ = &tiling;
82 tensorStart_ = tensorStart;
83 tensorEnd_ = tensorEnd;
84 
85 paramsList_ = ListTensorDesc(reinterpret_cast<__gm__ void*>(params));
86 gradsList_ = ListTensorDesc(reinterpret_cast<__gm__ void*>(grads));
87 paramsRefList_ = ListTensorDesc(reinterpret_cast<__gm__ void*>(params_ref));
88 gradsRefList_ = ListTensorDesc(reinterpret_cast<__gm__ void*>(grads_ref));
89 if (this->useMomentum) {
90 momentumList_ = ListTensorDesc(reinterpret_cast<__gm__ void*>(momentum_buffer_list));
91 momentumOutList_ = ListTensorDesc(reinterpret_cast<__gm__ void*>(momentum_buffer_list_out));
92 }
93 
94 // UB Buffer布局: inQue = [原始类型(param+grad+momentum)] + [FP32(param+grad+momentum)]
95 // 前半存原始类型,后半(偏移3*sizeof(T))存Cast后的FP32
96 pipe_->InitBuffer(inQue, BUFFER_NUM, this->coreCalcMax * (sizeof(T) + sizeof(float)) * 3);
97 pipe_->InitBuffer(outQue, BUFFER_NUM, this->coreCalcMax * sizeof(float) * 3);
98 
99 paramsOffset = this->coreCalcMax * INDEX_PARAMS;
100 gradsOffset = this->coreCalcMax * INDEX_GRADS;
101 momentumOffset = this->coreCalcMax * INDEX_MOMENTUM_BUFFER;
102 // FP32区域偏移 = 前半3份原始类型 + 对应的FP32偏移
103 paramsOffsetC = this->coreCalcMax * 3 + paramsOffset;
104 gradsOffsetC = this->coreCalcMax * 3 + gradsOffset;
105 momentumOffsetC = this->coreCalcMax * 3 + momentumOffset;
106 
107 hasGradScale = 0;
108 if (this->useGradScale) {
109 gmGradScale.SetGlobalBuffer((__gm__ float*)grad_scale, 1);
110 gradScaleValue = static_cast<float>(gmGradScale.GetValue(0));
111 hasGradScale = 1;
112 }
113}
114 
115template <typename T>
116__aicore__ inline void FusedSgdF16Bf16<T>::Compute(const uint64_t index, const uint64_t dataCount)
117{
118 uint64_t offset = index * this->coreCalcMax;
119 DataCopyParams copyParams = {1, static_cast<uint16_t>(dataCount * sizeof(T)), 0, 0};
120 DataCopyPadParams padParams = {false, 0, 0, 0};
121 
122 LocalTensor<T> inLocal = inQue.AllocTensor<T>();
123 LocalTensor<float> outLocal = outQue.AllocTensor<float>();
124 
125 PipeSync<AscendC::HardEvent::MTE3_MTE2>();
126 PipeSync<AscendC::HardEvent::S_MTE2>();
127 PipeSync<AscendC::HardEvent::V_MTE2>();
128 DataCopyPad(inLocal[paramsOffset], gmParams[offset], copyParams, padParams);
129 DataCopyPad(inLocal[gradsOffset], gmGrads[offset], copyParams, padParams);
130 if (this->useMomentum) {
131 DataCopyPad(inLocal[momentumOffset], gmMomentumBuffer[offset], copyParams, padParams);
132 }
133 PipeSync<AscendC::HardEvent::MTE2_V>();
134 PipeBarrier<PIPE_V>();
135 
136 LocalTensor<float> inLocalC = inLocal[this->coreCalcMax * 3].template ReinterpretCast<float>();
137 Cast(inLocalC[paramsOffset], inLocal[paramsOffset], RoundMode::CAST_NONE, dataCount);
138 PipeBarrier<PIPE_V>();
139 Cast(inLocalC[gradsOffset], inLocal[gradsOffset], RoundMode::CAST_NONE, dataCount);
140 PipeBarrier<PIPE_V>();
141 Cast(inLocalC[momentumOffset], inLocal[momentumOffset], RoundMode::CAST_NONE, dataCount);
atomgit-bot
atomgit-botatomgit-bot6月18日

🔵 Low Priority

fused_sgd_f16_bf16.hCompute() 函数中,第141行无条件地对 momentum 数据执行 Cast: Cast(inLocalC[momentumOffset], inLocal[momentumOffset], RoundMode::CAST_NONE, dataCount);

然而,当 useMomentum 为 false 时,inLocal[momentumOffset] 区域从未通过 DataCopyPad 写入(第130-132行有 if (this->useMomentum) 保护)。这意味着该 Cast 读取了 UB 中未初始化的数据,将其转换后写入 inLocalC[momentumOffset](该结果后续因 useMomentum 为 false 而不会被使用)。

功能上无实质影响(结果未被消费),但:

  1. 浪费了一次 Vector 单元的 Cast 计算和 PipeBarrier
  2. 在安全敏感场景下读取未初始化内存属于未定义行为

建议添加 if (this->useMomentum) 保护该 Cast 调用。

变更:第141行新增的无条件 Cast。

likedislike
142 PipeBarrier<PIPE_V>();
143 
144 // Step 1: 梯度缩放,并Cast回原始类型写回
145 if (hasGradScale) {
146 float invGradScale = 1.0f / gradScaleValue;
147 Muls(inLocalC[gradsOffset], inLocalC[gradsOffset], invGradScale, dataCount);
148 PipeBarrier<PIPE_V>();
149 Cast(inLocal[gradsOffset], inLocalC[gradsOffset], RoundMode::CAST_RINT, dataCount);
150 PipeSync<AscendC::HardEvent::V_MTE3>();
151 DataCopyPad(gmGradsRef[offset], inLocal[gradsOffset], copyParams);
152 PipeSync<AscendC::HardEvent::MTE3_V>();
153 }
154 // Step 2: 最大化处理
155 if (this->maximize) {
156 Muls(inLocalC[gradsOffset], inLocalC[gradsOffset], -1.0f, dataCount);
157 PipeBarrier<PIPE_V>();
158 }
159 // Step 3: 权重衰减
160 if (this->weightDecay != 0.0f) {
161 Muls(outLocal[gradsOffset], inLocalC[paramsOffset], this->weightDecay, dataCount);
162 PipeBarrier<PIPE_V>();
163 Add(inLocalC[gradsOffset], inLocalC[gradsOffset], outLocal[gradsOffset], dataCount);
164 PipeBarrier<PIPE_V>();
165 }
166 
167 // Step 4: 动量更新 (FP32计算,写回时Cast回原始类型)
168 if (this->useMomentum) {
169 if (this->isFirstStep) {
170 Muls(outLocal[momentumOffset], inLocalC[gradsOffset], 1.0f, dataCount);
171 PipeBarrier<PIPE_V>();
172 } else {
173 Muls(outLocal[momentumOffset], inLocalC[momentumOffset], this->momentum, dataCount);
174 PipeBarrier<PIPE_V>();
175 Muls(outLocal[paramsOffset], inLocalC[gradsOffset], 1.0f - this->dampening, dataCount);
176 PipeBarrier<PIPE_V>();
177 Add(outLocal[momentumOffset], outLocal[momentumOffset], outLocal[paramsOffset], dataCount);
178 PipeBarrier<PIPE_V>();
179 }
180 // 动量Cast回原始类型并写回GM
181 Cast(inLocal[momentumOffset], outLocal[momentumOffset], RoundMode::CAST_RINT, dataCount);
182 PipeBarrier<PIPE_V>();
183 PipeSync<AscendC::HardEvent::V_MTE3>();
184 DataCopyPad(gmMomentumBufferOut[offset], inLocal[momentumOffset], copyParams);
185 PipeSync<AscendC::HardEvent::MTE3_V>();
186 // Nesterov: grad = grad + momentum * buf
187 if (this->nesterov) {
188 Muls(outLocal[momentumOffset], outLocal[momentumOffset], this->momentum, dataCount);
189 PipeBarrier<PIPE_V>();
190 Add(inLocalC[gradsOffset], outLocal[momentumOffset], inLocalC[gradsOffset], dataCount);
191 PipeBarrier<PIPE_V>();
192 } else {
193 Muls(inLocalC[gradsOffset], outLocal[momentumOffset], 1.0f, dataCount);
194 PipeBarrier<PIPE_V>();
195 }
196 }
197 
198 // Step 5: 参数更新 (param = param - lr * grad),Cast回原始类型写回
199 Muls(inLocalC[gradsOffset], inLocalC[gradsOffset], this->lr, dataCount);
200 PipeBarrier<PIPE_V>();
201 Sub(inLocalC[gradsOffset], inLocalC[paramsOffset], inLocalC[gradsOffset], dataCount);
202 PipeBarrier<PIPE_V>();
203 Cast(inLocal[paramsOffset], inLocalC[gradsOffset], RoundMode::CAST_RINT, dataCount);
204 PipeBarrier<PIPE_V>();
205 PipeSync<AscendC::HardEvent::V_MTE3>();
206 DataCopyPad(gmParamsRef[offset], inLocal[paramsOffset], copyParams);
207 
208 inQue.FreeTensor(inLocal);
209 outQue.FreeTensor(outLocal);
210}
211 
212template <typename T>
213__aicore__ inline void FusedSgdF16Bf16<T>::Process()
214{
215 for (uint64_t idx = tensorStart_; idx < tensorEnd_; idx++) {
216 uint64_t buf[10];
217 desc_.SetShapeAddr(buf);
218 paramsList_.GetDesc(desc_, static_cast<uint32_t>(idx));
219
220 uint64_t tensorDataNum = 1;
221 for (uint32_t j = 0; j < desc_.GetDim(); j++) {
222 tensorDataNum *= desc_.GetShape(j);
223 }
224 if (tensorDataNum == 0) {
225 continue;
226 }
227 
228 gmParams.SetGlobalBuffer(paramsList_.GetDataPtr<T>(idx), tensorDataNum);
229 gmGrads.SetGlobalBuffer(gradsList_.GetDataPtr<T>(idx), tensorDataNum);
230 gmParamsRef.SetGlobalBuffer(paramsRefList_.GetDataPtr<T>(idx), tensorDataNum);
231 gmGradsRef.SetGlobalBuffer(gradsRefList_.GetDataPtr<T>(idx), tensorDataNum);
232 if (this->useMomentum) {
233 gmMomentumBuffer.SetGlobalBuffer(momentumList_.GetDataPtr<T>(idx), tensorDataNum);
234 gmMomentumBufferOut.SetGlobalBuffer(momentumOutList_.GetDataPtr<T>(idx), tensorDataNum);
235 }
236 
237 uint64_t loopNum = (tensorDataNum + this->coreCalcMax - 1) / this->coreCalcMax;
238 for (uint64_t n = 0; n < loopNum - 1; n++) {
239 Compute(n, this->coreCalcMax);
240 }
241 uint64_t lastCount = tensorDataNum - this->coreCalcMax * (loopNum - 1);
242 Compute(loopNum - 1, lastCount);
243 }
244}
245 
246} // namespace FusedSgd
247 
248#endif // FUSED_SGD_F16_BF16_H
Aoptim/fused_sgd/op_kernel/fused_sgd_f32.h+226-0
@@ -0,0 +1,226 @@
1/**
2 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 * CANN Open Software License Agreement Version 2.0 (the "License").
5 * Please refer to the License for details. You may not use this file except in compliance with the License.
6 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 * See LICENSE in the root of the software repository for the full text of the License.
9 */
10 
11/*!
12 * \file fused_sgd_f32.h
13 * \brief
14 */
15 
16#ifndef FUSED_SGD_F32_H
17#define FUSED_SGD_F32_H
18 
19#include "fused_sgd_base.h"
20 
21namespace FusedSgd {
22using namespace AscendC;
23 
24template <typename T>
25class FusedSgdF32 : public FusedSgdBase<T>
26{
27public:
28 __aicore__ inline FusedSgdF32(TPipe *pipe) : pipe_(pipe) {};
29 __aicore__ inline void Init(
30 GM_ADDR params, GM_ADDR grads, GM_ADDR momentum_buffer_list,
31 GM_ADDR grad_scale, GM_ADDR params_ref, GM_ADDR grads_ref,
32 GM_ADDR momentum_buffer_list_out, const FusedSgdTilingData& tiling,
33 uint64_t tensorStart, uint64_t tensorEnd);
34 __aicore__ inline void Process();
35 
36protected:
37 __aicore__ inline void Compute(const uint64_t index, const uint64_t dataCount);
38 
39 TQue<QuePosition::VECIN, BUFFER_NUM> inQue;
40 TQue<QuePosition::VECOUT, BUFFER_NUM> outQue;
41 
42 GlobalTensor<T> gmParams;
43 GlobalTensor<T> gmGrads;
44 GlobalTensor<T> gmMomentumBuffer;
45 GlobalTensor<float> gmGradScale;
46 GlobalTensor<float> gmFoundInf;
47 GlobalTensor<T> gmParamsRef;
48 GlobalTensor<T> gmGradsRef;
49 GlobalTensor<T> gmMomentumBufferOut;
50 
51 ListTensorDesc paramsList_;
52 ListTensorDesc gradsList_;
53 ListTensorDesc momentumList_;
54 ListTensorDesc paramsRefList_;
55 ListTensorDesc gradsRefList_;
56 ListTensorDesc momentumOutList_;
57 TensorDesc<uint64_t> desc_;
58 
59 float gradScaleValue;
60 uint64_t hasGradScale;
61 uint64_t tensorStart_;
62 uint64_t tensorEnd_;
63 int64_t paramsOffset;
64 int64_t gradsOffset;
65 int64_t momentumOffset;
66 TPipe *pipe_;
67 const FusedSgdTilingData* tiling_;
68};
69 
70template <typename T>
71__aicore__ inline void FusedSgdF32<T>::Init(
72 GM_ADDR params, GM_ADDR grads, GM_ADDR momentum_buffer_list,
73 GM_ADDR grad_scale, GM_ADDR params_ref, GM_ADDR grads_ref,
74 GM_ADDR momentum_buffer_list_out, const FusedSgdTilingData& tiling,
75 uint64_t tensorStart, uint64_t tensorEnd)
76{
77 this->InitData(tiling);
78 tiling_ = &tiling;
79 tensorStart_ = tensorStart;
80 tensorEnd_ = tensorEnd;
81 
82 paramsList_ = ListTensorDesc(reinterpret_cast<__gm__ void*>(params));
83 gradsList_ = ListTensorDesc(reinterpret_cast<__gm__ void*>(grads));
84 paramsRefList_ = ListTensorDesc(reinterpret_cast<__gm__ void*>(params_ref));
85 gradsRefList_ = ListTensorDesc(reinterpret_cast<__gm__ void*>(grads_ref));
86 if (this->useMomentum) {
87 momentumList_ = ListTensorDesc(reinterpret_cast<__gm__ void*>(momentum_buffer_list));
88 momentumOutList_ = ListTensorDesc(reinterpret_cast<__gm__ void*>(momentum_buffer_list_out));
89 }
90 
91 pipe_->InitBuffer(inQue, BUFFER_NUM, this->coreCalcMax * sizeof(T) * 3);
92 pipe_->InitBuffer(outQue, BUFFER_NUM, this->coreCalcMax * sizeof(T) * 3);
93 
94 paramsOffset = this->coreCalcMax * INDEX_PARAMS;
95 gradsOffset = this->coreCalcMax * INDEX_GRADS;
96 momentumOffset = this->coreCalcMax * INDEX_MOMENTUM_BUFFER;
97 
98 hasGradScale = 0;
99 if (this->useGradScale) {
100 gmGradScale.SetGlobalBuffer((__gm__ float*)grad_scale, 1);
101 gradScaleValue = static_cast<float>(gmGradScale.GetValue(0));
102 hasGradScale = 1;
103 }
104}
105 
106template <typename T>
107__aicore__ inline void FusedSgdF32<T>::Compute(const uint64_t index, const uint64_t dataCount)
108{
109 uint64_t offset = index * this->coreCalcMax;
110 DataCopyParams copyParams = {1, static_cast<uint16_t>(dataCount * sizeof(T)), 0, 0};
111 DataCopyPadParams padParams = {false, 0, 0, 0};
112 
113 LocalTensor<T> inLocal = inQue.AllocTensor<T>();
114 LocalTensor<T> outLocal = outQue.AllocTensor<T>();
115 
116 PipeSync<AscendC::HardEvent::MTE3_MTE2>();
117 PipeSync<AscendC::HardEvent::S_MTE2>();
118 PipeSync<AscendC::HardEvent::V_MTE2>();
119 DataCopyPad(inLocal[paramsOffset], gmParams[offset], copyParams, padParams);
120 DataCopyPad(inLocal[gradsOffset], gmGrads[offset], copyParams, padParams);
121 if (this->useMomentum) {
122 DataCopyPad(inLocal[momentumOffset], gmMomentumBuffer[offset], copyParams, padParams);
123 }
124 PipeSync<AscendC::HardEvent::MTE2_V>();
125 PipeBarrier<PIPE_V>();
126 
127 // Step 1: 梯度缩放 (grad = grad / grad_scale),并回写缩放后的梯度
128 if (hasGradScale) {
129 float invGradScale = 1.0f / gradScaleValue;
130 Muls(inLocal[gradsOffset], inLocal[gradsOffset], invGradScale, dataCount);
131 PipeBarrier<PIPE_V>();
132 PipeSync<AscendC::HardEvent::V_MTE3>();
133 DataCopyPad(gmGradsRef[offset], inLocal[gradsOffset], copyParams);
134 PipeSync<AscendC::HardEvent::MTE3_V>();
135 }
136 // Step 2: 最大化处理 (grad = -grad),用于最大化目标函数
137 if (this->maximize) {
138 Muls(inLocal[gradsOffset], inLocal[gradsOffset], -1.0f, dataCount);
139 PipeBarrier<PIPE_V>();
140 }
141 // Step 3: 权重衰减 (grad = grad + weight_decay * param)
142 if (this->weightDecay != 0.0f) {
143 Muls(outLocal[gradsOffset], inLocal[paramsOffset], this->weightDecay, dataCount);
144 PipeBarrier<PIPE_V>();
145 Add(inLocal[gradsOffset], inLocal[gradsOffset], outLocal[gradsOffset], dataCount);
146 PipeBarrier<PIPE_V>();
147 }
148 
149 // Step 4: 动量更新 (SGD with Momentum)
150 if (this->useMomentum) {
151 if (this->isFirstStep) {
152 Muls(outLocal[momentumOffset], inLocal[gradsOffset], 1.0f, dataCount);
153 PipeBarrier<PIPE_V>();
154 } else {
155 Muls(outLocal[momentumOffset], inLocal[momentumOffset], this->momentum, dataCount);
156 PipeBarrier<PIPE_V>();
157 Muls(outLocal[paramsOffset], inLocal[gradsOffset], 1.0f - this->dampening, dataCount);
158 PipeBarrier<PIPE_V>();
159 Add(outLocal[momentumOffset], outLocal[momentumOffset], outLocal[paramsOffset], dataCount);
160 PipeBarrier<PIPE_V>();
161 }
162 // 动量写回GM
163 PipeSync<AscendC::HardEvent::V_MTE3>();
164 DataCopyPad(gmMomentumBufferOut[offset], outLocal[momentumOffset], copyParams);
165 PipeSync<AscendC::HardEvent::MTE3_V>();
166 // Nesterov: grad = grad + momentum * buf
167 if (this->nesterov) {
168 Muls(outLocal[momentumOffset], outLocal[momentumOffset], this->momentum, dataCount);
169 PipeBarrier<PIPE_V>();
170 Add(inLocal[gradsOffset], outLocal[momentumOffset], inLocal[gradsOffset], dataCount);
171 PipeBarrier<PIPE_V>();
172 } else {
173 Muls(inLocal[gradsOffset], outLocal[momentumOffset], 1.0f, dataCount);
174 PipeBarrier<PIPE_V>();
175 }
176 }
177 
178 // Step 5: 参数更新 (param = param - lr * grad),并写回GM
179 Muls(outLocal[gradsOffset], inLocal[gradsOffset], this->lr, dataCount);
180 PipeBarrier<PIPE_V>();
181 Sub(outLocal[paramsOffset], inLocal[paramsOffset], outLocal[gradsOffset], dataCount);
182 PipeBarrier<PIPE_V>();
183 PipeSync<AscendC::HardEvent::V_MTE3>();
184 DataCopyPad(gmParamsRef[offset], outLocal[paramsOffset], copyParams);
185 
186 inQue.FreeTensor(inLocal);
187 outQue.FreeTensor(outLocal);
188}
189 
190template <typename T>
191__aicore__ inline void FusedSgdF32<T>::Process()
192{
193 for (uint64_t idx = tensorStart_; idx < tensorEnd_; idx++) {
194 uint64_t buf[10];
195 desc_.SetShapeAddr(buf);
196 paramsList_.GetDesc(desc_, static_cast<uint32_t>(idx));
197
198 uint64_t tensorDataNum = 1;
199 for (uint32_t j = 0; j < desc_.GetDim(); j++) {
200 tensorDataNum *= desc_.GetShape(j);
201 }
202 if (tensorDataNum == 0) {
203 continue;
204 }
205 
206 gmParams.SetGlobalBuffer(paramsList_.GetDataPtr<T>(idx));
207 gmGrads.SetGlobalBuffer(gradsList_.GetDataPtr<T>(idx));
208 gmParamsRef.SetGlobalBuffer(paramsRefList_.GetDataPtr<T>(idx));
209 gmGradsRef.SetGlobalBuffer(gradsRefList_.GetDataPtr<T>(idx));
210 if (this->useMomentum) {
211 gmMomentumBuffer.SetGlobalBuffer(momentumList_.GetDataPtr<T>(idx));
212 gmMomentumBufferOut.SetGlobalBuffer(momentumOutList_.GetDataPtr<T>(idx));
atomgit-bot
atomgit-botatomgit-bot6月18日

🟠 High Priority

fused_sgd_f32.hProcess() 函数中(第206-212行),SetGlobalBuffer 调用只传入了数据指针,没有传入 tensorDataNum 作为 buffer size 参数。

对比 F16/BF16 内核(fused_sgd_f16_bf16.h 第228-234行),所有 SetGlobalBuffer 调用都正确地传入了 tensorDataNum

在 AscendC 框架中,GlobalTensor::SetGlobalBuffer(T* addr, uint64_t size) 设置 buffer size 用于 DataCopyPad 等操作的边界检查。缺少 size 参数意味着边界检查失效,可能导致超出 tensor 实际大小的越界读写无法被检测到,在数据量较大的 tensor 上存在数据损坏风险。

变更:第206-212行新增,SetGlobalBuffer 调用未传入大小参数。

建议:在 F32 内核的 Process() 函数中,为所有 SetGlobalBuffer 调用添加 tensorDataNum 作为第二个参数,与 F16/BF16 内核保持一致。

likedislike
majiajian_hw
6月22日 评论:
likedislike
213 }
214 
215 uint64_t loopNum = (tensorDataNum + this->coreCalcMax - 1) / this->coreCalcMax;
216 for (uint64_t n = 0; n < loopNum - 1; n++) {
217 Compute(n, this->coreCalcMax);
218 }
219 uint64_t lastCount = tensorDataNum - this->coreCalcMax * (loopNum - 1);
220 Compute(loopNum - 1, lastCount);
221 }
222}
223 
224} // namespace FusedSgd
225 
226#endif // FUSED_SGD_F32_H
Aoptim/fused_sgd/op_kernel/fused_sgd_tiling_data.h+34-0
@@ -0,0 +1,34 @@
1/**
2 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 * CANN Open Software License Agreement Version 2.0 (the "License").
5 * Please refer to the License for details. You may not use this file except in compliance with the License.
6 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 * See LICENSE in the root of the software repository for the full text of the License.
9 */
10 
11/*!
12 * \file fused_sgd_tiling_data.h
13 * \brief tiling data struct
14 */
15 
16#ifndef _FUSED_SGD_TILING_DATA_H_
17#define _FUSED_SGD_TILING_DATA_H_
18 
19struct FusedSgdTilingData {
20 float weightDecay;
21 float momentum;
22 float lr;
23 float dampening;
24 uint64_t nesterov;
25 uint64_t maximize;
26 uint64_t isFirstStep;
27 uint64_t useGradScale;
28 uint64_t useMomentum;
29 uint64_t tensorNum;
30 uint64_t tensorsPerCore;
31 uint64_t usedCoreNum;
32 uint64_t coreCalcMax;
33};
34#endif
Aoptim/fused_sgd/tests/CMakeLists.txt+18-0
@@ -0,0 +1,18 @@
1# ----------------------------------------------------------------------------
2# Copyright (c) 2026 Huawei Technologies Co., Ltd.
3# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4# CANN Open Software License Agreement Version 2.0 (the "License").
5# Please refer to the License for details. You may not use this file except in compliance with the License.
6# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8# See LICENSE in the root of the software repository for the full text of the License.
9# ----------------------------------------------------------------------------
10 
11message(STATUS "=== Debug: start ops.optim.fused_sgd.tests.CMakeLists.txt ")
12file(GLOB CURRENT_SOURCE_DIRS LIST_DIRECTORIES true ${CMAKE_CURRENT_SOURCE_DIR}/*)
13message(STATUS "=== Debug: CURRENT_SOURCE_DIRS =${CURRENT_SOURCE_DIRS} ")
14foreach(SUB_DIR ${CURRENT_SOURCE_DIRS})
15 if(EXISTS "${SUB_DIR}/CMakeLists.txt")
16 add_subdirectory(${SUB_DIR})
17 endif()
18endforeach()
Aoptim/fused_sgd/tests/ut/CMakeLists.txt+17-0
@@ -0,0 +1,17 @@
1# ----------------------------------------------------------------------------
2# Copyright (c) 2026 Huawei Technologies Co., Ltd.
3# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4# CANN Open Software License Agreement Version 2.0 (the "License").
5# Please refer to the License for details. You may not use this file except in compliance with the License.
6# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8# See LICENSE in the root of the software repository for the full text of the License.
9# ----------------------------------------------------------------------------
10 
11file(GLOB CURRENT_SOURCE_DIRS LIST_DIRECTORIES true ${CMAKE_CURRENT_SOURCE_DIR}/*)
12message(STATUS "=== Debug: CURRENT_SOURCE_DIRS =${CURRENT_SOURCE_DIRS} ")
13foreach(SUB_DIR ${CURRENT_SOURCE_DIRS})
14 if(EXISTS "${SUB_DIR}/CMakeLists.txt")
15 add_subdirectory(${SUB_DIR})
16 endif()
17endforeach()
Aoptim/fused_sgd/tests/ut/op_host/CMakeLists.txt+16-0
@@ -0,0 +1,16 @@
1# ----------------------------------------------------------------------------
2# Copyright (c) 2026 Huawei Technologies Co., Ltd.
3# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4# CANN Open Software License Agreement Version 2.0 (the "License").
5# Please refer to the License for details. You may not use this file except in compliance with the License.
6# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8# See LICENSE in the root of the software repository for the full text of the License.
9# ----------------------------------------------------------------------------
10 
11message(STATUS "=== Debug: CURRENT_DIRS =${CURRENT_DIRS} ")
12file(GLOB CURRENT_DIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*)
13if(UT_TEST_ALL OR OP_HOST_UT)
14 add_modules_ut_sources(HOSTNAME ${OP_TILING_MODULE_NAME} MODE PRIVATE DIR ${CMAKE_CURRENT_SOURCE_DIR})
15 add_modules_ut_sources(HOSTNAME ${OP_INFERSHAPE_MODULE_NAME} MODE PRIVATE DIR ${CMAKE_CURRENT_SOURCE_DIR})
16endif()
Aoptim/fused_sgd/tests/ut/op_host/test_fused_sgd_infershape.cpp+84-0
@@ -0,0 +1,84 @@
1/**
2 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 * CANN Open Software License Agreement Version 2.0 (the "License").
5 * Please refer to the License for details. You may not use this file except in compliance with the License.
6 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 * See LICENSE in the root of the software repository for the full text of the License.
9 */
10 
11#include <gtest/gtest.h>
12#include <array>
13#include "register/op_impl_registry.h"
14#include "exe_graph/runtime/storage_shape.h"
15#include "exe_graph/runtime/storage_format.h"
16#include "ut_op_util.h"
17#include "infershape_test_util.h"
18#include "kernel_run_context_facker.h"
19#include "ut_op_common.h"
20 
21#include "gtest/gtest.h"
22#include "exe_graph/runtime/storage_format.h"
23#include "exe_graph/runtime/storage_shape.h"
24#include "exe_graph/runtime/kernel_context.h"
25#include "register/op_impl_registry.h"
26#include "kernel_run_context_facker.h"
27#include "ut_op_common.h"
28#include "infershape_test_util.h"
29#include "log/log.h"
30 
31using namespace ge;
32using namespace ut_util;
33using namespace std;
34 
35class FusedSgdInferShape : public testing::Test
36{
37protected:
38 static void SetUpTestCase()
39 {
40 std::cout << "FusedSgdInferShape SetUp" << std::endl;
41 }
42 static void TearDownTestCase()
43 {
44 std::cout << "FusedSgdInferShape TearDown" << std::endl;
45 }
46};
47 
48std::vector<int64_t> ToVectorForFused(const gert::Shape& shape) {
49 size_t shape_size = shape.GetDimNum();
50 std::vector<int64_t> shape_vec(shape_size, 0);
51 
52 for (size_t i = 0; i < shape_size; i++) {
53 shape_vec[i] = shape.GetDim(i);
54 }
55 return shape_vec;
56}
57 
58TEST_F(FusedSgdInferShape, test_fused_sgd_infershape_same_shape)
59{
60 gert::StorageShape paramsShape = {{3, 4, 5}, {3, 6}};
61 gert::StorageShape gradsShape = {{3, 4, 5}, {3, 6}};
62 gert::StorageShape momentumShape = {{3, 4, 5}, {3, 6}};
63 std::string opType("FusedSgd");
64 auto infershape_func = gert::OpImplRegistry::GetInstance().GetOpImpl(opType)->infer_shape;
65 
66 gert::StorageShape paramsRefShape;
67 gert::StorageShape gradsRefShape;
68 gert::StorageShape momentumRefShape;
69 auto holder = gert::InferShapeContextFaker()
70 .NodeIoNum(5, 3)
71 .IrInstanceNum({1,1,1,1,1})
72 .InputShapes({&paramsShape, &gradsShape, &momentumShape})
73 .OutputShapes({&paramsShape, &gradsShape, &momentumShape})
74 .Build();
75 
76 gert::InferShapeContext* context = holder.GetContext<gert::InferShapeContext>();
77 EXPECT_EQ(infershape_func(context), ge::GRAPH_SUCCESS);
78 
79 std::vector<int64_t> expectedOutputShape1 = {3, 4, 5};
80 for(int i=0;i<3;i++){
81 auto tmpOutShape1 = context->GetOutputShape(i);
82 EXPECT_EQ(ToVectorForFused(*tmpOutShape1), expectedOutputShape1);
83 }
84}
Aoptim/fused_sgd/tests/ut/op_host/test_fused_sgd_tiling.cpp+232-0
@@ -0,0 +1,232 @@
1/**
2 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 * CANN Open Software License Agreement Version 2.0 (the "License").
5 * Please refer to the License for details. You may not use this file except in compliance with the License.
6 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 * See LICENSE in the root of the software repository for the full text of the License.
9 */
10 
11#include <iostream>
12#include <fstream>
13#include <vector>
14#include <gtest/gtest.h>
15#include "../../../op_host/fused_sgd_tiling.h"
16#include "log/log.h"
17#include "ut_op_common.h"
18#include "register/op_impl_registry.h"
19#include "platform/platform_infos_def.h"
20#include "ut_op_util.h"
21#include "kernel_run_context_facker.h"
22#include "test_cube_util.h"
23#include "exe_graph/runtime/storage_format.h"
24#include "exe_graph/runtime/storage_shape.h"
25 
26using namespace ut_util;
27using namespace std;
28using namespace ge;
29 
30class FusedSgdTiling : public testing::Test
31{
32protected:
33 static void SetUpTestCase()
34 {
35 std::cout << "FusedSgdTiling SetUp" << std::endl;
36 }
37 
38 static void TearDownTestCase()
39 {
40 std::cout << "FusedSgdTiling TearDown" << std::endl;
41 }
42};
43 
44TEST_F(FusedSgdTiling, test_tiling_float32)
45{
46 gert::StorageShape paramsShape = {{300, 4, 2}, {300, 4, 2}};
47 gert::StorageShape gradsShape = {{300, 4, 2}, {300, 4, 2}};
48 gert::StorageShape momentumShape = {{300, 4, 2}, {300, 4, 2}};
49 gert::StorageShape gradScaleShape = {{1}, {1}};
50 
51 string compile_info_string = R"({"hardware_info": {"BT_SIZE": 0, "load3d_constraints": "1",
52 "Intrinsic_fix_pipe_l0c2out": false,
53 "Intrinsic_data_move_l12ub": true,
54 "Intrinsic_data_move_l0c2ub": true,
55 "Intrinsic_data_move_out2l1_nd2nz": false,
56 "UB_SIZE": 196608, "L2_SIZE": 33554432, "L1_SIZE": 524288,
57 "L0A_SIZE": 65536, "L0B_SIZE": 65536, "L0C_SIZE": 131072,
58 "CORE_NUM": 48}
59 })";
60 map<string, string> soc_infos;
61 map<string, string> aicore_spec;
62 map<string, string> intrinsics;
63 
64 GetPlatFormInfos(compile_info_string.c_str(), soc_infos, aicore_spec, intrinsics);
65 
66 fe::PlatFormInfos platform_info;
67 platform_info.Init();
68 
69 struct FusedSgdCompileInfo {
70 };
71 FusedSgdCompileInfo compile_info;
72 
73 std::string op_type("FusedSgd");
74 ASSERT_NE(gert::OpImplRegistry::GetInstance().GetOpImpl(op_type.c_str()), nullptr);
75 auto tiling_func = gert::OpImplRegistry::GetInstance().GetOpImpl(op_type.c_str())->tiling;
76 auto tiling_parse_func = gert::OpImplRegistry::GetInstance().GetOpImpl(op_type.c_str())->tiling_parse;
77 
78 auto kernel_holder =
79 gert::KernelRunContextFaker()
80 .KernelIONum(2, 1)
81 .Inputs({const_cast<char*>(compile_info_string.c_str()), reinterpret_cast<void*>(&platform_info)})
82 .Outputs({&compile_info})
83 .Build();
84 
85 ASSERT_TRUE(kernel_holder.GetContext<gert::TilingParseContext>()->GetPlatformInfo()->Init());
86 kernel_holder.GetContext<gert::TilingParseContext>()->GetPlatformInfo()->SetPlatformRes("SoCInfo", soc_infos);
87 kernel_holder.GetContext<gert::TilingParseContext>()->GetPlatformInfo()->SetPlatformRes("AICoreSpec", aicore_spec);
88 kernel_holder.GetContext<gert::TilingParseContext>()->GetPlatformInfo()->SetCoreNumByCoreType("AICore");
89 kernel_holder.GetContext<gert::TilingParseContext>()->GetPlatformInfo()->SetPlatformRes(
90 "AICoreintrinsicDtypeMap", intrinsics);
91 
92 ASSERT_EQ(tiling_parse_func(kernel_holder.GetContext<gert::KernelContext>()), ge::GRAPH_SUCCESS);
93 
94 auto param = gert::TilingData::CreateCap(4096);
95 auto workspace_size_holer = gert::ContinuousVector::Create<size_t>(4096);
96 auto ws_size = reinterpret_cast<gert::ContinuousVector*>(workspace_size_holer.get());
97 ASSERT_NE(param, nullptr);
98 auto holder = gert::TilingContextFaker()
99 .NodeIoNum(5, 3)
100 .IrInstanceNum({1, 1, 1, 1, 1})
101 .InputShapes({&paramsShape, &gradsShape, &momentumShape, &gradScaleShape})
102 .OutputShapes({&paramsShape, &gradsShape, &momentumShape})
103 .CompileInfo(&compile_info)
104 .PlatformInfo(reinterpret_cast<char*>(&platform_info))
105 .NodeInputTd(0, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND)
106 .NodeInputTd(1, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND)
107 .NodeInputTd(2, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND)
108 .NodeInputTd(3, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND)
109 .NodeInputTd(4, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND)
110 .NodeOutputTd(0, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND)
111 .NodeOutputTd(1, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND)
112 .NodeOutputTd(2, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND)
113 .NodeAttrs(
114 {{"weight_decay", Ops::NN::AnyValue::CreateFrom(0.01f)},
115 {"momentum", Ops::NN::AnyValue::CreateFrom(0.9f)},
116 {"lr", Ops::NN::AnyValue::CreateFrom(0.001f)},
117 {"dampening", Ops::NN::AnyValue::CreateFrom(0.0f)},
118 {"nesterov", Ops::NN::AnyValue::CreateFrom<bool>(false)},
119 {"maximize", Ops::NN::AnyValue::CreateFrom<bool>(false)},
120 {"is_first_step", Ops::NN::AnyValue::CreateFrom<bool>(false)}})
121 .TilingData(param.get())
122 .Workspace(ws_size)
123 .Build();
124 
125 gert::TilingContext* tiling_context = holder.GetContext<gert::TilingContext>();
126 ASSERT_NE(tiling_context, nullptr);
127 ASSERT_NE(tiling_context->GetPlatformInfo(), nullptr);
128 holder.GetContext<gert::TilingContext>()->GetPlatformInfo()->SetPlatformRes("SoCInfo", soc_infos);
129 holder.GetContext<gert::TilingContext>()->GetPlatformInfo()->SetPlatformRes("AICoreSpec", aicore_spec);
130 holder.GetContext<gert::TilingContext>()->GetPlatformInfo()->SetCoreNumByCoreType("AICore");
131 holder.GetContext<gert::TilingContext>()->GetPlatformInfo()->SetPlatformRes("AICoreintrinsicDtypeMap", intrinsics);
132 
133 EXPECT_EQ(tiling_func(tiling_context), ge::GRAPH_SUCCESS);
134 
135 auto tiling_key = tiling_context->GetTilingKey();
136 ASSERT_EQ(tiling_key, 0);
137}
138 
139TEST_F(FusedSgdTiling, test_tiling_float16)
140{
141 gert::StorageShape paramsShape = {{6400, 42}, {6400, 42}};
142 gert::StorageShape gradsShape = {{6400, 42}, {6400, 42}};
143 gert::StorageShape momentumShape = {{6400, 42}, {6400, 42}};
144 gert::StorageShape gradScaleShape = {{1}, {1}};
145 
146 string compile_info_string = R"({"hardware_info": {"BT_SIZE": 0, "load3d_constraints": "1",
147 "Intrinsic_fix_pipe_l0c2out": false,
148 "Intrinsic_data_move_l12ub": true,
149 "Intrinsic_data_move_l0c2ub": true,
150 "Intrinsic_data_move_out2l1_nd2nz": false,
151 "UB_SIZE": 196608, "L2_SIZE": 33554432, "L1_SIZE": 524288,
152 "L0A_SIZE": 65536, "L0B_SIZE": 65536, "L0C_SIZE": 131072,
153 "CORE_NUM": 48}
154 })";
155 map<string, string> soc_infos;
156 map<string, string> aicore_spec;
157 map<string, string> intrinsics;
158 
159 GetPlatFormInfos(compile_info_string.c_str(), soc_infos, aicore_spec, intrinsics);
160 
161 fe::PlatFormInfos platform_info;
162 platform_info.Init();
163 
164 struct FusedSgdCompileInfo {
165 };
166 FusedSgdCompileInfo compile_info;
167 
168 std::string op_type("FusedSgd");
169 ASSERT_NE(gert::OpImplRegistry::GetInstance().GetOpImpl(op_type.c_str()), nullptr);
170 auto tiling_func = gert::OpImplRegistry::GetInstance().GetOpImpl(op_type.c_str())->tiling;
171 auto tiling_parse_func = gert::OpImplRegistry::GetInstance().GetOpImpl(op_type.c_str())->tiling_parse;
172 
173 auto kernel_holder =
174 gert::KernelRunContextFaker()
175 .KernelIONum(2, 1)
176 .Inputs({const_cast<char*>(compile_info_string.c_str()), reinterpret_cast<void*>(&platform_info)})
177 .Outputs({&compile_info})
178 .Build();
179 
180 ASSERT_TRUE(kernel_holder.GetContext<gert::TilingParseContext>()->GetPlatformInfo()->Init());
181 kernel_holder.GetContext<gert::TilingParseContext>()->GetPlatformInfo()->SetPlatformRes("SoCInfo", soc_infos);
182 kernel_holder.GetContext<gert::TilingParseContext>()->GetPlatformInfo()->SetPlatformRes("AICoreSpec", aicore_spec);
183 kernel_holder.GetContext<gert::TilingParseContext>()->GetPlatformInfo()->SetCoreNumByCoreType("AICore");
184 kernel_holder.GetContext<gert::TilingParseContext>()->GetPlatformInfo()->SetPlatformRes(
185 "AICoreintrinsicDtypeMap", intrinsics);
186 
187 ASSERT_EQ(tiling_parse_func(kernel_holder.GetContext<gert::KernelContext>()), ge::GRAPH_SUCCESS);
188 
189 auto param = gert::TilingData::CreateCap(4096);
190 auto workspace_size_holer = gert::ContinuousVector::Create<size_t>(4096);
191 auto ws_size = reinterpret_cast<gert::ContinuousVector*>(workspace_size_holer.get());
192 ASSERT_NE(param, nullptr);
193 auto holder = gert::TilingContextFaker()
194 .NodeIoNum(5, 3)
195 .IrInstanceNum({1, 1, 1, 1, 1})
196 .InputShapes({&paramsShape, &gradsShape, &momentumShape, &gradScaleShape})
197 .OutputShapes({&paramsShape, &gradsShape, &momentumShape})
198 .CompileInfo(&compile_info)
199 .PlatformInfo(reinterpret_cast<char*>(&platform_info))
200 .NodeInputTd(0, ge::DT_FLOAT16, ge::FORMAT_ND, ge::FORMAT_ND)
201 .NodeInputTd(1, ge::DT_FLOAT16, ge::FORMAT_ND, ge::FORMAT_ND)
202 .NodeInputTd(2, ge::DT_FLOAT16, ge::FORMAT_ND, ge::FORMAT_ND)
203 .NodeInputTd(3, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND)
204 .NodeInputTd(4, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND)
205 .NodeOutputTd(0, ge::DT_FLOAT16, ge::FORMAT_ND, ge::FORMAT_ND)
206 .NodeOutputTd(1, ge::DT_FLOAT16, ge::FORMAT_ND, ge::FORMAT_ND)
207 .NodeOutputTd(2, ge::DT_FLOAT16, ge::FORMAT_ND, ge::FORMAT_ND)
208 .NodeAttrs(
209 {{"weight_decay", Ops::NN::AnyValue::CreateFrom(0.01f)},
210 {"momentum", Ops::NN::AnyValue::CreateFrom(0.9f)},
211 {"lr", Ops::NN::AnyValue::CreateFrom(0.001f)},
212 {"dampening", Ops::NN::AnyValue::CreateFrom(0.0f)},
213 {"nesterov", Ops::NN::AnyValue::CreateFrom<bool>(true)},
214 {"maximize", Ops::NN::AnyValue::CreateFrom<bool>(false)},
215 {"is_first_step", Ops::NN::AnyValue::CreateFrom<bool>(true)}})
216 .TilingData(param.get())
217 .Workspace(ws_size)
218 .Build();
219 
220 gert::TilingContext* tiling_context = holder.GetContext<gert::TilingContext>();
221 ASSERT_NE(tiling_context, nullptr);
222 ASSERT_NE(tiling_context->GetPlatformInfo(), nullptr);
223 holder.GetContext<gert::TilingContext>()->GetPlatformInfo()->SetPlatformRes("SoCInfo", soc_infos);
224 holder.GetContext<gert::TilingContext>()->GetPlatformInfo()->SetPlatformRes("AICoreSpec", aicore_spec);
225 holder.GetContext<gert::TilingContext>()->GetPlatformInfo()->SetCoreNumByCoreType("AICore");
226 holder.GetContext<gert::TilingContext>()->GetPlatformInfo()->SetPlatformRes("AICoreintrinsicDtypeMap", intrinsics);
227 
228 EXPECT_EQ(tiling_func(tiling_context), ge::GRAPH_SUCCESS);
229 
230 auto tiling_key = tiling_context->GetTilingKey();
231 ASSERT_EQ(tiling_key, 0);
232}
Aoptim/fused_sgd/tests/ut/op_kernel/CMakeLists.txt+13-0
@@ -0,0 +1,13 @@
1# ----------------------------------------------------------------------------
2# Copyright (c) 2026 Huawei Technologies Co., Ltd.
3# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4# CANN Open Software License Agreement Version 2.0 (the "License").
5# Please refer to the License for details. You may not use this file except in compliance with the License.
6# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8# See LICENSE in the root of the software repository for the full text of the License.
9# ----------------------------------------------------------------------------
10 
11if ((UT_TEST_ALL OR OP_KERNEL_UT) AND NOT UT_DONE)
12 AddOpTestCase(fused_sgd "ascend910B1" "-DDTYPE_X=float")
13endif()
Aoptim/fused_sgd/tests/ut/op_kernel/sgd_data/compare_data.py+57-0
@@ -0,0 +1,57 @@
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# ----------------------------------------------------------------------------
4# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6# CANN Open Software License Agreement Version 2.0 (the "License").
7# Please refer to the License for details. You may not use this file except in compliance with the License.
8# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10# See LICENSE in the root of the software repository for the full text of the License.
11# ----------------------------------------------------------------------------
12 
13import sys
14import numpy as np
15import glob
16import os
17 
18curr_dir = os.path.dirname(os.path.realpath(__file__))
19 
20 
21def compare_data(d_type):
22 if d_type == "float16":
23 np_dtype = np.float16
24 precision = 1 / 1000
25 elif d_type == "float32":
26 np_dtype = np.float32
27 precision = 1 / 10000
28 else:
29 np_dtype = np.float32
30 precision = 1 / 1000
31 
32 golden_file_lists = sorted(glob.glob(curr_dir + "/*golden*.bin"))
33 output_file_lists = sorted(glob.glob(curr_dir + "/*output*.bin"))
34 
35 data_same = True
36 for gold, out in zip(golden_file_lists, output_file_lists):
37 tmp_out = np.fromfile(out, np_dtype)
38 tmp_gold = np.fromfile(gold, np_dtype)
39 diff_res = np.isclose(tmp_out, tmp_gold, precision, 0, True)
40 diff_idx = np.where(diff_res != True)[0]
41 if len(diff_idx) == 0:
42 print(f"PASSED! {os.path.basename(gold)} vs {os.path.basename(out)}")
43 else:
44 print(f"FAILED! {os.path.basename(gold)} vs {os.path.basename(out)}")
45 for idx in diff_idx[:5]:
46 print(f" index: {idx}, output: {tmp_out[idx]}, golden: {tmp_gold[idx]}")
47 data_same = False
48 
49 if not data_same:
50 exit(1)
51 
52 
53if __name__ == '__main__':
54 if len(sys.argv) != 2:
55 print("Param num must be 2.")
56 exit(1)
57 compare_data(sys.argv[1])
Aoptim/fused_sgd/tests/ut/op_kernel/sgd_data/gen_data.py+96-0
@@ -0,0 +1,96 @@
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# ----------------------------------------------------------------------------
4# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6# CANN Open Software License Agreement Version 2.0 (the "License").
7# Please refer to the License for details. You may not use this file except in compliance with the License.
8# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10# See LICENSE in the root of the software repository for the full text of the License.
11# ----------------------------------------------------------------------------
12 
13import sys
14import os
15import numpy as np
16import re
17 
18 
19def parse_str_to_shape_list(shape_str):
20 shape_list = []
21 shape_str_arr = re.findall(r"\{([0-9 ,]+)\}", shape_str)
22 for shape_str in shape_str_arr:
23 single_shape = [int(x) for x in shape_str.split(",")]
24 shape_list.append(single_shape)
25 return shape_list
26 
27 
28def gen_data_and_golden(shape_str, d_type="float32"):
29 d_type_dict = {
30 "float32": np.float32,
31 "float16": np.float16,
32 }
33 np_type = d_type_dict[d_type]
34 shape_list = parse_str_to_shape_list(shape_str)
35 
36 weight_decay = 0.01
37 momentum = 0.0
38 lr = 0.001
39 dampening = 0.0
40 nesterov = 0
41 maximize = 0
42 is_first_step = 1
43 use_grad_scale = 1
44 use_momentum = 0
45 grad_scale = 0.5
46 
47 for index, shape in enumerate(shape_list):
48 params = (np.random.rand(*shape) * 2 - 1).astype(np.float32) * 100
49 grads = (np.random.rand(*shape) * 2 - 1).astype(np.float32) * 100
50 momentum_buf = (np.random.rand(*shape) * 2 - 1).astype(np.float32) * 100
51 
52 grads_inv = grads.copy()
53 if use_grad_scale:
54 inv_grad_scale = 1.0 / grad_scale
55 grads_inv = grads_inv * inv_grad_scale
56 
57 grads_ref = grads_inv.copy()
58 
59 if maximize:
60 grads_inv = -grads_inv
61 
62 if weight_decay != 0.0:
63 grads_inv = grads_inv + weight_decay * params
64 
65 if use_momentum:
66 if is_first_step:
67 momentum_buf_out = grads_inv.copy()
68 else:
69 momentum_buf_out = momentum * momentum_buf + (1.0 - dampening) * grads_inv
70 if nesterov:
71 grads_inv = grads_inv + momentum * momentum_buf_out
72 else:
73 grads_inv = momentum_buf_out.copy()
74 else:
75 momentum_buf_out = momentum_buf.copy()
76 
77 params_ref = params - lr * grads_inv
78 
79 params.astype(np_type).tofile(f"{d_type}_input_t_params_{index}.bin")
80 grads.astype(np_type).tofile(f"{d_type}_input_t_grads_{index}.bin")
81 momentum_buf.astype(np_type).tofile(f"{d_type}_input_t_momentum_{index}.bin")
82 params.astype(np_type).tofile(f"{d_type}_input_t_params_ref_{index}.bin")
83 grads.astype(np_type).tofile(f"{d_type}_input_t_grads_ref_{index}.bin")
84 momentum_buf.astype(np_type).tofile(f"{d_type}_input_t_momentum_ref_{index}.bin")
85 
86 params_ref.astype(np_type).tofile(f"{d_type}_golden_t_params_ref_{index}.bin")
87 grads_ref.astype(np_type).tofile(f"{d_type}_golden_t_grads_ref_{index}.bin")
88 momentum_buf_out.astype(np_type).tofile(f"{d_type}_golden_t_momentum_ref_{index}.bin")
89 
90 
91if __name__ == "__main__":
92 if len(sys.argv) != 3:
93 print("Param num must be 3.")
94 exit(1)
95 os.system("rm -rf *.bin")
atomgit-bot
atomgit-botatomgit-bot6月18日

🟡 Medium Priority

gen_data.py 第95行,os.system("rm -rf *.bin") 没有指定工作目录,会直接在当前工作目录下删除所有 .bin 文件。

该脚本被测试代码通过 system("cd ./sgd_data/ && python3 gen_data.py ...") 调用(见 test_fused_sgd.cpp 第111行),此时工作目录已是 sgd_data/,所以实际行为可能是正确的。但如果脚本被单独调用或从其他目录运行,rm -rf *.bin 会作用到错误的目录上。虽然这是一个测试辅助脚本,风险较低,但仍建议显式使用脚本自身所在目录(curr_dir,文件头部已定义但未使用)来限定删除范围。

变更:第95行新增 os.system("rm -rf *.bin")

建议:使用 curr_dir 限定删除范围:os.system(f"rm -rf {curr_dir}/*.bin") 或使用 glob + os.remove 替代 shell 命令。

likedislike
96 gen_data_and_golden(sys.argv[1], sys.argv[2])
Aoptim/fused_sgd/tests/ut/op_kernel/test_fused_sgd.cpp+221-0
@@ -0,0 +1,221 @@
1/**
2 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 * CANN Open Software License Agreement Version 2.0 (the "License").
5 * Please refer to the License for details. You may not use this file except in compliance with the License.
6 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 * See LICENSE in the root of the software repository for the full text of the License.
9 */
10 
11#define __CCE_UT_TEST__
12 
13#include <cstdint>
14#include <cstring>
15#include <iostream>
16#include <sstream>
17#include <string>
18#include <vector>
19#include "gtest/gtest.h"
20#include "tikicpulib.h"
21#include "data_utils.h"
22#include "../../../op_host/fused_sgd_tiling.h"
23 
24using namespace std;
25 
26extern "C" __global__ __aicore__ void fused_sgd(
27 GM_ADDR params, GM_ADDR grads, GM_ADDR momentum_buffer_list,
28 GM_ADDR grad_scale,
29 GM_ADDR params_ref, GM_ADDR grads_ref, GM_ADDR momentum_buffer_list_ref,
30 GM_ADDR workspace, GM_ADDR tiling);
31 
32class FusedSgdKernelTest : public testing::Test {
33protected:
34 static void SetUpTestCase() { cout << "FusedSgdKernelTest SetUp" << endl; }
35 static void TearDownTestCase() { cout << "FusedSgdKernelTest TearDown" << endl; }
36};
37 
38template <typename T1, typename T2>
39inline T1 CeilA2B(T1 a, T2 b)
40{
41 if (b == 0) {
42 return a;
43 }
44 return (a + b - 1) / b;
45}
46 
47template <typename T>
48uint8_t* CreateNormTensorList(const std::vector<std::vector<uint64_t>>& shapeInfos, char* d_type, const char* tag = "fused_sgd")
49{
50 uint64_t tensorListDescCount = 1 + shapeInfos.size() * 2;
51 for (auto s : shapeInfos) {
52 tensorListDescCount += s.size();
53 }
54 std::vector<uint64_t> shapeSizeList;
55 uint64_t* tensorListDesc = (uint64_t*)AscendC::GmAlloc(tensorListDescCount * sizeof(uint64_t));
56 *tensorListDesc = (tensorListDescCount - shapeInfos.size()) * sizeof(uint64_t);
57 uint64_t addrIndex = 0;
58 for (size_t i = 0; i < shapeInfos.size(); i++) {
59 addrIndex++;
60 uint16_t dimCount = shapeInfos[i].size();
61 *(tensorListDesc + addrIndex) = ((uint64_t)(i) << 32) + dimCount;
62 uint64_t shapeSize = 1;
63 for (size_t j = 0; j < dimCount; j++) {
64 addrIndex++;
65 *(tensorListDesc + addrIndex) = shapeInfos[i][j];
66 shapeSize *= shapeInfos[i][j];
67 }
68 shapeSizeList.push_back(shapeSize);
69 }
70 for (size_t i = 0; i < shapeInfos.size(); i++) {
71 addrIndex++;
72 uint64_t dataSize = shapeSizeList[i] * sizeof(T);
73 uint8_t* dataPtr = (uint8_t*)AscendC::GmAlloc(CeilA2B(dataSize, 32) * 32);
74 std::stringstream fileName;
75 fileName << "./sgd_data/" << d_type << "_input_t_" << tag << "_" << i << ".bin";
76 ReadFile(fileName.str(), dataSize, dataPtr, dataSize);
77 *(tensorListDesc + addrIndex) = (uint64_t)dataPtr;
78 }
79 return (uint8_t*)tensorListDesc;
80}
81 
82template <typename T>
83void FreeNormTensorList(uint8_t* addr, const std::vector<std::vector<uint64_t>>& shapeInfos, char* d_type, const char* tag = "fused_sgd")
84{
85 uint64_t dataPtrOffset = *((uint64_t*)addr);
86 uint8_t* dataAddr = addr + dataPtrOffset;
87 for (size_t i = 0; i < shapeInfos.size(); i++) {
88 uint64_t shapeSize = 1;
89 for (size_t j = 0; j < shapeInfos[i].size(); j++) {
90 shapeSize *= shapeInfos[i][j];
91 }
92 uint8_t* tensorAddr = (uint8_t*)(*((uint64_t*)(dataAddr) + i));
93 std::stringstream fileName;
94 fileName << "./sgd_data/" << d_type << "_output_t_" << tag << "_" << i << ".bin";
95 WriteFile(fileName.str(), tensorAddr, shapeSize * sizeof(T));
96 AscendC::GmFree((void*)(tensorAddr));
97 }
98 AscendC::GmFree((void*)addr);
99}
100 
101TEST_F(FusedSgdKernelTest, test_fp32_basic)
102{
103 size_t tilingSize = sizeof(FusedSgdTilingData);
104 uint32_t blockDim = 1;
105 std::vector<std::vector<uint64_t>> shapeInfos = {{4}};
106 
107 system(
108 "cp -rf "
109 "../../../../optim/fused_sgd/tests/ut/op_kernel/sgd_data ./");
110 system("chmod -R 755 ./sgd_data/");
111 system("cd ./sgd_data/ && python3 gen_data.py '{{4}}' 'float32'");
112 
113 uint8_t* paramsBuf = CreateNormTensorList<float>(shapeInfos, "float32", "params");
114 uint8_t* gradsBuf = CreateNormTensorList<float>(shapeInfos, "float32", "grads");
115 uint8_t* momentumBuf = CreateNormTensorList<float>(shapeInfos, "float32", "momentum");
116 uint8_t* paramsRefBuf = CreateNormTensorList<float>(shapeInfos, "float32", "params_ref");
117 uint8_t* gradsRefBuf = CreateNormTensorList<float>(shapeInfos, "float32", "grads_ref");
118 uint8_t* momentumRefBuf = CreateNormTensorList<float>(shapeInfos, "float32", "momentum_ref");
119 uint8_t* gradScaleBuf = (uint8_t*)AscendC::GmAlloc(sizeof(float));
120 uint8_t* workspace = (uint8_t*)AscendC::GmAlloc(16 * 1024 * 1024);
121 uint8_t* tiling = (uint8_t*)AscendC::GmAlloc(tilingSize);
122 
123 float* gradScalePtr = reinterpret_cast<float*>(gradScaleBuf);
124 gradScalePtr[0] = 0.5f;
125 
126 FusedSgdTilingData* tilingData = reinterpret_cast<FusedSgdTilingData*>(tiling);
127 tilingData->weightDecay = 0.01f;
128 tilingData->momentum = 0.5f;
129 tilingData->lr = 0.001f;
130 tilingData->dampening = 0.0f;
131 tilingData->nesterov = 0;
132 tilingData->maximize = 0;
133 tilingData->isFirstStep = 1;
134 tilingData->useGradScale = 1;
135 tilingData->useMomentum = 1;
136 tilingData->tensorNum = 1;
137 tilingData->tensorsPerCore = 1;
138 tilingData->usedCoreNum = 1;
139 tilingData->coreCalcMax = 4088;
140 
141 AscendC::SetKernelMode(KernelMode::AIV_MODE);
142 ICPU_SET_TILING_KEY(0);
143 ICPU_RUN_KF(fused_sgd, blockDim,
144 paramsBuf, gradsBuf, momentumBuf,
145 gradScaleBuf,
146 paramsRefBuf, gradsRefBuf, momentumRefBuf,
147 workspace, (uint8_t*)(tiling));
148 
149 FreeNormTensorList<float>(paramsRefBuf, shapeInfos, "float32", "params_ref");
150 FreeNormTensorList<float>(gradsRefBuf, shapeInfos, "float32", "grads_ref");
151 FreeNormTensorList<float>(momentumRefBuf, shapeInfos, "float32", "momentum_ref");
152 FreeNormTensorList<float>(paramsBuf, shapeInfos, "float32", "params_ref");
153 FreeNormTensorList<float>(gradsBuf, shapeInfos, "float32", "grads_ref");
154 FreeNormTensorList<float>(momentumBuf, shapeInfos, "float32", "momentum_ref");
155 AscendC::GmFree(gradScaleBuf);
156 AscendC::GmFree(workspace);
157 AscendC::GmFree(tiling);
158 
159 system("cd ./sgd_data/ && python3 compare_data.py 'float32'");
160}
161 
162TEST_F(FusedSgdKernelTest, test_fp16_basic)
163{
164 size_t tilingSize = sizeof(FusedSgdTilingData);
165 uint32_t blockDim = 1;
166 std::vector<std::vector<uint64_t>> shapeInfos = {{4}};
167 
168 system(
169 "cp -rf "
170 "../../../../optim/fused_sgd/tests/ut/op_kernel/sgd_data ./");
171 system("chmod -R 755 ./sgd_data/");
172 system("cd ./sgd_data/ && python3 gen_data.py '{{4}}' 'float16'");
173 
174 uint8_t* paramsBuf = CreateNormTensorList<half>(shapeInfos, "float16", "params");
175 uint8_t* gradsBuf = CreateNormTensorList<half>(shapeInfos, "float16", "grads");
176 uint8_t* momentumBuf = CreateNormTensorList<half>(shapeInfos, "float16", "momentum");
177 uint8_t* paramsRefBuf = CreateNormTensorList<half>(shapeInfos, "float16", "params_ref");
178 uint8_t* gradsRefBuf = CreateNormTensorList<half>(shapeInfos, "float16", "grads_ref");
179 uint8_t* momentumRefBuf = CreateNormTensorList<half>(shapeInfos, "float16", "momentum_ref");
180 uint8_t* gradScaleBuf = (uint8_t*)AscendC::GmAlloc(sizeof(float));
181 uint8_t* workspace = (uint8_t*)AscendC::GmAlloc(16 * 1024 * 1024);
182 uint8_t* tiling = (uint8_t*)AscendC::GmAlloc(tilingSize);
183 
184 float* gradScalePtr = reinterpret_cast<float*>(gradScaleBuf);
185 gradScalePtr[0] = 0.5f;
186 
187 FusedSgdTilingData* tilingData = reinterpret_cast<FusedSgdTilingData*>(tiling);
188 tilingData->weightDecay = 0.01f;
189 tilingData->momentum = 0.5f;
190 tilingData->lr = 0.001f;
191 tilingData->dampening = 0.0f;
192 tilingData->nesterov = 0;
193 tilingData->maximize = 0;
194 tilingData->isFirstStep = 1;
195 tilingData->useGradScale = 1;
196 tilingData->useMomentum = 1;
197 tilingData->tensorNum = 1;
198 tilingData->tensorsPerCore = 1;
199 tilingData->usedCoreNum = 1;
200 tilingData->coreCalcMax = 4088;
201 
202 AscendC::SetKernelMode(KernelMode::AIV_MODE);
203 ICPU_SET_TILING_KEY(0);
204 ICPU_RUN_KF(fused_sgd, blockDim,
205 paramsBuf, gradsBuf, momentumBuf,
206 gradScaleBuf,
207 paramsRefBuf, gradsRefBuf, momentumRefBuf,
208 workspace, (uint8_t*)(tiling));
209 
210 FreeNormTensorList<half>(paramsRefBuf, shapeInfos, "float16", "params_ref");
211 FreeNormTensorList<half>(gradsRefBuf, shapeInfos, "float16", "grads_ref");
212 FreeNormTensorList<half>(momentumRefBuf, shapeInfos, "float16", "momentum_ref");
213 FreeNormTensorList<half>(paramsBuf, shapeInfos, "float16", "params_ref");
214 FreeNormTensorList<half>(gradsBuf, shapeInfos, "float16", "grads_ref");
215 FreeNormTensorList<half>(momentumBuf, shapeInfos, "float16", "momentum_ref");
216 AscendC::GmFree(gradScaleBuf);
217 AscendC::GmFree(workspace);
218 AscendC::GmFree(tiling);
219 
220 system("cd ./sgd_data/ && python3 compare_data.py 'float16'");
221}
atomgit-bot
atomgit-botatomgit-bot6月18日

🟡 Medium Priority

test_fused_sgd.cpp 中,__CCE_UT_TEST__ 宏被定义(第11行),导致 fused_sgd.cpp 中的内核入口(第22-45行)始终编译为 FusedSgdF32<DTYPE_X> op(&pipe),即仅 F32 版本。

CMakeLists.txt(optim/fused_sgd/tests/ut/op_kernel/CMakeLists.txt 第12行)通过 -DDTYPE_X=float 编译了一个测试二进制。

test_fp16_basic 测试用例(第162-221行)使用 CreateNormTensorList<half> 创建 fp16 数据,并调用 ICPU_RUN_KF(fused_sgd, ...) 执行内核。由于内核编译为 FusedSgdF32<float>,它会将 fp16 数据按 float 解释,导致:

  1. 数据类型不匹配,计算结果完全错误
  2. 可能因越界读取导致崩溃

该测试用例预期验证 fp16 路径,但实际执行的是 F32 内核,测试结果无意义。

变更:第162-221行新增的 test_fp16_basic 测试,但内核仅编译为 F32。

建议:需要在 __CCE_UT_TEST__ 内核入口中根据 ORIG_DTYPE_PARAMS 进行类型分发(与 #else 分支第66-84行一致),或为 fp16 单独编译一个测试二进制(在 CMakeLists.txt 中添加 -DDTYPE_X=half 且设置合适的 ORIG_DTYPE_PARAMS)。

likedislike
majiajian_hw
6月22日 评论:
likedislike