| @@ -0,0 +1,14 @@ | |||
| 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 | + | ||
| 6 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 7 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
W | |||
| 8 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 9 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 10 | +# ---------------------------------------------------------------------------------------------------------- | ||
| 11 | +# NOTE: Portions of this code were AI-generated and have been | ||
| 12 | +# technically reviewed for functional accuracy and security | ||
| 13 | + | ||
| 14 | +add_modules_sources(HOSTNAME ${OPHOST_NAME} MODE PRIVATE DIR ${CMAKE_CURRENT_SOURCE_DIR} OPTYPE apply_adadelta ACLNNTYPE aclnn_exclude COMPUTE_UNIT ${SUPPORT_COMPUTE_UNIT} TILING_DIR ${SUPPORT_TILING_DIR} DISABLE_IN_OPP TRUE) | ||
| @@ -0,0 +1,145 @@ | |||
| 1 | +# ApplyAdadelta | ||
| 2 | + | ||
| 3 | +## 算子说明 | ||
| 4 | + | ||
| 5 | +执行 Adadelta 优化器的单步参数更新。根据当前梯度、梯度平方累积和更新量平方累积,计算参数更新量并**原地**更新权重参数(inplace 语义)。对标 TensorFlow `tf.raw_ops.ApplyAdadelta` 接口和 PyTorch `torch.optim.Adadelta` 的计算语义。 | ||
| 6 | + | ||
| 7 | +### 计算公式 | ||
| 8 | + | ||
| 9 | +$$ | ||
| 10 | +\begin{aligned} | ||
| 11 | +accum_{t} &= \rho \cdot accum_{t-1} + (1 - \rho) \cdot grad^2 \\ | ||
| 12 | +update &= \frac{\sqrt{accum\_update_{t-1} + \epsilon}}{\sqrt{accum_{t} + \epsilon}} \cdot grad \\ | ||
| 13 | +var_{t} &= var_{t-1} - lr \cdot update \\ | ||
| 14 | +accum\_update_{t} &= \rho \cdot accum\_update_{t-1} + (1 - \rho) \cdot update^2 | ||
| 15 | +\end{aligned} | ||
| 16 | +$$ | ||
| 17 | + | ||
op_api_list.md op_list.md 交付件缺少, 以及缺少ut ![]() ![]() | |||
| 18 | +其中: | ||
| 19 | +- `var`:待更新的权重参数 | ||
| 20 | +- `accum`:梯度平方的指数移动平均($E[g^2]$) | ||
| 21 | +- `accum_update`:参数更新量平方的指数移动平均($E[\Delta x^2]$) | ||
| 22 | +- `lr`:学习率 | ||
| 23 | +- `rho`:衰减系数,取值范围 [0, 1) | ||
| 24 | +- `epsilon`:数值稳定常数,必须 > 0 | ||
| 25 | +- `grad`:当前梯度 | ||
| 26 | + | ||
| 27 | +## 产品支持情况 | ||
| 28 | + | ||
| 29 | +| 产品 | 是否支持 | | ||
| 30 | +|------|:------:| | ||
| 31 | +| Ascend 950PR / Ascend 950DT(Atlas A5 推理系列,DAV_3510) | √ | | ||
| 32 | +| Atlas A3 训练系列 / Atlas A3 推理系列 | × | | ||
| 33 | +| Atlas A2 训练系列 / Atlas A2 推理系列 | × | | ||
| 34 | +| Atlas 200I/500 A2 推理产品 | × | | ||
| 35 | +| Atlas 推理系列 | × | | ||
| 36 | +| Atlas 训练系列 | × | | ||
| 37 | + | ||
| 38 | +## 调用方式支持 | ||
| 39 | + | ||
| 40 | +| 调用方式 | 是否支持 | | ||
| 41 | +|---------|:--------:| | ||
| 42 | +| ACLNN 单算子调用 | √ | | ||
| 43 | +| torch_npu 单算子调用 | × | | ||
| 44 | +| torch.compile 入图 | × | | ||
| 45 | +| GE 图模式(静态 shape) | × | | ||
| 46 | +| GE 图模式(动态 shape) | × | | ||
| 47 | + | ||
| 48 | +> **说明**:本算子仅支持 ACLNN 单算子调用,暂不支持 GE 图模式、torch_npu 单算子及 torch.compile 入图。因此仅提供 ACLNN 调用示例 `test_aclnn_apply_adadelta.cpp`,**不提供 GE IR 示例**。 | ||
| 49 | + | ||
| 50 | +## 数据类型 | ||
| 51 | + | ||
| 52 | +| 参数 | 支持 dtype | | ||
| 53 | +|-----|-----------| | ||
| 54 | +| var / accum / accumUpdate / grad | FLOAT16、FLOAT | | ||
| 55 | +| lr / rho / epsilon (aclScalar) | FLOAT16、FLOAT(需与 Tensor dtype 一致) | | ||
| 56 | + | ||
| 57 | +## 参数说明 | ||
| 58 | + | ||
| 59 | +| 参数名 | 输入/输出 | 说明 | | ||
| 60 | +|-------|----------|------| | ||
| 61 | +| var | 输入+输出 (inplace) | 权重参数 Tensor,dtype ∈ {FLOAT16, FLOAT},1-8 维 ND 格式 | | ||
| 62 | +| accum | 输入+输出 (inplace) | 梯度平方累积 Tensor,shape/dtype 必须与 var 一致 | | ||
| 63 | +| accumUpdate | 输入+输出 (inplace) | 更新量平方累积 Tensor,shape/dtype 必须与 var 一致 | | ||
| 64 | +| lr | 输入 | 学习率标量,dtype 必须与 Tensor 一致 | | ||
| 65 | +| rho | 输入 | 衰减系数标量,取值范围 [0, 1) | | ||
| 66 | +| epsilon | 输入 | 数值稳定常数标量,必须 > 0 | | ||
| 67 | +| grad | 输入 | 梯度 Tensor,shape/dtype 必须与 var 一致 | | ||
| 68 | +| varOut | 输出 | 与 var 共享 Device 内存 | | ||
| 69 | +| accumOut | 输出 | 与 accum 共享 Device 内存 | | ||
| 70 | +| accumUpdateOut | 输出 | 与 accumUpdate 共享 Device 内存 | | ||
| 71 | + | ||
| 72 | +详细接口参数与返回码定义请参见 [aclnnApplyAdadelta 接口文档](docs/aclnnApplyAdadelta.md)。 | ||
| 73 | + | ||
| 74 | +## 约束说明 | ||
| 75 | + | ||
| 76 | +- 本算子仅支持 Ascend 950PR / Ascend 950DT 产品(Atlas A5 推理系列,DAV_3510 架构)。 | ||
| 77 | +- `var`、`accum`、`accumUpdate`、`grad` 的 shape 必须**完全相同**,不支持广播。 | ||
| 78 | +- `var`、`accum`、`accumUpdate`、`grad` 的 dtype 必须**完全一致**,`lr`、`rho`、`epsilon` 的 dtype 也需与之一致。 | ||
| 79 | +- 不支持 0 维标量 Tensor 作为 var / accum / accumUpdate / grad。 | ||
| 80 | +- 参数值域:`epsilon > 0`,`rho ∈ [0, 1)`。 | ||
| 81 | +- **Inplace 语义**:`varOut / accumOut / accumUpdateOut` 分别与 `var / accum / accumUpdate` 共享 GM 地址,输出直接写回输入内存。调用方需自行保存调用前的原始值(如需)。 | ||
| 82 | +- 空 Tensor 处理:元素数为 0 时直接返回成功,不执行计算。 | ||
| 83 | +- FP16 精度路径:Kernel 内部提升为 FP32 计算,输出时转回 FP16,以保证精度。 | ||
| 84 | +- 确定性:本算子为逐元素运算,无 Reduce,相同输入产出相同输出。 | ||
| 85 | + | ||
| 86 | +## 目录结构 | ||
| 87 | + | ||
| 88 | +``` | ||
| 89 | +apply_adadelta/ | ||
| 90 | +├── CMakeLists.txt | ||
| 91 | +├── README.md | ||
| 92 | +├── build.sh | ||
| 93 | +├── docs/ | ||
| 94 | +│ ├── aclnnApplyAdadelta.md # aclnn 接口说明 | ||
| 95 | +│ ├── REQUIREMENTS.md # 需求文档 | ||
| 96 | +│ ├── DESIGN.md # 详细设计 | ||
| 97 | +│ ├── TEST.md # 测试设计 | ||
| 98 | +│ ├── precision-report.md # 最终精度验收报告 | ||
| 99 | +│ └── performance-report.md # 性能验收报告 | ||
| 100 | +├── examples/ | ||
| 101 | +│ ├── CMakeLists.txt | ||
| 102 | +│ └── test_aclnn_apply_adadelta.cpp # aclnn 调用示例 | ||
| 103 | +├── op_host/ | ||
| 104 | +├── op_kernel/ | ||
| 105 | +├── op_api/ | ||
| 106 | +└── tests/ | ||
| 107 | + ├── ut/ | ||
| 108 | + └── st/ | ||
| 109 | +``` | ||
| 110 | + | ||
| 111 | +## 调用示例 | ||
| 112 | + | ||
| 113 | +本算子提供 ACLNN 调用示例,样例代码位于 [`examples/test_aclnn_apply_adadelta.cpp`](examples/test_aclnn_apply_adadelta.cpp)。 | ||
| 114 | + | ||
| 115 | +### 编译运行 | ||
| 116 | + | ||
| 117 | +前置条件:已安装 CANN Toolkit,并已编译安装本算子自定义包(生成 `opp/vendors/apply_adadelta_custom/`)。 | ||
| 118 | + | ||
| 119 | +```bash | ||
| 120 | +# 1. 设置环境 | ||
| 121 | +source ${ASCEND_HOME_PATH}/bin/setenv.bash | ||
| 122 | + | ||
| 123 | +# 2. 编译示例 | ||
| 124 | +cd examples | ||
| 125 | +mkdir -p build && cd build | ||
| 126 | +cmake .. | ||
| 127 | +make | ||
| 128 | + | ||
| 129 | +# 3. 运行示例 | ||
| 130 | +./test_aclnn_apply_adadelta | ||
| 131 | +``` | ||
| 132 | + | ||
| 133 | +### 示例输出 | ||
| 134 | + | ||
| 135 | +示例以 shape=[4,2] 的 fp32 张量、`lr=0.01, rho=0.9, epsilon=1e-6` 执行一次 Adadelta 更新,典型输出: | ||
| 136 | + | ||
| 137 | +``` | ||
| 138 | +var_out[0]=0.998526 accum_out[0]=0.115000 accum_update_out[0]=0.011174 | ||
| 139 | +var_out[1]=2.000976 accum_out[1]=0.189000 accum_update_out[1]=0.018952 | ||
| 140 | +... | ||
| 141 | +``` | ||
| 142 | + | ||
| 143 | +> **不支持 GE 图模式**:如前述调用方式支持矩阵所述,本算子**不支持 GE 图模式**,因此不提供 `test_geir_apply_adadelta.cpp` 示例。 | ||
| 144 | + | ||
| 145 | +具体编译和运行过程可参考 [编译与运行样例](../../../docs/context/编译与运行样例.md)。 | ||
| @@ -0,0 +1,218 @@ | |||
| 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 | + * @file test_aclnn_apply_adadelta.cpp | ||
| 16 | + * @brief aclnnApplyAdadelta 算子调用示例。 | ||
| 17 | + * | ||
| 18 | + * 功能:Adadelta 优化器单步参数更新(inplace) | ||
| 19 | + * 公式: | ||
| 20 | + * accum_new = rho * accum + (1 - rho) * grad^2 | ||
| 21 | + * update = sqrt(accum_update + eps) / sqrt(accum_new + eps) * grad | ||
| 22 | + * var_new = var - lr * update | ||
| 23 | + * accum_update_new = rho * accum_update + (1 - rho) * update^2 | ||
| 24 | + * | ||
| 25 | + * 注意:本算子为 inplace 语义,varOut/accumOut/accumUpdateOut 分别与 | ||
| 26 | + * var/accum/accumUpdate 共享 Device 内存。 | ||
| 27 | + */ | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + do { \ | ||
| 35 | + if (!(cond)) { \ | ||
| 36 | + return_expr; \ | ||
| 37 | + } \ | ||
| 38 | + } while (0) | ||
| 39 | + | ||
| 40 | + | ||
| 41 | + do { \ | ||
| 42 | + printf(message, ##__VA_ARGS__); \ | ||
| 43 | + } while (0) | ||
| 44 | + | ||
| 45 | +int64_t GetShapeSize(const std::vector<int64_t>& shape) { | ||
| 46 | + int64_t shape_size = 1; | ||
| 47 | + for (auto i : shape) { | ||
| 48 | + shape_size *= i; | ||
| 49 | + } | ||
| 50 | + return shape_size; | ||
| 51 | +} | ||
| 52 | + | ||
| 53 | +int Init(int32_t deviceId, aclrtStream* stream) { | ||
| 54 | + // 固定写法,资源初始化 | ||
| 55 | + auto ret = aclInit(nullptr); | ||
| 56 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret); | ||
| 57 | + ret = aclrtSetDevice(deviceId); | ||
| 58 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret); | ||
| 59 | + ret = aclrtCreateStream(stream); | ||
| 60 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret); | ||
| 61 | + return 0; | ||
| 62 | +} | ||
| 63 | + | ||
| 64 | +template <typename T> | ||
| 65 | +int CreateAclTensor(const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr, | ||
| 66 | + aclDataType dataType, aclTensor** tensor) { | ||
| 67 | + auto size = GetShapeSize(shape) * sizeof(T); | ||
| 68 | + // 调用aclrtMalloc申请Device侧内存 | ||
| 69 | + auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 70 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret); | ||
| 71 | + // 调用aclrtMemcpy将Host侧数据拷贝到Device侧内存上 | ||
| 72 | + ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 73 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret); | ||
| 74 | + // 计算连续tensor的strides | ||
| 75 | + std::vector<int64_t> strides(shape.size(), 1); | ||
| 76 | + for (int64_t i = shape.size() - 2; i >= 0; i--) { | ||
| 77 | + strides[i] = shape[i + 1] * strides[i + 1]; | ||
| 78 | + } | ||
| 79 | + // 调用aclCreateTensor接口创建aclTensor | ||
| 80 | + *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, | ||
| 81 | + shape.data(), shape.size(), *deviceAddr); | ||
| 82 | + return 0; | ||
| 83 | +} | ||
| 84 | + | ||
| 85 | +int main() { | ||
| 86 | + // 1. (固定写法)device/stream 初始化 | ||
| 87 | + int32_t deviceId = 0; | ||
| 88 | + aclrtStream stream; | ||
| 89 | + auto ret = Init(deviceId, &stream); | ||
| 90 | + CHECK_RET(ret == 0, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret); | ||
| 91 | + | ||
| 92 | + // 2. 构造输入与输出 | ||
| 93 | + // var/accum/accumUpdate/grad shape 必须完全一致,dtype 必须一致(fp16 或 fp32)。 | ||
| 94 | + // lr/rho/epsilon 为 aclScalar,dtype 需与 Tensor 一致。 | ||
| 95 | + std::vector<int64_t> shape = {4, 2}; | ||
| 96 | + void* varDeviceAddr = nullptr; | ||
| 97 | + void* accumDeviceAddr = nullptr; | ||
| 98 | + void* accumUpdateDeviceAddr = nullptr; | ||
| 99 | + void* gradDeviceAddr = nullptr; | ||
| 100 | + aclTensor* var = nullptr; | ||
| 101 | + aclTensor* accum = nullptr; | ||
| 102 | + aclTensor* accumUpdate = nullptr; | ||
| 103 | + aclTensor* grad = nullptr; | ||
| 104 | + aclTensor* varOut = nullptr; | ||
| 105 | + aclTensor* accumOut = nullptr; | ||
| 106 | + aclTensor* accumUpdateOut = nullptr; | ||
| 107 | + aclScalar* lr = nullptr; | ||
| 108 | + aclScalar* rho = nullptr; | ||
| 109 | + aclScalar* epsilon = nullptr; | ||
| 110 | + | ||
| 111 | + std::vector<float> varHostData = {1.0f, 2.0f, -1.0f, 0.5f, 0.0f, -0.5f, 1.5f, -2.0f}; | ||
| 112 | + std::vector<float> accumHostData = {0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.1f, 0.2f, 0.3f}; | ||
| 113 | + std::vector<float> accumUpdateHostData = {0.01f, 0.02f, 0.03f, 0.04f, 0.05f, 0.01f, 0.02f, 0.03f}; | ||
| 114 | + std::vector<float> gradHostData = {0.5f, -0.3f, 0.1f, -0.2f, 0.4f, -0.1f, 0.3f, -0.4f}; | ||
| 115 | + float lrValue = 0.01f; | ||
| 116 | + float rhoValue = 0.9f; | ||
| 117 | + float epsilonValue = 1e-6f; | ||
| 118 | + | ||
| 119 | + // 创建 var/accum/accumUpdate/grad aclTensor | ||
| 120 | + ret = CreateAclTensor(varHostData, shape, &varDeviceAddr, aclDataType::ACL_FLOAT, &var); | ||
| 121 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 122 | + ret = CreateAclTensor(accumHostData, shape, &accumDeviceAddr, aclDataType::ACL_FLOAT, &accum); | ||
| 123 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 124 | + ret = CreateAclTensor(accumUpdateHostData, shape, &accumUpdateDeviceAddr, aclDataType::ACL_FLOAT, &accumUpdate); | ||
| 125 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 126 | + ret = CreateAclTensor(gradHostData, shape, &gradDeviceAddr, aclDataType::ACL_FLOAT, &grad); | ||
| 127 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 128 | + | ||
| 129 | + // inplace 语义:输出 tensor 复用输入 Device 地址 | ||
| 130 | + std::vector<int64_t> strides(shape.size(), 1); | ||
| 131 | + for (int64_t i = shape.size() - 2; i >= 0; i--) { | ||
| 132 | + strides[i] = shape[i + 1] * strides[i + 1]; | ||
| 133 | + } | ||
| 134 | + varOut = aclCreateTensor(shape.data(), shape.size(), aclDataType::ACL_FLOAT, strides.data(), 0, | ||
| 135 | + aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), varDeviceAddr); | ||
| 136 | + accumOut = aclCreateTensor(shape.data(), shape.size(), aclDataType::ACL_FLOAT, strides.data(), 0, | ||
| 137 | + aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), accumDeviceAddr); | ||
| 138 | + accumUpdateOut = aclCreateTensor(shape.data(), shape.size(), aclDataType::ACL_FLOAT, strides.data(), 0, | ||
| 139 | + aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), accumUpdateDeviceAddr); | ||
| 140 | + | ||
| 141 | + // 创建 Scalar 参数(dtype 需与 Tensor 保持一致) | ||
| 142 | + lr = aclCreateScalar(&lrValue, aclDataType::ACL_FLOAT); | ||
| 143 | + rho = aclCreateScalar(&rhoValue, aclDataType::ACL_FLOAT); | ||
| 144 | + epsilon = aclCreateScalar(&epsilonValue, aclDataType::ACL_FLOAT); | ||
| 145 | + | ||
| 146 | + // 3. 调用 CANN 算子库 API(两段式接口) | ||
| 147 | + uint64_t workspaceSize = 0; | ||
| 148 | + aclOpExecutor* executor = nullptr; | ||
| 149 | + | ||
| 150 | + // 调用 aclnnApplyAdadelta 第一段接口获取 workspace 大小 | ||
| 151 | + ret = aclnnApplyAdadeltaGetWorkspaceSize(var, accum, accumUpdate, lr, rho, epsilon, grad, | ||
| 152 | + varOut, accumOut, accumUpdateOut, | ||
| 153 | + &workspaceSize, &executor); | ||
| 154 | + CHECK_RET(ret == ACL_SUCCESS, | ||
| 155 | + LOG_PRINT("aclnnApplyAdadeltaGetWorkspaceSize failed. ERROR: %d\n", ret); | ||
| 156 | + return ret); | ||
| 157 | + | ||
| 158 | + // 根据第一段接口计算出的 workspaceSize 申请 Device 内存 | ||
| 159 | + void* workspaceAddr = nullptr; | ||
| 160 | + if (workspaceSize > 0) { | ||
| 161 | + ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 162 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret); | ||
| 163 | + } | ||
| 164 | + | ||
| 165 | + // 调用 aclnnApplyAdadelta 第二段接口执行计算 | ||
| 166 | + ret = aclnnApplyAdadelta(workspaceAddr, workspaceSize, executor, stream); | ||
| 167 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnApplyAdadelta failed. ERROR: %d\n", ret); return ret); | ||
| 168 | + | ||
| 169 | + // 4. (固定写法)同步等待任务执行结束 | ||
| 170 | + ret = aclrtSynchronizeStream(stream); | ||
| 171 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret); | ||
| 172 | + | ||
| 173 | + // 5. 获取输出(inplace:从输入 Device 地址读回) | ||
| 174 | + auto size = GetShapeSize(shape); | ||
| 175 | + std::vector<float> varResult(size, 0); | ||
| 176 | + std::vector<float> accumResult(size, 0); | ||
| 177 | + std::vector<float> accumUpdateResult(size, 0); | ||
| 178 | + | ||
| 179 | + ret = aclrtMemcpy(varResult.data(), varResult.size() * sizeof(float), | ||
| 180 | + varDeviceAddr, size * sizeof(float), ACL_MEMCPY_DEVICE_TO_HOST); | ||
| 181 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy var result failed. ERROR: %d\n", ret); return ret); | ||
| 182 | + ret = aclrtMemcpy(accumResult.data(), accumResult.size() * sizeof(float), | ||
| 183 | + accumDeviceAddr, size * sizeof(float), ACL_MEMCPY_DEVICE_TO_HOST); | ||
| 184 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy accum result failed. ERROR: %d\n", ret); return ret); | ||
| 185 | + ret = aclrtMemcpy(accumUpdateResult.data(), accumUpdateResult.size() * sizeof(float), | ||
| 186 | + accumUpdateDeviceAddr, size * sizeof(float), ACL_MEMCPY_DEVICE_TO_HOST); | ||
| 187 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy accum_update result failed. ERROR: %d\n", ret); return ret); | ||
| 188 | + | ||
| 189 | + for (int64_t i = 0; i < size; i++) { | ||
| 190 | + LOG_PRINT("var_out[%ld]=%.6f accum_out[%ld]=%.6f accum_update_out[%ld]=%.6f\n", | ||
| 191 | + i, varResult[i], i, accumResult[i], i, accumUpdateResult[i]); | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + // 6. 释放 aclTensor / aclScalar | ||
| 195 | + aclDestroyTensor(var); | ||
| 196 | + aclDestroyTensor(accum); | ||
| 197 | + aclDestroyTensor(accumUpdate); | ||
| 198 | + aclDestroyTensor(grad); | ||
| 199 | + aclDestroyTensor(varOut); | ||
| 200 | + aclDestroyTensor(accumOut); | ||
| 201 | + aclDestroyTensor(accumUpdateOut); | ||
| 202 | + aclDestroyScalar(lr); | ||
| 203 | + aclDestroyScalar(rho); | ||
| 204 | + aclDestroyScalar(epsilon); | ||
| 205 | + | ||
| 206 | + // 7. 释放 Device 资源 | ||
| 207 | + aclrtFree(varDeviceAddr); | ||
| 208 | + aclrtFree(accumDeviceAddr); | ||
| 209 | + aclrtFree(accumUpdateDeviceAddr); | ||
| 210 | + aclrtFree(gradDeviceAddr); | ||
| 211 | + if (workspaceSize > 0) { | ||
| 212 | + aclrtFree(workspaceAddr); | ||
| 213 | + } | ||
| 214 | + aclrtDestroyStream(stream); | ||
| 215 | + aclrtResetDevice(deviceId); | ||
| 216 | + aclFinalize(); | ||
| 217 | + return 0; | ||
| 218 | +} | ||
| @@ -0,0 +1,256 @@ | |||
| 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 | + * @file aclnn_apply_adadelta.cpp | ||
| 17 | + * @brief ACLNN L2 API implementation for ApplyAdadelta | ||
| 18 | + * | ||
| 19 | + * Two-stage interface: | ||
| 20 | + * 1. aclnnApplyAdadeltaGetWorkspaceSize - parameter checking, Contiguous, L0 dispatch | ||
| 21 | + * 2. aclnnApplyAdadelta - execute computation | ||
| 22 | + * | ||
| 23 | + * Scalar parameters (lr, rho, epsilon) are extracted from aclScalar and passed | ||
| 24 | + * as attrs to the Tiling function via the executor. | ||
| 25 | + */ | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + | ||
| 38 | +using namespace op; | ||
| 39 | + | ||
| 40 | + | ||
| 41 | + | ||
| 42 | +static const std::initializer_list<op::DataType> AICORE_DTYPE_SUPPORT_LIST = { | ||
| 43 | + DataType::DT_FLOAT, DataType::DT_FLOAT16 | ||
| 44 | +}; | ||
| 45 | + | ||
| 46 | +static bool CheckNotNull( | ||
| 47 | + const aclTensor* var, const aclTensor* accum, const aclTensor* accumUpdate, | ||
| 48 | + const aclScalar* lr, const aclScalar* rho, const aclScalar* epsilon, | ||
| 49 | + const aclTensor* grad, const aclTensor* varOut, | ||
| 50 | + const aclTensor* accumOut, const aclTensor* accumUpdateOut) | ||
| 51 | +{ | ||
| 52 | + OP_CHECK_NULL(var, return false); | ||
| 53 | + OP_CHECK_NULL(accum, return false); | ||
| 54 | + OP_CHECK_NULL(accumUpdate, return false); | ||
| 55 | + OP_CHECK_NULL(lr, return false); | ||
| 56 | + OP_CHECK_NULL(rho, return false); | ||
| 57 | + OP_CHECK_NULL(epsilon, return false); | ||
| 58 | + OP_CHECK_NULL(grad, return false); | ||
| 59 | + OP_CHECK_NULL(varOut, return false); | ||
| 60 | + OP_CHECK_NULL(accumOut, return false); | ||
| 61 | + OP_CHECK_NULL(accumUpdateOut, return false); | ||
| 62 | + return true; | ||
| 63 | +} | ||
| 64 | + | ||
| 65 | +static bool CheckDtypeValid( | ||
| 66 | + const aclTensor* var, const aclTensor* accum, | ||
| 67 | + const aclTensor* accumUpdate, const aclTensor* grad) | ||
| 68 | +{ | ||
| 69 | + auto dtype = var->GetDataType(); | ||
| 70 | + if (!CheckType(dtype, AICORE_DTYPE_SUPPORT_LIST)) { | ||
| 71 | + OP_LOGE(ACLNN_ERR_PARAM_INVALID, | ||
| 72 | + "Unsupported dtype: %d. Only FLOAT and FLOAT16 are supported.", | ||
| 73 | + static_cast<int>(dtype)); | ||
| 74 | + return false; | ||
| 75 | + } | ||
| 76 | + // All tensor dtypes must match | ||
| 77 | + OP_CHECK_DTYPE_NOT_MATCH(accum, dtype, return false); | ||
| 78 | + OP_CHECK_DTYPE_NOT_MATCH(accumUpdate, dtype, return false); | ||
| 79 | + OP_CHECK_DTYPE_NOT_MATCH(grad, dtype, return false); | ||
| 80 | + return true; | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +static bool CheckShapeConsistent( | ||
| 84 | + const aclTensor* var, const aclTensor* accum, | ||
| 85 | + const aclTensor* accumUpdate, const aclTensor* grad) | ||
| 86 | +{ | ||
| 87 | + OP_CHECK_MAX_DIM(var, ACLNN_MAX_SHAPE_RANK, return false); | ||
| 88 | + | ||
| 89 | + auto varShape = var->GetViewShape(); | ||
| 90 | + auto accumShape = accum->GetViewShape(); | ||
| 91 | + auto auShape = accumUpdate->GetViewShape(); | ||
| 92 | + auto gradShape = grad->GetViewShape(); | ||
| 93 | + | ||
| 94 | + // Reject 0-dim scalar tensors (REQUIREMENTS.md §5.3: supported shape dim 1~8). | ||
| 95 | + // See docs/aclnnApplyAdadelta.md "不支持 0 维标量 Tensor". | ||
| 96 | + if (varShape.GetDimNum() == 0 || accumShape.GetDimNum() == 0 || | ||
| 97 | + auShape.GetDimNum() == 0 || gradShape.GetDimNum() == 0) { | ||
| 98 | + OP_LOGE(ACLNN_ERR_PARAM_INVALID, | ||
| 99 | + "0-dim scalar Tensor is not supported; supported dim range is 1~%d", | ||
| 100 | + ACLNN_MAX_SHAPE_RANK); | ||
| 101 | + return false; | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + if (varShape.GetDimNum() != accumShape.GetDimNum() || | ||
| 105 | + varShape.GetDimNum() != auShape.GetDimNum() || | ||
| 106 | + varShape.GetDimNum() != gradShape.GetDimNum()) { | ||
| 107 | + OP_LOGE(ACLNN_ERR_PARAM_INVALID, | ||
| 108 | + "Shape dims mismatch: var=%zu, accum=%zu, accumUpdate=%zu, grad=%zu", | ||
| 109 | + varShape.GetDimNum(), accumShape.GetDimNum(), | ||
| 110 | + auShape.GetDimNum(), gradShape.GetDimNum()); | ||
| 111 | + return false; | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + for (size_t i = 0; i < varShape.GetDimNum(); i++) { | ||
| 115 | + if (varShape.GetDim(i) != accumShape.GetDim(i) || | ||
| 116 | + varShape.GetDim(i) != auShape.GetDim(i) || | ||
| 117 | + varShape.GetDim(i) != gradShape.GetDim(i)) { | ||
| 118 | + OP_LOGE(ACLNN_ERR_PARAM_INVALID, | ||
| 119 | + "Shape mismatch at dim %zu: var=%ld, accum=%ld, accumUpdate=%ld, grad=%ld", | ||
| 120 | + i, varShape.GetDim(i), accumShape.GetDim(i), | ||
| 121 | + auShape.GetDim(i), gradShape.GetDim(i)); | ||
| 122 | + return false; | ||
| 123 | + } | ||
| 124 | + } | ||
| 125 | + return true; | ||
| 126 | +} | ||
| 127 | + | ||
| 128 | +// Validate scalar values: | ||
| 129 | +// - epsilon must be > 0 (numerical stability constant) | ||
| 130 | +// - rho must be in [0, 1) (decay coefficient, per REQUIREMENTS.md) | ||
| 131 | +// - lr must be >= 0 (learning rate; standard Adadelta requires non-negative lr. | ||
| 132 | +// Note: Under negative lr, NPU observations show var_out is left unchanged | ||
| 133 | +// while accum/accum_update are correctly updated, i.e. the computation is | ||
| 134 | +// ill-defined. The current hypothesis is related to the CANN ACLNN tiling | ||
| 135 | +// cache key missing scalar attrs; see docs/precision-report.md | ||
| 136 | +// "CANN ACLNN tiling 缓存键缺失 attr" section for background. We therefore | ||
| 137 | +// reject lr < 0 at the ACLNN layer as a conservative guard.) | ||
| 138 | +static bool CheckScalarValues(const aclScalar* lr, const aclScalar* rho, const aclScalar* epsilon) | ||
【建议级】lr<0 拒绝原因注释需澄清 注释提到 "CANN ACLNN tiling 缓存键缺失 attr" 问题,但未引用具体 Issue ID。 建议:
![]() ![]() | |||
| 139 | +{ | ||
| 140 | + float lrVal = lr->ToFloat(); | ||
| 141 | + float rhoVal = rho->ToFloat(); | ||
| 142 | + float epsVal = epsilon->ToFloat(); | ||
| 143 | + if (lrVal < 0.0f) { | ||
| 144 | + OP_LOGE(ACLNN_ERR_PARAM_INVALID, | ||
| 145 | + "lr must be >= 0, got %f", lrVal); | ||
| 146 | + return false; | ||
| 147 | + } | ||
【建议级】rho 范围检查需对标 TensorFlow 当前检查 参考:
建议:查阅 TensorFlow 源码确认 rho 的实际限制范围。 ![]() ![]() | |||
| 148 | + if (epsVal <= 0.0f) { | ||
| 149 | + OP_LOGE(ACLNN_ERR_PARAM_INVALID, | ||
| 150 | + "epsilon must be > 0, got %f", epsVal); | ||
| 151 | + return false; | ||
| 152 | + } | ||
| 153 | + if (rhoVal < 0.0f || rhoVal >= 1.0f) { | ||
| 154 | + OP_LOGE(ACLNN_ERR_PARAM_INVALID, | ||
| 155 | + "rho must be in [0, 1), got %f", rhoVal); | ||
| 156 | + return false; | ||
| 157 | + } | ||
| 158 | + return true; | ||
| 159 | +} | ||
| 160 | + | ||
| 161 | +static aclnnStatus CheckParams( | ||
| 162 | + const aclTensor* var, const aclTensor* accum, const aclTensor* accumUpdate, | ||
| 163 | + const aclScalar* lr, const aclScalar* rho, const aclScalar* epsilon, | ||
| 164 | + const aclTensor* grad, const aclTensor* varOut, | ||
| 165 | + const aclTensor* accumOut, const aclTensor* accumUpdateOut) | ||
| 166 | +{ | ||
| 167 | + if (!CheckNotNull(var, accum, accumUpdate, lr, rho, epsilon, grad, varOut, accumOut, accumUpdateOut)) { | ||
| 168 | + OP_LOGE(ACLNN_ERR_PARAM_NULLPTR, "Null pointer in input parameters"); | ||
| 169 | + return ACLNN_ERR_PARAM_NULLPTR; | ||
| 170 | + } | ||
| 171 | + if (!CheckDtypeValid(var, accum, accumUpdate, grad)) { | ||
| 172 | + return ACLNN_ERR_PARAM_INVALID; | ||
| 173 | + } | ||
| 174 | + if (!CheckShapeConsistent(var, accum, accumUpdate, grad)) { | ||
| 175 | + return ACLNN_ERR_PARAM_INVALID; | ||
| 176 | + } | ||
| 177 | + if (!CheckScalarValues(lr, rho, epsilon)) { | ||
| 178 | + return ACLNN_ERR_PARAM_INVALID; | ||
| 179 | + } | ||
| 180 | + return ACLNN_SUCCESS; | ||
| 181 | +} | ||
| 182 | + | ||
| 183 | +extern "C" aclnnStatus aclnnApplyAdadeltaGetWorkspaceSize( | ||
| 184 | + const aclTensor* var, | ||
| 185 | + const aclTensor* accum, | ||
| 186 | + const aclTensor* accumUpdate, | ||
| 187 | + const aclScalar* lr, | ||
| 188 | + const aclScalar* rho, | ||
| 189 | + const aclScalar* epsilon, | ||
| 190 | + const aclTensor* grad, | ||
| 191 | + aclTensor* varOut, | ||
| 192 | + aclTensor* accumOut, | ||
| 193 | + aclTensor* accumUpdateOut, | ||
| 194 | + uint64_t* workspaceSize, | ||
| 195 | + aclOpExecutor** executor) | ||
| 196 | +{ | ||
| 197 | + L2_DFX_PHASE_1(aclnnApplyAdadelta, | ||
| 198 | + DFX_IN(var, accum, accumUpdate, grad), | ||
| 199 | + DFX_OUT(varOut, accumOut, accumUpdateOut)); | ||
| 200 | + | ||
| 201 | + auto uniqueExecutor = CREATE_EXECUTOR(); | ||
| 202 | + CHECK_RET(uniqueExecutor.get() != nullptr, ACLNN_ERR_INNER_CREATE_EXECUTOR); | ||
| 203 | + | ||
| 204 | + auto ret = CheckParams(var, accum, accumUpdate, lr, rho, epsilon, grad, varOut, accumOut, accumUpdateOut); | ||
| 205 | + CHECK_RET(ret == ACLNN_SUCCESS, ret); | ||
| 206 | + | ||
| 207 | + if (var->IsEmpty()) { | ||
| 208 | + *workspaceSize = 0; | ||
| 209 | + uniqueExecutor.ReleaseTo(executor); | ||
| 210 | + return ACLNN_SUCCESS; | ||
| 211 | + } | ||
| 212 | + | ||
| 213 | + // Make inputs contiguous | ||
| 214 | + auto varContiguous = l0op::Contiguous(var, uniqueExecutor.get()); | ||
| 215 | + CHECK_RET(varContiguous != nullptr, ACLNN_ERR_INNER_NULLPTR); | ||
| 216 | + auto accumContiguous = l0op::Contiguous(accum, uniqueExecutor.get()); | ||
| 217 | + CHECK_RET(accumContiguous != nullptr, ACLNN_ERR_INNER_NULLPTR); | ||
| 218 | + auto accumUpdateContiguous = l0op::Contiguous(accumUpdate, uniqueExecutor.get()); | ||
| 219 | + CHECK_RET(accumUpdateContiguous != nullptr, ACLNN_ERR_INNER_NULLPTR); | ||
| 220 | + auto gradContiguous = l0op::Contiguous(grad, uniqueExecutor.get()); | ||
| 221 | + CHECK_RET(gradContiguous != nullptr, ACLNN_ERR_INNER_NULLPTR); | ||
| 222 | + | ||
| 223 | + // Extract scalar values and pass to L0 API | ||
| 224 | + float lrVal = lr->ToFloat(); | ||
| 225 | + float rhoVal = rho->ToFloat(); | ||
| 226 | + float epsVal = epsilon->ToFloat(); | ||
| 227 | + | ||
| 228 | + // Call L0 API | ||
| 229 | + auto opResults = l0op::ApplyAdadelta( | ||
| 230 | + varContiguous, accumContiguous, accumUpdateContiguous, gradContiguous, | ||
| 231 | + lrVal, rhoVal, epsVal, | ||
| 232 | + uniqueExecutor.get()); | ||
| 233 | + CHECK_RET(opResults.varOut != nullptr, ACLNN_ERR_INNER_NULLPTR); | ||
| 234 | + | ||
| 235 | + // ViewCopy for all inplace outputs | ||
| 236 | + auto viewCopy1 = l0op::ViewCopy(opResults.varOut, varOut, uniqueExecutor.get()); | ||
| 237 | + CHECK_RET(viewCopy1 != nullptr, ACLNN_ERR_INNER_NULLPTR); | ||
| 238 | + auto viewCopy2 = l0op::ViewCopy(opResults.accumOut, accumOut, uniqueExecutor.get()); | ||
| 239 | + CHECK_RET(viewCopy2 != nullptr, ACLNN_ERR_INNER_NULLPTR); | ||
| 240 | + auto viewCopy3 = l0op::ViewCopy(opResults.accumUpdateOut, accumUpdateOut, uniqueExecutor.get()); | ||
| 241 | + CHECK_RET(viewCopy3 != nullptr, ACLNN_ERR_INNER_NULLPTR); | ||
| 242 | + | ||
| 243 | + *workspaceSize = uniqueExecutor->GetWorkspaceSize(); | ||
| 244 | + uniqueExecutor.ReleaseTo(executor); | ||
| 245 | + return ACLNN_SUCCESS; | ||
| 246 | +} | ||
| 247 | + | ||
| 248 | +extern "C" aclnnStatus aclnnApplyAdadelta( | ||
| 249 | + void* workspace, | ||
| 250 | + uint64_t workspaceSize, | ||
| 251 | + aclOpExecutor* executor, | ||
| 252 | + aclrtStream stream) | ||
| 253 | +{ | ||
| 254 | + L2_DFX_PHASE_2(aclnnApplyAdadelta); | ||
| 255 | + return CommonOpExecutorRun(workspace, workspaceSize, executor, stream); | ||
| 256 | +} | ||
| @@ -0,0 +1,61 @@ | |||
| 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 | + * @file aclnn_apply_adadelta.h | ||
| 17 | + * @brief ACLNN L2 API declaration for ApplyAdadelta | ||
| 18 | + * | ||
| 19 | + * Two-stage interface: | ||
| 20 | + * - aclnnApplyAdadeltaGetWorkspaceSize: compute workspace size, create executor | ||
| 21 | + * - aclnnApplyAdadelta: execute computation | ||
| 22 | + */ | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | +extern "C" { | ||
| 35 | + | ||
| 36 | + | ||
| 37 | +ACLNN_API aclnnStatus aclnnApplyAdadeltaGetWorkspaceSize( | ||
| 38 | + const aclTensor *var, | ||
| 39 | + const aclTensor *accum, | ||
| 40 | + const aclTensor *accumUpdate, | ||
| 41 | + const aclScalar *lr, | ||
| 42 | + const aclScalar *rho, | ||
| 43 | + const aclScalar *epsilon, | ||
| 44 | + const aclTensor *grad, | ||
| 45 | + aclTensor *varOut, | ||
| 46 | + aclTensor *accumOut, | ||
| 47 | + aclTensor *accumUpdateOut, | ||
| 48 | + uint64_t *workspaceSize, | ||
| 49 | + aclOpExecutor **executor); | ||
| 50 | + | ||
| 51 | +ACLNN_API aclnnStatus aclnnApplyAdadelta( | ||
| 52 | + void *workspace, | ||
| 53 | + uint64_t workspaceSize, | ||
| 54 | + aclOpExecutor *executor, | ||
| 55 | + aclrtStream stream); | ||
| 56 | + | ||
| 57 | + | ||
| 58 | +} | ||
| 59 | + | ||
| 60 | + | ||
| 61 | + | ||
| @@ -0,0 +1,109 @@ | |||
| 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 | + * @file apply_adadelta.cpp | ||
| 17 | + * @brief ACLNN L0 API implementation for ApplyAdadelta | ||
| 18 | + * | ||
| 19 | + * L0 API: InferShape, IsAiCoreSupport, AllocTensor, ApplyAdadeltaAiCore | ||
| 20 | + * | ||
| 21 | + * This is an inplace operator. The Kernel receives both input and output GM addresses, | ||
| 22 | + * where output addresses overlap with input addresses (var/accum/accumUpdate). | ||
| 23 | + */ | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | +using namespace op; | ||
| 32 | + | ||
| 33 | +namespace l0op { | ||
| 34 | + | ||
| 35 | +OP_TYPE_REGISTER(ApplyAdadelta); | ||
| 36 | + | ||
| 37 | +static const std::initializer_list<op::DataType> AICORE_DTYPE_SUPPORT_LIST = { | ||
| 38 | + DataType::DT_FLOAT, DataType::DT_FLOAT16 | ||
| 39 | +}; | ||
| 40 | + | ||
| 41 | +static bool IsAiCoreSupport(const aclTensor* var) | ||
| 42 | +{ | ||
| 43 | + return CheckType(var->GetDataType(), AICORE_DTYPE_SUPPORT_LIST); | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +static const aclTensor* ApplyAdadeltaAiCore( | ||
| 47 | + const aclTensor* var, | ||
| 48 | + const aclTensor* accum, | ||
| 49 | + const aclTensor* accumUpdate, | ||
| 50 | + const aclTensor* grad, | ||
| 51 | + const aclTensor* varOut, | ||
| 52 | + const aclTensor* accumOut, | ||
| 53 | + const aclTensor* accumUpdateOut, | ||
| 54 | + float lr, float rho, float epsilon, | ||
| 55 | + aclOpExecutor* executor) | ||
| 56 | +{ | ||
| 57 | + L0_DFX(ApplyAdadeltaAiCore, var, accum, accumUpdate, grad, varOut, accumOut, accumUpdateOut); | ||
| 58 | + | ||
| 59 | + auto ret = ADD_TO_LAUNCHER_LIST_AICORE(ApplyAdadelta, | ||
| 60 | + OP_INPUT(var, accum, accumUpdate, grad), | ||
| 61 | + OP_OUTPUT(varOut, accumOut, accumUpdateOut), | ||
| 62 | + OP_ATTR(lr, rho, epsilon)); | ||
| 63 | + OP_CHECK( | ||
| 64 | + ret == ACLNN_SUCCESS, | ||
| 65 | + OP_LOGE(ACLNN_ERR_INNER_NULLPTR, "ApplyAdadeltaAiCore failed."), | ||
| 66 | + return nullptr); | ||
| 67 | + return varOut; | ||
| 68 | +} | ||
| 69 | + | ||
| 70 | +ApplyAdadeltaOutputs ApplyAdadelta( | ||
| 71 | + const aclTensor* var, | ||
| 72 | + const aclTensor* accum, | ||
| 73 | + const aclTensor* accumUpdate, | ||
| 74 | + const aclTensor* grad, | ||
| 75 | + float lr, | ||
| 76 | + float rho, | ||
| 77 | + float epsilon, | ||
| 78 | + aclOpExecutor* executor) | ||
| 79 | +{ | ||
| 80 | + ApplyAdadeltaOutputs emptyResult = {nullptr, nullptr, nullptr}; | ||
| 81 | + | ||
| 82 | + if (!IsAiCoreSupport(var)) { | ||
| 83 | + OP_LOGE(ACLNN_ERR_PARAM_INVALID, | ||
| 84 | + "ApplyAdadelta not supported: dtype=%d.", | ||
| 85 | + static_cast<int>(var->GetDataType())); | ||
| 86 | + return emptyResult; | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + // Output shape = input shape (inplace) | ||
| 90 | + auto varShape = var->GetViewShape(); | ||
| 91 | + | ||
| 92 | + // Allocate output tensors (inplace: framework handles address aliasing; | ||
| 93 | + // the L2 API completes aliasing / write-back to the original var/accum/ | ||
| 94 | + // accumUpdate buffers via ViewCopy after this L0 call returns.) | ||
| 95 | + const aclTensor* varOut = executor->AllocTensor(varShape, var->GetDataType()); | ||
| 96 | + const aclTensor* accumOut = executor->AllocTensor(varShape, var->GetDataType()); | ||
| 97 | + const aclTensor* accumUpdateOut = executor->AllocTensor(varShape, var->GetDataType()); | ||
| 98 | + | ||
| 99 | + const aclTensor* result = ApplyAdadeltaAiCore(var, accum, accumUpdate, grad, | ||
| 100 | + varOut, accumOut, accumUpdateOut, | ||
| 101 | + lr, rho, epsilon, executor); | ||
| 102 | + if (result == nullptr) { | ||
| 103 | + return emptyResult; | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + return {varOut, accumOut, accumUpdateOut}; | ||
| 107 | +} | ||
| 108 | + | ||
| 109 | +} // namespace l0op | ||
| @@ -0,0 +1,49 @@ | |||
| 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 | + * @file apply_adadelta.h | ||
| 17 | + * @brief ACLNN L0 API declaration for ApplyAdadelta | ||
| 18 | + */ | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | +namespace l0op { | ||
| 26 | + | ||
| 27 | +/** | ||
| 28 | + * ApplyAdadelta L0 API - returns 3 output tensors via a struct | ||
| 29 | + * (var_out, accum_out, accum_update_out) | ||
| 30 | + */ | ||
| 31 | +struct ApplyAdadeltaOutputs { | ||
| 32 | + const aclTensor* varOut; | ||
| 33 | + const aclTensor* accumOut; | ||
| 34 | + const aclTensor* accumUpdateOut; | ||
| 35 | +}; | ||
| 36 | + | ||
| 37 | +ApplyAdadeltaOutputs ApplyAdadelta( | ||
| 38 | + const aclTensor* var, | ||
| 39 | + const aclTensor* accum, | ||
| 40 | + const aclTensor* accumUpdate, | ||
| 41 | + const aclTensor* grad, | ||
| 42 | + float lr, | ||
| 43 | + float rho, | ||
| 44 | + float epsilon, | ||
| 45 | + aclOpExecutor* executor); | ||
| 46 | + | ||
| 47 | +} // namespace l0op | ||
| 48 | + | ||
| 49 | + | ||
| @@ -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 | + * NOTE: Portions of this code were AI-generated and have been | ||
| 13 | + * technically reviewed for functional accuracy and security | ||
| 14 | + */ | ||
| 15 | +/*! | ||
| 16 | + * \file apply_adadelta_def.cpp | ||
| 17 | + * \brief ApplyAdadelta operator definition | ||
| 18 | + * | ||
| 19 | + * Inputs: var, accum, accumUpdate, grad (Tensor) + lr, rho, epsilon (Scalar via TilingData) | ||
| 20 | + * Outputs: varOut, accumOut, accumUpdateOut (Tensor, inplace with inputs) | ||
| 21 | + * Target: ascend950 (arch35) | ||
| 22 | + */ | ||
| 23 | + | ||
| 24 | + | ||
| 25 | +namespace ops { | ||
| 26 | + | ||
| 27 | +class ApplyAdadelta : public OpDef { | ||
| 28 | +public: | ||
| 29 | + explicit ApplyAdadelta(const char* name) : OpDef(name) | ||
| 30 | + { | ||
| 31 | + // Helper: configure a Tensor Input with standard (FP32/FP16, ND) schema. | ||
| 32 | + auto cfgInput = [this](const char* n) -> void { | ||
| 33 | + this->Input(n) | ||
| 34 | + .ParamType(REQUIRED) | ||
| 35 | + .DataType({ge::DT_FLOAT, ge::DT_FLOAT16}) | ||
| 36 | + .Format({ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 37 | + .UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 38 | + .AutoContiguous(); | ||
| 39 | + }; | ||
| 40 | + auto cfgOutput = [this](const char* n) -> void { | ||
| 41 | + this->Output(n) | ||
| 42 | + .ParamType(REQUIRED) | ||
| 43 | + .DataType({ge::DT_FLOAT, ge::DT_FLOAT16}) | ||
| 44 | + .Format({ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 45 | + .UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 46 | + .AutoContiguous(); | ||
| 47 | + }; | ||
| 48 | + | ||
| 49 | + // 4 Tensor inputs: var, accum, accumUpdate, grad | ||
| 50 | + cfgInput("var"); | ||
| 51 | + cfgInput("accum"); | ||
| 52 | + cfgInput("accumUpdate"); | ||
| 53 | + cfgInput("grad"); | ||
| 54 | + | ||
| 55 | + // 3 Tensor outputs: varOut, accumOut, accumUpdateOut (inplace) | ||
| 56 | + cfgOutput("varOut"); | ||
| 57 | + cfgOutput("accumOut"); | ||
| 58 | + cfgOutput("accumUpdateOut"); | ||
| 59 | + | ||
| 60 | + // Scalar attrs: lr, rho, epsilon (set by ACLNN L0 API, accessed by index in Tiling) | ||
| 61 | + this->Attr("lr").AttrType(REQUIRED).Float(); | ||
| 62 | + this->Attr("rho").AttrType(REQUIRED).Float(); | ||
| 63 | + this->Attr("epsilon").AttrType(REQUIRED).Float(); | ||
| 64 | + | ||
| 65 | + // ascend950 (arch35) configuration | ||
| 66 | + OpAICoreConfig aiCoreConfig; | ||
| 67 | + aiCoreConfig.DynamicCompileStaticFlag(true) | ||
| 68 | + .DynamicFormatFlag(false) | ||
| 69 | + .DynamicRankSupportFlag(true) | ||
| 70 | + .DynamicShapeSupportFlag(true) | ||
| 71 | + .NeedCheckSupportFlag(false) | ||
| 72 | + .PrecisionReduceFlag(true) | ||
| 73 | + .ExtendCfgInfo("opFile.value", "apply_adadelta"); | ||
| 74 | + this->AICore().AddConfig("ascend950", aiCoreConfig); | ||
| 75 | + } | ||
| 76 | +}; | ||
| 77 | + | ||
| 78 | +OP_ADD(ApplyAdadelta); | ||
| 79 | + | ||
| 80 | +} // namespace ops | ||
| @@ -0,0 +1,52 @@ | |||
| 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 | + * \file apply_adadelta_infershape.cpp | ||
| 17 | + * \brief ApplyAdadelta shape inference | ||
| 18 | + * | ||
| 19 | + * All inputs share the same shape. Outputs = input shape (inplace). | ||
| 20 | + * 3 outputs: varOut, accumOut, accumUpdateOut | ||
| 21 | + */ | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | +using namespace ge; | ||
| 27 | + | ||
| 28 | +namespace ops { | ||
| 29 | + | ||
| 30 | +static ge::graphStatus InferShape4ApplyAdadelta(gert::InferShapeContext* context) | ||
| 31 | +{ | ||
| 32 | + // Input(0) = var | ||
| 33 | + const gert::Shape* inputShape = context->GetInputShape(0); | ||
| 34 | + if (inputShape == nullptr) { | ||
| 35 | + return ge::GRAPH_FAILED; | ||
OP_CHECK_NULL_WITH_CONTEXT判断即可 ![]() ![]() | |||
| 36 | + } | ||
| 37 | + | ||
| 38 | + // 3 outputs: varOut(0), accumOut(1), accumUpdateOut(2) | ||
| 39 | + for (size_t i = 0; i < 3; i++) { | ||
| 40 | + gert::Shape* outputShape = context->GetOutputShape(i); | ||
| 41 | + if (outputShape == nullptr) { | ||
OP_CHECK_NULL_WITH_CONTEXT判断即可 ![]() ![]() | |||
| 42 | + return ge::GRAPH_FAILED; | ||
| 43 | + } | ||
| 44 | + *outputShape = *inputShape; | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + return ge::GRAPH_SUCCESS; | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +IMPL_OP_INFERSHAPE(ApplyAdadelta).InferShape(InferShape4ApplyAdadelta); | ||
| 51 | + | ||
| 52 | +} // namespace ops | ||
| @@ -0,0 +1,220 @@ | |||
| 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 | + * \file apply_adadelta_tiling.cpp | ||
| 17 | + * \brief ApplyAdadelta tiling implementation (arch35) | ||
| 18 | + * | ||
| 19 | + * TilingKey selection: | ||
| 20 | + * TilingKey_0: D_T_X=C_DT_FLOAT, BUFFER_MODE=0 (FP32, single buffer) | ||
【建议级】MIN_SPLIT_THRESHOLD 阈值偏小 当前阈值 建议:
![]() ![]() | |||
| 21 | + * TilingKey_1: D_T_X=C_DT_FLOAT, BUFFER_MODE=1 (FP32, double buffer) | ||
| 22 | + * TilingKey_2: D_T_X=C_DT_FLOAT16, BUFFER_MODE=0 (FP16, single buffer) | ||
| 23 | + * TilingKey_3: D_T_X=C_DT_FLOAT16, BUFFER_MODE=1 (FP16, double buffer) | ||
| 24 | + * | ||
| 25 | + * Scalar parameters (lr, rho, epsilon) are passed via TilingData. | ||
| 26 | + * The ACLNN layer reads aclScalar values and writes them into attrs | ||
| 27 | + * named "lr", "rho", "epsilon" for the Tiling function to extract. | ||
| 28 | + */ | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + | ||
【建议级】GetTensorSlots 注释表述易混淆 注释说
建议重新表述为:
![]() ![]() | |||
| 36 | + | ||
| 37 | +namespace optiling { | ||
| 38 | + | ||
| 39 | +using Ops::Base::CeilDiv; | ||
| 40 | +using Ops::Base::CeilAlign; | ||
| 41 | +using Ops::Base::FloorDiv; | ||
| 42 | +using Ops::Base::FloorAlign; | ||
| 43 | +using Ops::Base::GetUbBlockSize; | ||
| 44 | + | ||
| 45 | +constexpr int64_t MIN_SPLIT_THRESHOLD = 1024; | ||
| 46 | + | ||
| 47 | +// UB tensor slot count per TilingKey (see DESIGN.md 3.4.7/3.5.6/3.6.6/3.7.6) | ||
| 48 | +// | ||
| 49 | +// Per-element UB byte cost (in fp32-equivalent slots; 1 slot == 4 B == sizeof(float)): | ||
| 50 | +// FP32 single buffer: 7 TQue (fp32, 4B) x 1 + 2 TBuf (fp32, 4B) | ||
| 51 | +// = (7 * 4 + 2 * 4) B/elem = 36 B/elem = 9 slots | ||
| 52 | +// FP32 double buffer: 7 TQue (fp32, 4B) x 2 + 2 TBuf (fp32, 4B) | ||
| 53 | +// = (7 * 4 * 2 + 2 * 4) B/elem = 64 B/elem = 16 slots | ||
| 54 | +// FP16 single buffer: 7 TQue (half, 2B) x 1 + 6 TBuf (fp32, 4B) | ||
| 55 | +// = (7 * 2 + 6 * 4) B/elem = 38 B/elem | ||
| 56 | +// => ceil(38 / 4) = 10 slots | ||
| 57 | +// FP16 double buffer: 7 TQue (half, 2B) x 2 + 6 TBuf (fp32, 4B) | ||
| 58 | +// = (7 * 2 * 2 + 6 * 4) B/elem = 52 B/elem = 13 slots | ||
| 59 | +// | ||
| 60 | +// Note: BUFFER_MODE only affects TQue (which may be double-buffered via | ||
| 61 | +// BUFFER_NUM=2); TBuf is NEVER double-buffered regardless of BUFFER_MODE. | ||
| 62 | +// The "+2 TBuf" / "+6 fp32 TBuf" terms above stay constant across both | ||
| 63 | +// single-buffer and double-buffer paths. | ||
| 64 | +static int64_t GetTensorSlots(ge::DataType dataType, uint64_t bufferMode) | ||
| 65 | +{ | ||
| 66 | + if (dataType == ge::DT_FLOAT) { | ||
| 67 | + // FP32: see header comment for derivation | ||
| 68 | + return bufferMode ? 16 : 9; | ||
| 69 | + } else { | ||
魔鬼数字 ![]() ![]() | |||
| 70 | + // FP16: see header comment for derivation | ||
| 71 | + return bufferMode ? 13 : 10; | ||
| 72 | + } | ||
| 73 | +} | ||
| 74 | + | ||
| 75 | +static ge::graphStatus GetPlatformInfo(gert::TilingContext* context, uint64_t& ubSize, int64_t& coreNum) | ||
| 76 | +{ | ||
| 77 | + fe::PlatFormInfos* platformInfoPtr = context->GetPlatformInfo(); | ||
| 78 | + OP_CHECK_NULL_WITH_CONTEXT(context, platformInfoPtr); | ||
| 79 | + auto ascendcPlatform = platform_ascendc::PlatformAscendC(platformInfoPtr); | ||
| 80 | + coreNum = ascendcPlatform.GetCoreNumAiv(); | ||
| 81 | + OP_CHECK_IF(coreNum <= 0, OP_LOGE(context, "coreNum must be > 0, got %ld", coreNum), return ge::GRAPH_FAILED); | ||
| 82 | + ascendcPlatform.GetCoreMemSize(platform_ascendc::CoreMemType::UB, ubSize); | ||
| 83 | + OP_CHECK_IF(ubSize == 0, OP_LOGE(context, "ubSize is 0"), return ge::GRAPH_FAILED); | ||
| 84 | + return ge::GRAPH_SUCCESS; | ||
| 85 | +} | ||
| 86 | + | ||
| 87 | +// Attr order matches op_def: lr(0), rho(1), epsilon(2). | ||
| 88 | +// epsilon == 0 would trigger Div-by-zero in sqrt(accum+eps) denominator. | ||
| 89 | +static ge::graphStatus ParseScalarAttrs(gert::TilingContext* context, ApplyAdadeltaTilingData* tiling) | ||
| 90 | +{ | ||
| 91 | + auto* attrs = context->GetAttrs(); | ||
| 92 | + OP_CHECK_NULL_WITH_CONTEXT(context, attrs); | ||
| 93 | + const float* lrPtr = attrs->GetFloat(0); | ||
| 94 | + OP_CHECK_NULL_WITH_CONTEXT(context, lrPtr); | ||
| 95 | + const float* rhoPtr = attrs->GetFloat(1); | ||
| 96 | + OP_CHECK_NULL_WITH_CONTEXT(context, rhoPtr); | ||
| 97 | + const float* epsPtr = attrs->GetFloat(2); | ||
| 98 | + OP_CHECK_NULL_WITH_CONTEXT(context, epsPtr); | ||
| 99 | + OP_CHECK_IF( | ||
| 100 | + *epsPtr <= 0.0f, | ||
| 101 | + OP_LOGE(context, "epsilon must be > 0, got %f", *epsPtr), | ||
日志有最新要求的整改,请按照最新日志要求整改日志 ![]() ![]() | |||
| 102 | + return ge::GRAPH_FAILED); | ||
| 103 | + tiling->lr = *lrPtr; | ||
| 104 | + tiling->rho = *rhoPtr; | ||
| 105 | + tiling->oneMinusRho = 1.0f - *rhoPtr; | ||
| 106 | + tiling->epsilon = *epsPtr; | ||
| 107 | + return ge::GRAPH_SUCCESS; | ||
| 108 | +} | ||
| 109 | + | ||
| 110 | +static int64_t ComputeSplit(ApplyAdadeltaTilingData* tiling, ge::DataType dataType, | ||
| 111 | + uint64_t ubSize, int64_t coreNum, int64_t ubBlockSize, uint64_t& bufferMode) | ||
| 112 | +{ | ||
| 113 | + tiling->blockFactor = CeilAlign(CeilDiv(tiling->totalNum, coreNum), ubBlockSize); | ||
| 114 | + int64_t usedCoreNum = CeilDiv(tiling->totalNum, tiling->blockFactor); | ||
| 115 | + bufferMode = (tiling->totalNum > MIN_SPLIT_THRESHOLD) ? 1 : 0; | ||
| 116 | + int64_t slots = GetTensorSlots(dataType, bufferMode); | ||
| 117 | + constexpr int64_t elemBytes = 4; // Internal computation in fp32 | ||
| 118 | + tiling->ubFactor = FloorAlign( | ||
| 119 | + FloorDiv(static_cast<int64_t>(ubSize) / elemBytes, slots), ubBlockSize); | ||
| 120 | + return usedCoreNum; | ||
| 121 | +} | ||
| 122 | + | ||
| 123 | +static ge::graphStatus GetShapeAndDataType(gert::TilingContext* context, | ||
| 124 | + int64_t& totalNum, ge::DataType& dataType) | ||
| 125 | +{ | ||
| 126 | + auto varShape = context->GetInputShape(0); | ||
| 127 | + OP_CHECK_NULL_WITH_CONTEXT(context, varShape); | ||
| 128 | + totalNum = varShape->GetStorageShape().GetShapeSize(); | ||
| 129 | + auto varDesc = context->GetInputDesc(0); | ||
| 130 | + OP_CHECK_NULL_WITH_CONTEXT(context, varDesc); | ||
| 131 | + dataType = varDesc->GetDataType(); | ||
| 132 | + return ge::GRAPH_SUCCESS; | ||
| 133 | +} | ||
| 134 | + | ||
| 135 | +static ge::graphStatus SetWorkspace(gert::TilingContext* context) | ||
| 136 | +{ | ||
| 137 | + size_t* ws = context->GetWorkspaceSizes(1); | ||
| 138 | + OP_CHECK_NULL_WITH_CONTEXT(context, ws); | ||
| 139 | + fe::PlatFormInfos* platformInfoPtr = context->GetPlatformInfo(); | ||
| 140 | + OP_CHECK_NULL_WITH_CONTEXT(context, platformInfoPtr); | ||
| 141 | + auto ascendcPlatform = platform_ascendc::PlatformAscendC(platformInfoPtr); | ||
| 142 | + // User workspace is 0 (operator needs no extra workspace), system workspace | ||
| 143 | + // is obtained from AscendC platform interface to stay consistent with framework | ||
| 144 | + // expectations across SoC versions. | ||
| 145 | + ws[0] = static_cast<size_t>(ascendcPlatform.GetLibApiWorkSpaceSize()); | ||
| 146 | + return ge::GRAPH_SUCCESS; | ||
| 147 | +} | ||
| 148 | + | ||
| 149 | +static ge::graphStatus HandleEmptyTensor(gert::TilingContext* context, uint32_t dTypeX) | ||
| 150 | +{ | ||
| 151 | + context->SetBlockDim(1); | ||
| 152 | + uint32_t bufMode = 0; | ||
| 153 | + ASCENDC_TPL_SEL_PARAM(context, dTypeX, bufMode); | ||
| 154 | + return ge::GRAPH_SUCCESS; | ||
| 155 | +} | ||
| 156 | + | ||
| 157 | +static ge::graphStatus InitTilingData(gert::TilingContext* context, | ||
| 158 | + int64_t totalNum, ApplyAdadeltaTilingData*& tiling) | ||
| 159 | +{ | ||
| 160 | + tiling = context->GetTilingData<ApplyAdadeltaTilingData>(); | ||
| 161 | + OP_CHECK_NULL_WITH_CONTEXT(context, tiling); | ||
| 162 | + OP_CHECK_IF( | ||
| 163 | + memset_s(tiling, sizeof(*tiling), 0, sizeof(*tiling)) != EOK, | ||
| 164 | + OP_LOGE(context, "set tiling data error"), return ge::GRAPH_FAILED); | ||
| 165 | + tiling->totalNum = totalNum; | ||
| 166 | + return ge::GRAPH_SUCCESS; | ||
| 167 | +} | ||
| 168 | + | ||
| 169 | +static ge::graphStatus ApplyAdadeltaTilingFunc(gert::TilingContext* context) | ||
| 170 | +{ | ||
| 171 | + uint64_t ubSize; | ||
| 172 | + int64_t coreNum; | ||
| 173 | + OP_CHECK_IF( | ||
| 174 | + GetPlatformInfo(context, ubSize, coreNum) != ge::GRAPH_SUCCESS, | ||
| 175 | + OP_LOGE(context, "GetPlatformInfo error"), return ge::GRAPH_FAILED); | ||
| 176 | + | ||
| 177 | + int64_t totalNum = 0; | ||
| 178 | + ge::DataType dataType = ge::DT_FLOAT; | ||
| 179 | + OP_CHECK_IF( | ||
| 180 | + GetShapeAndDataType(context, totalNum, dataType) != ge::GRAPH_SUCCESS, | ||
| 181 | + OP_LOGE(context, "GetShapeAndDataType error"), return ge::GRAPH_FAILED); | ||
| 182 | + | ||
| 183 | + OP_CHECK_IF( | ||
| 184 | + SetWorkspace(context) != ge::GRAPH_SUCCESS, | ||
| 185 | + OP_LOGE(context, "SetWorkspace error"), return ge::GRAPH_FAILED); | ||
| 186 | + | ||
WS_SYS_SIZE 改为用接口获取的ascendc的workspace,不要用自己写的0或者16m或者32b这种数字,asendc接口是ascendcPlatform.GetLibApiWorkSpaceSize(); ![]() ![]() | |||
| 187 | + uint32_t dTypeX = static_cast<uint32_t>(dataType); | ||
| 188 | + if (totalNum == 0) { | ||
| 189 | + return HandleEmptyTensor(context, dTypeX); | ||
| 190 | + } | ||
| 191 | + | ||
| 192 | + ApplyAdadeltaTilingData* tiling = nullptr; | ||
| 193 | + OP_CHECK_IF( | ||
| 194 | + InitTilingData(context, totalNum, tiling) != ge::GRAPH_SUCCESS, | ||
| 195 | + OP_LOGE(context, "InitTilingData error"), return ge::GRAPH_FAILED); | ||
| 196 | + | ||
| 197 | + uint64_t bufferMode = 0; | ||
| 198 | + int64_t usedCoreNum = ComputeSplit(tiling, dataType, ubSize, coreNum, GetUbBlockSize(context), bufferMode); | ||
| 199 | + | ||
| 200 | + OP_CHECK_IF( | ||
| 201 | + ParseScalarAttrs(context, tiling) != ge::GRAPH_SUCCESS, | ||
| 202 | + OP_LOGE(context, "ParseScalarAttrs error"), return ge::GRAPH_FAILED); | ||
| 203 | + | ||
| 204 | + context->SetBlockDim(usedCoreNum); | ||
| 205 | + ASCENDC_TPL_SEL_PARAM(context, dTypeX, static_cast<uint32_t>(bufferMode)); | ||
| 206 | + return ge::GRAPH_SUCCESS; | ||
| 207 | +} | ||
| 208 | + | ||
| 209 | +static ge::graphStatus TilingParseForApplyAdadelta([[maybe_unused]] gert::TilingParseContext* context) | ||
| 210 | +{ | ||
| 211 | + return ge::GRAPH_SUCCESS; | ||
| 212 | +} | ||
| 213 | + | ||
| 214 | +struct ApplyAdadeltaCompileInfo {}; | ||
| 215 | + | ||
| 216 | +IMPL_OP_OPTILING(ApplyAdadelta) | ||
| 217 | + .Tiling(ApplyAdadeltaTilingFunc) | ||
| 218 | + .TilingParse<ApplyAdadeltaCompileInfo>(TilingParseForApplyAdadelta); | ||
| 219 | + | ||
| 220 | +} // namespace optiling | ||
| @@ -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 | + * \file apply_adadelta.cpp | ||
| 17 | + * \brief ApplyAdadelta kernel entry (arch35) | ||
| 18 | + * | ||
| 19 | + * Template parameters (matching apply_adadelta_tiling_key.h): | ||
| 20 | + * - D_T_X: Data type, from ASCENDC_TPL_DATATYPE_DECL | ||
| 21 | + * - BUFFER_MODE: Buffer mode (0=single, 1=double), from ASCENDC_TPL_UINT_DECL | ||
| 22 | + */ | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | +template <typename D_T_X, int BUFFER_MODE> | ||
| 27 | +__global__ __aicore__ void apply_adadelta( | ||
| 28 | + GM_ADDR var, GM_ADDR accum, GM_ADDR accumUpdate, GM_ADDR grad, | ||
| 29 | + GM_ADDR varOut, GM_ADDR accumOut, GM_ADDR accumUpdateOut, | ||
| 30 | + GM_ADDR workspace, GM_ADDR tiling) | ||
| 31 | +{ | ||
| 32 | + REGISTER_TILING_DEFAULT(ApplyAdadeltaTilingData); | ||
| 33 | + GET_TILING_DATA_WITH_STRUCT(ApplyAdadeltaTilingData, tilingData, tiling); | ||
| 34 | + NsApplyAdadelta::ApplyAdadelta<D_T_X, BUFFER_MODE> op; | ||
| 35 | + op.Init(var, accum, accumUpdate, grad, varOut, accumOut, accumUpdateOut, &tilingData); | ||
| 36 | + op.Process(); | ||
| 37 | +} | ||
| @@ -0,0 +1,306 @@ | |||
| 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 | + * \file apply_adadelta.h | ||
| 17 | + * \brief ApplyAdadelta kernel class definition (arch35) | ||
| 18 | + * | ||
| 19 | + * Template parameters: | ||
| 20 | + * - T: Data type (float / half) | ||
| 21 | + * - BUFFER_MODE: Buffer mode (0=single buffer, 1=double buffer) | ||
| 22 | + * | ||
| 23 | + * Adadelta update formula (4 steps): | ||
| 24 | + * Step1: accum_new = rho * accum + (1-rho) * grad^2 | ||
| 25 | + * Step2: update = sqrt(accum_update + eps) / sqrt(accum_new + eps) * grad | ||
| 26 | + * Step3: var_new = var - lr * update | ||
| 27 | + * Step4: accum_update_new = rho * accum_update + (1-rho) * update^2 | ||
| 28 | + */ | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + | ||
| 37 | +namespace NsApplyAdadelta { | ||
| 38 | + | ||
| 39 | +using namespace AscendC; | ||
| 40 | + | ||
| 41 | +template <typename T, int BUFFER_MODE> | ||
| 42 | +class ApplyAdadelta { | ||
| 43 | + static constexpr int32_t BUFFER_NUM = BUFFER_MODE ? 2 : 1; | ||
| 44 | + static constexpr bool IS_FP16 = std::is_same_v<T, half>; | ||
| 45 | + | ||
| 46 | +public: | ||
| 47 | + __aicore__ inline ApplyAdadelta() {} | ||
| 48 | + | ||
| 49 | + __aicore__ inline void Init( | ||
| 50 | + GM_ADDR var, GM_ADDR accum, GM_ADDR accumUpdate, GM_ADDR grad, | ||
| 51 | + GM_ADDR varOut, GM_ADDR accumOut, GM_ADDR accumUpdateOut, | ||
| 52 | + const ApplyAdadeltaTilingData* tilingData); | ||
| 53 | + | ||
| 54 | + __aicore__ inline void Process(); | ||
| 55 | + | ||
| 56 | +private: | ||
| 57 | + __aicore__ inline void CopyIn(int64_t progress, int64_t currentNum); | ||
| 58 | + __aicore__ inline void Compute(int64_t currentNum); | ||
| 59 | + __aicore__ inline void CopyOut(int64_t progress, int64_t currentNum); | ||
| 60 | + | ||
| 61 | +private: | ||
| 62 | + TPipe pipe; | ||
| 63 | + // 4 input queues | ||
| 64 | + TQue<QuePosition::VECIN, BUFFER_NUM> inQueVar; | ||
| 65 | + TQue<QuePosition::VECIN, BUFFER_NUM> inQueAccum; | ||
| 66 | + TQue<QuePosition::VECIN, BUFFER_NUM> inQueAccumUpdate; | ||
| 67 | + TQue<QuePosition::VECIN, BUFFER_NUM> inQueGrad; | ||
| 68 | + // 3 output queues | ||
| 69 | + TQue<QuePosition::VECOUT, BUFFER_NUM> outQueVar; | ||
| 70 | + TQue<QuePosition::VECOUT, BUFFER_NUM> outQueAccum; | ||
| 71 | + TQue<QuePosition::VECOUT, BUFFER_NUM> outQueAccumUpdate; | ||
| 72 | + // FP32 path: 2 temporary buffers for intermediate computation | ||
| 73 | + TBuf<QuePosition::VECCALC> tmpBuf1; // tmp1: grad^2 / den / updateSq etc. | ||
| 74 | + TBuf<QuePosition::VECCALC> tmpBuf2; // tmp2: num / update / lr*update etc. | ||
| 75 | + // FP16 path: 6 fp32 TBuf for Cast workspace + computation | ||
| 76 | + // (4 for Cast-up workspace: var/accum/accumUpdate/grad fp32 copies, | ||
| 77 | + // 2 for intermediate computation: tmp1/tmp2) | ||
| 78 | + TBuf<QuePosition::VECCALC> fp32VarBuf; | ||
| 79 | + TBuf<QuePosition::VECCALC> fp32AccumBuf; | ||
| 80 | + TBuf<QuePosition::VECCALC> fp32AccumUpdateBuf; | ||
| 81 | + TBuf<QuePosition::VECCALC> fp32GradBuf; | ||
| 82 | + | ||
| 83 | + GlobalTensor<T> varGm, accumGm, accumUpdateGm, gradGm; | ||
| 84 | + GlobalTensor<T> varOutGm, accumOutGm, accumUpdateOutGm; | ||
| 85 | + | ||
| 86 | + int64_t blockLength_ = 0; | ||
| 87 | + int64_t ubLength_ = 0; | ||
| 88 | + float lr_ = 0.0f; | ||
| 89 | + float rho_ = 0.0f; | ||
| 90 | + float oneMinusRho_ = 1.0f; | ||
| 91 | + float eps_ = 1e-6f; | ||
| 92 | +}; | ||
| 93 | + | ||
| 94 | +template <typename T, int BUFFER_MODE> | ||
| 95 | +__aicore__ inline void ApplyAdadelta<T, BUFFER_MODE>::Init( | ||
| 96 | + GM_ADDR var, GM_ADDR accum, GM_ADDR accumUpdate, GM_ADDR grad, | ||
| 97 | + GM_ADDR varOut, GM_ADDR accumOut, GM_ADDR accumUpdateOut, | ||
| 98 | + const ApplyAdadeltaTilingData* tilingData) | ||
| 99 | +{ | ||
| 100 | + // Compute per-core element range | ||
| 101 | + int64_t remain = tilingData->totalNum - tilingData->blockFactor * GetBlockIdx(); | ||
| 102 | + blockLength_ = (remain > tilingData->blockFactor) ? tilingData->blockFactor : remain; | ||
| 103 | + ubLength_ = tilingData->ubFactor; | ||
| 104 | + | ||
| 105 | + // Read scalar parameters from TilingData | ||
| 106 | + lr_ = tilingData->lr; | ||
| 107 | + rho_ = tilingData->rho; | ||
| 108 | + eps_ = tilingData->epsilon; | ||
| 109 | + oneMinusRho_ = tilingData->oneMinusRho; | ||
| 110 | + | ||
| 111 | + // Set up GM tensors with per-core offset | ||
| 112 | + int64_t off = tilingData->blockFactor * GetBlockIdx(); | ||
| 113 | + varGm.SetGlobalBuffer((__gm__ T*)var + off, blockLength_); | ||
| 114 | + accumGm.SetGlobalBuffer((__gm__ T*)accum + off, blockLength_); | ||
| 115 | + accumUpdateGm.SetGlobalBuffer((__gm__ T*)accumUpdate + off, blockLength_); | ||
| 116 | + gradGm.SetGlobalBuffer((__gm__ T*)grad + off, blockLength_); | ||
| 117 | + varOutGm.SetGlobalBuffer((__gm__ T*)varOut + off, blockLength_); | ||
| 118 | + accumOutGm.SetGlobalBuffer((__gm__ T*)accumOut + off, blockLength_); | ||
| 119 | + accumUpdateOutGm.SetGlobalBuffer((__gm__ T*)accumUpdateOut + off, blockLength_); | ||
| 120 | + | ||
| 121 | + // Initialize queues | ||
| 122 | + pipe.InitBuffer(inQueVar, BUFFER_NUM, ubLength_ * sizeof(T)); | ||
| 123 | + pipe.InitBuffer(inQueAccum, BUFFER_NUM, ubLength_ * sizeof(T)); | ||
| 124 | + pipe.InitBuffer(inQueAccumUpdate, BUFFER_NUM, ubLength_ * sizeof(T)); | ||
| 125 | + pipe.InitBuffer(inQueGrad, BUFFER_NUM, ubLength_ * sizeof(T)); | ||
| 126 | + pipe.InitBuffer(outQueVar, BUFFER_NUM, ubLength_ * sizeof(T)); | ||
| 127 | + pipe.InitBuffer(outQueAccum, BUFFER_NUM, ubLength_ * sizeof(T)); | ||
| 128 | + pipe.InitBuffer(outQueAccumUpdate, BUFFER_NUM, ubLength_ * sizeof(T)); | ||
| 129 | + | ||
| 130 | + // Initialize temporary buffers based on dtype path | ||
| 131 | + if constexpr (IS_FP16) { | ||
| 132 | + // FP16 path: 4 fp32 Cast workspace + 2 fp32 tmp = 6 fp32 TBuf | ||
| 133 | + pipe.InitBuffer(fp32VarBuf, ubLength_ * sizeof(float)); | ||
| 134 | + pipe.InitBuffer(fp32AccumBuf, ubLength_ * sizeof(float)); | ||
| 135 | + pipe.InitBuffer(fp32AccumUpdateBuf, ubLength_ * sizeof(float)); | ||
| 136 | + pipe.InitBuffer(fp32GradBuf, ubLength_ * sizeof(float)); | ||
| 137 | + pipe.InitBuffer(tmpBuf1, ubLength_ * sizeof(float)); | ||
| 138 | + pipe.InitBuffer(tmpBuf2, ubLength_ * sizeof(float)); | ||
| 139 | + } else { | ||
| 140 | + // FP32 path: 2 fp32 TBuf for intermediate computation | ||
| 141 | + pipe.InitBuffer(tmpBuf1, ubLength_ * sizeof(float)); | ||
| 142 | + pipe.InitBuffer(tmpBuf2, ubLength_ * sizeof(float)); | ||
| 143 | + } | ||
| 144 | +} | ||
| 145 | + | ||
| 146 | +template <typename T, int BUFFER_MODE> | ||
| 147 | +__aicore__ inline void ApplyAdadelta<T, BUFFER_MODE>::CopyIn(int64_t progress, int64_t currentNum) | ||
| 148 | +{ | ||
| 149 | + LocalTensor<T> v = inQueVar.template AllocTensor<T>(); | ||
| 150 | + LocalTensor<T> a = inQueAccum.template AllocTensor<T>(); | ||
| 151 | + LocalTensor<T> au = inQueAccumUpdate.template AllocTensor<T>(); | ||
| 152 | + LocalTensor<T> g = inQueGrad.template AllocTensor<T>(); | ||
| 153 | + | ||
| 154 | + DataCopyExtParams cp; | ||
| 155 | + cp.blockCount = 1; | ||
| 156 | + cp.blockLen = currentNum * sizeof(T); | ||
| 157 | + cp.srcStride = 0; | ||
| 158 | + cp.dstStride = 0; | ||
| 159 | + DataCopyPadExtParams<T> padParams{false, 0, 0, 0}; | ||
| 160 | + int64_t gmOff = progress * ubLength_; | ||
| 161 | + DataCopyPad(v, varGm[gmOff], cp, padParams); | ||
| 162 | + DataCopyPad(a, accumGm[gmOff], cp, padParams); | ||
| 163 | + DataCopyPad(au, accumUpdateGm[gmOff], cp, padParams); | ||
| 164 | + DataCopyPad(g, gradGm[gmOff], cp, padParams); | ||
| 165 | + | ||
| 166 | + inQueVar.EnQue(v); | ||
| 167 | + inQueAccum.EnQue(a); | ||
| 168 | + inQueAccumUpdate.EnQue(au); | ||
| 169 | + inQueGrad.EnQue(g); | ||
| 170 | +} | ||
| 171 | + | ||
| 172 | +template <typename T, int BUFFER_MODE> | ||
| 173 | +__aicore__ inline void ApplyAdadelta<T, BUFFER_MODE>::Compute(int64_t currentNum) | ||
| 174 | +{ | ||
| 175 | + LocalTensor<T> v = inQueVar.template DeQue<T>(); | ||
| 176 | + LocalTensor<T> a = inQueAccum.template DeQue<T>(); | ||
| 177 | + LocalTensor<T> au = inQueAccumUpdate.template DeQue<T>(); | ||
| 178 | + LocalTensor<T> g = inQueGrad.template DeQue<T>(); | ||
| 179 | + | ||
| 180 | + LocalTensor<T> vOut = outQueVar.template AllocTensor<T>(); | ||
| 181 | + LocalTensor<T> aOut = outQueAccum.template AllocTensor<T>(); | ||
| 182 | + LocalTensor<T> auOut = outQueAccumUpdate.template AllocTensor<T>(); | ||
| 183 | + | ||
| 184 | + // Get tmp buffers | ||
| 185 | + LocalTensor<float> tmp1 = tmpBuf1.Get<float>(); | ||
| 186 | + LocalTensor<float> tmp2 = tmpBuf2.Get<float>(); | ||
| 187 | + | ||
| 188 | + if constexpr (IS_FP16) { | ||
你只有这些操作可以用atovoss模板去做,不用自己实现,你现在全部用memory base实现,性能是否可以达标,编译器是否可以做到vf融合?,如果改为atvoss实现,记得ascend-config文件开启vf融合编译选项 ![]() ![]() Nerddddddddddd 4月21日 评论: 4月21日 评论: | |||
| 189 | + // FP16 path: Cast up to fp32 -> compute -> Cast down to fp16 | ||
| 190 | + // Get fp32 workspace buffers | ||
| 191 | + LocalTensor<float> vF = fp32VarBuf.Get<float>(); | ||
| 192 | + LocalTensor<float> aF = fp32AccumBuf.Get<float>(); | ||
| 193 | + LocalTensor<float> auF = fp32AccumUpdateBuf.Get<float>(); | ||
| 194 | + LocalTensor<float> gF = fp32GradBuf.Get<float>(); | ||
| 195 | + | ||
| 196 | + // Cast up: half -> fp32 | ||
| 197 | + Cast(vF, v, RoundMode::CAST_NONE, currentNum); | ||
| 198 | + Cast(aF, a, RoundMode::CAST_NONE, currentNum); | ||
| 199 | + Cast(auF, au, RoundMode::CAST_NONE, currentNum); | ||
| 200 | + Cast(gF, g, RoundMode::CAST_NONE, currentNum); | ||
| 201 | + | ||
| 202 | + // Step1: accum_new = accum * rho + grad^2 * (1 - rho) | ||
| 203 | + Mul(tmp1, gF, gF, currentNum); // tmp1 = grad^2 | ||
| 204 | + Muls(tmp1, tmp1, oneMinusRho_, currentNum); // tmp1 = grad^2 * (1-rho) | ||
| 205 | + Muls(aF, aF, rho_, currentNum); // aF = accum * rho | ||
| 206 | + Add(aF, aF, tmp1, currentNum); // aF = accum_new | ||
| 207 | + | ||
| 208 | + // Step2: update = sqrt(au_old + eps) / sqrt(accum_new + eps) * grad | ||
| 209 | + Adds(tmp1, auF, eps_, currentNum); // tmp1 = au_old + eps | ||
| 210 | + Sqrt(tmp1, tmp1, currentNum); // tmp1 = sqrt(au_old + eps) = numerator | ||
| 211 | + Adds(tmp2, aF, eps_, currentNum); // tmp2 = accum_new + eps | ||
| 212 | + Sqrt(tmp2, tmp2, currentNum); // tmp2 = sqrt(accum_new + eps) = denominator | ||
| 213 | + Div(tmp1, tmp1, tmp2, currentNum); // tmp1 = ratio | ||
| 214 | + Mul(tmp1, tmp1, gF, currentNum); // tmp1 = update | ||
| 215 | + | ||
| 216 | + // Step3: var_new = var - lr * update | ||
| 217 | + Muls(tmp2, tmp1, lr_, currentNum); // tmp2 = lr * update | ||
| 218 | + Sub(vF, vF, tmp2, currentNum); // vF = var_new | ||
| 219 | + | ||
| 220 | + // Step4: accum_update_new = au_old * rho + update^2 * (1 - rho) | ||
| 221 | + Mul(tmp2, tmp1, tmp1, currentNum); // tmp2 = update^2 | ||
| 222 | + Muls(tmp2, tmp2, oneMinusRho_, currentNum); // tmp2 = update^2 * (1-rho) | ||
| 223 | + Muls(auF, auF, rho_, currentNum); // auF = au_old * rho | ||
| 224 | + Add(auF, auF, tmp2, currentNum); // auF = accum_update_new | ||
| 225 | + | ||
| 226 | + // Cast down: fp32 -> half | ||
| 227 | + Cast(vOut, vF, RoundMode::CAST_RINT, currentNum); | ||
| 228 | + Cast(aOut, aF, RoundMode::CAST_RINT, currentNum); | ||
| 229 | + Cast(auOut, auF, RoundMode::CAST_RINT, currentNum); | ||
| 230 | + } else { | ||
| 231 | + // FP32 path: T == float, so v/a/au/g/vOut/aOut/auOut are already | ||
| 232 | + // LocalTensor<float>. No Cast needed; operate directly on them. | ||
| 233 | + | ||
| 234 | + // Step1: accum_new = rho * accum + (1-rho) * grad^2 | ||
| 235 | + Mul(tmp1, g, g, currentNum); // tmp1 = grad^2 | ||
| 236 | + Muls(tmp1, tmp1, oneMinusRho_, currentNum); // tmp1 = grad^2 * (1-rho) | ||
| 237 | + Muls(aOut, a, rho_, currentNum); // aOut = accum * rho | ||
| 238 | + Add(aOut, aOut, tmp1, currentNum); // aOut = accum*rho + grad^2*(1-rho) | ||
| 239 | + | ||
| 240 | + // Step2: update = sqrt(accum_update + eps) / sqrt(accum_new + eps) * grad | ||
| 241 | + Adds(tmp1, au, eps_, currentNum); // tmp1 = accum_update + eps | ||
| 242 | + Sqrt(tmp1, tmp1, currentNum); // tmp1 = sqrt(accum_update + eps) = numerator | ||
| 243 | + Adds(tmp2, aOut, eps_, currentNum); // tmp2 = accum_new + eps | ||
| 244 | + Sqrt(tmp2, tmp2, currentNum); // tmp2 = sqrt(accum_new + eps) = denominator | ||
| 245 | + Div(tmp1, tmp1, tmp2, currentNum); // tmp1 = ratio = num / den | ||
| 246 | + Mul(tmp1, tmp1, g, currentNum); // tmp1 = update = ratio * grad | ||
| 247 | + | ||
| 248 | + // Step3: var_new = var - lr * update | ||
| 249 | + Muls(tmp2, tmp1, lr_, currentNum); // tmp2 = lr * update | ||
| 250 | + Sub(vOut, v, tmp2, currentNum); // vOut = var - lr * update | ||
| 251 | + | ||
| 252 | + // Step4: accum_update_new = rho * accum_update + (1-rho) * update^2 | ||
| 253 | + Mul(tmp2, tmp1, tmp1, currentNum); // tmp2 = update^2 | ||
| 254 | + Muls(tmp2, tmp2, oneMinusRho_, currentNum); // tmp2 = update^2 * (1-rho) | ||
| 255 | + Muls(auOut, au, rho_, currentNum); // auOut = accum_update * rho | ||
| 256 | + Add(auOut, auOut, tmp2, currentNum); // auOut = au*rho + update^2*(1-rho) | ||
| 257 | + } | ||
| 258 | + | ||
| 259 | + // Free input tensors | ||
| 260 | + inQueVar.FreeTensor(v); | ||
| 261 | + inQueAccum.FreeTensor(a); | ||
| 262 | + inQueAccumUpdate.FreeTensor(au); | ||
| 263 | + inQueGrad.FreeTensor(g); | ||
| 264 | + | ||
| 265 | + // Enqueue output tensors | ||
| 266 | + outQueVar.EnQue(vOut); | ||
| 267 | + outQueAccum.EnQue(aOut); | ||
| 268 | + outQueAccumUpdate.EnQue(auOut); | ||
| 269 | +} | ||
| 270 | + | ||
| 271 | +template <typename T, int BUFFER_MODE> | ||
| 272 | +__aicore__ inline void ApplyAdadelta<T, BUFFER_MODE>::CopyOut(int64_t progress, int64_t currentNum) | ||
| 273 | +{ | ||
| 274 | + LocalTensor<T> vOut = outQueVar.template DeQue<T>(); | ||
| 275 | + LocalTensor<T> aOut = outQueAccum.template DeQue<T>(); | ||
| 276 | + LocalTensor<T> auOut = outQueAccumUpdate.template DeQue<T>(); | ||
| 277 | + | ||
| 278 | + DataCopyExtParams cp; | ||
| 279 | + cp.blockCount = 1; | ||
| 280 | + cp.blockLen = currentNum * sizeof(T); | ||
| 281 | + cp.srcStride = 0; | ||
| 282 | + cp.dstStride = 0; | ||
【阻塞级】Process() 未实现真正的双缓冲流水线 当前代码串行执行 建议改为 3-stage 流水线实现:
参考: ![]() ![]() | |||
| 283 | + int64_t gmOff = progress * ubLength_; | ||
| 284 | + DataCopyPad(varOutGm[gmOff], vOut, cp); | ||
| 285 | + DataCopyPad(accumOutGm[gmOff], aOut, cp); | ||
| 286 | + DataCopyPad(accumUpdateOutGm[gmOff], auOut, cp); | ||
| 287 | + | ||
| 288 | + outQueVar.FreeTensor(vOut); | ||
| 289 | + outQueAccum.FreeTensor(aOut); | ||
| 290 | + outQueAccumUpdate.FreeTensor(auOut); | ||
| 291 | +} | ||
| 292 | + | ||
| 293 | +template <typename T, int BUFFER_MODE> | ||
| 294 | +__aicore__ inline void ApplyAdadelta<T, BUFFER_MODE>::Process() | ||
| 295 | +{ | ||
| 296 | + int64_t loopCount = (blockLength_ + ubLength_ - 1) / ubLength_; | ||
| 297 | + for (int64_t i = 0; i < loopCount; i++) { | ||
| 298 | + int64_t currentNum = (i == (loopCount - 1)) ? (blockLength_ - ubLength_ * i) : ubLength_; | ||
| 299 | + CopyIn(i, currentNum); | ||
| 300 | + Compute(currentNum); | ||
| 301 | + CopyOut(i, currentNum); | ||
| 302 | + } | ||
| 303 | +} | ||
| 304 | + | ||
| 305 | +} // namespace NsApplyAdadelta | ||
| 306 | + | ||
| @@ -0,0 +1,33 @@ | |||
| 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 | + * \file apply_adadelta_tiling_data.h | ||
| 17 | + * \brief ApplyAdadelta TilingData structure | ||
| 18 | + */ | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | +struct ApplyAdadeltaTilingData { | ||
| 24 | + int64_t totalNum = 0; // Total number of elements | ||
| 25 | + int64_t blockFactor = 0; // Number of elements per core (aligned to UB block) | ||
| 26 | + int64_t ubFactor = 0; // Number of elements per UB loop iteration (aligned to UB block) | ||
| 27 | + float lr = 0.0f; // Learning rate (from aclScalar, stored as fp32) | ||
| 28 | + float rho = 0.0f; // Decay coefficient | ||
| 29 | + float epsilon = 0.0f; // Numerical stability constant | ||
| 30 | + float oneMinusRho = 0.0f; // 1 - rho, pre-computed to avoid Kernel redundant computation | ||
| 31 | +}; | ||
| 32 | + | ||
| 33 | + | ||
| @@ -0,0 +1,45 @@ | |||
| 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 | + * \file apply_adadelta_tiling_key.h | ||
| 17 | + * \brief ApplyAdadelta TilingKey template parameter declaration | ||
| 18 | + * | ||
| 19 | + * Template parameters: | ||
| 20 | + * - D_T_X: Data type (C_DT_FLOAT, C_DT_FLOAT16) | ||
| 21 | + * - BUFFER_MODE: Buffer mode (0=single buffer, 1=double buffer) | ||
| 22 | + */ | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | +ASCENDC_TPL_ARGS_DECL(ApplyAdadelta, | ||
| 30 | + ASCENDC_TPL_DATATYPE_DECL(D_T_X, C_DT_FLOAT, C_DT_FLOAT16, ASCENDC_TPL_INPUT(0)), | ||
| 31 | + ASCENDC_TPL_UINT_DECL(BUFFER_MODE, 8, ASCENDC_TPL_UI_LIST, 0, 1) | ||
| 32 | +); | ||
| 33 | + | ||
| 34 | +ASCENDC_TPL_SEL( | ||
| 35 | + ASCENDC_TPL_ARGS_SEL( | ||
| 36 | + ASCENDC_TPL_DATATYPE_SEL(D_T_X, C_DT_FLOAT), | ||
| 37 | + ASCENDC_TPL_UINT_SEL(BUFFER_MODE, ASCENDC_TPL_UI_LIST, 0, 1) | ||
| 38 | + ), | ||
| 39 | + ASCENDC_TPL_ARGS_SEL( | ||
| 40 | + ASCENDC_TPL_DATATYPE_SEL(D_T_X, C_DT_FLOAT16), | ||
| 41 | + ASCENDC_TPL_UINT_SEL(BUFFER_MODE, ASCENDC_TPL_UI_LIST, 0, 1) | ||
| 42 | + ) | ||
| 43 | +); | ||
| 44 | + | ||
| 45 | + | ||


补充PR描述和Issue信息