已合并
[CANNbot] add apply_proximal_adagrad operator for ascend950 #4101
gxx_gitcode创建于 4月22日
[CANNbot] add apply_proximal_adagrad operator for ascend950 #4101
已合并
共 11 个文件变更+1426-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 | +# NOTE: Portions of this code were AI-generated and have been | ||
| 11 | +# technically reviewed for functional accuracy and security | ||
| 12 | + | ||
| 13 | +# 设置算子定义时支持的芯片类型 | ||
| 14 | +set(SUPPORT_COMPUTE_UNIT "ascend950") | ||
| 15 | +# 平铺后 op_host 下不再有 arch 子目录,tiling 文件直接位于 op_host/ 下 | ||
| 16 | +set(SUPPORT_TILING_DIR "${CMAKE_CURRENT_SOURCE_DIR}/op_host") | ||
| 17 | + | ||
| 18 | +add_modules_sources(HOSTNAME ${OPHOST_NAME} MODE PRIVATE DIR ${CMAKE_CURRENT_SOURCE_DIR} OPTYPE apply_proximal_adagrad ACLNNTYPE aclnn COMPUTE_UNIT ${SUPPORT_COMPUTE_UNIT} TILING_DIR ${SUPPORT_TILING_DIR} DISABLE_IN_OPP TRUE) | ||
| @@ -0,0 +1,139 @@ | |||
| 1 | +# ApplyProximalAdagrad | ||
| 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 | +- 算子功能:ApplyProximalAdagrad 是结合 Adagrad 自适应学习率与 FOBOS(Forward-Backward Splitting)Proximal 近端算法的优化器算子,功能对标 `tf.raw_ops.ApplyProximalAdagrad`。基于梯度平方累加器自适应调整学习率,并通过软阈值(L1 正则化)与缩放(L2 正则化)对模型参数进行原地更新。 | ||
| 17 | +- 计算公式: | ||
| 18 | + | ||
| 19 | + $$ | ||
| 20 | + \begin{aligned} | ||
| 21 | + \text{accum}_t &= \text{accum}_{t-1} + \text{grad}_t^2 \\ | ||
| 22 | + \eta_t &= \frac{\text{lr}}{\sqrt{\text{accum}_t}} \\ | ||
| 23 | + \text{prox}_t &= \text{var}_{t-1} - \eta_t \cdot \text{grad}_t \\ | ||
| 24 | + \text{var}_t &= \frac{\text{sign}(\text{prox}_t)}{1 + \eta_t \cdot \text{l2}} \cdot \max\!\left(|\text{prox}_t| - \eta_t \cdot \text{l1},\ 0\right) | ||
| 25 | + \end{aligned} | ||
| 26 | + $$ | ||
| 27 | + | ||
| 28 | + 当 L1 = 0 时简化为: | ||
| 29 | + | ||
| 30 | + $$ | ||
| 31 | + \text{var}_t = \frac{\text{prox}_t}{1 + \eta_t \cdot \text{l2}} | ||
| 32 | + $$ | ||
| 33 | + | ||
| 34 | +- 说明: | ||
| 35 | + - `var`(参数)与 `accum`(梯度平方累加器)均为 Ref Tensor,算子执行后**原地更新**。 | ||
| 36 | + - `lr`、`l1`、`l2` 为 0-D 标量 Tensor,分别要求 `lr > 0`、`l1 ≥ 0`、`l2 ≥ 0`。 | ||
| 37 | + - 逐元素独立计算,天然确定性,无跨元素/跨核依赖。 | ||
| 38 | + | ||
| 39 | +## 参数说明 | ||
| 40 | + | ||
| 41 | +<table style="table-layout: fixed; width: 1576px"><colgroup> | ||
| 42 | +<col style="width: 150px"> | ||
| 43 | +<col style="width: 150px"> | ||
| 44 | +<col style="width: 420px"> | ||
| 45 | +<col style="width: 140px"> | ||
| 46 | +<col style="width: 140px"> | ||
| 47 | +</colgroup> | ||
| 48 | +<thead> | ||
| 49 | + <tr> | ||
| 50 | + <th>参数名</th> | ||
| 51 | + <th>输入/输出/属性</th> | ||
| 52 | + <th>描述</th> | ||
| 53 | + <th>数据类型</th> | ||
| 54 | + <th>数据格式</th> | ||
| 55 | + </tr></thead> | ||
| 56 | +<tbody> | ||
| 57 | + <tr> | ||
| 58 | + <td>var</td> | ||
| 59 | + <td>输入</td> | ||
| 60 | + <td>公式中的 var,待更新的模型参数(Ref Tensor,原地更新)。shape 与 accum/grad 一致。</td> | ||
| 61 | + <td>FLOAT</td> | ||
| 62 | + <td>ND</td> | ||
| 63 | + </tr> | ||
| 64 | + <tr> | ||
| 65 | + <td>accum</td> | ||
| 66 | + <td>输入</td> | ||
| 67 | + <td>公式中的 accum,梯度平方累加器(Ref Tensor,原地更新)。shape 与 var/grad 一致,要求各元素非负。</td> | ||
| 68 | + <td>FLOAT</td> | ||
| 69 | + <td>ND</td> | ||
| 70 | + </tr> | ||
| 71 | + <tr> | ||
| 72 | + <td>lr</td> | ||
| 73 | + <td>输入</td> | ||
| 74 | + <td>公式中的 lr,学习率。0-D 或 1 元素 1-D Tensor,要求 lr > 0。</td> | ||
| 75 | + <td>FLOAT</td> | ||
| 76 | + <td>ND</td> | ||
| 77 | + </tr> | ||
| 78 | + <tr> | ||
| 79 | + <td>l1</td> | ||
| 80 | + <td>输入</td> | ||
| 81 | + <td>公式中的 l1,L1 正则化强度。0-D 或 1 元素 1-D Tensor,要求 l1 ≥ 0。</td> | ||
| 82 | + <td>FLOAT</td> | ||
| 83 | + <td>ND</td> | ||
| 84 | + </tr> | ||
| 85 | + <tr> | ||
| 86 | + <td>l2</td> | ||
| 87 | + <td>输入</td> | ||
| 88 | + <td>公式中的 l2,L2 正则化强度。0-D 或 1 元素 1-D Tensor,要求 l2 ≥ 0。</td> | ||
| 89 | + <td>FLOAT</td> | ||
| 90 | + <td>ND</td> | ||
| 91 | + </tr> | ||
| 92 | + <tr> | ||
| 93 | + <td>grad</td> | ||
| 94 | + <td>输入</td> | ||
| 95 | + <td>公式中的 grad,当前步的梯度张量。shape 与 var/accum 一致。</td> | ||
| 96 | + <td>FLOAT</td> | ||
| 97 | + <td>ND</td> | ||
| 98 | + </tr> | ||
| 99 | + <tr> | ||
| 100 | + <td>var_out</td> | ||
| 101 | + <td>输出</td> | ||
| 102 | + <td>更新后的参数,与输入 var 共享存储(inplace 更新)。</td> | ||
| 103 | + <td>FLOAT</td> | ||
| 104 | + <td>ND</td> | ||
| 105 | + </tr> | ||
| 106 | + <tr> | ||
| 107 | + <td>accum_out</td> | ||
| 108 | + <td>输出</td> | ||
| 109 | + <td>更新后的累加器,与输入 accum 共享存储(inplace 更新)。</td> | ||
| 110 | + <td>FLOAT</td> | ||
| 111 | + <td>ND</td> | ||
| 112 | + </tr> | ||
| 113 | +</tbody></table> | ||
| 114 | + | ||
| 115 | +## 约束说明 | ||
| 116 | + | ||
| 117 | +- 仅支持 <term>Ascend 950PR/Ascend 950DT</term>(arch35 / DAV_3510),不适配其他芯片代际。 | ||
| 118 | +- 仅支持 `float32` 数据类型。 | ||
| 119 | +- `var`、`accum`、`grad` 三者 shape 必须完全一致,且均为连续排布的 ND Tensor。 | ||
| 120 | +- `lr`、`l1`、`l2` 必须为 0-D 或 1 元素 1-D 的标量 Tensor。 | ||
| 121 | +- 调用方需保证 `accum ≥ 0`、`lr > 0`、`l1 ≥ 0`、`l2 ≥ 0`;算子内部不做运行时值域校验。 | ||
| 122 | +- `accum + grad^2 == 0` 时 `rsqrt` 输出 Inf/NaN,行为与 PyTorch / TensorFlow 原生实现一致,需由上游调用方规避。 | ||
| 123 | +- `var` 与 `accum` 为 Ref Tensor,Host aclnn 侧必须显式构造两个占位输出 Tensor(`var_out`、`accum_out`),并与各自输入共享 Device 地址以保证 inplace 语义。 | ||
| 124 | + | ||
| 125 | +## 调用说明 | ||
| 126 | + | ||
| 127 | +<table><thead> | ||
| 128 | + <tr> | ||
| 129 | + <th>调用方式</th> | ||
| 130 | + <th>调用样例</th> | ||
| 131 | + <th>说明</th> | ||
| 132 | + </tr></thead> | ||
| 133 | +<tbody> | ||
| 134 | + <tr> | ||
| 135 | + <td>aclnn 调用</td> | ||
| 136 | + <td><a href="./examples/arch35/test_aclnn_apply_proximal_adagrad.cpp">test_aclnn_apply_proximal_adagrad</a></td> | ||
| 137 | + <td>Ascend 950 上通过 aclnn 两段式接口 <code>aclnnApplyProximalAdagradGetWorkspaceSize</code> → <code>aclnnApplyProximalAdagrad</code> 调用。<code>var_out</code> / <code>accum_out</code> 需与 <code>var</code> / <code>accum</code> 共享 Device 地址以保证 inplace 更新。</td> | ||
| 138 | + </tr> | ||
| 139 | +</tbody></table> | ||
Aexperimental/optim/apply_proximal_adagrad/examples/arch35/test_aclnn_apply_proximal_adagrad.cpp+279-0
| @@ -0,0 +1,279 @@ | |||
| 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 | + * NOTE: Portions of this code were AI-generated and have been | ||
| 13 | + * technically reviewed for functional accuracy and security | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +/** | ||
| 17 | + * @file test_aclnn_apply_proximal_adagrad.cpp | ||
| 18 | + * @brief ApplyProximalAdagrad 算子 ACLNN 调用示例(两段式接口) | ||
| 19 | + */ | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + do { \ | ||
| 34 | + auto _ret = (expr); \ | ||
| 35 | + if (_ret != ACL_SUCCESS) { \ | ||
| 36 | + std::cerr << "ACL Error: " << | ||
| 37 | + << " at " << __FILE__ << ":" << __LINE__ << std::endl; \ | ||
| 38 | + goto cleanup; \ | ||
| 39 | + } \ | ||
| 40 | + } while (0) | ||
| 41 | + | ||
| 42 | +namespace { | ||
| 43 | + | ||
| 44 | +// --------------------------------------------------------------------------- | ||
| 45 | +// CPU Golden: 与算子语义完全一致 | ||
| 46 | +// accum' = accum + grad^2 | ||
| 47 | +// eta = lr / sqrt(accum') | ||
| 48 | +// prox = var - eta * grad | ||
| 49 | +// var' = (l1==0) ? prox / (1 + eta*l2) | ||
| 50 | +// : sign(prox) / (1 + eta*l2) * max(|prox| - eta*l1, 0) | ||
| 51 | +// --------------------------------------------------------------------------- | ||
| 52 | +inline void CpuGoldenStep(float& var, float& accum, float grad, float lr, float l1, float l2) | ||
| 53 | +{ | ||
| 54 | + accum = accum + grad * grad; | ||
| 55 | + float eta = lr / std::sqrt(accum); | ||
| 56 | + float prox = var - eta * grad; | ||
| 57 | + float denom = 1.0f + eta * l2; | ||
| 58 | + if (l1 == 0.0f) { | ||
| 59 | + var = prox / denom; | ||
| 60 | + } else { | ||
| 61 | + float sgn = (prox > 0.0f) ? 1.0f : ((prox < 0.0f) ? -1.0f : 0.0f); | ||
| 62 | + float mag = std::fabs(prox) - eta * l1; | ||
| 63 | + if (mag < 0.0f) mag = 0.0f; | ||
| 64 | + var = sgn / denom * mag; | ||
| 65 | + } | ||
| 66 | +} | ||
| 67 | + | ||
| 68 | +inline bool ApproxEq(float a, float b, float atol = 1e-4f, float rtol = 1e-4f) | ||
| 69 | +{ | ||
| 70 | + float diff = std::fabs(a - b); | ||
| 71 | + return diff <= atol + rtol * std::fabs(b); | ||
| 72 | +} | ||
| 73 | + | ||
| 74 | +} // namespace | ||
| 75 | + | ||
| 76 | +int main() | ||
| 77 | +{ | ||
| 78 | + // ========================================================================= | ||
| 79 | + // 1. 参数设置:shape=[16]、dtype=float32 | ||
| 80 | + // ========================================================================= | ||
| 81 | + constexpr int64_t ELEM_COUNT = 16; | ||
| 82 | + const int64_t shape[] = {ELEM_COUNT}; | ||
| 83 | + const int64_t strides[] = {1}; | ||
| 84 | + constexpr int64_t ndim = 1; | ||
| 85 | + | ||
| 86 | + // 标量 (lr, l1, l2) 用 numel=1 的 1-D Tensor 表示 | ||
| 87 | + const int64_t scalarShape[] = {1}; | ||
| 88 | + const int64_t scalarStrides[] = {1}; | ||
| 89 | + | ||
| 90 | + float hostVar[ELEM_COUNT] = { | ||
| 91 | + // 覆盖 +/- / 0 / 较大值 | ||
| 92 | + 1.00f, -1.00f, 0.50f, -0.50f, | ||
| 93 | + 0.10f, -0.10f, 0.00f, 2.00f, | ||
| 94 | + -2.00f, 0.05f, -0.05f, 3.50f, | ||
| 95 | + -3.50f, 0.01f, -0.01f, 0.25f, | ||
| 96 | + }; | ||
| 97 | + float hostAccum[ELEM_COUNT] = { | ||
| 98 | + // 必须非负(调用方保证)。覆盖小值 / 中等值。 | ||
| 99 | + 0.10f, 0.10f, 0.20f, 0.20f, | ||
| 100 | + 0.05f, 0.05f, 0.10f, 1.00f, | ||
| 101 | + 1.00f, 0.01f, 0.01f, 0.50f, | ||
| 102 | + 0.50f, 0.30f, 0.30f, 0.40f, | ||
| 103 | + }; | ||
| 104 | + float hostGrad[ELEM_COUNT] = { | ||
| 105 | + 0.10f, 0.10f, -0.20f, 0.20f, | ||
| 106 | + 0.30f, -0.30f, 0.10f, -0.50f, | ||
| 107 | + 0.50f, 0.05f, -0.05f, 1.00f, | ||
| 108 | + -1.00f, 0.40f, -0.40f, 0.15f, | ||
| 109 | + }; | ||
| 110 | + float hostLr[1] = {0.1f}; | ||
| 111 | + float hostL1[1] = {0.01f}; // l1 > 0 → 走 HAS_L1=1 分支(含 abs/max 阈值收缩) | ||
| 112 | + float hostL2[1] = {0.0f}; | ||
| 113 | + | ||
| 114 | + // CPU Golden(独立副本,避免污染输入) | ||
| 115 | + float goldVar[ELEM_COUNT]; | ||
| 116 | + float goldAccum[ELEM_COUNT]; | ||
| 117 | + std::memcpy(goldVar, hostVar, sizeof(hostVar)); | ||
| 118 | + std::memcpy(goldAccum, hostAccum, sizeof(hostAccum)); | ||
| 119 | + for (int i = 0; i < ELEM_COUNT; ++i) { | ||
| 120 | + CpuGoldenStep(goldVar[i], goldAccum[i], hostGrad[i], hostLr[0], hostL1[0], hostL2[0]); | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + // ========================================================================= | ||
| 124 | + // 2. ACL 初始化 | ||
| 125 | + // ========================================================================= | ||
| 126 | + int32_t ret = 1; | ||
| 127 | + aclrtStream stream = nullptr; | ||
| 128 | + void *devVar = nullptr, *devAccum = nullptr; | ||
| 129 | + void *devLr = nullptr, *devL1 = nullptr, *devL2 = nullptr; | ||
| 130 | + void *devGrad = nullptr; | ||
| 131 | + void *workspace = nullptr; | ||
| 132 | + aclTensor *tVar = nullptr, *tAccum = nullptr; | ||
| 133 | + aclTensor *tLr = nullptr, *tL1 = nullptr, *tL2 = nullptr; | ||
| 134 | + aclTensor *tGrad = nullptr; | ||
| 135 | + aclTensor *tVarOut = nullptr, *tAccumOut = nullptr; | ||
| 136 | + | ||
| 137 | + CHECK_ACL(aclInit(nullptr)); | ||
| 138 | + CHECK_ACL(aclrtSetDevice(0)); | ||
| 139 | + CHECK_ACL(aclrtCreateStream(&stream)); | ||
| 140 | + | ||
| 141 | + // ========================================================================= | ||
| 142 | + // 3. 设备内存分配 & 输入数据拷贝 Host→Device | ||
| 143 | + // ========================================================================= | ||
| 144 | + { | ||
| 145 | + const size_t vecBytes = ELEM_COUNT * sizeof(float); | ||
| 146 | + const size_t scalarBytes = 1 * sizeof(float); | ||
| 147 | + | ||
| 148 | + CHECK_ACL(aclrtMalloc(&devVar, vecBytes, ACL_MEM_MALLOC_HUGE_FIRST)); | ||
| 149 | + CHECK_ACL(aclrtMalloc(&devAccum, vecBytes, ACL_MEM_MALLOC_HUGE_FIRST)); | ||
| 150 | + CHECK_ACL(aclrtMalloc(&devGrad, vecBytes, ACL_MEM_MALLOC_HUGE_FIRST)); | ||
| 151 | + CHECK_ACL(aclrtMalloc(&devLr, scalarBytes, ACL_MEM_MALLOC_HUGE_FIRST)); | ||
| 152 | + CHECK_ACL(aclrtMalloc(&devL1, scalarBytes, ACL_MEM_MALLOC_HUGE_FIRST)); | ||
| 153 | + CHECK_ACL(aclrtMalloc(&devL2, scalarBytes, ACL_MEM_MALLOC_HUGE_FIRST)); | ||
| 154 | + | ||
| 155 | + CHECK_ACL(aclrtMemcpy(devVar, vecBytes, hostVar, vecBytes, ACL_MEMCPY_HOST_TO_DEVICE)); | ||
| 156 | + CHECK_ACL(aclrtMemcpy(devAccum, vecBytes, hostAccum, vecBytes, ACL_MEMCPY_HOST_TO_DEVICE)); | ||
| 157 | + CHECK_ACL(aclrtMemcpy(devGrad, vecBytes, hostGrad, vecBytes, ACL_MEMCPY_HOST_TO_DEVICE)); | ||
| 158 | + CHECK_ACL(aclrtMemcpy(devLr, scalarBytes, hostLr, scalarBytes, ACL_MEMCPY_HOST_TO_DEVICE)); | ||
| 159 | + CHECK_ACL(aclrtMemcpy(devL1, scalarBytes, hostL1, scalarBytes, ACL_MEMCPY_HOST_TO_DEVICE)); | ||
| 160 | + CHECK_ACL(aclrtMemcpy(devL2, scalarBytes, hostL2, scalarBytes, ACL_MEMCPY_HOST_TO_DEVICE)); | ||
| 161 | + | ||
| 162 | + // ===================================================================== | ||
| 163 | + // 4. 创建 aclTensor | ||
| 164 | + // var/accum/grad: FLOAT, ND, [16] | ||
| 165 | + // lr/l1/l2: FLOAT, ND, [1] (numel=1 1-D 标量) | ||
| 166 | + // varOutOut / accumOutOut: 绑定与 var / accum 同一 Device 存储 → 观察 inplace | ||
| 167 | + // ===================================================================== | ||
| 168 | + tVar = aclCreateTensor(shape, ndim, ACL_FLOAT, strides, 0, | ||
| 169 | + ACL_FORMAT_ND, shape, ndim, devVar); | ||
| 170 | + tAccum = aclCreateTensor(shape, ndim, ACL_FLOAT, strides, 0, | ||
| 171 | + ACL_FORMAT_ND, shape, ndim, devAccum); | ||
| 172 | + tGrad = aclCreateTensor(shape, ndim, ACL_FLOAT, strides, 0, | ||
| 173 | + ACL_FORMAT_ND, shape, ndim, devGrad); | ||
| 174 | + tLr = aclCreateTensor(scalarShape, 1, ACL_FLOAT, scalarStrides, 0, | ||
| 175 | + ACL_FORMAT_ND, scalarShape, 1, devLr); | ||
| 176 | + tL1 = aclCreateTensor(scalarShape, 1, ACL_FLOAT, scalarStrides, 0, | ||
| 177 | + ACL_FORMAT_ND, scalarShape, 1, devL1); | ||
| 178 | + tL2 = aclCreateTensor(scalarShape, 1, ACL_FLOAT, scalarStrides, 0, | ||
| 179 | + ACL_FORMAT_ND, scalarShape, 1, devL2); | ||
| 180 | + // 占位输出复用同一 Device buffer,调用结束后通过 devVar / devAccum 直接读取 | ||
| 181 | + tVarOut = aclCreateTensor(shape, ndim, ACL_FLOAT, strides, 0, | ||
| 182 | + ACL_FORMAT_ND, shape, ndim, devVar); | ||
| 183 | + tAccumOut = aclCreateTensor(shape, ndim, ACL_FLOAT, strides, 0, | ||
| 184 | + ACL_FORMAT_ND, shape, ndim, devAccum); | ||
| 185 | + | ||
| 186 | + if (!tVar || !tAccum || !tLr || !tL1 || !tL2 || !tGrad || !tVarOut || !tAccumOut) { | ||
| 187 | + std::cerr << "aclCreateTensor failed" << std::endl; | ||
| 188 | + goto cleanup; | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | + // ===================================================================== | ||
| 192 | + // 5. 调用 aclnnApplyProximalAdagrad(两段式接口) | ||
| 193 | + // ===================================================================== | ||
| 194 | + uint64_t workspaceSize = 0; | ||
| 195 | + aclOpExecutor *executor = nullptr; | ||
| 196 | + | ||
| 197 | + CHECK_ACL(aclnnApplyProximalAdagradGetWorkspaceSize( | ||
| 198 | + tVar, tAccum, tLr, tL1, tL2, tGrad, tVarOut, tAccumOut, | ||
| 199 | + &workspaceSize, &executor)); | ||
| 200 | + | ||
| 201 | + if (workspaceSize > 0) { | ||
| 202 | + CHECK_ACL(aclrtMalloc(&workspace, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST)); | ||
| 203 | + } | ||
| 204 | + | ||
| 205 | + CHECK_ACL(aclnnApplyProximalAdagrad(workspace, workspaceSize, executor, stream)); | ||
| 206 | + CHECK_ACL(aclrtSynchronizeStream(stream)); | ||
| 207 | + | ||
| 208 | + // ===================================================================== | ||
| 209 | + // 6. 读取 inplace 结果(var / accum 已被更新)& 精度验证 | ||
| 210 | + // ===================================================================== | ||
| 211 | + float npuVar[ELEM_COUNT] = {}; | ||
| 212 | + float npuAccum[ELEM_COUNT] = {}; | ||
| 213 | + const size_t vecBytesOut = ELEM_COUNT * sizeof(float); | ||
| 214 | + CHECK_ACL(aclrtMemcpy(npuVar, vecBytesOut, devVar, vecBytesOut, ACL_MEMCPY_DEVICE_TO_HOST)); | ||
| 215 | + CHECK_ACL(aclrtMemcpy(npuAccum, vecBytesOut, devAccum, vecBytesOut, ACL_MEMCPY_DEVICE_TO_HOST)); | ||
| 216 | + | ||
| 217 | + std::cout << "ApplyProximalAdagrad Example (shape: [16], dtype: float32)" << std::endl; | ||
| 218 | + std::cout << " scalars: lr=" << hostLr[0] | ||
| 219 | + << " l1=" << hostL1[0] | ||
| 220 | + << " l2=" << hostL2[0] << std::endl; | ||
| 221 | + std::cout << " formula: accum' = accum + grad^2" << std::endl; | ||
| 222 | + std::cout << " eta = lr / sqrt(accum')" << std::endl; | ||
| 223 | + std::cout << " prox = var - eta * grad" << std::endl; | ||
| 224 | + std::cout << " var' = sign(prox) / (1 + eta*l2) * max(|prox| - eta*l1, 0)" << std::endl; | ||
| 225 | + std::cout << "-------------------------------------------------------------------------------" << std::endl; | ||
| 226 | + std::printf(" %4s | %10s | %10s | %10s | %10s | %10s | %s\n", | ||
| 227 | + "Idx", "var", "accum", "grad", "var_npu", "var_gold", "Status"); | ||
| 228 | + std::cout << "-------------------------------------------------------------------------------" << std::endl; | ||
| 229 | + | ||
| 230 | + int passVar = 0, passAccum = 0; | ||
| 231 | + for (int i = 0; i < ELEM_COUNT; ++i) { | ||
| 232 | + bool okV = ApproxEq(npuVar[i], goldVar[i]); | ||
| 233 | + bool okA = ApproxEq(npuAccum[i], goldAccum[i]); | ||
| 234 | + if (okV) ++passVar; | ||
| 235 | + if (okA) ++passAccum; | ||
| 236 | + std::printf(" %4d | %10.5f | %10.5f | %10.5f | %10.5f | %10.5f | %s/%s\n", | ||
| 237 | + i, hostVar[i], hostAccum[i], hostGrad[i], | ||
| 238 | + npuVar[i], goldVar[i], | ||
| 239 | + okV ? "PASS" : "FAIL", | ||
| 240 | + okA ? "PASS" : "FAIL"); | ||
| 241 | + } | ||
| 242 | + | ||
| 243 | + std::cout << "-------------------------------------------------------------------------------" << std::endl; | ||
| 244 | + std::cout << "Result (var): " << passVar << "/" << ELEM_COUNT << " passed" << std::endl; | ||
| 245 | + std::cout << "Result (accum): " << passAccum << "/" << ELEM_COUNT << " passed" << std::endl; | ||
| 246 | + if (passVar == ELEM_COUNT && passAccum == ELEM_COUNT) { | ||
| 247 | + std::cout << "ALL PASS" << std::endl; | ||
| 248 | + ret = 0; | ||
| 249 | + } else { | ||
| 250 | + std::cout << "FAILED" << std::endl; | ||
| 251 | + ret = 1; | ||
| 252 | + } | ||
| 253 | + } | ||
| 254 | + | ||
| 255 | + // ========================================================================= | ||
| 256 | + // 7. 资源释放 | ||
| 257 | + // ========================================================================= | ||
| 258 | +cleanup: | ||
| 259 | + if (tVar) aclDestroyTensor(tVar); | ||
| 260 | + if (tAccum) aclDestroyTensor(tAccum); | ||
| 261 | + if (tLr) aclDestroyTensor(tLr); | ||
| 262 | + if (tL1) aclDestroyTensor(tL1); | ||
| 263 | + if (tL2) aclDestroyTensor(tL2); | ||
| 264 | + if (tGrad) aclDestroyTensor(tGrad); | ||
| 265 | + if (tVarOut) aclDestroyTensor(tVarOut); | ||
| 266 | + if (tAccumOut) aclDestroyTensor(tAccumOut); | ||
| 267 | + if (workspace) aclrtFree(workspace); | ||
| 268 | + if (devVar) aclrtFree(devVar); | ||
| 269 | + if (devAccum) aclrtFree(devAccum); | ||
| 270 | + if (devGrad) aclrtFree(devGrad); | ||
| 271 | + if (devLr) aclrtFree(devLr); | ||
| 272 | + if (devL1) aclrtFree(devL1); | ||
| 273 | + if (devL2) aclrtFree(devL2); | ||
| 274 | + if (stream) aclrtDestroyStream(stream); | ||
| 275 | + aclrtResetDevice(0); | ||
| 276 | + aclFinalize(); | ||
| 277 | + | ||
| 278 | + return ret; | ||
| 279 | +} | ||
| @@ -0,0 +1,105 @@ | |||
| 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 | + * NOTE: Portions of this code were AI-generated and have been | ||
| 13 | + * technically reviewed for functional accuracy and security | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +/*! | ||
| 17 | + * \file apply_proximal_adagrad_def.cpp | ||
| 18 | + * \brief ApplyProximalAdagrad operator definition | ||
| 19 | + * | ||
| 20 | + * Optimizer-style operator: 6 inputs, 2 inplace outputs. | ||
| 21 | + * | ||
| 22 | + * Inputs (in order): | ||
| 23 | + * var (Tensor, float32, ND, shape == accum == grad) -- inplace updated | ||
| 24 | + * accum (Tensor, float32, ND, shape == var == grad) -- inplace updated | ||
| 25 | + * lr (Tensor, float32, ND, 0-D or 1-element 1-D) -- scalar | ||
| 26 | + * l1 (Tensor, float32, ND, 0-D or 1-element 1-D) -- scalar | ||
| 27 | + * l2 (Tensor, float32, ND, 0-D or 1-element 1-D) -- scalar | ||
| 28 | + * grad (Tensor, float32, ND, shape == var == accum) | ||
| 29 | + * | ||
| 30 | + * Outputs: | ||
| 31 | + * var_out (Tensor, float32, ND) -- shares storage with input var | ||
| 32 | + * accum_out (Tensor, float32, ND) -- shares storage with input accum | ||
| 33 | + * | ||
| 34 | + * Target: Ascend950 (arch35 / DAV_3510) only. | ||
| 35 | + */ | ||
| 36 | + | ||
| 37 | + | ||
| 38 | +namespace ops { | ||
| 39 | +class ApplyProximalAdagrad : public OpDef { | ||
| 40 | +public: | ||
| 41 | + explicit ApplyProximalAdagrad(const char* name) : OpDef(name) | ||
| 42 | + { | ||
| 43 | + // --- Inputs (6) --------------------------------------------------- | ||
| 44 | + this->Input("var") | ||
| 45 | + .ParamType(REQUIRED) | ||
| 46 | + .DataType({ge::DT_FLOAT}) | ||
| 47 | + .Format({ge::FORMAT_ND}) | ||
| 48 | + .UnknownShapeFormat({ge::FORMAT_ND}) | ||
| 49 | + .AutoContiguous(); | ||
| 50 | + this->Input("accum") | ||
| 51 | + .ParamType(REQUIRED) | ||
| 52 | + .DataType({ge::DT_FLOAT}) | ||
| 53 | + .Format({ge::FORMAT_ND}) | ||
| 54 | + .UnknownShapeFormat({ge::FORMAT_ND}) | ||
| 55 | + .AutoContiguous(); | ||
| 56 | + this->Input("lr") | ||
| 57 | + .ParamType(REQUIRED) | ||
| 58 | + .DataType({ge::DT_FLOAT}) | ||
| 59 | + .Format({ge::FORMAT_ND}) | ||
| 60 | + .UnknownShapeFormat({ge::FORMAT_ND}); | ||
| 61 | + this->Input("l1") | ||
| 62 | + .ParamType(REQUIRED) | ||
| 63 | + .DataType({ge::DT_FLOAT}) | ||
| 64 | + .Format({ge::FORMAT_ND}) | ||
| 65 | + .UnknownShapeFormat({ge::FORMAT_ND}); | ||
| 66 | + this->Input("l2") | ||
| 67 | + .ParamType(REQUIRED) | ||
| 68 | + .DataType({ge::DT_FLOAT}) | ||
| 69 | + .Format({ge::FORMAT_ND}) | ||
| 70 | + .UnknownShapeFormat({ge::FORMAT_ND}); | ||
| 71 | + this->Input("grad") | ||
| 72 | + .ParamType(REQUIRED) | ||
| 73 | + .DataType({ge::DT_FLOAT}) | ||
| 74 | + .Format({ge::FORMAT_ND}) | ||
| 75 | + .UnknownShapeFormat({ge::FORMAT_ND}) | ||
| 76 | + .AutoContiguous(); | ||
| 77 | + | ||
| 78 | + // --- Outputs (2, inplace via shared storage at L2 layer) ---------- | ||
| 79 | + this->Output("var_out") | ||
| 80 | + .ParamType(REQUIRED) | ||
| 81 | + .DataType({ge::DT_FLOAT}) | ||
| 82 | + .Format({ge::FORMAT_ND}) | ||
| 83 | + .UnknownShapeFormat({ge::FORMAT_ND}) | ||
| 84 | + .AutoContiguous(); | ||
| 85 | + this->Output("accum_out") | ||
| 86 | + .ParamType(REQUIRED) | ||
| 87 | + .DataType({ge::DT_FLOAT}) | ||
| 88 | + .Format({ge::FORMAT_ND}) | ||
| 89 | + .UnknownShapeFormat({ge::FORMAT_ND}) | ||
| 90 | + .AutoContiguous(); | ||
| 91 | + | ||
| 92 | + // --- Ascend950 (arch35) AI Core configuration --------------------- | ||
| 93 | + OpAICoreConfig aiCoreConfig; | ||
| 94 | + aiCoreConfig.DynamicCompileStaticFlag(true) | ||
| 95 | + .DynamicFormatFlag(false) | ||
| 96 | + .DynamicRankSupportFlag(true) | ||
| 97 | + .DynamicShapeSupportFlag(true) | ||
| 98 | + .NeedCheckSupportFlag(false) | ||
| 99 | + .PrecisionReduceFlag(false) | ||
| 100 | + .ExtendCfgInfo("opFile.value", "apply_proximal_adagrad"); | ||
| 101 | + this->AICore().AddConfig("ascend950", aiCoreConfig); | ||
| 102 | + } | ||
| 103 | +}; | ||
| 104 | +OP_ADD(ApplyProximalAdagrad); | ||
| 105 | +} // namespace ops | ||
| @@ -0,0 +1,72 @@ | |||
| 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 | + * NOTE: Portions of this code were AI-generated and have been | ||
| 13 | + * technically reviewed for functional accuracy and security | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +/*! | ||
| 17 | + * \file apply_proximal_adagrad_infershape.cpp | ||
| 18 | + * \brief ApplyProximalAdagrad shape / dtype inference. | ||
| 19 | + * | ||
| 20 | + * - var_out.shape = var.shape | ||
| 21 | + * - accum_out.shape = accum.shape | ||
| 22 | + * - both outputs are float32 | ||
| 23 | + */ | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | +using namespace ge; | ||
| 30 | + | ||
| 31 | +namespace ops { | ||
| 32 | + | ||
| 33 | +static ge::graphStatus InferShape4ApplyProximalAdagrad(gert::InferShapeContext* context) | ||
| 34 | +{ | ||
| 35 | + // Input 0 = var, output 0 = var_out (inplace) | ||
| 36 | + const gert::Shape* varShape = context->GetInputShape(0); | ||
| 37 | + if (varShape == nullptr) { | ||
| 38 | + return ge::GRAPH_FAILED; | ||
| 39 | + } | ||
| 40 | + // Input 1 = accum, output 1 = accum_out (inplace) | ||
| 41 | + const gert::Shape* accumShape = context->GetInputShape(1); | ||
| 42 | + if (accumShape == nullptr) { | ||
| 43 | + return ge::GRAPH_FAILED; | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + gert::Shape* varOutShape = context->GetOutputShape(0); | ||
| 47 | + if (varOutShape == nullptr) { | ||
| 48 | + return ge::GRAPH_FAILED; | ||
| 49 | + } | ||
| 50 | + gert::Shape* accumOutShape = context->GetOutputShape(1); | ||
| 51 | + if (accumOutShape == nullptr) { | ||
| 52 | + return ge::GRAPH_FAILED; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + *varOutShape = *varShape; | ||
| 56 | + *accumOutShape = *accumShape; | ||
| 57 | + return ge::GRAPH_SUCCESS; | ||
| 58 | +} | ||
| 59 | + | ||
| 60 | +static ge::graphStatus InferDataType4ApplyProximalAdagrad(gert::InferDataTypeContext* context) | ||
| 61 | +{ | ||
| 62 | + // Outputs dtype follow var/accum (both must be float32 per spec). | ||
| 63 | + context->SetOutputDataType(0, context->GetInputDataType(0)); | ||
| 64 | + context->SetOutputDataType(1, context->GetInputDataType(1)); | ||
| 65 | + return ge::GRAPH_SUCCESS; | ||
| 66 | +} | ||
| 67 | + | ||
| 68 | +IMPL_OP_INFERSHAPE(ApplyProximalAdagrad) | ||
| 69 | + .InferShape(InferShape4ApplyProximalAdagrad) | ||
| 70 | + .InferDataType(InferDataType4ApplyProximalAdagrad); | ||
| 71 | + | ||
| 72 | +} // namespace ops | ||
| @@ -0,0 +1,231 @@ | |||
| 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 | + * NOTE: Portions of this code were AI-generated and have been | ||
| 13 | + * technically reviewed for functional accuracy and security | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +/*! | ||
| 17 | + * \file apply_proximal_adagrad_tiling.cpp | ||
| 18 | + * \brief ApplyProximalAdagrad tiling (arch35). | ||
| 19 | + * | ||
| 20 | + * Tiling strategy (iteration-1 skeleton, single TilingKey path): | ||
| 21 | + * 1. Multi-core: split total elements evenly across AI vector cores. | ||
| 22 | + * blockFactor = CeilAlign(CeilDiv(total, coreNum), ubBlockSize) | ||
| 23 | + * so neighbouring cores do not trample each other's GM output. | ||
| 24 | + * 2. UB: allocate the per-tile size (ubFactor) based on an 8-buffer layout | ||
| 25 | + * that covers var / accum / grad IN queues (x 2 double-buffered), | ||
| 26 | + * var / accum OUT queues (x 2 double-buffered), and a few tmp scratch | ||
| 27 | + * buffers (eta / prox / thresh / scale / sign) kept in float32. | ||
| 28 | + * Total co-resident float32 tensors per tile ~= 3*2 (in) + 2*2 (out) + | ||
| 29 | + * 4 (tmp) = 14 buffers. ubFactor = floor(UB / (14 * 4B)) aligned down | ||
| 30 | + * to 32-byte granularity; an absolute ceiling of 2048 elements keeps | ||
| 31 | + * the first iteration deterministic (see DESIGN s5.2, TILE_ELEM_NUM). | ||
| 32 | + */ | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + | ||
| 38 | + | ||
| 39 | + | ||
| 40 | + | ||
| 41 | +namespace optiling { | ||
| 42 | + | ||
| 43 | +using Ops::Base::CeilDiv; | ||
| 44 | +using Ops::Base::CeilAlign; | ||
| 45 | +using Ops::Base::FloorAlign; | ||
| 46 | +using Ops::Base::FloorDiv; | ||
| 47 | +using Ops::Base::GetUbBlockSize; | ||
| 48 | + | ||
| 49 | +constexpr uint32_t WS_SYS_SIZE = 0U; | ||
| 50 | +constexpr int64_t TYPE_SIZE = 4; // sizeof(float) -- fp32 only. | ||
| 51 | +// Per-tile target element count (float32 * 2048 = 8KB). | ||
| 52 | +// Iteration-1 uses this conservative tile; later iterations may make it | ||
| 53 | +// platform-derived. | ||
| 54 | +constexpr int64_t TILE_ELEM_NUM_TARGET = 2048; | ||
| 55 | +// Co-resident fp32 UB tensors per tile: var/accum/grad IN (double buffer) + | ||
| 56 | +// var/accum OUT (double buffer) + 4 tmp (eta / prox / thresh / scale). | ||
| 57 | +// SUG-002: This MUST stay in sync with the kernel-side constant | ||
| 58 | +// `kUbResidentFp32TensorCount` defined at the top of | ||
| 59 | +// `op_kernel/apply_proximal_adagrad.h`. Any change to the kernel's UB | ||
| 60 | +// buffer layout (adding/removing TBuf or TQue, changing double-buffer depth) | ||
| 61 | +// MUST update both constants together; see the kernel-side static_assert and | ||
| 62 | +// breakdown comment. | ||
| 63 | +constexpr int64_t UB_BUFFER_COUNT = 14; | ||
| 64 | + | ||
| 65 | +static const gert::Shape g_vec_1_shape = {1}; | ||
| 66 | + | ||
| 67 | +static inline const gert::Shape EnsureNotScalar(const gert::Shape& in_shape) | ||
| 68 | +{ | ||
| 69 | + if (in_shape.GetDimNum() == 0) { | ||
| 70 | + return g_vec_1_shape; | ||
| 71 | + } | ||
| 72 | + return in_shape; | ||
| 73 | +} | ||
| 74 | + | ||
| 75 | +static ge::graphStatus GetPlatformInfo(gert::TilingContext* context, | ||
| 76 | + uint64_t& ubSize, int64_t& coreNum) | ||
| 77 | +{ | ||
| 78 | + fe::PlatFormInfos* platformInfoPtr = context->GetPlatformInfo(); | ||
| 79 | + OP_CHECK_NULL_WITH_CONTEXT(context, platformInfoPtr); | ||
| 80 | + auto ascendcPlatform = platform_ascendc::PlatformAscendC(platformInfoPtr); | ||
| 81 | + coreNum = ascendcPlatform.GetCoreNumAiv(); | ||
| 82 | + OP_CHECK_IF(coreNum == 0, OP_LOGE(context, "coreNum is 0"), | ||
| 83 | + return ge::GRAPH_FAILED); | ||
| 84 | + ascendcPlatform.GetCoreMemSize(platform_ascendc::CoreMemType::UB, ubSize); | ||
| 85 | + OP_CHECK_IF(ubSize == 0, OP_LOGE(context, "ubSize is 0"), | ||
| 86 | + return ge::GRAPH_FAILED); | ||
| 87 | + return ge::GRAPH_SUCCESS; | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +static ge::graphStatus GetShapeInfo(gert::TilingContext* context, | ||
| 91 | + int64_t& totalElements, | ||
| 92 | + ge::DataType& dataType) | ||
| 93 | +{ | ||
| 94 | + // Input 0 = var; its shape is canonical (var/accum/grad must match). | ||
| 95 | + auto inputVar = context->GetInputShape(0); | ||
| 96 | + OP_CHECK_NULL_WITH_CONTEXT(context, inputVar); | ||
| 97 | + auto varShape = EnsureNotScalar(inputVar->GetStorageShape()); | ||
| 98 | + totalElements = varShape.GetShapeSize(); | ||
| 99 | + | ||
| 100 | + auto inputDesc = context->GetInputDesc(0); | ||
| 101 | + OP_CHECK_NULL_WITH_CONTEXT(context, inputDesc); | ||
| 102 | + dataType = inputDesc->GetDataType(); | ||
| 103 | + OP_CHECK_IF(dataType != ge::DT_FLOAT, | ||
| 104 | + OP_LOGE(context, | ||
| 105 | + "ApplyProximalAdagrad: only float32 is supported, got %d", | ||
| 106 | + static_cast<int>(dataType)), | ||
| 107 | + return ge::GRAPH_FAILED); | ||
| 108 | + return ge::GRAPH_SUCCESS; | ||
| 109 | +} | ||
| 110 | + | ||
| 111 | +static ge::graphStatus GetWorkspaceSize(gert::TilingContext* context) | ||
| 112 | +{ | ||
| 113 | + size_t* currentWorkspace = context->GetWorkspaceSizes(1); | ||
| 114 | + OP_CHECK_NULL_WITH_CONTEXT(context, currentWorkspace); | ||
| 115 | + currentWorkspace[0] = WS_SYS_SIZE; | ||
| 116 | + return ge::GRAPH_SUCCESS; | ||
| 117 | +} | ||
| 118 | + | ||
| 119 | +static ge::graphStatus ApplyProximalAdagradTilingFunc(gert::TilingContext* context) | ||
| 120 | +{ | ||
| 121 | + // 1. Platform info | ||
| 122 | + uint64_t ubSize = 0; | ||
| 123 | + int64_t coreNum = 0; | ||
| 124 | + OP_CHECK_IF(GetPlatformInfo(context, ubSize, coreNum) != ge::GRAPH_SUCCESS, | ||
| 125 | + OP_LOGE(context, "GetPlatformInfo error"), | ||
| 126 | + return ge::GRAPH_FAILED); | ||
| 127 | + | ||
| 128 | + // 2. Shape / dtype info | ||
| 129 | + int64_t totalElements = 0; | ||
| 130 | + ge::DataType dataType = ge::DT_FLOAT; | ||
| 131 | + OP_CHECK_IF(GetShapeInfo(context, totalElements, dataType) != ge::GRAPH_SUCCESS, | ||
| 132 | + OP_LOGE(context, "GetShapeInfo error"), | ||
| 133 | + return ge::GRAPH_FAILED); | ||
| 134 | + | ||
| 135 | + // 3. Workspace | ||
| 136 | + OP_CHECK_IF(GetWorkspaceSize(context) != ge::GRAPH_SUCCESS, | ||
| 137 | + OP_LOGE(context, "GetWorkspaceSize error"), | ||
| 138 | + return ge::GRAPH_FAILED); | ||
| 139 | + | ||
| 140 | + // 4. Fill TilingData | ||
| 141 | + ApplyProximalAdagradTilingData* tiling = | ||
| 142 | + context->GetTilingData<ApplyProximalAdagradTilingData>(); | ||
| 143 | + OP_CHECK_NULL_WITH_CONTEXT(context, tiling); | ||
| 144 | + OP_CHECK_IF( | ||
| 145 | + memset_s(tiling, sizeof(ApplyProximalAdagradTilingData), 0, | ||
| 146 | + sizeof(ApplyProximalAdagradTilingData)) != EOK, | ||
| 147 | + OP_LOGE(context, "set tiling data error"), | ||
| 148 | + return ge::GRAPH_FAILED); | ||
| 149 | + | ||
| 150 | + tiling->totalElements = totalElements; | ||
| 151 | + | ||
| 152 | + // Empty tensor: run a single idle core so the launcher still succeeds. | ||
| 153 | + if (totalElements == 0) { | ||
| 154 | + tiling->blockFactor = 0; | ||
| 155 | + tiling->ubFactor = 0; | ||
| 156 | + context->SetBlockDim(1); | ||
| 157 | + uint32_t dTypeVar = static_cast<uint32_t>(dataType); | ||
| 158 | + // Pick the simplest binary (PAD_TAIL=0, HAS_L1=1) for the empty path - | ||
| 159 | + // Process() short-circuits before doing any compute. | ||
| 160 | + uint32_t padTail = 0U; | ||
| 161 | + uint32_t hasL1 = 1U; | ||
| 162 | + ASCENDC_TPL_SEL_PARAM(context, dTypeVar, padTail, hasL1); | ||
| 163 | + return ge::GRAPH_SUCCESS; | ||
| 164 | + } | ||
| 165 | + | ||
| 166 | + // ubBlockSize = 32B / sizeof(T), with T = fp32 -> 8 elements. | ||
| 167 | + int64_t ubBlockSize = Ops::Base::GetUbBlockSize(context); | ||
| 168 | + OP_CHECK_IF(ubBlockSize <= 0, | ||
| 169 | + OP_LOGE(context, "invalid ubBlockSize=%ld", ubBlockSize), | ||
| 170 | + return ge::GRAPH_FAILED); | ||
| 171 | + | ||
| 172 | + // Multi-core split: ceil-aligned to DMA granularity. | ||
| 173 | + int64_t blockFactor = CeilAlign(CeilDiv(totalElements, coreNum), ubBlockSize); | ||
| 174 | + int64_t usedCoreNum = CeilDiv(totalElements, blockFactor); | ||
| 175 | + | ||
| 176 | + // UB split. Cap by platform UB, target TILE_ELEM_NUM_TARGET (2048). | ||
| 177 | + int64_t ubCapacityElem = | ||
| 178 | + FloorAlign(FloorDiv(static_cast<int64_t>(ubSize) / TYPE_SIZE, | ||
| 179 | + UB_BUFFER_COUNT), | ||
| 180 | + ubBlockSize); | ||
| 181 | + OP_CHECK_IF(ubCapacityElem <= 0, | ||
| 182 | + OP_LOGE(context, "UB too small: ubCapacityElem=%ld", | ||
| 183 | + ubCapacityElem), | ||
| 184 | + return ge::GRAPH_FAILED); | ||
| 185 | + | ||
| 186 | + int64_t ubFactor = (TILE_ELEM_NUM_TARGET < ubCapacityElem) | ||
| 187 | + ? TILE_ELEM_NUM_TARGET | ||
| 188 | + : ubCapacityElem; | ||
| 189 | + | ||
| 190 | + // Also cap by blockFactor so a single core does not allocate more UB | ||
| 191 | + // space than it will ever use. | ||
| 192 | + if (ubFactor > blockFactor) { | ||
| 193 | + ubFactor = FloorAlign(blockFactor, ubBlockSize); | ||
| 194 | + if (ubFactor <= 0) { | ||
| 195 | + ubFactor = ubBlockSize; | ||
| 196 | + } | ||
| 197 | + } | ||
| 198 | + | ||
| 199 | + tiling->blockFactor = blockFactor; | ||
| 200 | + tiling->ubFactor = ubFactor; | ||
| 201 | + | ||
| 202 | + context->SetBlockDim(usedCoreNum); | ||
| 203 | + | ||
| 204 | + // 5. TilingKey via ASCENDC_TPL_SEL_PARAM (template-argument mechanism). | ||
| 205 | + // Iteration-2: derive PAD_TAIL from shape alignment. HAS_L1 cannot be | ||
| 206 | + // determined from host-side tiling without a Host<->Device sync (lr/l1/l2 | ||
| 207 | + // are aclTensor inputs whose values live in Device GM), so we default it | ||
| 208 | + // to 1 and rely on the kernel to take a runtime fast-path when l1 == 0. | ||
| 209 | + // The HAS_L1 = 0 binary is still produced so UT can drive TilingKey | ||
| 210 | + // 10003 directly (and a future L0-API hint can flip this from host). | ||
| 211 | + uint32_t dTypeVar = static_cast<uint32_t>(dataType); | ||
| 212 | + uint32_t padTail = ((totalElements % ubBlockSize) != 0) ? 1U : 0U; | ||
| 213 | + uint32_t hasL1 = 1U; | ||
| 214 | + ASCENDC_TPL_SEL_PARAM(context, dTypeVar, padTail, hasL1); | ||
| 215 | + | ||
| 216 | + return ge::GRAPH_SUCCESS; | ||
| 217 | +} | ||
| 218 | + | ||
| 219 | +static ge::graphStatus TilingParseForApplyProximalAdagrad( | ||
| 220 | + [[maybe_unused]] gert::TilingParseContext* context) | ||
| 221 | +{ | ||
| 222 | + return ge::GRAPH_SUCCESS; | ||
| 223 | +} | ||
| 224 | + | ||
| 225 | +struct ApplyProximalAdagradCompileInfo {}; | ||
| 226 | + | ||
| 227 | +IMPL_OP_OPTILING(ApplyProximalAdagrad) | ||
| 228 | + .Tiling(ApplyProximalAdagradTilingFunc) | ||
| 229 | + .TilingParse<ApplyProximalAdagradCompileInfo>(TilingParseForApplyProximalAdagrad); | ||
| 230 | + | ||
| 231 | +} // namespace optiling | ||
| @@ -0,0 +1,50 @@ | |||
| 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 | + * NOTE: Portions of this code were AI-generated and have been | ||
| 13 | + * technically reviewed for functional accuracy and security | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +/*! | ||
| 17 | + * \file apply_proximal_adagrad.cpp | ||
| 18 | + * \brief ApplyProximalAdagrad kernel entry (arch35 / Ascend950). | ||
| 19 | + * | ||
| 20 | + * Signature follows registry-invoke convention: | ||
| 21 | + * all inputs -> all outputs -> workspace -> tiling | ||
| 22 | + * | ||
| 23 | + * Template parameters are driven by ASCENDC_TPL_SEL_PARAM on the host: | ||
| 24 | + * - D_T_VAR : input dtype (currently C_DT_FLOAT only). | ||
| 25 | + * - PAD_TAIL : whether the per-core tail is non-32B-aligned (0/1). | ||
| 26 | + * In iteration-2 the kernel always uses DataCopyPad on the | ||
| 27 | + * trailing tile, so the parameter is reserved for future | ||
| 28 | + * aligned-only fast-path optimisations and serves as the | ||
| 29 | + * TilingKey 10001 vs 10002 discriminator. | ||
| 30 | + * - HAS_L1 : compile-time hint that l1 may be non-zero. When 0, the | ||
| 31 | + * kernel statically drops the sign + soft-threshold branch | ||
| 32 | + * (TilingKey 10003 fast path). | ||
| 33 | + */ | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + | ||
| 37 | +template <typename D_T_VAR, uint32_t PAD_TAIL, uint32_t HAS_L1> | ||
| 38 | +__global__ __aicore__ void apply_proximal_adagrad( | ||
| 39 | + GM_ADDR var, GM_ADDR accum, | ||
| 40 | + GM_ADDR lr, GM_ADDR l1, GM_ADDR l2, | ||
| 41 | + GM_ADDR grad, | ||
| 42 | + GM_ADDR var_out, GM_ADDR accum_out, | ||
| 43 | + GM_ADDR workspace, GM_ADDR tiling) | ||
| 44 | +{ | ||
| 45 | + REGISTER_TILING_DEFAULT(ApplyProximalAdagradTilingData); | ||
| 46 | + GET_TILING_DATA_WITH_STRUCT(ApplyProximalAdagradTilingData, tilingData, tiling); | ||
| 47 | + NsApplyProximalAdagrad::ApplyProximalAdagrad<D_T_VAR, (PAD_TAIL != 0U), (HAS_L1 != 0U)> op; | ||
| 48 | + op.Init(var, accum, lr, l1, l2, grad, var_out, accum_out, &tilingData); | ||
| 49 | + op.Process(); | ||
| 50 | +} | ||
| @@ -0,0 +1,435 @@ | |||
| 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 | + * NOTE: Portions of this code were AI-generated and have been | ||
| 13 | + * technically reviewed for functional accuracy and security | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +/** | ||
| 17 | + * \file apply_proximal_adagrad.h | ||
| 18 | + * \brief ApplyProximalAdagrad kernel class (arch35 / Ascend950). | ||
| 19 | + * | ||
| 20 | + * Implements the Proximal Adagrad update per element: | ||
| 21 | + * | ||
| 22 | + * accum = accum + grad * grad | ||
| 23 | + * eta = lr * rsqrt(accum) | ||
| 24 | + * prox = var - eta * grad | ||
| 25 | + * if (l1 > 0): | ||
| 26 | + * var = sign(prox) * max(|prox| - eta*l1, 0) / (1 + eta*l2) | ||
| 27 | + * else: | ||
| 28 | + * var = prox / (1 + eta*l2) | ||
| 29 | + * | ||
| 30 | + * Iteration-2 routes: | ||
| 31 | + * - PAD_TAIL: 0 (aligned tail) / 1 (non-aligned tail). Both branches use | ||
| 32 | + * DataCopyPad which is alignment-tolerant; the flag is preserved so future | ||
| 33 | + * iterations can swap in a faster pure-DataCopy fast path on PAD_TAIL=0. | ||
| 34 | + * - HAS_L1: 1 (general path with sign + soft-threshold). The kernel also | ||
| 35 | + * applies a runtime fast-path when l1Scalar_ == 0 inside this branch (this | ||
| 36 | + * is the path taken when host cannot read l1 from GM at tiling time). | ||
| 37 | + * - HAS_L1: 0 (TilingKey 10003 dedicated fast path - sign / soft-threshold | ||
| 38 | + * statically dropped). Reachable via host-side hint or UT. | ||
| 39 | + */ | ||
| 40 | + | ||
| 41 | + | ||
| 42 | + | ||
| 43 | + | ||
| 44 | + | ||
| 45 | + | ||
| 46 | + | ||
| 47 | + | ||
| 48 | + | ||
| 49 | +namespace NsApplyProximalAdagrad { | ||
| 50 | + | ||
| 51 | +// SUG-002 fix: encode the UB resident-tensor count as a kernel-side constant so | ||
| 52 | +// that any change to the buffer layout below MUST update this value. The host | ||
| 53 | +// tiling (apply_proximal_adagrad_tiling.cpp::UB_BUFFER_COUNT) sizes the per-tile | ||
| 54 | +// UB capacity using the same constant; the two values MUST be kept in sync, or | ||
| 55 | +// the tile size computed by the host may exceed actual UB usage and risk | ||
| 56 | +// overflow. The breakdown is: | ||
| 57 | +// - 3 IN queues (var / accum / grad) x double-buffer (2) = 6 | ||
| 58 | +// - 2 OUT queues (var / accum) x double-buffer (2) = 4 | ||
| 59 | +// - 4 scratch buffers (eta / prox / thresh / scale), single-buffered = 4 | ||
| 60 | +// total = 14 | ||
| 61 | +constexpr int64_t kUbResidentFp32TensorCount = 14; | ||
| 62 | +static_assert(kUbResidentFp32TensorCount == 14, | ||
| 63 | + "UB resident tensor count must stay in sync with host tiling " | ||
| 64 | + "constant UB_BUFFER_COUNT in apply_proximal_adagrad_tiling.cpp; " | ||
| 65 | + "update both together when adding/removing UB buffers."); | ||
| 66 | + | ||
| 67 | +using AscendC::TPipe; | ||
| 68 | +using AscendC::TQue; | ||
| 69 | +using AscendC::TBuf; | ||
| 70 | +using AscendC::QuePosition; | ||
| 71 | +using AscendC::GlobalTensor; | ||
| 72 | +using AscendC::LocalTensor; | ||
| 73 | +using AscendC::DataCopyParams; | ||
| 74 | +using AscendC::DataCopyPad; | ||
| 75 | +using AscendC::DataCopyExtParams; | ||
| 76 | +using AscendC::DataCopyPadExtParams; | ||
| 77 | +using AscendC::GetBlockIdx; | ||
| 78 | +using AscendC::Add; | ||
| 79 | +using AscendC::Sub; | ||
| 80 | +using AscendC::Mul; | ||
| 81 | +using AscendC::Muls; | ||
| 82 | +using AscendC::Adds; | ||
| 83 | +using AscendC::Abs; | ||
| 84 | +using AscendC::Div; | ||
| 85 | +using AscendC::Rsqrt; | ||
| 86 | +using AscendC::Maxs; | ||
| 87 | +using AscendC::Compare; | ||
| 88 | +using AscendC::Compares; | ||
| 89 | +using AscendC::CompareScalar; | ||
| 90 | +using AscendC::Select; | ||
| 91 | +using AscendC::Duplicate; | ||
| 92 | +using AscendC::CMPMODE; | ||
| 93 | +using AscendC::SELMODE; | ||
| 94 | + | ||
| 95 | +template <typename T, bool PAD_TAIL = true, bool HAS_L1 = true> | ||
| 96 | +class ApplyProximalAdagrad { | ||
| 97 | +public: | ||
| 98 | + __aicore__ inline ApplyProximalAdagrad() {} | ||
| 99 | + | ||
| 100 | + __aicore__ inline void Init(GM_ADDR var, GM_ADDR accum, | ||
| 101 | + GM_ADDR lr, GM_ADDR l1, GM_ADDR l2, | ||
| 102 | + GM_ADDR grad, | ||
| 103 | + GM_ADDR varOut, GM_ADDR accumOut, | ||
| 104 | + const ApplyProximalAdagradTilingData* tilingData); | ||
| 105 | + __aicore__ inline void Process(); | ||
| 106 | + | ||
| 107 | +private: | ||
| 108 | + __aicore__ inline void CopyInTile(int64_t gmOffset, int64_t currentNum); | ||
| 109 | + __aicore__ inline void Compute(int64_t currentNum); | ||
| 110 | + __aicore__ inline void CopyOutTile(int64_t gmOffset, int64_t currentNum); | ||
| 111 | + | ||
| 112 | + __aicore__ inline float LoadScalar(const GlobalTensor<T>& src) const; | ||
| 113 | + | ||
| 114 | +private: | ||
| 115 | + TPipe pipe_; | ||
| 116 | + // IN queues: var / accum / grad, double-buffered. | ||
| 117 | + TQue<QuePosition::VECIN, 2> varInQue_; | ||
| 118 | + TQue<QuePosition::VECIN, 2> accumInQue_; | ||
| 119 | + TQue<QuePosition::VECIN, 2> gradInQue_; | ||
| 120 | + // OUT queues: var / accum, double-buffered. | ||
| 121 | + TQue<QuePosition::VECOUT, 2> varOutQue_; | ||
| 122 | + TQue<QuePosition::VECOUT, 2> accumOutQue_; | ||
| 123 | + // Scratch compute buffers (VECCALC, not queue-synchronised). | ||
| 124 | + TBuf<QuePosition::VECCALC> etaBuf_; // lr * rsqrt(accum) | ||
| 125 | + TBuf<QuePosition::VECCALC> proxBuf_; // var - eta * grad | ||
| 126 | + TBuf<QuePosition::VECCALC> threshBuf_; // max(|prox| - eta*l1, 0) and helpers | ||
| 127 | + TBuf<QuePosition::VECCALC> scaleBuf_; // 1 + eta * l2 | ||
| 128 | + | ||
| 129 | + GlobalTensor<T> varGm_; | ||
| 130 | + GlobalTensor<T> accumGm_; | ||
| 131 | + GlobalTensor<T> gradGm_; | ||
| 132 | + GlobalTensor<T> lrGm_; | ||
| 133 | + GlobalTensor<T> l1Gm_; | ||
| 134 | + GlobalTensor<T> l2Gm_; | ||
| 135 | + GlobalTensor<T> varOutGm_; | ||
| 136 | + GlobalTensor<T> accumOutGm_; | ||
| 137 | + | ||
| 138 | + // Scalars loaded from GM at Init time. | ||
| 139 | + float lrScalar_ = 0.0f; | ||
| 140 | + float l1Scalar_ = 0.0f; | ||
| 141 | + float l2Scalar_ = 0.0f; | ||
| 142 | + | ||
| 143 | + int64_t blockOffset_ = 0; | ||
| 144 | + int64_t blockLen_ = 0; | ||
| 145 | + int64_t ubFactor_ = 0; | ||
| 146 | +}; | ||
| 147 | + | ||
| 148 | +// ============================================================================= | ||
| 149 | +// LoadScalar: pick the first element of a 1-element GM tensor into a register. | ||
| 150 | +// ============================================================================= | ||
| 151 | +template <typename T, bool PAD_TAIL, bool HAS_L1> | ||
| 152 | +__aicore__ inline float ApplyProximalAdagrad<T, PAD_TAIL, HAS_L1>::LoadScalar( | ||
| 153 | + const GlobalTensor<T>& src) const | ||
| 154 | +{ | ||
| 155 | + return static_cast<float>(src.GetValue(0)); | ||
| 156 | +} | ||
| 157 | + | ||
| 158 | +// ============================================================================= | ||
| 159 | +// Init | ||
| 160 | +// ============================================================================= | ||
| 161 | +template <typename T, bool PAD_TAIL, bool HAS_L1> | ||
| 162 | +__aicore__ inline void ApplyProximalAdagrad<T, PAD_TAIL, HAS_L1>::Init( | ||
| 163 | + GM_ADDR var, GM_ADDR accum, | ||
| 164 | + GM_ADDR lr, GM_ADDR l1, GM_ADDR l2, | ||
| 165 | + GM_ADDR grad, | ||
| 166 | + GM_ADDR varOut, GM_ADDR accumOut, | ||
| 167 | + const ApplyProximalAdagradTilingData* tilingData) | ||
| 168 | +{ | ||
| 169 | + ubFactor_ = tilingData->ubFactor; | ||
| 170 | + | ||
| 171 | + // Empty-tensor / degenerate tiling -> just record zero length; Process() | ||
| 172 | + // will short-circuit. | ||
| 173 | + if (tilingData->totalElements == 0 || tilingData->blockFactor == 0) { | ||
| 174 | + blockOffset_ = 0; | ||
| 175 | + blockLen_ = 0; | ||
| 176 | + return; | ||
| 177 | + } | ||
| 178 | + | ||
| 179 | + blockOffset_ = tilingData->blockFactor * static_cast<int64_t>(GetBlockIdx()); | ||
| 180 | + int64_t remaining = tilingData->totalElements - blockOffset_; | ||
| 181 | + if (remaining <= 0) { | ||
| 182 | + blockLen_ = 0; | ||
| 183 | + return; | ||
| 184 | + } | ||
| 185 | + blockLen_ = (remaining > tilingData->blockFactor) | ||
| 186 | + ? tilingData->blockFactor | ||
| 187 | + : remaining; | ||
| 188 | + | ||
| 189 | + // Main vectorised tensors -- slice each core's view. | ||
| 190 | + varGm_.SetGlobalBuffer((__gm__ T*)var + blockOffset_, blockLen_); | ||
| 191 | + accumGm_.SetGlobalBuffer((__gm__ T*)accum + blockOffset_, blockLen_); | ||
| 192 | + gradGm_.SetGlobalBuffer((__gm__ T*)grad + blockOffset_, blockLen_); | ||
| 193 | + varOutGm_.SetGlobalBuffer((__gm__ T*)varOut + blockOffset_, blockLen_); | ||
| 194 | + accumOutGm_.SetGlobalBuffer((__gm__ T*)accumOut + blockOffset_, blockLen_); | ||
| 195 | + | ||
| 196 | + // Scalar tensors: single element broadcast-read. | ||
| 197 | + lrGm_.SetGlobalBuffer((__gm__ T*)lr, 1); | ||
| 198 | + l1Gm_.SetGlobalBuffer((__gm__ T*)l1, 1); | ||
| 199 | + l2Gm_.SetGlobalBuffer((__gm__ T*)l2, 1); | ||
| 200 | + lrScalar_ = LoadScalar(lrGm_); | ||
| 201 | + l1Scalar_ = LoadScalar(l1Gm_); | ||
| 202 | + l2Scalar_ = LoadScalar(l2Gm_); | ||
| 203 | + | ||
| 204 | + // UB buffer allocation. | ||
| 205 | + pipe_.InitBuffer(varInQue_, 2, ubFactor_ * sizeof(T)); | ||
| 206 | + pipe_.InitBuffer(accumInQue_, 2, ubFactor_ * sizeof(T)); | ||
| 207 | + pipe_.InitBuffer(gradInQue_, 2, ubFactor_ * sizeof(T)); | ||
| 208 | + pipe_.InitBuffer(varOutQue_, 2, ubFactor_ * sizeof(T)); | ||
| 209 | + pipe_.InitBuffer(accumOutQue_, 2, ubFactor_ * sizeof(T)); | ||
| 210 | + | ||
| 211 | + pipe_.InitBuffer(etaBuf_, ubFactor_ * sizeof(float)); | ||
| 212 | + pipe_.InitBuffer(proxBuf_, ubFactor_ * sizeof(float)); | ||
| 213 | + pipe_.InitBuffer(threshBuf_, ubFactor_ * sizeof(float)); | ||
| 214 | + pipe_.InitBuffer(scaleBuf_, ubFactor_ * sizeof(float)); | ||
| 215 | +} | ||
| 216 | + | ||
| 217 | +// ============================================================================= | ||
| 218 | +// CopyInTile: pad-aware DataCopyPad of var / accum / grad. | ||
| 219 | +// DataCopyPad transparently handles aligned and non-aligned blockLen, so the | ||
| 220 | +// same primitive serves both PAD_TAIL=0 (aligned tile) and PAD_TAIL=1 | ||
| 221 | +// (non-aligned tail) routes. | ||
| 222 | +// ============================================================================= | ||
| 223 | +template <typename T, bool PAD_TAIL, bool HAS_L1> | ||
| 224 | +__aicore__ inline void ApplyProximalAdagrad<T, PAD_TAIL, HAS_L1>::CopyInTile( | ||
| 225 | + int64_t gmOffset, int64_t currentNum) | ||
| 226 | +{ | ||
| 227 | + LocalTensor<T> varLocal = varInQue_.template AllocTensor<T>(); | ||
| 228 | + LocalTensor<T> accumLocal = accumInQue_.template AllocTensor<T>(); | ||
| 229 | + LocalTensor<T> gradLocal = gradInQue_.template AllocTensor<T>(); | ||
| 230 | + | ||
| 231 | + DataCopyExtParams copyParams; | ||
| 232 | + copyParams.blockCount = 1; | ||
| 233 | + copyParams.blockLen = static_cast<uint32_t>(currentNum * sizeof(T)); | ||
| 234 | + copyParams.srcStride = 0; | ||
| 235 | + copyParams.dstStride = 0; | ||
| 236 | + | ||
| 237 | + // ISSUE-001 fix: defensively right-pad the tail lanes ([currentNum, alignedNum)) | ||
| 238 | + // with safe values so Compute's Rsqrt/Div on padding lanes does not produce | ||
| 239 | + // Inf/NaN. We compute padding count up to the next 8-element (32B) block. | ||
| 240 | + // The padding lanes are never written back to GM (DataCopyPad honours the | ||
| 241 | + // exact byte length), so the value choice only matters for in-UB compute | ||
| 242 | + // stability: | ||
| 243 | + // - var / grad: pad with 0.0f -> grad=0 keeps accum unchanged, prox=var. | ||
| 244 | + // - accum: pad with 1.0f -> Rsqrt(accum + grad^2) = Rsqrt(1) = 1, | ||
| 245 | + // avoiding the Rsqrt(0)=+Inf -> NaN cascade. | ||
| 246 | + constexpr int64_t kAlignBlock = 32 / sizeof(T); | ||
| 247 | + int64_t alignedNum = | ||
| 248 | + ((currentNum + kAlignBlock - 1) / kAlignBlock) * kAlignBlock; | ||
| 249 | + uint8_t rightPadCount = static_cast<uint8_t>(alignedNum - currentNum); | ||
| 250 | + DataCopyPadExtParams<T> padZeroParams{true, 0, rightPadCount, static_cast<T>(0)}; | ||
| 251 | + DataCopyPadExtParams<T> padOneParams{true, 0, rightPadCount, static_cast<T>(1.0f)}; | ||
| 252 | + | ||
| 253 | + DataCopyPad(varLocal, varGm_[gmOffset], copyParams, padZeroParams); | ||
| 254 | + DataCopyPad(accumLocal, accumGm_[gmOffset], copyParams, padOneParams); | ||
| 255 | + DataCopyPad(gradLocal, gradGm_[gmOffset], copyParams, padZeroParams); | ||
| 256 | + | ||
| 257 | + varInQue_.EnQue(varLocal); | ||
| 258 | + accumInQue_.EnQue(accumLocal); | ||
| 259 | + gradInQue_.EnQue(gradLocal); | ||
| 260 | +} | ||
| 261 | + | ||
| 262 | +// ============================================================================= | ||
| 263 | +// CopyOutTile: write back var / accum to their inplace GM slots. DataCopyPad | ||
| 264 | +// honours the exact byte length, so non-aligned tails do not over-write | ||
| 265 | +// neighbouring cores' data. | ||
| 266 | +// ============================================================================= | ||
| 267 | +template <typename T, bool PAD_TAIL, bool HAS_L1> | ||
| 268 | +__aicore__ inline void ApplyProximalAdagrad<T, PAD_TAIL, HAS_L1>::CopyOutTile( | ||
| 269 | + int64_t gmOffset, int64_t currentNum) | ||
| 270 | +{ | ||
| 271 | + LocalTensor<T> varOutLocal = varOutQue_.template DeQue<T>(); | ||
| 272 | + LocalTensor<T> accumOutLocal = accumOutQue_.template DeQue<T>(); | ||
| 273 | + | ||
| 274 | + DataCopyExtParams copyParams; | ||
| 275 | + copyParams.blockCount = 1; | ||
| 276 | + copyParams.blockLen = static_cast<uint32_t>(currentNum * sizeof(T)); | ||
| 277 | + copyParams.srcStride = 0; | ||
| 278 | + copyParams.dstStride = 0; | ||
| 279 | + | ||
| 280 | + DataCopyPad(varOutGm_[gmOffset], varOutLocal, copyParams); | ||
| 281 | + DataCopyPad(accumOutGm_[gmOffset], accumOutLocal, copyParams); | ||
| 282 | + | ||
| 283 | + varOutQue_.FreeTensor(varOutLocal); | ||
| 284 | + accumOutQue_.FreeTensor(accumOutLocal); | ||
| 285 | +} | ||
| 286 | + | ||
| 287 | +// ============================================================================= | ||
| 288 | +// Compute: main per-tile computation (float32 only in iteration 1/2). | ||
| 289 | +// | ||
| 290 | +// accum' = accum + grad*grad | ||
| 291 | +// eta = lr * rsqrt(accum') | ||
| 292 | +// prox = var - eta * grad | ||
| 293 | +// | ||
| 294 | +// HAS_L1 = true (TilingKey 10001 / 10002 - general path): | ||
| 295 | +// thresh = max(|prox| - eta*l1, 0) | ||
| 296 | +// signed = prox >= 0 ? thresh : -thresh | ||
| 297 | +// var' = signed / (1 + eta*l2) | ||
| 298 | +// Includes a runtime fast-path: if l1Scalar_ == 0 the kernel skips the | ||
| 299 | +// sign / soft-threshold steps and behaves like the HAS_L1=false branch. | ||
| 300 | +// | ||
| 301 | +// HAS_L1 = false (TilingKey 10003 - dedicated fast path): | ||
| 302 | +// var' = prox / (1 + eta*l2) | ||
| 303 | +// ============================================================================= | ||
| 304 | +template <typename T, bool PAD_TAIL, bool HAS_L1> | ||
| 305 | +__aicore__ inline void ApplyProximalAdagrad<T, PAD_TAIL, HAS_L1>::Compute( | ||
| 306 | + int64_t currentNum) | ||
| 307 | +{ | ||
| 308 | + LocalTensor<T> varLocal = varInQue_.template DeQue<T>(); | ||
| 309 | + LocalTensor<T> accumLocal = accumInQue_.template DeQue<T>(); | ||
| 310 | + LocalTensor<T> gradLocal = gradInQue_.template DeQue<T>(); | ||
| 311 | + | ||
| 312 | + LocalTensor<T> varOutLocal = varOutQue_.template AllocTensor<T>(); | ||
| 313 | + LocalTensor<T> accumOutLocal = accumOutQue_.template AllocTensor<T>(); | ||
| 314 | + | ||
| 315 | + LocalTensor<float> etaTmp = etaBuf_.template Get<float>(); | ||
| 316 | + LocalTensor<float> proxTmp = proxBuf_.template Get<float>(); | ||
| 317 | + LocalTensor<float> threshTmp = threshBuf_.template Get<float>(); | ||
| 318 | + LocalTensor<float> scaleTmp = scaleBuf_.template Get<float>(); | ||
| 319 | + | ||
| 320 | + // Align work count to 32B / sizeof(float) = 8 elements. This guarantees | ||
| 321 | + // Compare / Select 256-byte alignment (ubFactor is a 2048-elem multiple, | ||
| 322 | + // and we round non-aligned tails up to the next 8-elem block; the extra | ||
| 323 | + // padding lanes are computed but never written back to GM thanks to | ||
| 324 | + // DataCopyPad honouring the exact byte length). | ||
| 325 | + constexpr int64_t kAlignBlock = 32 / sizeof(float); | ||
| 326 | + int64_t alignedNum = | ||
| 327 | + ((currentNum + kAlignBlock - 1) / kAlignBlock) * kAlignBlock; | ||
| 328 | + int32_t n = static_cast<int32_t>(alignedNum); | ||
| 329 | + | ||
| 330 | + // Compile-time specialisation: iteration 1/2 only supports float32. If | ||
| 331 | + // new dtypes are added later they should Cast at the beginning and Cast | ||
| 332 | + // back at the end, leaving the inner block below unchanged. | ||
| 333 | + if constexpr (std::is_same_v<T, float>) { | ||
| 334 | + // ----- Common: S1 / S2 / S3 ----- | ||
| 335 | + // S1: accum' = accum + grad*grad -> write into accumOutLocal. | ||
| 336 | + Mul(accumOutLocal, gradLocal, gradLocal, n); | ||
| 337 | + Add(accumOutLocal, accumLocal, accumOutLocal, n); | ||
| 338 | + | ||
| 339 | + // S2: eta = lr * rsqrt(accum') | ||
| 340 | + // NOTE: Caller contract guarantees accum >= 0 (typically > 0). If | ||
| 341 | + // accum + grad^2 == 0, Rsqrt returns +Inf and downstream eta*grad | ||
| 342 | + // becomes Inf*0 = NaN. This matches the semantics of PyTorch's | ||
| 343 | + // ApplyProximalAdagrad and TensorFlow's ApplyProximalAdagrad, where | ||
| 344 | + // the safe-input contract is the caller's responsibility (see | ||
| 345 | + // README.md / DESIGN.md). Defensive padding in CopyInTile ensures | ||
| 346 | + // the [currentNum, alignedNum) tail lanes use accum=1, not 0, so | ||
| 347 | + // padding lanes never trigger this path. | ||
| 348 | + Rsqrt(etaTmp, accumOutLocal, n); | ||
| 349 | + Muls(etaTmp, etaTmp, lrScalar_, n); | ||
| 350 | + | ||
| 351 | + // S3: prox = var - eta * grad | ||
| 352 | + Mul(proxTmp, etaTmp, gradLocal, n); | ||
| 353 | + Sub(proxTmp, varLocal, proxTmp, n); | ||
| 354 | + | ||
| 355 | + // ----- S4 / S5 branch ----- | ||
| 356 | + if constexpr (HAS_L1) { | ||
| 357 | + // Runtime fast-path: if l1 == 0 the sign + soft-threshold steps | ||
| 358 | + // collapse to identity, so we behave like the HAS_L1=false branch. | ||
| 359 | + if (l1Scalar_ == 0.0f) { | ||
| 360 | + // scale = 1 + eta*l2; var = prox / scale | ||
| 361 | + Muls(scaleTmp, etaTmp, l2Scalar_, n); | ||
| 362 | + Adds(scaleTmp, scaleTmp, 1.0f, n); | ||
| 363 | + Div(varOutLocal, proxTmp, scaleTmp, n); | ||
| 364 | + } else { | ||
| 365 | + // S4a: thresh = max(|prox| - eta*l1, 0) | ||
| 366 | + Abs(threshTmp, proxTmp, n); | ||
| 367 | + Muls(scaleTmp, etaTmp, l1Scalar_, n); // scaleTmp reused as eta*l1 | ||
| 368 | + Sub(threshTmp, threshTmp, scaleTmp, n); | ||
| 369 | + Maxs(threshTmp, threshTmp, 0.0f, n); | ||
| 370 | + | ||
| 371 | + // S4b: signed_thresh = prox >= 0 ? thresh : -thresh | ||
| 372 | + Muls(scaleTmp, threshTmp, -1.0f, n); // scaleTmp = -thresh | ||
| 373 | + // SUG-001 NOTE: We temporarily reinterpret varOutLocal's storage | ||
| 374 | + // as a uint8_t mask buffer. This relies on an implicit ordering | ||
| 375 | + // invariant: varOutLocal must NOT be read between this point and | ||
| 376 | + // the final Div() below that overwrites it. The mask is consumed | ||
| 377 | + // by the immediately following Select() call and never read again | ||
| 378 | + // in this Compute(). If future maintenance inserts any read of | ||
| 379 | + // varOutLocal before the final Div, this aliasing must be | ||
| 380 | + // replaced by a dedicated mask buffer (e.g. add a maskBuf_ in | ||
| 381 | + // Init or carve out the tail of threshBuf_). | ||
| 382 | + LocalTensor<uint8_t> maskTensor = | ||
| 383 | + varOutLocal.template ReinterpretCast<uint8_t>(); | ||
| 384 | + CompareScalar(maskTensor, proxTmp, 0.0f, CMPMODE::GE, n); | ||
| 385 | + Select(proxTmp, maskTensor, threshTmp, scaleTmp, | ||
| 386 | + SELMODE::VSEL_TENSOR_TENSOR_MODE, n); | ||
| 387 | + | ||
| 388 | + // S5: scale = 1 + eta*l2 | ||
| 389 | + Muls(scaleTmp, etaTmp, l2Scalar_, n); | ||
| 390 | + Adds(scaleTmp, scaleTmp, 1.0f, n); | ||
| 391 | + | ||
| 392 | + // var' = signed_thresh / scale | ||
| 393 | + Div(varOutLocal, proxTmp, scaleTmp, n); | ||
| 394 | + } | ||
| 395 | + } else { | ||
| 396 | + // HAS_L1 == false (TilingKey 10003): dedicated simplified path. | ||
| 397 | + // var = prox / (1 + eta*l2) | ||
| 398 | + Muls(scaleTmp, etaTmp, l2Scalar_, n); | ||
| 399 | + Adds(scaleTmp, scaleTmp, 1.0f, n); | ||
| 400 | + Div(varOutLocal, proxTmp, scaleTmp, n); | ||
| 401 | + } | ||
| 402 | + } | ||
| 403 | + | ||
| 404 | + varOutQue_.template EnQue<T>(varOutLocal); | ||
| 405 | + accumOutQue_.template EnQue<T>(accumOutLocal); | ||
| 406 | + | ||
| 407 | + varInQue_.FreeTensor(varLocal); | ||
| 408 | + accumInQue_.FreeTensor(accumLocal); | ||
| 409 | + gradInQue_.FreeTensor(gradLocal); | ||
| 410 | +} | ||
| 411 | + | ||
| 412 | +// ============================================================================= | ||
| 413 | +// Process: main loop over UB-sized chunks. | ||
| 414 | +// ============================================================================= | ||
| 415 | +template <typename T, bool PAD_TAIL, bool HAS_L1> | ||
| 416 | +__aicore__ inline void ApplyProximalAdagrad<T, PAD_TAIL, HAS_L1>::Process() | ||
| 417 | +{ | ||
| 418 | + if (blockLen_ <= 0) { | ||
| 419 | + return; | ||
| 420 | + } | ||
| 421 | + int64_t loopCount = (blockLen_ + ubFactor_ - 1) / ubFactor_; | ||
| 422 | + for (int64_t i = 0; i < loopCount; i++) { | ||
| 423 | + int64_t gmOffset = i * ubFactor_; | ||
| 424 | + int64_t currentNum = (i == (loopCount - 1)) | ||
| 425 | + ? (blockLen_ - gmOffset) | ||
| 426 | + : ubFactor_; | ||
| 427 | + CopyInTile(gmOffset, currentNum); | ||
| 428 | + Compute(currentNum); | ||
| 429 | + CopyOutTile(gmOffset, currentNum); | ||
| 430 | + } | ||
| 431 | +} | ||
| 432 | + | ||
| 433 | +} // namespace NsApplyProximalAdagrad | ||
| 434 | + | ||
| 435 | + | ||
| @@ -0,0 +1,37 @@ | |||
| 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 | + * NOTE: Portions of this code were AI-generated and have been | ||
| 13 | + * technically reviewed for functional accuracy and security | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +/*! | ||
| 17 | + * \file apply_proximal_adagrad_tiling_data.h | ||
| 18 | + * \brief ApplyProximalAdagrad TilingData structure (arch35). | ||
| 19 | + * | ||
| 20 | + * Standard C++ struct form (per REQUIREMENTS s8.4 "禁用 BEGIN_TILING_DATA_DEF"). | ||
| 21 | + */ | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | +struct ApplyProximalAdagradTilingData { | ||
| 29 | + // Total number of elements in var/accum/grad (all share the same shape). | ||
| 30 | + int64_t totalElements = 0; | ||
| 31 | + // Number of elements per-core (aligned up to ubBlockSize to keep DMA safe). | ||
| 32 | + int64_t blockFactor = 0; | ||
| 33 | + // Number of elements processed per UB iteration. | ||
| 34 | + int64_t ubFactor = 0; | ||
| 35 | +}; | ||
| 36 | + | ||
| 37 | + | ||
| @@ -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 | + * NOTE: Portions of this code were AI-generated and have been | ||
| 13 | + * technically reviewed for functional accuracy and security | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +/*! | ||
| 17 | + * \file apply_proximal_adagrad_tiling_key.h | ||
| 18 | + * \brief ApplyProximalAdagrad TilingKey template-argument declaration (arch35). | ||
| 19 | + * | ||
| 20 | + * Iteration-2 introduces three TilingKey routes (DESIGN s5.5): | ||
| 21 | + * 10001 - aligned tail block + L1 >= 0 general path | ||
| 22 | + * 10002 - non-aligned tail block + L1 >= 0 general path (DataCopyPad write-back) | ||
| 23 | + * 10003 - any tail block + L1 == 0 fast path (skip sign/soft-threshold) | ||
| 24 | + * | ||
| 25 | + * They are encoded via two boolean template parameters (PAD_TAIL, HAS_L1): | ||
| 26 | + * (PAD_TAIL=0, HAS_L1=1) -> 10001 | ||
| 27 | + * (PAD_TAIL=1, HAS_L1=1) -> 10002 | ||
| 28 | + * (PAD_TAIL=0|1, HAS_L1=0) -> 10003 | ||
| 29 | + * | ||
| 30 | + * - PAD_TAIL is derived in host tiling from (totalElements % ubBlockSize != 0). | ||
| 31 | + * - HAS_L1 is normally driven from host = 1 (lr/l1/l2 sit in Device GM and we | ||
| 32 | + * cannot synchronously inspect them at tiling time). The kernel additionally | ||
| 33 | + * performs a cheap runtime fast-path check when l1Scalar == 0. We still | ||
| 34 | + * register the HAS_L1 = 0 binary so UT (and a future L0-API hint) can drive | ||
| 35 | + * the dedicated TilingKey 10003. | ||
| 36 | + * | ||
| 37 | + * Uses ASCENDC_TPL_ARGS_DECL template-argument mechanism. | ||
| 38 | + * TILING_KEY_IS macro is forbidden (see REQUIREMENTS s8.4). | ||
| 39 | + */ | ||
| 40 | + | ||
| 41 | + | ||
| 42 | + | ||
| 43 | + | ||
| 44 | + | ||
| 45 | + | ||
| 46 | +ASCENDC_TPL_ARGS_DECL(ApplyProximalAdagrad, | ||
| 47 | + ASCENDC_TPL_DATATYPE_DECL(D_T_VAR, C_DT_FLOAT, ASCENDC_TPL_INPUT(0)), | ||
| 48 | + ASCENDC_TPL_UINT_DECL(PAD_TAIL, 8, ASCENDC_TPL_UI_LIST, 0, 1), | ||
| 49 | + ASCENDC_TPL_UINT_DECL(HAS_L1, 8, ASCENDC_TPL_UI_LIST, 0, 1) | ||
| 50 | +); | ||
| 51 | + | ||
| 52 | +ASCENDC_TPL_SEL( | ||
| 53 | + ASCENDC_TPL_ARGS_SEL( | ||
| 54 | + ASCENDC_TPL_DATATYPE_SEL(D_T_VAR, C_DT_FLOAT), | ||
| 55 | + ASCENDC_TPL_UINT_SEL(PAD_TAIL, ASCENDC_TPL_UI_LIST, 0, 1), | ||
| 56 | + ASCENDC_TPL_UINT_SEL(HAS_L1, ASCENDC_TPL_UI_LIST, 0, 1) | ||
| 57 | + ), | ||
| 58 | +); | ||
| 59 | + | ||
| 60 | + | ||
The file is empty