已合并
[CANNBOT]ThresholdGradV2D适配Ascend950 Ascend实现 #6179
liuyi2025创建于 6月17日
[CANNBOT]ThresholdGradV2D适配Ascend950 Ascend实现 #6179
已合并
共 19 个文件变更+1737-29
| @@ -1,18 +1,16 @@ | |||
| 1 | -# Copyright (c) 2025 Huawei Technologies Co., Ltd. | 1 | +# ---------------------------------------------------------------------------------------------------------- |
| 2 | -# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | 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 | ||
| 3 | # CANN Open Software License Agreement Version 2.0 (the "License"). | 4 | # CANN Open Software License Agreement Version 2.0 (the "License"). |
| 4 | # Please refer to the License for details. You may not use this file except in compliance with the License. | 5 | # Please refer to the License for details. You may not use this file except in compliance with the License. |
| 5 | -# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | 6 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, |
| 6 | # INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | 7 | # INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. |
| 7 | # See LICENSE in the root of the software repository for the full text of the License. | 8 | # See LICENSE in the root of the software repository for the full text of the License. |
| 8 | -#/ | 9 | +# ---------------------------------------------------------------------------------------------------------- |
| 10 | +# Generated By CANNBot | ||
| 9 | 11 | ||
| 10 | -file(GLOB CURRENT_DIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*) | 12 | +# 设置算子定义时支持的芯片类型 |
| 11 | -if(NOT ENABLE_TEST AND NOT BENCHMARK) | 13 | +set(SUPPORT_COMPUTE_UNIT "ascend950") |
| 12 | - list(REMOVE_ITEM CURRENT_DIRS tests) | 14 | +# 设置每种芯片类型对应的tiling文件目录,即采用op_host目录下哪个文件夹下的tiling文件编译 |
| 13 | -endif() | 15 | +set(SUPPORT_TILING_DIR "arch35") |
| 14 | -foreach(SUB_DIR ${CURRENT_DIRS}) | 16 | +add_modules_sources(HOSTNAME ${OPHOST_NAME} MODE PRIVATE DIR ${CMAKE_CURRENT_SOURCE_DIR} OPTYPE threshold_grad_v2_d ACLNNTYPE aclnn_exclude COMPUTE_UNIT ${SUPPORT_COMPUTE_UNIT} TILING_DIR ${SUPPORT_TILING_DIR} DISABLE_IN_OPP TRUE DEPENDENCIES relu_grad) |
| 15 | - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUB_DIR}/CMakeLists.txt") | ||
| 16 | - add_subdirectory(${SUB_DIR}) | ||
| 17 | - endif() | ||
| 18 | -endforeach() | ||
| @@ -1,3 +1,46 @@ | |||
| 1 | -# ThresholdGradV2 | 1 | +# ThresholdGradV2D |
| 2 | 2 | ||
| 3 | -本目录仅包含ThresholdGradV2算子对应的aclnn接口;如您想要贡献该算子的AscendC实现,请参考[贡献流程](../../CONTRIBUTING.md)。 | 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 | +- 算子功能:完成threshold正向操作的反向传播梯度计算。当threshold==0时等价于ReluGrad。 | ||
| 17 | + | ||
| 18 | +- 计算公式: | ||
| 19 | + | ||
| 20 | + $$ | ||
| 21 | + out_i = | ||
| 22 | + \begin{cases} | ||
| 23 | + gradOutput_i, \quad self_i > threshold\\ | ||
| 24 | + 0, \quad self_i \leq threshold | ||
| 25 | + \end{cases} | ||
| 26 | + $$ | ||
| 27 | + | ||
| 28 | +## 参数说明 | ||
| 29 | + | ||
| 30 | +| 参数名 | 输入/输出 | 描述 | 数据类型 | 数据格式 | | ||
| 31 | +| :--------- | :-------- | :----------------------------------------------------------- | :--------------------------------------------- | :------- | | ||
| 32 | +| gradOutput | 输入 | 上游梯度,与self广播兼容。 | FLOAT16、FLOAT、BF16、INT32、INT8、UINT8 | ND | | ||
| 33 | +| self | 输入 | 正向输入,决定门控掩码。 | FLOAT16、FLOAT、BF16、INT32、INT8、UINT8 | ND | | ||
| 34 | +| threshold | 属性 | 阈值标量,OPTIONAL,默认1.0。threshold==0时等价ReluGrad。 | FLOAT | - | | ||
| 35 | +| out | 输出 | 反向梯度,dtype与self一致,shape为广播后形状。 | FLOAT16、FLOAT、BF16、INT32、INT8、UINT8 | ND | | ||
| 36 | + | ||
| 37 | +## 约束说明 | ||
| 38 | + | ||
| 39 | +- gradOutput、self、out三者 dtype一致。 | ||
| 40 | +- threshold==0走ReluGrad路径(regbase上额外支持INT64)。 | ||
| 41 | + | ||
| 42 | +## 调用说明 | ||
| 43 | + | ||
| 44 | +| 调用方式 | 调用样例 | 说明 | | ||
| 45 | +| :--------- | :------------------------------------------------------ | :------------------------------ | | ||
| 46 | +| aclnn调用 | [test_aclnn_threshold_backward.cpp](examples/test_aclnn_threshold_backward.cpp) | 通过aclnnThresholdBackward接口调用 | | ||
| @@ -0,0 +1,152 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2025 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 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + do { \ | ||
| 17 | + if (!(cond)) { \ | ||
| 18 | + return_expr; \ | ||
| 19 | + } \ | ||
| 20 | + } while (0) | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + do { \ | ||
| 24 | + printf(message, ##__VA_ARGS__); \ | ||
| 25 | + } while (0) | ||
| 26 | + | ||
| 27 | +int64_t GetShapeSize(const std::vector<int64_t>& shape) { | ||
| 28 | + int64_t shapeSize = 1; | ||
| 29 | + for (auto i : shape) { | ||
| 30 | + shapeSize *= i; | ||
| 31 | + } | ||
| 32 | + return shapeSize; | ||
| 33 | +} | ||
| 34 | + | ||
| 35 | +int Init(int32_t deviceId, aclrtStream* stream) { | ||
| 36 | + // 固定写法,资源初始化 | ||
| 37 | + auto ret = aclInit(nullptr); | ||
| 38 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret); | ||
| 39 | + ret = aclrtSetDevice(deviceId); | ||
| 40 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret); | ||
| 41 | + ret = aclrtCreateStream(stream); | ||
| 42 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret); | ||
| 43 | + return 0; | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +template <typename T> | ||
| 47 | +int CreateAclTensor(const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr, | ||
| 48 | + aclDataType dataType, aclTensor** tensor) { | ||
| 49 | + auto size = GetShapeSize(shape) * sizeof(T); | ||
| 50 | + // 调用aclrtMalloc申请device侧内存 | ||
| 51 | + auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 52 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret); | ||
| 53 | + // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上 | ||
| 54 | + ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 55 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret); | ||
| 56 | + | ||
| 57 | + // 计算连续tensor的strides | ||
| 58 | + std::vector<int64_t> strides(shape.size(), 1); | ||
| 59 | + for (int64_t i = shape.size() - 2; i >= 0; i--) { | ||
| 60 | + strides[i] = shape[i + 1] * strides[i + 1]; | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + // 调用aclCreateTensor接口创建aclTensor | ||
| 64 | + *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, | ||
| 65 | + shape.data(), shape.size(), *deviceAddr); | ||
| 66 | + return 0; | ||
| 67 | +} | ||
| 68 | + | ||
| 69 | +int main() { | ||
| 70 | + // 1. (固定写法)device/stream初始化,参考acl API手册 | ||
| 71 | + // 根据自己的实际device填写deviceId | ||
| 72 | + int32_t deviceId = 0; | ||
| 73 | + aclrtStream stream; | ||
| 74 | + auto ret = Init(deviceId, &stream); | ||
| 75 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret); | ||
| 76 | + | ||
| 77 | + // 2. 构造输入与输出,需要根据API的接口自定义构造 | ||
| 78 | + std::vector<int64_t> selfShape = {2, 2}; | ||
| 79 | + std::vector<int64_t> gradOutputShape = {2, 2}; | ||
| 80 | + std::vector<int64_t> outShape = {2, 2}; | ||
| 81 | + void* selfDeviceAddr = nullptr; | ||
| 82 | + void* gradOutputDeviceAddr = nullptr; | ||
| 83 | + void* outDeviceAddr = nullptr; | ||
| 84 | + aclTensor* self = nullptr; | ||
| 85 | + aclTensor* gradOutput = nullptr; | ||
| 86 | + aclScalar* threshold = nullptr; | ||
| 87 | + aclTensor* out = nullptr; | ||
| 88 | + std::vector<float> selfHostData = {0.2, 1.2, 2.2, 3.2}; | ||
| 89 | + std::vector<float> gradOutputHostData = {4.5, 4.4, 4.3, 4.2}; | ||
| 90 | + std::vector<float> outHostData = {0.0, 0.0, 0.0, 0.0}; | ||
| 91 | + float thresholdValue = 1.0f; | ||
| 92 | + // 创建self aclTensor | ||
| 93 | + ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_FLOAT, &self); | ||
| 94 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 95 | + // 创建gradOutput aclTensor | ||
| 96 | + ret = CreateAclTensor(gradOutputHostData, gradOutputShape, &gradOutputDeviceAddr, aclDataType::ACL_FLOAT, &gradOutput); | ||
| 97 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 98 | + // 创建threshold aclScalar | ||
| 99 | + threshold = aclCreateScalar(&thresholdValue, aclDataType::ACL_FLOAT); | ||
| 100 | + CHECK_RET(threshold != nullptr, return ret); | ||
| 101 | + // 创建out aclTensor | ||
| 102 | + ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT, &out); | ||
| 103 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 104 | + | ||
| 105 | + // 3. 调用CANN算子库API,需要修改为具体的API名称 | ||
| 106 | + uint64_t workspaceSize = 0; | ||
| 107 | + aclOpExecutor* executor; | ||
| 108 | + // 调用aclnnThresholdBackward第一段接口 | ||
| 109 | + ret = aclnnThresholdBackwardGetWorkspaceSize(gradOutput, self, threshold, out, &workspaceSize, &executor); | ||
| 110 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnThresholdBackwardGetWorkspaceSize failed. ERROR: %d\n", ret); return ret); | ||
| 111 | + // 根据第一段接口计算出的workspaceSize申请device内存 | ||
| 112 | + void* workspaceAddr = nullptr; | ||
| 113 | + if (workspaceSize > 0) { | ||
| 114 | + ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 115 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret); | ||
| 116 | + } | ||
| 117 | + // 调用aclnnThresholdBackward第二段接口 | ||
| 118 | + ret = aclnnThresholdBackward(workspaceAddr, workspaceSize, executor, stream); | ||
| 119 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnThresholdBackward failed. ERROR: %d\n", ret); return ret); | ||
| 120 | + | ||
| 121 | + // 4. (固定写法)同步等待任务执行结束 | ||
| 122 | + ret = aclrtSynchronizeStream(stream); | ||
| 123 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret); | ||
| 124 | + | ||
| 125 | + // 5. 获取输出的值,将device侧内存上的结果拷贝至host侧,需要根据具体API的接口定义修改 | ||
| 126 | + auto size = GetShapeSize(outShape); | ||
| 127 | + std::vector<float> resultData(size, 0); | ||
| 128 | + ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, | ||
| 129 | + size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST); | ||
| 130 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret); | ||
| 131 | + for (int64_t i = 0; i < size; i++) { | ||
| 132 | + LOG_PRINT("result[%ld] is: %f\n", i, resultData[i]); | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + // 6. 释放aclTensor和aclScalar,需要根据具体API的接口定义修改 | ||
| 136 | + aclDestroyTensor(self); | ||
| 137 | + aclDestroyTensor(gradOutput); | ||
| 138 | + aclDestroyScalar(threshold); | ||
| 139 | + aclDestroyTensor(out); | ||
| 140 | + | ||
| 141 | + // 7. 释放device资源,需要根据具体API的接口定义修改 | ||
| 142 | + aclrtFree(selfDeviceAddr); | ||
| 143 | + aclrtFree(gradOutputDeviceAddr); | ||
| 144 | + aclrtFree(outDeviceAddr); | ||
| 145 | + if (workspaceSize > 0) { | ||
| 146 | + aclrtFree(workspaceAddr); | ||
| 147 | + } | ||
| 148 | + aclrtDestroyStream(stream); | ||
| 149 | + aclrtResetDevice(deviceId); | ||
| 150 | + aclFinalize(); | ||
| 151 | + return 0; | ||
| 152 | +} | ||
Ractivation/threshold_grad_v2_d/op_host/op_api/aclnn_threshold_backward.cpp→activation/threshold_grad_v2_d/op_api/aclnn_threshold_backward.cpp+1-1
| @@ -8,7 +8,7 @@ | |||
| 8 | * See LICENSE in the root of the software repository for the full text of the License. | 8 | * See LICENSE in the root of the software repository for the full text of the License. |
| 9 | */ | 9 | */ |
| 10 | 10 | ||
| 11 | -#include "../../../relu_grad/op_api/relu_grad.h" | 11 | +#include "../../relu_grad/op_api/relu_grad.h" |
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | 14 | ||
Ractivation/threshold_grad_v2_d/op_host/op_api/aclnn_threshold_backward.h→activation/threshold_grad_v2_d/op_api/aclnn_threshold_backward.h+5-5
| @@ -38,12 +38,12 @@ extern "C" { | |||
| 38 | * G --> H[(out)] | 38 | * G --> H[(out)] |
| 39 | * ``` | 39 | * ``` |
| 40 | * | 40 | * |
| 41 | - * @param [in] gradOutput: npu device侧的aclTensor,数据类型支持FLOAT、BFLOAT16、FLOAT16、INT32、INT8、UINT8,shape需要与self一致。 | 41 | + * @param [in] gradOutput: npu device侧的aclTensor,数据类型支持FLOAT、FLOAT16、BFLOAT16、INT32、INT8、UINT8,shape需要与self一致。 |
| 42 | * 支持非连续的Tensor,数据格式支持ND,且数据格式需要与self一致。 | 42 | * 支持非连续的Tensor,数据格式支持ND,且数据格式需要与self一致。 |
| 43 | - * @param [in] self: npu device侧的aclTensor,数据类型支持FLOAT、BFLOAT16、FLOAT16、INT32、INT8、UINT8。 | 43 | + * @param [in] self: npu device侧的aclTensor,数据类型支持FLOAT、FLOAT16、BFLOAT16、INT32、INT8、UINT8。 |
| 44 | * 支持非连续的Tensor,数据格式支持ND。 | 44 | * 支持非连续的Tensor,数据格式支持ND。 |
| 45 | * @param [in] threshold: host侧的aclScalar,数据类型需要可转换成self与other推导后的数据类型。 | 45 | * @param [in] threshold: host侧的aclScalar,数据类型需要可转换成self与other推导后的数据类型。 |
| 46 | - * @param [in] out: npu device侧的aclTensor,数据类型支持FLOAT、BFLOAT16、FLOAT16、INT32、INT8、UINT8,shape需要与self一致。 | 46 | + * @param [in] out: npu device侧的aclTensor,数据类型支持FLOAT、FLOAT16、BFLOAT16、INT32、INT8、UINT8,shape需要与self一致。 |
| 47 | * 支持非连续的Tensor,数据格式支持ND,且数据格式需要与self一致。 | 47 | * 支持非连续的Tensor,数据格式支持ND,且数据格式需要与self一致。 |
| 48 | * @param [out] workspaceSize: 返回用户需要在npu device侧申请的workspace大小。 | 48 | * @param [out] workspaceSize: 返回用户需要在npu device侧申请的workspace大小。 |
| 49 | * @param [out] executor: 返回op执行器,包含算子计算流程。 | 49 | * @param [out] executor: 返回op执行器,包含算子计算流程。 |
| @@ -53,10 +53,10 @@ ACLNN_API aclnnStatus aclnnThresholdBackwardGetWorkspaceSize(const aclTensor *gr | |||
| 53 | const aclScalar *threshold, aclTensor *out, | 53 | const aclScalar *threshold, aclTensor *out, |
| 54 | uint64_t *workspaceSize, aclOpExecutor **executor); | 54 | uint64_t *workspaceSize, aclOpExecutor **executor); |
| 55 | /** | 55 | /** |
| 56 | - * @brief aclnnAdd的第二段接口,用于执行计算。 | 56 | + * @brief aclnnThresholdBackward的第二段接口,用于执行计算。 |
| 57 | * | 57 | * |
| 58 | * @param [in] workspace: 在npu device侧申请的workspace内存起址。 | 58 | * @param [in] workspace: 在npu device侧申请的workspace内存起址。 |
| 59 | - * @param [in] workspaceSize: 在npu device侧申请的workspace大小,由第一段接口aclnnAddGetWorkspaceSize获取。 | 59 | + * @param [in] workspaceSize: 在npu device侧申请的workspace大小,由第一段接口aclnnThresholdBackwardGetWorkspaceSize获取。 |
| 60 | * @param [in] stream: acl stream流。 | 60 | * @param [in] stream: acl stream流。 |
| 61 | * @param [in] executor: op执行器,包含了算子计算流程。 | 61 | * @param [in] executor: op执行器,包含了算子计算流程。 |
| 62 | * @return aclnnStatus: 返回状态码。 | 62 | * @return aclnnStatus: 返回状态码。 |
Ractivation/threshold_grad_v2_d/op_host/op_api/threshold_grad.cpp→activation/threshold_grad_v2_d/op_api/threshold_grad.cpp+0-0
文件重命名但无更改。
Ractivation/threshold_grad_v2_d/op_host/op_api/threshold_grad.h→activation/threshold_grad_v2_d/op_api/threshold_grad.h+0-0
文件重命名但无更改。
| @@ -0,0 +1,301 @@ | |||
| 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 | +/* Generated By CANNBot */ | ||
| 12 | + | ||
| 13 | +/*! | ||
| 14 | + * \file threshold_grad_v2_d_tiling_arch35.cpp | ||
| 15 | + * \brief ThresholdGradV2D Tiling — arch35 (RegBase Broadcast 范式) | ||
| 16 | + * PadAndSqueeze → FindSplitAxis → MultiCoreSplit, 无 workspace | ||
| 17 | + * 目标架构: DAV_3510 (ascend950) | ||
| 18 | + */ | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | +namespace optiling { | ||
| 31 | + | ||
| 32 | +namespace tgv2d { | ||
| 33 | + | ||
| 34 | +constexpr int64_t THRESHOLD_ATTR_IDX = 0; | ||
| 35 | +constexpr float DEFAULT_THRESHOLD = 1.0f; | ||
| 36 | + | ||
| 37 | +// 5.0 CheckBroadcastShape — 补 1 后逐维检查 broadcast 兼容性:同维非 1 大小必须一致 | ||
| 38 | +static bool CheckBroadcastShape( | ||
| 39 | + const std::vector<std::vector<int64_t>>& padded_in, | ||
| 40 | + const std::vector<std::vector<int64_t>>& padded_out, | ||
| 41 | + int64_t max_rank) | ||
| 42 | +{ | ||
| 43 | + for (int64_t d = 0; d < max_rank; d++) { | ||
| 44 | + int64_t ref = -1; | ||
| 45 | + for (size_t i = 0; i < padded_in.size(); i++) { | ||
| 46 | + if (padded_in[i][d] != 1) { | ||
| 47 | + if (ref == -1) ref = padded_in[i][d]; | ||
| 48 | + else if (padded_in[i][d] != ref) { | ||
| 49 | + OP_LOGE("PadAndSqueeze", "dim %d broadcast incompatible: input[%d] size %d != %d", | ||
| 50 | + (int)d, (int)i, (int)padded_in[i][d], (int)ref); | ||
| 51 | + return false; | ||
| 52 | + } | ||
| 53 | + } | ||
| 54 | + } | ||
| 55 | + for (size_t i = 0; i < padded_out.size(); i++) { | ||
| 56 | + if (padded_out[i][d] != 1) { | ||
| 57 | + if (ref == -1) ref = padded_out[i][d]; | ||
| 58 | + else if (padded_out[i][d] != ref) { | ||
| 59 | + OP_LOGE("PadAndSqueeze", "dim %d broadcast incompatible: output[%d] size %d != %d", | ||
| 60 | + (int)d, (int)i, (int)padded_out[i][d], (int)ref); | ||
| 61 | + return false; | ||
| 62 | + } | ||
| 63 | + } | ||
| 64 | + } | ||
| 65 | + } | ||
| 66 | + return true; | ||
| 67 | +} | ||
| 68 | + | ||
| 69 | +bool PadAndSqueeze( | ||
| 70 | + const std::vector<std::vector<int64_t>>& input_shapes, | ||
| 71 | + const std::vector<std::vector<int64_t>>& output_shapes, | ||
| 72 | + std::vector<int64_t>& maximum_bro_shape, | ||
| 73 | + std::vector<std::vector<int64_t>>& normal_input_shapes, | ||
| 74 | + std::vector<std::vector<int64_t>>& normal_output_shapes) | ||
| 75 | +{ | ||
| 76 | + int64_t num_inputs = (int64_t)input_shapes.size(); | ||
| 77 | + int64_t num_outputs = (int64_t)output_shapes.size(); | ||
| 78 | + int64_t max_rank = 0; | ||
| 79 | + for (auto& s : input_shapes) max_rank = std::max(max_rank, (int64_t)s.size()); | ||
| 80 | + for (auto& s : output_shapes) max_rank = std::max(max_rank, (int64_t)s.size()); | ||
| 81 | + auto pad = [&](const std::vector<int64_t>& s) { | ||
| 82 | + std::vector<int64_t> p; | ||
| 83 | + p.assign(max_rank - (int64_t)s.size(), 1); | ||
| 84 | + p.insert(p.end(), s.begin(), s.end()); | ||
| 85 | + return p; | ||
| 86 | + }; | ||
| 87 | + std::vector<std::vector<int64_t>> padded_in(num_inputs), padded_out(num_outputs); | ||
| 88 | + for (int64_t i = 0; i < num_inputs; i++) padded_in[i] = pad(input_shapes[i]); | ||
| 89 | + for (int64_t i = 0; i < num_outputs; i++) padded_out[i] = pad(output_shapes[i]); | ||
| 90 | + // 校验 — 补 1 后逐维 broadcast 兼容性,不兼容返回 false(调用方转 GRAPH_FAILED) | ||
| 91 | + if (!CheckBroadcastShape(padded_in, padded_out, max_rank)) return false; | ||
| 92 | + maximum_bro_shape.clear(); | ||
| 93 | + normal_input_shapes.assign(num_inputs, std::vector<int64_t>()); | ||
| 94 | + normal_output_shapes.assign(num_outputs, std::vector<int64_t>()); | ||
| 95 | + for (int64_t d = 0; d < max_rank; d++) { | ||
| 96 | + bool all_one = true; | ||
| 97 | + int64_t max_dim = 0; | ||
| 98 | + for (int64_t i = 0; i < num_inputs; i++) { if (padded_in[i][d] != 1) all_one = false; max_dim = std::max(max_dim, padded_in[i][d]); } | ||
| 99 | + for (int64_t i = 0; i < num_outputs; i++) { if (padded_out[i][d] != 1) all_one = false; max_dim = std::max(max_dim, padded_out[i][d]); } | ||
| 100 | + if (!all_one) { | ||
| 101 | + maximum_bro_shape.push_back(max_dim); | ||
| 102 | + for (int64_t i = 0; i < num_inputs; i++) normal_input_shapes[i].push_back(padded_in[i][d]); | ||
| 103 | + for (int64_t i = 0; i < num_outputs; i++) normal_output_shapes[i].push_back(padded_out[i][d]); | ||
| 104 | + } | ||
| 105 | + } | ||
| 106 | + if (maximum_bro_shape.empty()) { | ||
| 107 | + maximum_bro_shape.push_back(1); | ||
| 108 | + for (int64_t i = 0; i < num_inputs; i++) normal_input_shapes[i].push_back(1); | ||
| 109 | + for (int64_t i = 0; i < num_outputs; i++) normal_output_shapes[i].push_back(1); | ||
| 110 | + } | ||
| 111 | + return true; | ||
| 112 | +} | ||
| 113 | + | ||
| 114 | +bool FindSplitAxis(const std::vector<int64_t>& max_bro_shape, | ||
| 115 | + int64_t ub_per_core, int64_t phys_nodes, SplitResult& out) | ||
| 116 | +{ | ||
| 117 | + if (phys_nodes <= 0) { return false; } | ||
| 118 | + int64_t per_buf_bytes = (ub_per_core / phys_nodes) & ~31LL; | ||
| 119 | + int64_t per_buf_elems = per_buf_bytes / 4; // 统一 FP32 | ||
| 120 | + int64_t rank = (int64_t)max_bro_shape.size(); | ||
| 121 | + int64_t inner = 1; | ||
| 122 | + for (int64_t k = rank - 1; k >= 0; k--) { | ||
| 123 | + if (max_bro_shape[k] * inner > per_buf_elems) { | ||
| 124 | + out.a_i = per_buf_elems / inner; | ||
| 125 | + out.a_o = (max_bro_shape[k] + out.a_i - 1) / out.a_i; | ||
| 126 | + int64_t rem = max_bro_shape[k] % out.a_i; | ||
| 127 | + out.a_i_tail = (rem == 0) ? out.a_i : rem; | ||
| 128 | + out.axis = k; | ||
| 129 | + return true; | ||
| 130 | + } | ||
| 131 | + if (k == 0) { out.axis = 0; out.a_i = max_bro_shape[0]; out.a_o = 1; out.a_i_tail = max_bro_shape[0]; return true; } | ||
| 132 | + inner *= max_bro_shape[k]; | ||
| 133 | + } | ||
| 134 | + return true; | ||
| 135 | +} | ||
| 136 | + | ||
| 137 | +bool MultiCoreSplit(const std::vector<int64_t>& max_bro_shape, | ||
| 138 | + const SplitResult& ub_split, int64_t max_cores, MultiCoreResult& out) | ||
| 139 | +{ | ||
| 140 | + int64_t k = ub_split.axis, outer_prod = 1; | ||
| 141 | + for (int64_t j = 0; j < k; j++) outer_prod *= max_bro_shape[j]; | ||
| 142 | + out.total_tiles = outer_prod * ub_split.a_o; | ||
| 143 | + out.num_cores = (out.total_tiles < max_cores) ? out.total_tiles : max_cores; | ||
| 144 | + out.tiles_main = out.total_tiles / out.num_cores; | ||
| 145 | + out.cores_tail = out.total_tiles % out.num_cores; | ||
| 146 | + return true; | ||
| 147 | +} | ||
| 148 | + | ||
| 149 | +bool PrecomputeStrides(const std::vector<int64_t>& s, std::vector<int64_t>& strides) { | ||
| 150 | + int64_t rank = (int64_t)s.size(); | ||
| 151 | + strides.assign(rank, 0); | ||
| 152 | + for (int64_t d = rank - 1; d >= 0; d--) { | ||
| 153 | + if (s[d] == 1) { strides[d] = 0; continue; } | ||
| 154 | + int64_t prod = 1; | ||
| 155 | + for (int64_t j = d + 1; j < rank; j++) prod *= s[j]; | ||
| 156 | + strides[d] = prod; | ||
| 157 | + } | ||
| 158 | + return true; | ||
| 159 | +} | ||
| 160 | + | ||
| 161 | +} // namespace tgv2d | ||
| 162 | + | ||
| 163 | +struct ThresholdGradV2DCompileInfo { uint64_t coreNum; uint64_t ubSize; }; | ||
| 164 | + | ||
| 165 | +static std::string Arr2String(const int64_t* arr, int64_t n) { | ||
| 166 | + std::ostringstream oss; oss << "["; | ||
| 167 | + if (n > 0) { for (int64_t i = 0; i < n - 1; ++i) oss << arr[i] << ","; oss << arr[n - 1]; } | ||
| 168 | + oss << "]"; return oss.str(); | ||
| 169 | +} | ||
| 170 | + | ||
| 171 | +class ThresholdGradV2DTilingImpl { | ||
| 172 | +public: | ||
| 173 | + explicit ThresholdGradV2DTilingImpl(gert::TilingContext* ctx) : ctx_(ctx) {} | ||
| 174 | + | ||
| 175 | + ge::graphStatus RunTiling() { | ||
| 176 | + ge::graphStatus ret = GetShapeInfo(); | ||
| 177 | + if (ret != ge::GRAPH_SUCCESS) return ret; | ||
| 178 | + int64_t total_out = 1; | ||
| 179 | + for (auto d : raw_output_shapes_[0]) total_out *= d; | ||
| 180 | + if (total_out == 0) { ctx_->SetBlockDim(1); ctx_->SetTilingKey(GET_TPL_TILING_KEY(THRESHOLD_GRAD_V2_D_RANK_4)); return ge::GRAPH_SUCCESS; } | ||
| 181 | + if (rank_ <= 4) { ret = DoTilingAndSet<4>(); ctx_->SetTilingKey(GET_TPL_TILING_KEY(THRESHOLD_GRAD_V2_D_RANK_4)); } | ||
| 182 | + else { ret = DoTilingAndSet<8>(); ctx_->SetTilingKey(GET_TPL_TILING_KEY(THRESHOLD_GRAD_V2_D_RANK_8)); } | ||
| 183 | + return ret; | ||
| 184 | + } | ||
| 185 | + | ||
| 186 | +private: | ||
| 187 | + ge::graphStatus GetShapeInfo() { | ||
| 188 | + fe::PlatFormInfos* platformInfo = ctx_->GetPlatformInfo(); | ||
| 189 | + OP_CHECK_NULL_WITH_CONTEXT(ctx_, platformInfo); | ||
| 190 | + auto ap = platform_ascendc::PlatformAscendC(platformInfo); | ||
| 191 | + coreNum_ = ap.GetCoreNumAiv(); | ||
| 192 | + ap.GetCoreMemSize(platform_ascendc::CoreMemType::UB, ubSize_); | ||
| 193 | + | ||
| 194 | + for (size_t i = 0; i < ctx_->GetComputeNodeInfo()->GetInputsNum(); ++i) { | ||
| 195 | + auto shape = ctx_->GetInputShape(i); | ||
| 196 | + OP_CHECK_NULL_WITH_CONTEXT(ctx_, shape); | ||
| 197 | + std::vector<int64_t> dims; gert::Shape s = shape->GetStorageShape(); | ||
| 198 | + for (size_t d = 0; d < s.GetDimNum(); ++d) dims.push_back(s.GetDim(d)); | ||
| 199 | + if (dims.empty()) dims.push_back(1); // rank0 标量当 1 | ||
| 200 | + raw_input_shapes_.push_back(dims); | ||
| 201 | + } | ||
| 202 | + for (size_t i = 0; i < ctx_->GetComputeNodeInfo()->GetOutputsNum(); ++i) { | ||
| 203 | + auto shape = ctx_->GetOutputShape(i); | ||
| 204 | + OP_CHECK_NULL_WITH_CONTEXT(ctx_, shape); | ||
| 205 | + std::vector<int64_t> dims; gert::Shape s = shape->GetStorageShape(); | ||
| 206 | + for (size_t d = 0; d < s.GetDimNum(); ++d) dims.push_back(s.GetDim(d)); | ||
| 207 | + if (dims.empty()) dims.push_back(1); | ||
| 208 | + raw_output_shapes_.push_back(dims); | ||
| 209 | + } | ||
| 210 | + auto inputDesc = ctx_->GetInputDesc(0); | ||
| 211 | + OP_CHECK_NULL_WITH_CONTEXT(ctx_, inputDesc); | ||
| 212 | + ge::DataType dtype = inputDesc->GetDataType(); | ||
| 213 | + const std::set<ge::DataType> sup = {ge::DT_FLOAT16, ge::DT_FLOAT, ge::DT_BF16, ge::DT_INT32, ge::DT_INT8, ge::DT_UINT8}; | ||
| 214 | + OP_CHECK_IF(sup.count(dtype) == 0, OP_LOGE(ctx_->GetNodeName(), "Unsupported dtype"), return ge::GRAPH_FAILED); | ||
| 215 | + | ||
| 216 | + threshold_ = tgv2d::DEFAULT_THRESHOLD; | ||
| 217 | + const auto* attrs = ctx_->GetAttrs(); | ||
| 218 | + if (attrs != nullptr) { const float* th = attrs->GetFloat(tgv2d::THRESHOLD_ATTR_IDX); if (th) threshold_ = *th; } | ||
| 219 | + | ||
| 220 | + OP_CHECK_IF(!tgv2d::PadAndSqueeze(raw_input_shapes_, raw_output_shapes_, max_bro_shape_, normal_input_shapes_, normal_output_shapes_), | ||
| 221 | + OP_LOGE(ctx_->GetNodeName(), "broadcast shape incompatible"), return ge::GRAPH_FAILED); | ||
| 222 | + rank_ = (int64_t)max_bro_shape_.size(); | ||
| 223 | + OP_CHECK_IF(rank_ > 8, OP_LOGE(ctx_->GetNodeName(), "rank>8 unsupported"), return ge::GRAPH_FAILED); | ||
| 224 | + return ge::GRAPH_SUCCESS; | ||
| 225 | + } | ||
| 226 | + | ||
| 227 | + template<int64_t R> | ||
| 228 | + ge::graphStatus DoTilingAndSet() { | ||
| 229 | + auto* tiling = ctx_->GetTilingData<ThresholdGradV2DTilingData<R>>(); | ||
| 230 | + OP_CHECK_NULL_WITH_CONTEXT(ctx_, tiling); | ||
| 231 | + int64_t ub_per_core = (int64_t)ubSize_; | ||
| 232 | + int64_t per_buf_bytes = (ub_per_core / kPhysNodes) & ~31LL; | ||
| 233 | + tgv2d::FindSplitAxis(max_bro_shape_, ub_per_core, kPhysNodes, tiling->split); | ||
| 234 | + tgv2d::MultiCoreSplit(max_bro_shape_, tiling->split, (int64_t)coreNum_, tiling->multicore); | ||
| 235 | + tiling->per_buf_bytes = per_buf_bytes; | ||
| 236 | + tiling->per_buf_elems = per_buf_bytes / 4; | ||
| 237 | + | ||
| 238 | + int64_t num_in = (int64_t)normal_input_shapes_.size(), num_out = (int64_t)normal_output_shapes_.size(); | ||
| 239 | + std::vector<std::vector<int64_t>> in_strides(num_in), out_strides(num_out); | ||
| 240 | + for (int64_t i = 0; i < num_in; i++) tgv2d::PrecomputeStrides(normal_input_shapes_[i], in_strides[i]); | ||
| 241 | + for (int64_t i = 0; i < num_out; i++) tgv2d::PrecomputeStrides(normal_output_shapes_[i], out_strides[i]); | ||
| 242 | + | ||
| 243 | + tiling->rank = rank_; | ||
| 244 | + tiling->threshold = threshold_; | ||
| 245 | + int64_t delta = R - rank_; | ||
| 246 | + for (int64_t d = 0; d < delta; d++) tiling->max_bro_shape[d] = 1; | ||
| 247 | + for (int64_t d = 0; d < rank_; d++) tiling->max_bro_shape[d + delta] = max_bro_shape_[d]; | ||
| 248 | + tiling->split.axis += delta; | ||
| 249 | + tiling->num_inputs = num_in; tiling->num_outputs = num_out; | ||
| 250 | + for (int64_t i = 0; i < num_in; i++) { | ||
| 251 | + for (int64_t d = 0; d < delta; d++) { tiling->input_shapes[i][d] = 1; tiling->input_strides[i][d] = 0; } | ||
| 252 | + for (int64_t d = 0; d < rank_; d++) { tiling->input_shapes[i][d + delta] = normal_input_shapes_[i][d]; tiling->input_strides[i][d + delta] = in_strides[i][d]; } | ||
| 253 | + } | ||
| 254 | + for (int64_t i = num_in; i < kMaxInputSlots; i++) for (int64_t d = 0; d < R; d++) { tiling->input_shapes[i][d] = 1; tiling->input_strides[i][d] = 0; } | ||
| 255 | + for (int64_t i = 0; i < num_out; i++) { | ||
| 256 | + for (int64_t d = 0; d < delta; d++) { tiling->output_shapes[i][d] = 1; tiling->output_strides[i][d] = 0; } | ||
| 257 | + for (int64_t d = 0; d < rank_; d++) { tiling->output_shapes[i][d + delta] = normal_output_shapes_[i][d]; tiling->output_strides[i][d + delta] = out_strides[i][d]; } | ||
| 258 | + } | ||
| 259 | + for (int64_t i = num_out; i < kMaxOutputSlots; i++) for (int64_t d = 0; d < R; d++) { tiling->output_shapes[i][d] = 1; tiling->output_strides[i][d] = 0; } | ||
| 260 | + | ||
| 261 | + ctx_->SetBlockDim(tiling->multicore.num_cores); | ||
| 262 | + OP_LOGI(ctx_->GetNodeName(), "TGV2D per_buf=%ld rank=%ld->R=%d mb=%s split(ax=%ld ai=%ld ao=%ld tail=%ld) mc(c=%ld t=%ld m=%ld ct=%ld)", | ||
| 263 | + tiling->per_buf_bytes, rank_, (int)R, Arr2String(tiling->max_bro_shape, R).c_str(), | ||
| 264 | + tiling->split.axis, tiling->split.a_i, tiling->split.a_o, tiling->split.a_i_tail, | ||
| 265 | + tiling->multicore.num_cores, tiling->multicore.total_tiles, tiling->multicore.tiles_main, tiling->multicore.cores_tail); | ||
| 266 | + return ge::GRAPH_SUCCESS; | ||
| 267 | + } | ||
| 268 | + | ||
| 269 | + gert::TilingContext* ctx_; | ||
| 270 | + std::vector<std::vector<int64_t>> raw_input_shapes_, raw_output_shapes_; | ||
| 271 | + std::vector<int64_t> max_bro_shape_; | ||
| 272 | + std::vector<std::vector<int64_t>> normal_input_shapes_, normal_output_shapes_; | ||
| 273 | + int64_t rank_ = 0; float threshold_ = 1.0f; | ||
| 274 | + uint64_t coreNum_ = 0, ubSize_ = 0; | ||
| 275 | +}; | ||
| 276 | + | ||
| 277 | +static ge::graphStatus ThresholdGradV2DTilingFunc(gert::TilingContext* context) { | ||
| 278 | + ThresholdGradV2DTilingImpl impl(context); | ||
| 279 | + auto ret = impl.RunTiling(); | ||
| 280 | + if (ret != ge::GRAPH_SUCCESS) return ret; | ||
| 281 | + size_t* ws = context->GetWorkspaceSizes(1); | ||
| 282 | + ws[0] = 0; | ||
| 283 | + return ge::GRAPH_SUCCESS; | ||
| 284 | +} | ||
| 285 | + | ||
| 286 | +static ge::graphStatus TilingPrepareForThresholdGradV2D(gert::TilingParseContext* context) { | ||
| 287 | + fe::PlatFormInfos* platformInfo = context->GetPlatformInfo(); | ||
| 288 | + auto compileInfo = context->GetCompiledInfo<ThresholdGradV2DCompileInfo>(); | ||
| 289 | + OP_CHECK_NULL_WITH_CONTEXT(context, platformInfo); | ||
| 290 | + OP_CHECK_NULL_WITH_CONTEXT(context, compileInfo); | ||
| 291 | + auto ap = platform_ascendc::PlatformAscendC(platformInfo); | ||
| 292 | + compileInfo->coreNum = ap.GetCoreNumAiv(); | ||
| 293 | + ap.GetCoreMemSize(platform_ascendc::CoreMemType::UB, compileInfo->ubSize); | ||
| 294 | + return ge::GRAPH_SUCCESS; | ||
| 295 | +} | ||
| 296 | + | ||
| 297 | +IMPL_OP_OPTILING(ThresholdGradV2D) | ||
| 298 | + .Tiling(ThresholdGradV2DTilingFunc) | ||
| 299 | + .TilingParse<ThresholdGradV2DCompileInfo>(TilingPrepareForThresholdGradV2D); | ||
| 300 | + | ||
| 301 | +} // namespace optiling | ||
| @@ -0,0 +1,59 @@ | |||
| 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 | +/* Generated By CANNBot */ | ||
| 12 | + | ||
| 13 | +/*! | ||
| 14 | + * \file threshold_grad_v2_d_def.cpp | ||
| 15 | + * \brief ThresholdGradV2D 算子定义:gradOutput/self -> out,fp16/fp32/bf16/int32/int8/uint8 | ||
| 16 | + * 属性 threshold(Float, 默认 1.0)。out = self>threshold ? gradOutput : 0 | ||
| 17 | + */ | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +namespace ops { | ||
| 21 | +class ThresholdGradV2D : public OpDef { | ||
| 22 | +public: | ||
| 23 | + explicit ThresholdGradV2D(const char* name) : OpDef(name) | ||
| 24 | + { | ||
| 25 | + this->Input("gradOutput") | ||
| 26 | + .ParamType(REQUIRED) | ||
| 27 | + .DataType({ge::DT_FLOAT16, ge::DT_FLOAT, ge::DT_BF16, ge::DT_INT32, ge::DT_INT8, ge::DT_UINT8}) | ||
| 28 | + .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 29 | + .UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 30 | + .AutoContiguous(); | ||
| 31 | + this->Input("self") | ||
| 32 | + .ParamType(REQUIRED) | ||
| 33 | + .DataType({ge::DT_FLOAT16, ge::DT_FLOAT, ge::DT_BF16, ge::DT_INT32, ge::DT_INT8, ge::DT_UINT8}) | ||
| 34 | + .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 35 | + .UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 36 | + .AutoContiguous(); | ||
| 37 | + this->Output("out") | ||
| 38 | + .ParamType(REQUIRED) | ||
| 39 | + .DataType({ge::DT_FLOAT16, ge::DT_FLOAT, ge::DT_BF16, ge::DT_INT32, ge::DT_INT8, ge::DT_UINT8}) | ||
| 40 | + .Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 41 | + .UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND}) | ||
| 42 | + .AutoContiguous(); | ||
| 43 | + | ||
| 44 | + this->Attr("threshold").AttrType(OPTIONAL).Float(1.0); | ||
| 45 | + | ||
| 46 | + // 目标芯片为 Ascend950PR/DT(arch35)。6 dtype(含 int8/uint8)依赖 arch35 矢量 ISA,arch22 不支持。 | ||
| 47 | + OpAICoreConfig aiCoreConfig; | ||
| 48 | + aiCoreConfig.DynamicCompileStaticFlag(true) | ||
| 49 | + .DynamicFormatFlag(false) | ||
| 50 | + .DynamicRankSupportFlag(true) | ||
| 51 | + .DynamicShapeSupportFlag(true) | ||
| 52 | + .NeedCheckSupportFlag(false) | ||
| 53 | + .PrecisionReduceFlag(true) | ||
| 54 | + .ExtendCfgInfo("opFile.value", "threshold_grad_v2_d"); | ||
| 55 | + this->AICore().AddConfig("ascend950", aiCoreConfig); | ||
| 56 | + } | ||
| 57 | +}; | ||
| 58 | +OP_ADD(ThresholdGradV2D); | ||
| 59 | +} // namespace ops | ||
| @@ -0,0 +1,59 @@ | |||
| 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 | +/* Generated By CANNBot */ | ||
| 12 | + | ||
| 13 | +/*! | ||
| 14 | + * \file threshold_grad_v2_d_infershape.cpp | ||
| 15 | + * \brief ThresholdGradV2D 形状/类型推导: out = broadcast(gradOutput, self), dtype=gradOutput | ||
| 16 | + */ | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | +using namespace ge; | ||
| 24 | + | ||
| 25 | +namespace ops { | ||
| 26 | + | ||
| 27 | +// numpy 广播:取最大 rank,对齐到右,逐维取 max(1 维可广播) | ||
| 28 | +static ge::graphStatus InferShape4ThresholdGradV2D(gert::InferShapeContext* context) | ||
| 29 | +{ | ||
| 30 | + const gert::Shape* g = context->GetInputShape(0); | ||
| 31 | + const gert::Shape* s = context->GetInputShape(1); | ||
| 32 | + OP_CHECK_NULL_WITH_CONTEXT(context, g); | ||
| 33 | + OP_CHECK_NULL_WITH_CONTEXT(context, s); | ||
| 34 | + gert::Shape* out = context->GetOutputShape(0); | ||
| 35 | + OP_CHECK_NULL_WITH_CONTEXT(context, out); | ||
| 36 | + | ||
| 37 | + size_t gn = g->GetDimNum(); | ||
| 38 | + size_t sn = s->GetDimNum(); | ||
| 39 | + size_t rn = gn > sn ? gn : sn; | ||
| 40 | + out->SetDimNum(rn); | ||
| 41 | + for (size_t i = 0; i < rn; ++i) { | ||
| 42 | + int64_t gd = (i < rn - gn) ? 1 : g->GetDim(i - (rn - gn)); | ||
| 43 | + int64_t sd = (i < rn - sn) ? 1 : s->GetDim(i - (rn - sn)); | ||
| 44 | + out->SetDim(i, gd > sd ? gd : sd); | ||
| 45 | + } | ||
| 46 | + return ge::GRAPH_SUCCESS; | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +static ge::graphStatus InferDataType4ThresholdGradV2D(gert::InferDataTypeContext* context) | ||
| 50 | +{ | ||
| 51 | + context->SetOutputDataType(0, context->GetInputDataType(0)); | ||
| 52 | + return ge::GRAPH_SUCCESS; | ||
| 53 | +} | ||
| 54 | + | ||
| 55 | +IMPL_OP_INFERSHAPE(ThresholdGradV2D) | ||
| 56 | + .InferShape(InferShape4ThresholdGradV2D) | ||
| 57 | + .InferDataType(InferDataType4ThresholdGradV2D); | ||
| 58 | + | ||
| 59 | +} // namespace ops | ||
| @@ -0,0 +1,352 @@ | |||
| 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 | +/* Generated By CANNBot */ | ||
| 12 | + | ||
| 13 | +/*! | ||
| 14 | + * \file threshold_grad_v2_d.h | ||
| 15 | + * \brief ThresholdGradV2D Kernel — ThresholdGradV2DKernel<T, RANK> | ||
| 16 | + * RegBase Broadcast 范式, 基于 DESIGN.md §3.4 | ||
| 17 | + * 公式: out = (self > threshold) ? gradOutput : 0 | ||
| 18 | + * - int32/int8/uint8/fp32: 原生零 cast, Compares<T> + Select<T> 直接在 T 寄存器 | ||
| 19 | + * - fp16/bf16: 仅比较项升精度 — self 升 fp32 与 fp32 threshold 在 fp32 域比 self<=th 出 mask, | ||
| 20 | + * grad 仍原生 T (UNPACK_B16) 经 Select<T>(零,grad) 搬出, 不 cast grad, 不全量升精度。 | ||
| 21 | + * 比较域与 golden(fp32) 对齐, 避免 threshold 量化成 fp16 造成边界回归。NaN<=th false 透传。 | ||
| 22 | + * 目标架构: DAV_3510 (arch35) | ||
| 23 | + */ | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | +// ============================================================ | ||
| 30 | +// VF: dst = (self > th) ? grad : 0 (CompareScalar GT + Select, mask 入寄存器零 UB) | ||
| 31 | +// int32/int8/uint8/fp32: T 原生比较 — 无量化, 比较域即 golden 域, 零 cast | ||
| 32 | +// gateMode 1/2: 恒透传/恒置0, grad 原生搬, 零 cast | ||
| 33 | +// ============================================================ | ||
| 34 | +template <typename T> | ||
| 35 | +__simd_vf__ inline void ThresholdGradVF( | ||
| 36 | + __ubuf__ T* dstAddr, __ubuf__ T* selfAddr, __ubuf__ T* gradAddr, | ||
| 37 | + T threshold, int gateMode, uint32_t count, uint32_t oneRepeatSize, uint16_t repeatTimes) | ||
| 38 | +{ | ||
| 39 | + AscendC::Reg::RegTensor<T> selfReg, gradReg, zeroReg, dstReg; | ||
| 40 | + AscendC::Reg::MaskReg cmpMask, mask; | ||
| 41 | + AscendC::Reg::AddrReg aReg; | ||
| 42 | + AscendC::Reg::Duplicate(zeroReg, static_cast<T>(0)); | ||
| 43 | + for (uint16_t i = 0; i < repeatTimes; ++i) { | ||
| 44 | + aReg = AscendC::Reg::CreateAddrReg<T>(i, oneRepeatSize); | ||
| 45 | + uint32_t rem = count - (uint32_t)i * oneRepeatSize; | ||
| 46 | + mask = AscendC::Reg::UpdateMask<T>(rem); | ||
| 47 | + AscendC::Reg::LoadAlign(gradReg, gradAddr, aReg); | ||
| 48 | + if (gateMode == 1) { // 恒透传 grad | ||
| 49 | + dstReg = gradReg; | ||
| 50 | + } else if (gateMode == 2) { // 恒置 0 | ||
| 51 | + dstReg = zeroReg; | ||
| 52 | + } else { // PyTorch: self<=th -> 0 ; 否则(含 NaN, NaN<=th 为 false) grad | ||
| 53 | + AscendC::Reg::LoadAlign(selfReg, selfAddr, aReg); | ||
| 54 | + AscendC::Reg::Compares<T, AscendC::CMPMODE::LE>(cmpMask, selfReg, threshold, mask); | ||
| 55 | + AscendC::Reg::Select<T>(dstReg, zeroReg, gradReg, cmpMask); | ||
| 56 | + } | ||
| 57 | + AscendC::Reg::StoreAlign(dstAddr, dstReg, aReg, mask); | ||
| 58 | + } | ||
| 59 | +} | ||
| 60 | + | ||
| 61 | +// ============================================================ | ||
| 62 | +// VF(fp16/bf16): 仅比较项升 fp32 与 golden 比较域对齐 (self 升 fp32, 与 fp32 threshold 在 fp32 域比 self<=th)。 | ||
| 63 | +// grad 仍原生 T, Select<T>(零,grad) 原生搬 —— 不 cast grad, 不全量升精度。NaN<=th 为 false 透传。 | ||
| 64 | +// 按 fp32 VL 步进 (UNPACK_B16: 一拍 128 个 b16 -> 128 个 fp32, mask 与 b16 lane 1:1)。 | ||
| 65 | +// ============================================================ | ||
| 66 | +template <typename T> | ||
| 67 | +__simd_vf__ inline void ThresholdGradVFb16( | ||
| 68 | + __ubuf__ T* dstAddr, __ubuf__ T* selfAddr, __ubuf__ T* gradAddr, | ||
| 69 | + float threshold, uint32_t count, uint32_t vlF, uint16_t repeatTimes) | ||
| 70 | +{ | ||
| 71 | + static constexpr AscendC::Reg::CastTrait kToFp32{ | ||
| 72 | + AscendC::Reg::RegLayout::ZERO, AscendC::Reg::SatMode::UNKNOWN, | ||
| 73 | + AscendC::Reg::MaskMergeMode::ZEROING, AscendC::RoundMode::CAST_NONE }; | ||
| 74 | + AscendC::Reg::RegTensor<T> selfReg, gradReg, zeroReg, dstReg; | ||
| 75 | + AscendC::Reg::RegTensor<float> selfF; | ||
| 76 | + AscendC::Reg::MaskReg cmpMask, mask; | ||
| 77 | + AscendC::Reg::Duplicate(zeroReg, static_cast<T>(0)); | ||
| 78 | + for (uint16_t i = 0; i < repeatTimes; ++i) { | ||
| 79 | + int32_t off = static_cast<int32_t>(i) * static_cast<int32_t>(vlF); | ||
| 80 | + uint32_t rem = count - (uint32_t)i * vlF; | ||
| 81 | + mask = AscendC::Reg::UpdateMask<float>(rem); | ||
| 82 | + AscendC::Reg::LoadAlign<T, AscendC::Reg::LoadDist::DIST_UNPACK_B16>(selfReg, selfAddr + off); | ||
| 83 | + AscendC::Reg::Cast<float, T, kToFp32>(selfF, selfReg, mask); | ||
| 84 | + AscendC::Reg::Compares<float, AscendC::CMPMODE::LE>(cmpMask, selfF, threshold, mask); | ||
| 85 | + AscendC::Reg::LoadAlign<T, AscendC::Reg::LoadDist::DIST_UNPACK_B16>(gradReg, gradAddr + off); | ||
| 86 | + AscendC::Reg::Select<T>(dstReg, zeroReg, gradReg, cmpMask); | ||
| 87 | + AscendC::Reg::StoreAlign<T, AscendC::Reg::StoreDist::DIST_PACK_B32>(dstAddr + off, dstReg, mask); | ||
| 88 | + } | ||
| 89 | +} | ||
| 90 | + | ||
| 91 | +// ============================================================ | ||
| 92 | +// Kernel 侧辅助函数 (int64_t* 版本) | ||
| 93 | +// ============================================================ | ||
| 94 | +__aicore__ inline void GetCoreRange(int64_t core_id, int64_t tiles_main, int64_t cores_tail, | ||
| 95 | + int64_t& start, int64_t& end) | ||
| 96 | +{ | ||
| 97 | + if (core_id < cores_tail) { | ||
| 98 | + start = core_id * (tiles_main + 1); | ||
| 99 | + end = start + tiles_main + 1; | ||
| 100 | + } else { | ||
| 101 | + start = cores_tail * (tiles_main + 1) + (core_id - cores_tail) * tiles_main; | ||
| 102 | + end = start + tiles_main; | ||
| 103 | + } | ||
| 104 | +} | ||
| 105 | + | ||
| 106 | +__aicore__ inline int64_t GetUBSplitRange(int64_t a_o_off, int64_t a_o, int64_t a_i, int64_t a_i_tail) | ||
| 107 | +{ | ||
| 108 | + return (a_o_off == a_o - 1) ? a_i_tail : a_i; | ||
| 109 | +} | ||
| 110 | + | ||
| 111 | +__aicore__ inline bool FlatToEffectiveCoord(int64_t flat, const int64_t* max_bro_shape, | ||
| 112 | + int64_t rank, int64_t split_axis, int64_t a_i, int64_t a_o, int64_t* eff_coord) | ||
| 113 | +{ | ||
| 114 | + for (int64_t d = 0; d < rank; d++) eff_coord[d] = 0; | ||
| 115 | + if (a_o <= 0) { return false; } | ||
| 116 | + int64_t a_o_off = flat % a_o; | ||
| 117 | + int64_t outer = flat / a_o; | ||
| 118 | + for (int64_t d = split_axis - 1; d >= 0; d--) { | ||
| 119 | + eff_coord[d] = outer % max_bro_shape[d]; | ||
| 120 | + outer /= max_bro_shape[d]; | ||
| 121 | + } | ||
| 122 | + eff_coord[split_axis] = a_o_off * a_i; | ||
| 123 | + return true; | ||
| 124 | +} | ||
| 125 | + | ||
| 126 | +__aicore__ inline int64_t CalcOffset(const int64_t* eff_coord, const int64_t* strides, int64_t rank) | ||
| 127 | +{ | ||
| 128 | + int64_t offset = 0; | ||
| 129 | + for (int64_t d = 0; d < rank; d++) offset += eff_coord[d] * strides[d]; | ||
| 130 | + return offset; | ||
| 131 | +} | ||
| 132 | + | ||
| 133 | +// ============================================================ | ||
| 134 | +// ThresholdGradV2DKernel<T, RANK> | ||
| 135 | +// ============================================================ | ||
| 136 | +template <typename T, int64_t RANK> | ||
| 137 | +class ThresholdGradV2DKernel { | ||
| 138 | + static constexpr int64_t ND = (RANK <= 5) ? RANK : 5; | ||
| 139 | + static constexpr uint32_t VL_T = AscendC::GetVecLen() / sizeof(T); | ||
| 140 | + | ||
| 141 | + AscendC::TPipe pipe_; | ||
| 142 | + const ThresholdGradV2DTilingData<RANK>* td_; | ||
| 143 | + AscendC::GlobalTensor<T> gmIn_[kMaxInputSlots]; | ||
| 144 | + AscendC::GlobalTensor<T> gmOut_[kMaxOutputSlots]; | ||
| 145 | + AscendC::TBuf<AscendC::TPosition::VECCALC> buf_[kPhysNodes]; | ||
| 146 | + AscendC::MultiCopyParams<T, ND> nddmaParams_[kMaxInputSlots]; | ||
| 147 | + int64_t nddmaOuterIters_[kMaxInputSlots]; | ||
| 148 | + int64_t nddma_dims_; | ||
| 149 | + float threshold_; | ||
| 150 | + | ||
| 151 | +public: | ||
| 152 | + static constexpr int IN_GRAD = 0; | ||
| 153 | + static constexpr int IN_SELF = 1; | ||
| 154 | + static constexpr int OUT_Y = 0; | ||
| 155 | + | ||
| 156 | + __aicore__ inline float TypeMin() { | ||
| 157 | + if constexpr (std::is_same_v<T, int8_t>) return -128.0f; | ||
| 158 | + if constexpr (std::is_same_v<T, uint8_t>) return 0.0f; | ||
| 159 | + if constexpr (std::is_same_v<T, int32_t>) return -2147483648.0f; | ||
| 160 | + return -3.4e38f; | ||
| 161 | + } | ||
| 162 | + __aicore__ inline float TypeMax() { | ||
| 163 | + if constexpr (std::is_same_v<T, int8_t>) return 127.0f; | ||
| 164 | + if constexpr (std::is_same_v<T, uint8_t>) return 255.0f; | ||
| 165 | + if constexpr (std::is_same_v<T, int32_t>) return 2147483647.0f; | ||
| 166 | + return 3.4e38f; | ||
| 167 | + } | ||
| 168 | + | ||
| 169 | + __aicore__ inline void Init(GM_ADDR inputs[kMaxInputSlots], GM_ADDR outputs[kMaxOutputSlots], | ||
| 170 | + const ThresholdGradV2DTilingData<RANK>* td) | ||
| 171 | + { | ||
| 172 | + td_ = td; | ||
| 173 | + threshold_ = td_->threshold; | ||
| 174 | + for (int i = 0; i < kMaxInputSlots; i++) gmIn_[i].SetGlobalBuffer((__gm__ T*)inputs[i]); | ||
| 175 | + for (int i = 0; i < kMaxOutputSlots; i++) gmOut_[i].SetGlobalBuffer((__gm__ T*)outputs[i]); | ||
| 176 | + for (int i = 0; i < kPhysNodes; i++) pipe_.InitBuffer(buf_[i], td_->per_buf_bytes); | ||
| 177 | + | ||
| 178 | + const int64_t* dstShape = td_->max_bro_shape; | ||
| 179 | + int64_t k = td_->split.axis; | ||
| 180 | + nddma_dims_ = (RANK - k <= ND) ? (RANK - k) : ND; | ||
| 181 | + for (int inp = 0; inp < kMaxInputSlots; inp++) { | ||
| 182 | + int64_t inner = 1; | ||
| 183 | + int64_t nd = 0; | ||
| 184 | + for (int64_t d = RANK - 1; d >= k && nd < ND; d--) { | ||
| 185 | + nddmaParams_[inp].loopInfo.loopSize[nd] = (d == k) ? 0 : dstShape[d]; | ||
| 186 | + nddmaParams_[inp].loopInfo.loopSrcStride[nd] = td_->input_strides[inp][d]; | ||
| 187 | + nddmaParams_[inp].loopInfo.loopDstStride[nd] = inner; | ||
| 188 | + nddmaParams_[inp].loopInfo.loopLpSize[nd] = 0; | ||
| 189 | + nddmaParams_[inp].loopInfo.loopRpSize[nd] = 0; | ||
| 190 | + inner *= (d == k) ? td_->split.a_i : dstShape[d]; | ||
| 191 | + nd++; | ||
| 192 | + } | ||
| 193 | + for (; nd < ND; nd++) { | ||
| 194 | + nddmaParams_[inp].loopInfo.loopSize[nd] = 1; | ||
| 195 | + nddmaParams_[inp].loopInfo.loopSrcStride[nd] = 0; | ||
| 196 | + nddmaParams_[inp].loopInfo.loopDstStride[nd] = inner; | ||
| 197 | + nddmaParams_[inp].loopInfo.loopLpSize[nd] = 0; | ||
| 198 | + nddmaParams_[inp].loopInfo.loopRpSize[nd] = 0; | ||
| 199 | + } | ||
| 200 | + nddmaOuterIters_[inp] = 1; | ||
| 201 | + for (int64_t d = k; d < RANK - nddma_dims_; d++) | ||
| 202 | + nddmaOuterIters_[inp] *= (d == k) ? td_->split.a_i : dstShape[d]; | ||
| 203 | + } | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + __aicore__ inline void Process() | ||
| 207 | + { | ||
| 208 | + ProcessNative(); | ||
| 209 | + } | ||
| 210 | + | ||
| 211 | +private: | ||
| 212 | + // ============================================================ | ||
| 213 | + // 全 dtype 原生域直算, 零 cast (fp32/fp16/bf16/int32/int8/uint8 同路) | ||
| 214 | + // ============================================================ | ||
| 215 | + __aicore__ inline void ProcessNative() | ||
| 216 | + { | ||
| 217 | + int32_t evMTE2toV = static_cast<int32_t>(GetTPipePtr()->FetchEventID(AscendC::HardEvent::MTE2_V)); | ||
| 218 | + int32_t evVtoMTE3 = static_cast<int32_t>(GetTPipePtr()->FetchEventID(AscendC::HardEvent::V_MTE3)); | ||
| 219 | + int32_t evMTE3toMTE2 = static_cast<int32_t>(GetTPipePtr()->FetchEventID(AscendC::HardEvent::MTE3_MTE2)); | ||
| 220 | + | ||
| 221 | + int64_t start, end; | ||
| 222 | + GetCoreRange(AscendC::GetBlockIdx(), td_->multicore.tiles_main, td_->multicore.cores_tail, start, end); | ||
| 223 | + | ||
| 224 | + constexpr int B0 = 0, B1 = 1, B2 = 2; | ||
| 225 | + int64_t inner_count = 1; | ||
| 226 | + for (int64_t d = td_->split.axis + 1; d < RANK; d++) inner_count *= td_->max_bro_shape[d]; | ||
| 227 | + | ||
| 228 | + int64_t coord[8] = {}; | ||
| 229 | + for (int64_t flat = start; flat < end; flat++) { | ||
| 230 | + int64_t a_i_seg = GetUBSplitRange(flat % td_->split.a_o, td_->split.a_o, | ||
| 231 | + td_->split.a_i, td_->split.a_i_tail); | ||
| 232 | + int64_t count = a_i_seg * inner_count; | ||
| 233 | + FlatToEffectiveCoord(flat, td_->max_bro_shape, RANK, | ||
| 234 | + td_->split.axis, td_->split.a_i, td_->split.a_o, coord); | ||
| 235 | + | ||
| 236 | + if (flat != start) AscendC::WaitFlag<AscendC::HardEvent::MTE3_MTE2>(evMTE3toMTE2); | ||
| 237 | + | ||
| 238 | + CopyInBrc(coord, IN_SELF, B0, a_i_seg); // self → B0 | ||
| 239 | + AscendC::SetFlag<AscendC::HardEvent::MTE2_V>(evMTE2toV); | ||
| 240 | + AscendC::WaitFlag<AscendC::HardEvent::MTE2_V>(evMTE2toV); | ||
| 241 | + CopyInBrc(coord, IN_GRAD, B1, a_i_seg); // grad → B1 | ||
| 242 | + AscendC::SetFlag<AscendC::HardEvent::MTE2_V>(evMTE2toV); | ||
| 243 | + AscendC::WaitFlag<AscendC::HardEvent::MTE2_V>(evMTE2toV); | ||
| 244 | + | ||
| 245 | + int gateMode = 0; // 0=比较, 1=全透传, 2=全置0 | ||
| 246 | + T thT = ResolveThreshold(gateMode); | ||
| 247 | + ComputeThresholdGrad(B0, B1, B2, thT, gateMode, count); | ||
| 248 | + | ||
| 249 | + AscendC::SetFlag<AscendC::HardEvent::V_MTE3>(evVtoMTE3); | ||
| 250 | + AscendC::WaitFlag<AscendC::HardEvent::V_MTE3>(evVtoMTE3); | ||
| 251 | + CopyOutOne(coord, OUT_Y, B2, a_i_seg); | ||
| 252 | + if (flat != end - 1) AscendC::SetFlag<AscendC::HardEvent::MTE3_MTE2>(evMTE3toMTE2); | ||
| 253 | + } | ||
| 254 | + } | ||
| 255 | + | ||
| 256 | + // 阈值解析: 整型阈值(float)可能超出 T 范围, self>th 整数语义。 | ||
| 257 | + // th<type_min -> 恒透传(gateMode=1); th>=type_max -> 恒置0(gateMode=2); 范围内取 floor(th) 整型比较。 | ||
| 258 | + __aicore__ inline T ResolveThreshold(int& gateMode) | ||
| 259 | + { | ||
| 260 | + if constexpr (std::is_same_v<T, float> || std::is_same_v<T, half> || | ||
| 261 | + std::is_same_v<T, bfloat16_t>) { | ||
| 262 | + // 浮点: threshold(float) 直接转 T 原生比较 (含 NaN 透传: self<=th 置0) | ||
| 263 | + return static_cast<T>(threshold_); | ||
| 264 | + } else { | ||
| 265 | + // 整型: float 阈值可能超出 T 范围; 范围内取 floor (self>floor(th) <=> self>th) | ||
| 266 | + if (threshold_ < TypeMin()) { gateMode = 1; return static_cast<T>(0); } | ||
| 267 | + if (threshold_ >= TypeMax()) { gateMode = 2; return static_cast<T>(0); } | ||
| 268 | + float f = threshold_; | ||
| 269 | + int64_t fl = (int64_t)f; | ||
| 270 | + if (f < (float)fl) fl -= 1; // floor | ||
| 271 | + return static_cast<T>(fl); | ||
| 272 | + } | ||
| 273 | + } | ||
| 274 | + | ||
| 275 | + __aicore__ inline void ComputeThresholdGrad(int b0, int b1, int b2, T thT, int gateMode, int64_t count) | ||
| 276 | + { | ||
| 277 | + uint16_t rep = AscendC::CeilDivision(count, VL_T); | ||
| 278 | + if constexpr (std::is_same_v<T, half> || std::is_same_v<T, bfloat16_t>) { | ||
| 279 | + if (gateMode == 0) { | ||
| 280 | + // fp16/bf16 比较项升 fp32 与 golden 比较域对齐; grad 原生 Select。fp32 VL 步进。 | ||
| 281 | + constexpr uint32_t VL_F = AscendC::GetVecLen() / sizeof(float); | ||
| 282 | + uint16_t repF = AscendC::CeilDivision(count, VL_F); | ||
| 283 | + asc_vf_call<ThresholdGradVFb16<T>>( | ||
| 284 | + (__ubuf__ T*)buf_[b2].Get<T>().GetPhyAddr(), | ||
| 285 | + (__ubuf__ T*)buf_[b0].Get<T>().GetPhyAddr(), | ||
| 286 | + (__ubuf__ T*)buf_[b1].Get<T>().GetPhyAddr(), | ||
| 287 | + threshold_, count, VL_F, repF); | ||
| 288 | + return; | ||
| 289 | + } | ||
| 290 | + } | ||
| 291 | + asc_vf_call<ThresholdGradVF<T>>( | ||
| 292 | + (__ubuf__ T*)buf_[b2].Get<T>().GetPhyAddr(), | ||
| 293 | + (__ubuf__ T*)buf_[b0].Get<T>().GetPhyAddr(), | ||
| 294 | + (__ubuf__ T*)buf_[b1].Get<T>().GetPhyAddr(), | ||
| 295 | + thT, gateMode, count, VL_T, rep); | ||
| 296 | + } | ||
| 297 | + | ||
| 298 | + // ============================================================ | ||
| 299 | + // CopyInBrc — NDDMA 随路 broadcast (broadcast 轴 src stride=0) | ||
| 300 | + // ============================================================ | ||
| 301 | + __aicore__ inline void CopyInBrc(const int64_t* coord, int inputIdx, int slot, int64_t a_i_seg) | ||
| 302 | + { | ||
| 303 | + int64_t k = td_->split.axis; | ||
| 304 | + int64_t off = CalcOffset(coord, td_->input_strides[inputIdx], RANK); | ||
| 305 | + const int64_t* dstShape = td_->max_bro_shape; | ||
| 306 | + | ||
| 307 | + auto params = nddmaParams_[inputIdx]; | ||
| 308 | + int64_t k_nd = RANK - 1 - k; | ||
| 309 | + int64_t inner = 1; | ||
| 310 | + for (int64_t nd = 0; nd < ND; nd++) { | ||
| 311 | + if (nd == k_nd) params.loopInfo.loopSize[nd] = a_i_seg; | ||
| 312 | + params.loopInfo.loopDstStride[nd] = inner; | ||
| 313 | + inner *= params.loopInfo.loopSize[nd]; | ||
| 314 | + } | ||
| 315 | + | ||
| 316 | + static constexpr AscendC::NdDmaConfig cfg = { false, AscendC::NdDmaConfig::unsetPad, | ||
| 317 | + AscendC::NdDmaConfig::unsetPad, false }; | ||
| 318 | + if constexpr (RANK <= 5) { | ||
| 319 | + AscendC::DataCopy<T, ND, cfg>(buf_[slot].Get<T>(), gmIn_[inputIdx][off], params); | ||
| 320 | + } else { | ||
| 321 | + AscendC::LocalTensor<T> buf = buf_[slot].Get<T>(); | ||
| 322 | + int64_t elem_base = off; | ||
| 323 | + for (int64_t oi = 0; oi < nddmaOuterIters_[inputIdx]; oi++) { | ||
| 324 | + int64_t elem_adj = 0, tmp = oi; | ||
| 325 | + for (int64_t d = RANK - nddma_dims_ - 1; d >= k; d--) { | ||
| 326 | + int64_t sz = (d == k) ? a_i_seg : dstShape[d]; | ||
| 327 | + elem_adj += (tmp % sz) * td_->input_strides[inputIdx][d]; | ||
| 328 | + tmp /= sz; | ||
| 329 | + } | ||
| 330 | + AscendC::DataCopy<T, ND, cfg>(buf[oi * inner], gmIn_[inputIdx][elem_base + elem_adj], params); | ||
| 331 | + } | ||
| 332 | + } | ||
| 333 | + } | ||
| 334 | + | ||
| 335 | + // ============================================================ | ||
| 336 | + // CopyOutOne — DataCopyPad 写回 (out.shape = max_bro, 无 broadcast) | ||
| 337 | + // ============================================================ | ||
| 338 | + __aicore__ inline void CopyOutOne(const int64_t* coord, int outputIdx, int slot, int64_t a_i_seg) | ||
| 339 | + { | ||
| 340 | + int64_t off = CalcOffset(coord, td_->output_strides[outputIdx], RANK); | ||
| 341 | + int64_t inner_elems = 1; | ||
| 342 | + for (int64_t d = td_->split.axis + 1; d < RANK; d++) inner_elems *= td_->output_shapes[outputIdx][d]; | ||
| 343 | + int64_t cnt = a_i_seg * inner_elems; | ||
| 344 | + | ||
| 345 | + AscendC::DataCopyExtParams extParams; | ||
| 346 | + extParams.blockCount = 1; | ||
| 347 | + extParams.blockLen = cnt * sizeof(T); | ||
| 348 | + extParams.srcStride = 0; | ||
| 349 | + extParams.dstStride = 0; | ||
| 350 | + AscendC::DataCopyPad(gmOut_[outputIdx][off], buf_[slot].Get<T>(), extParams); | ||
| 351 | + } | ||
| 352 | +}; | ||
| @@ -0,0 +1,35 @@ | |||
| 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 | +/* Generated By CANNBot */ | ||
| 12 | + | ||
| 13 | +/*! | ||
| 14 | + * \file threshold_grad_v2_d_struct.h | ||
| 15 | + * \brief ThresholdGradV2D TilingKey 模板参数 — 仅按 RANK 分叉, dtype 由模板 T 处理 (DESIGN §3.1) | ||
| 16 | + */ | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | +ASCENDC_TPL_ARGS_DECL(ThresholdGradV2D, | ||
| 26 | + ASCENDC_TPL_UINT_DECL(RANK, 8, ASCENDC_TPL_UI_LIST, | ||
| 27 | + THRESHOLD_GRAD_V2_D_RANK_4, THRESHOLD_GRAD_V2_D_RANK_8) | ||
| 28 | +); | ||
| 29 | + | ||
| 30 | +ASCENDC_TPL_SEL( | ||
| 31 | + ASCENDC_TPL_ARGS_SEL(ASCENDC_TPL_UINT_SEL(RANK, ASCENDC_TPL_UI_LIST, THRESHOLD_GRAD_V2_D_RANK_4)), | ||
| 32 | + ASCENDC_TPL_ARGS_SEL(ASCENDC_TPL_UINT_SEL(RANK, ASCENDC_TPL_UI_LIST, THRESHOLD_GRAD_V2_D_RANK_8)) | ||
| 33 | +); | ||
| 34 | + | ||
| 35 | + | ||
| @@ -0,0 +1,55 @@ | |||
| 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 | +/* Generated By CANNBot */ | ||
| 12 | + | ||
| 13 | +/*! | ||
| 14 | + * \file threshold_grad_v2_d_tiling_struct.h | ||
| 15 | + * \brief ThresholdGradV2D TilingData — 按 rank 模板化,体积分两档 (RANK<=4 / RANK<=8) | ||
| 16 | + * RegBase Broadcast 范式: out = (self > threshold) ? gradOutput : 0 | ||
| 17 | + */ | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | +// === 算子特定常量 === | ||
| 22 | +constexpr int64_t kMaxInputSlots = 2; // gradOutput, self | ||
| 23 | +constexpr int64_t kMaxOutputSlots = 1; // out | ||
| 24 | +constexpr int64_t kPhysNodes = 3; // self/grad/out 三槽 (fp16/bf16 比较升 fp32 仅在寄存器内, 无额外 UB 中转) | ||
| 25 | + | ||
| 26 | +struct SplitResult { | ||
| 27 | + int64_t axis; | ||
| 28 | + int64_t a_i; | ||
| 29 | + int64_t a_o; | ||
| 30 | + int64_t a_i_tail; | ||
| 31 | +}; | ||
| 32 | + | ||
| 33 | +struct MultiCoreResult { | ||
| 34 | + int64_t num_cores; | ||
| 35 | + int64_t total_tiles; | ||
| 36 | + int64_t tiles_main; | ||
| 37 | + int64_t cores_tail; | ||
| 38 | +}; | ||
| 39 | + | ||
| 40 | +template<int64_t kRank> | ||
| 41 | +struct ThresholdGradV2DTilingData { | ||
| 42 | + SplitResult split; | ||
| 43 | + MultiCoreResult multicore; | ||
| 44 | + int64_t rank; // 实际 rank (1~8) | ||
| 45 | + int64_t per_buf_bytes; // (UB/P)&~31; Kernel 用此初始化 TBuf | ||
| 46 | + int64_t per_buf_elems; // per_buf_bytes / 4(统一按 FP32 计算) | ||
| 47 | + int64_t max_bro_shape[kRank]; | ||
| 48 | + int64_t num_inputs; | ||
| 49 | + int64_t num_outputs; | ||
| 50 | + int64_t input_shapes [kMaxInputSlots][kRank]; | ||
| 51 | + int64_t input_strides[kMaxInputSlots][kRank]; | ||
| 52 | + int64_t output_shapes[kMaxOutputSlots][kRank]; | ||
| 53 | + int64_t output_strides[kMaxOutputSlots][kRank]; | ||
| 54 | + float threshold; // 属性标量, 默认 1.0 | ||
| 55 | +}; | ||
| @@ -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 | +/* Generated By CANNBot */ | ||
| 12 | + | ||
| 13 | +/*! | ||
| 14 | + * \file threshold_grad_v2_d_apt.cpp | ||
| 15 | + * \brief ThresholdGradV2D Kernel 入口 — apt 模式 | ||
| 16 | + * RANK 来自 TilingKey, DTYPE 来自 def 注册的 gradOutput dtype | ||
| 17 | + * 目标架构: DAV_3510 (arch35 / ascend950) | ||
| 18 | + * 参数顺序固定: gradOutput, self, out, workspace, tiling | ||
| 19 | + */ | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | +using TilingData4 = ThresholdGradV2DTilingData<4>; // RANK<=4: 数组维度 [4] | ||
| 25 | +using TilingData8 = ThresholdGradV2DTilingData<8>; // RANK>4: 数组维度 [8] | ||
| 26 | + | ||
| 27 | +template<int RANK> | ||
| 28 | +__global__ __aicore__ void threshold_grad_v2_d( | ||
| 29 | + GM_ADDR gradOutput, GM_ADDR self, GM_ADDR out, | ||
| 30 | + GM_ADDR workspace, GM_ADDR tiling) | ||
| 31 | +{ | ||
| 32 | + GM_ADDR ins[2] = {gradOutput, self}; | ||
| 33 | + GM_ADDR outs[1] = {out}; | ||
| 34 | + | ||
| 35 | + REGISTER_NONE_TILING; | ||
| 36 | + KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIV_ONLY); | ||
| 37 | + | ||
| 38 | + if constexpr (RANK == 4) { | ||
| 39 | + GET_TILING_DATA_WITH_STRUCT(TilingData4, td, tiling); | ||
| 40 | + ThresholdGradV2DKernel<DTYPE_GRADOUTPUT, 4> kernel; | ||
| 41 | + kernel.Init(ins, outs, &td); | ||
| 42 | + kernel.Process(); | ||
| 43 | + } else { | ||
| 44 | + GET_TILING_DATA_WITH_STRUCT(TilingData8, td, tiling); | ||
| 45 | + ThresholdGradV2DKernel<DTYPE_GRADOUTPUT, 8> kernel; | ||
| 46 | + kernel.Init(ins, outs, &td); | ||
| 47 | + kernel.Process(); | ||
| 48 | + } | ||
| 49 | +} | ||
Ractivation/threshold_grad_v2_d/op_host/CMakeLists.txt→activation/threshold_grad_v2_d/tests/ut/op_host/arch35/CMakeLists.txt+13-5
| @@ -1,10 +1,18 @@ | |||
| 1 | -# Copyright (c) 2025 Huawei Technologies Co., Ltd. | 1 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. |
| 2 | -# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | 2 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of |
| 3 | # CANN Open Software License Agreement Version 2.0 (the "License"). | 3 | # CANN Open Software License Agreement Version 2.0 (the "License"). |
| 4 | # Please refer to the License for details. You may not use this file except in compliance with the License. | 4 | # Please refer to the License for details. You may not use this file except in compliance with the License. |
| 5 | -# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | 5 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, |
| 6 | # INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | 6 | # INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. |
| 7 | # See LICENSE in the root of the software repository for the full text of the License. | 7 | # See LICENSE in the root of the software repository for the full text of the License. |
| 8 | -#/ | ||
| 9 | 8 | ||
| 10 | -add_modules_sources(HOSTNAME ${OPHOST_NAME} MODE PRIVATE DIR ${CMAKE_CURRENT_SOURCE_DIR} OPTYPE threshold_grad_v2_d ACLNNTYPE aclnn DEPENDENCIES relu_grad) | 9 | +if(UT_TEST_ALL OR OP_HOST_UT) |
| 10 | + add_modules_ut_sources(HOSTNAME ${OP_TILING_MODULE_NAME} MODE PRIVATE DIR ${CMAKE_CURRENT_SOURCE_DIR}) | ||
| 11 | + if(TARGET ${OP_TILING_MODULE_NAME}_cases_obj AND NOT TARGET ${OPHOST_NAME}_tiling_obj) | ||
| 12 | + target_sources(${OP_TILING_MODULE_NAME}_cases_obj PRIVATE | ||
| 13 | + ${CMAKE_CURRENT_SOURCE_DIR}/../../../../op_host/arch35/threshold_grad_v2_d_tiling_arch35.cpp) | ||
| 14 | + target_include_directories(${OP_TILING_MODULE_NAME}_cases_obj PRIVATE | ||
| 15 | + ${CMAKE_CURRENT_SOURCE_DIR}/../../../../ | ||
| 16 | + ${CMAKE_CURRENT_SOURCE_DIR}/../../../../op_host) | ||
| 17 | + endif() | ||
| 18 | +endif() | ||
| @@ -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 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | +using namespace std; | ||
| 22 | +using namespace ge; | ||
| 23 | +using namespace ut_util; | ||
| 24 | + | ||
| 25 | +struct ThresholdGradV2DCompileInfo { uint64_t coreNum; uint64_t ubSize; }; | ||
| 26 | + | ||
| 27 | +class ThresholdGradV2DTilingTest : public testing::Test { | ||
| 28 | +protected: | ||
| 29 | + static void SetUpTestCase() { | ||
| 30 | + std::cout << "ThresholdGradV2DTilingTest SetUp" << std::endl; | ||
| 31 | + } | ||
| 32 | + static void TearDownTestCase() { | ||
| 33 | + std::cout << "ThresholdGradV2DTilingTest TearDown" << std::endl; | ||
| 34 | + } | ||
| 35 | +}; | ||
| 36 | + | ||
| 37 | +static void InitPlatform(fe::PlatFormInfos& platFormInfo, map<string, string>& socInfos, | ||
| 38 | + map<string, string>& aicoreSpec, map<string, string>& intrinsics, map<string, string>& socVersion) | ||
| 39 | +{ | ||
| 40 | + string hardwareInfo = R"({ | ||
| 41 | + "hardware_info": {"BT_SIZE": 0, "load3d_constraints": "1", | ||
| 42 | + "Intrinsic_fix_pipe_l0c2out": false, | ||
| 43 | + "Intrinsic_data_move_l12ub": true, | ||
| 44 | + "Intrinsic_data_move_l0c2ub": true, | ||
| 45 | + "Intrinsic_data_move_out2l1_nd2nz": false, | ||
| 46 | + "UB_SIZE": 245760, "L2_SIZE": 33554432, "L1_SIZE": 524288, | ||
| 47 | + "L0A_SIZE": 65536, "L0B_SIZE": 65536, "L0C_SIZE": 131072, | ||
| 48 | + "CORE_NUM": 64, "socVersion": "Ascend950"}})"; | ||
| 49 | + GetPlatFormInfos(hardwareInfo.c_str(), socInfos, aicoreSpec, intrinsics, socVersion); | ||
| 50 | + platFormInfo.Init(); | ||
| 51 | +} | ||
| 52 | + | ||
| 53 | +static void DoThresholdGradV2DTilingCase(std::initializer_list<int64_t>& inputShape1, | ||
| 54 | + std::initializer_list<int64_t>& inputShape2, std::initializer_list<int64_t>& outputShape, | ||
| 55 | + ge::DataType inputDtype) | ||
| 56 | +{ | ||
| 57 | + fe::PlatFormInfos platFormInfo; | ||
| 58 | + map<string, string> socInfos; | ||
| 59 | + map<string, string> aicoreSpec; | ||
| 60 | + map<string, string> intrinsics; | ||
| 61 | + map<string, string> socVersion; | ||
| 62 | + InitPlatform(platFormInfo, socInfos, aicoreSpec, intrinsics, socVersion); | ||
| 63 | + | ||
| 64 | + ThresholdGradV2DCompileInfo compileInfo; | ||
| 65 | + std::string opType("ThresholdGradV2D"); | ||
| 66 | + ASSERT_NE(gert::OpImplRegistry::GetInstance().GetOpImpl(opType.c_str()), nullptr); | ||
| 67 | + | ||
| 68 | + string compileInfoStr = R"({})"; | ||
| 69 | + auto kernelHolder = gert::KernelRunContextFaker() | ||
| 70 | + .KernelIONum(2, 1) | ||
| 71 | + .Inputs({const_cast<char*>(compileInfoStr.c_str()), reinterpret_cast<void*>(&platFormInfo)}) | ||
| 72 | + .Outputs({&compileInfo}) | ||
| 73 | + .Build(); | ||
| 74 | + | ||
| 75 | + ASSERT_TRUE(kernelHolder.GetContext<gert::TilingParseContext>()->GetPlatformInfo()->Init()); | ||
| 76 | + kernelHolder.GetContext<gert::TilingParseContext>()->GetPlatformInfo()->SetPlatformRes("version", socVersion); | ||
| 77 | + kernelHolder.GetContext<gert::TilingParseContext>()->GetPlatformInfo()->SetPlatformRes("SoCInfo", socInfos); | ||
| 78 | + kernelHolder.GetContext<gert::TilingParseContext>()->GetPlatformInfo()->SetPlatformRes("AICoreSpec", aicoreSpec); | ||
| 79 | + kernelHolder.GetContext<gert::TilingParseContext>()->GetPlatformInfo()->SetCoreNumByCoreType("AICore"); | ||
| 80 | + kernelHolder.GetContext<gert::TilingParseContext>()->GetPlatformInfo()->SetPlatformRes("AICoreintrinsicDtypeMap", intrinsics); | ||
| 81 | + auto tilingParseFunc = gert::OpImplRegistry::GetInstance().GetOpImpl(opType.c_str())->tiling_parse; | ||
| 82 | + ASSERT_NE(tilingParseFunc, nullptr) << "tiling_parse not registered for " << opType; | ||
| 83 | + ASSERT_EQ(tilingParseFunc(kernelHolder.GetContext<gert::KernelContext>()), ge::GRAPH_SUCCESS); | ||
| 84 | + | ||
| 85 | + gert::StorageShape x1Shape = {inputShape1, inputShape1}; | ||
| 86 | + gert::StorageShape x2Shape = {inputShape2, inputShape2}; | ||
| 87 | + gert::StorageShape oShape = {outputShape, outputShape}; | ||
| 88 | + auto tilingFunc = gert::OpImplRegistry::GetInstance().GetOpImpl(opType.c_str())->tiling; | ||
| 89 | + ASSERT_NE(tilingFunc, nullptr) << "tiling not registered for " << opType; | ||
| 90 | + | ||
| 91 | + auto workspaceSizeHoler = gert::ContinuousVector::Create<size_t>(16 * 4096); | ||
| 92 | + auto wsSize = reinterpret_cast<gert::ContinuousVector*>(workspaceSizeHoler.get()); | ||
| 93 | + auto param = gert::TilingData::CreateCap(4096); | ||
| 94 | + ASSERT_NE(param, nullptr); | ||
| 95 | + | ||
| 96 | + auto holder = gert::TilingContextFaker() | ||
| 97 | + .NodeIoNum(2, 1) | ||
| 98 | + .IrInstanceNum({1, 1}) | ||
| 99 | + .InputShapes({&x1Shape, &x2Shape}) | ||
| 100 | + .OutputShapes({&oShape}) | ||
| 101 | + .CompileInfo(&compileInfo) | ||
| 102 | + .PlatformInfo(reinterpret_cast<char*>(&platFormInfo)) | ||
| 103 | + .NodeInputTd(0, inputDtype, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 104 | + .NodeInputTd(1, inputDtype, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 105 | + .NodeOutputTd(0, inputDtype, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 106 | + .TilingData(param.get()) | ||
| 107 | + .Workspace(wsSize) | ||
| 108 | + .Build(); | ||
| 109 | + | ||
| 110 | + gert::TilingContext* tiling_context = holder.GetContext<gert::TilingContext>(); | ||
| 111 | + ASSERT_NE(tiling_context->GetPlatformInfo(), nullptr); | ||
| 112 | + tiling_context->GetPlatformInfo()->SetPlatformRes("SoCInfo", socInfos); | ||
| 113 | + tiling_context->GetPlatformInfo()->SetPlatformRes("AICoreSpec", aicoreSpec); | ||
| 114 | + tiling_context->GetPlatformInfo()->SetCoreNumByCoreType("AICore"); | ||
| 115 | + tiling_context->GetPlatformInfo()->SetPlatformRes("AICoreintrinsicDtypeMap", intrinsics); | ||
| 116 | + | ||
| 117 | + EXPECT_EQ(tilingFunc(tiling_context), ge::GRAPH_SUCCESS); | ||
| 118 | +} | ||
| 119 | + | ||
| 120 | +TEST_F(ThresholdGradV2DTilingTest, tiling_fp32_2d) { | ||
| 121 | + std::initializer_list<int64_t> inputShape1 = {2, 128}; | ||
| 122 | + std::initializer_list<int64_t> inputShape2 = {2, 128}; | ||
| 123 | + std::initializer_list<int64_t> outputShape = {2, 128}; | ||
| 124 | + DoThresholdGradV2DTilingCase(inputShape1, inputShape2, outputShape, ge::DT_FLOAT); | ||
| 125 | +} | ||
| 126 | + | ||
| 127 | +TEST_F(ThresholdGradV2DTilingTest, tiling_fp16_2d) { | ||
| 128 | + std::initializer_list<int64_t> inputShape1 = {2, 128}; | ||
| 129 | + std::initializer_list<int64_t> inputShape2 = {2, 128}; | ||
| 130 | + std::initializer_list<int64_t> outputShape = {2, 128}; | ||
| 131 | + DoThresholdGradV2DTilingCase(inputShape1, inputShape2, outputShape, ge::DT_FLOAT16); | ||
| 132 | +} | ||
| 133 | + | ||
| 134 | +TEST_F(ThresholdGradV2DTilingTest, tiling_bf16_2d) { | ||
| 135 | + std::initializer_list<int64_t> inputShape1 = {2, 128}; | ||
| 136 | + std::initializer_list<int64_t> inputShape2 = {2, 128}; | ||
| 137 | + std::initializer_list<int64_t> outputShape = {2, 128}; | ||
| 138 | + DoThresholdGradV2DTilingCase(inputShape1, inputShape2, outputShape, ge::DT_BF16); | ||
| 139 | +} | ||
| 140 | + | ||
| 141 | +TEST_F(ThresholdGradV2DTilingTest, tiling_int32_2d) { | ||
| 142 | + std::initializer_list<int64_t> inputShape1 = {2, 128}; | ||
| 143 | + std::initializer_list<int64_t> inputShape2 = {2, 128}; | ||
| 144 | + std::initializer_list<int64_t> outputShape = {2, 128}; | ||
| 145 | + DoThresholdGradV2DTilingCase(inputShape1, inputShape2, outputShape, ge::DT_INT32); | ||
| 146 | +} | ||
| 147 | + | ||
| 148 | +TEST_F(ThresholdGradV2DTilingTest, tiling_int8_2d) { | ||
| 149 | + std::initializer_list<int64_t> inputShape1 = {2, 128}; | ||
| 150 | + std::initializer_list<int64_t> inputShape2 = {2, 128}; | ||
| 151 | + std::initializer_list<int64_t> outputShape = {2, 128}; | ||
| 152 | + DoThresholdGradV2DTilingCase(inputShape1, inputShape2, outputShape, ge::DT_INT8); | ||
| 153 | +} | ||
| 154 | + | ||
| 155 | +TEST_F(ThresholdGradV2DTilingTest, tiling_uint8_2d) { | ||
| 156 | + std::initializer_list<int64_t> inputShape1 = {2, 128}; | ||
| 157 | + std::initializer_list<int64_t> inputShape2 = {2, 128}; | ||
| 158 | + std::initializer_list<int64_t> outputShape = {2, 128}; | ||
| 159 | + DoThresholdGradV2DTilingCase(inputShape1, inputShape2, outputShape, ge::DT_UINT8); | ||
| 160 | +} | ||
| 161 | + | ||
| 162 | +TEST_F(ThresholdGradV2DTilingTest, tiling_fp32_4d) { | ||
| 163 | + std::initializer_list<int64_t> inputShape1 = {2, 4, 8, 16}; | ||
| 164 | + std::initializer_list<int64_t> inputShape2 = {2, 4, 8, 16}; | ||
| 165 | + std::initializer_list<int64_t> outputShape = {2, 4, 8, 16}; | ||
| 166 | + DoThresholdGradV2DTilingCase(inputShape1, inputShape2, outputShape, ge::DT_FLOAT); | ||
| 167 | +} | ||
| 168 | + | ||
| 169 | +TEST_F(ThresholdGradV2DTilingTest, tiling_fp16_1d) { | ||
| 170 | + std::initializer_list<int64_t> inputShape1 = {1024}; | ||
| 171 | + std::initializer_list<int64_t> inputShape2 = {1024}; | ||
| 172 | + std::initializer_list<int64_t> outputShape = {1024}; | ||
| 173 | + DoThresholdGradV2DTilingCase(inputShape1, inputShape2, outputShape, ge::DT_FLOAT16); | ||
| 174 | +} | ||
| 175 | + | ||
| 176 | +TEST_F(ThresholdGradV2DTilingTest, tiling_failed_unsupported_dtype) { | ||
| 177 | + fe::PlatFormInfos platFormInfo; | ||
| 178 | + map<string, string> socInfos; | ||
| 179 | + map<string, string> aicoreSpec; | ||
| 180 | + map<string, string> intrinsics; | ||
| 181 | + map<string, string> socVersion; | ||
| 182 | + InitPlatform(platFormInfo, socInfos, aicoreSpec, intrinsics, socVersion); | ||
| 183 | + | ||
| 184 | + std::string op_type("ThresholdGradV2D"); | ||
| 185 | + ASSERT_NE(gert::OpImplRegistry::GetInstance().GetOpImpl(op_type.c_str()), nullptr); | ||
| 186 | + auto tiling_func = gert::OpImplRegistry::GetInstance().GetOpImpl(op_type.c_str())->tiling; | ||
| 187 | + ASSERT_NE(tiling_func, nullptr) << "tiling not registered for " << op_type; | ||
| 188 | + | ||
| 189 | + auto param = gert::TilingData::CreateCap(4096); | ||
| 190 | + ASSERT_NE(param, nullptr); | ||
| 191 | + auto workspace_size_holer = gert::ContinuousVector::Create<size_t>(4096); | ||
| 192 | + auto ws_size = reinterpret_cast<gert::ContinuousVector*>(workspace_size_holer.get()); | ||
| 193 | + gert::StorageShape Shape1 = {{2, 128}, {2, 128}}; | ||
| 194 | + | ||
| 195 | + ThresholdGradV2DCompileInfo compile_info; | ||
| 196 | + compile_info.coreNum = 64; | ||
| 197 | + compile_info.ubSize = 262144; | ||
| 198 | + | ||
| 199 | + auto holder = gert::TilingContextFaker() | ||
| 200 | + .NodeIoNum(2, 1) | ||
| 201 | + .IrInstanceNum({1, 1}) | ||
| 202 | + .InputShapes({&Shape1, &Shape1}) | ||
| 203 | + .OutputShapes({&Shape1}) | ||
| 204 | + .CompileInfo(&compile_info) | ||
| 205 | + .PlatformInfo(reinterpret_cast<char*>(&platFormInfo)) | ||
| 206 | + .NodeInputTd(0, ge::DT_BOOL, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 207 | + .NodeInputTd(1, ge::DT_BOOL, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 208 | + .NodeOutputTd(0, ge::DT_BOOL, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 209 | + .TilingData(param.get()) | ||
| 210 | + .Workspace(ws_size) | ||
| 211 | + .Build(); | ||
| 212 | + gert::TilingContext* tiling_context = holder.GetContext<gert::TilingContext>(); | ||
| 213 | + tiling_context->GetPlatformInfo()->SetPlatformRes("SoCInfo", socInfos); | ||
| 214 | + tiling_context->GetPlatformInfo()->SetPlatformRes("AICoreSpec", aicoreSpec); | ||
| 215 | + tiling_context->GetPlatformInfo()->SetCoreNumByCoreType("AICore"); | ||
| 216 | + tiling_context->GetPlatformInfo()->SetPlatformRes("AICoreintrinsicDtypeMap", intrinsics); | ||
| 217 | + EXPECT_EQ(tiling_func(tiling_context), ge::GRAPH_FAILED); | ||
| 218 | +} | ||
| @@ -207,4 +207,141 @@ TEST_F(l2_threshold_backward_test, ascend910B2_l2_test_relu_grad_check_max_dim) | |||
| 207 | uint64_t workspaceSize = 0; | 207 | uint64_t workspaceSize = 0; |
| 208 | aclnnStatus getWorkspaceResult = ut.TestGetWorkspaceSize(&workspaceSize); | 208 | aclnnStatus getWorkspaceResult = ut.TestGetWorkspaceSize(&workspaceSize); |
| 209 | EXPECT_EQ(getWorkspaceResult, ACLNN_ERR_PARAM_INVALID); | 209 | EXPECT_EQ(getWorkspaceResult, ACLNN_ERR_PARAM_INVALID); |
| 210 | +} | ||
| 211 | + | ||
| 212 | +TEST_F(l2_threshold_backward_test, l2_test_relu_grad_uint8_success) { | ||
| 213 | + auto gradOutputDesc = TensorDesc({10,}, ACL_UINT8, ACL_FORMAT_ND); | ||
| 214 | + auto selfDesc = TensorDesc({10,}, ACL_UINT8, ACL_FORMAT_ND); | ||
| 215 | + auto scalarDesc = ScalarDesc(0.0f); | ||
| 216 | + auto outDesc = TensorDesc(selfDesc); | ||
| 217 | + auto ut = OP_API_UT(aclnnThresholdBackward, INPUT(gradOutputDesc, selfDesc, scalarDesc), OUTPUT(outDesc)); | ||
| 218 | + uint64_t workspaceSize = 0; | ||
| 219 | + aclnnStatus getWorkspaceResult = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 220 | + EXPECT_EQ(getWorkspaceResult, ACLNN_SUCCESS); | ||
| 221 | + ut.TestPrecision(); | ||
| 222 | +} | ||
| 223 | + | ||
| 224 | +TEST_F(l2_threshold_backward_test, l2_test_relu_grad_int64_unsupported_on_stub) { | ||
| 225 | + auto gradOutputDesc = TensorDesc({10,}, ACL_INT64, ACL_FORMAT_ND); | ||
| 226 | + auto selfDesc = TensorDesc({10,}, ACL_INT64, ACL_FORMAT_ND); | ||
| 227 | + auto scalarDesc = ScalarDesc(0.0f); | ||
| 228 | + auto outDesc = TensorDesc(selfDesc); | ||
| 229 | + auto ut = OP_API_UT(aclnnThresholdBackward, INPUT(gradOutputDesc, selfDesc, scalarDesc), OUTPUT(outDesc)); | ||
| 230 | + uint64_t workspaceSize = 0; | ||
| 231 | + aclnnStatus getWorkspaceResult = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 232 | + EXPECT_EQ(getWorkspaceResult, ACLNN_ERR_PARAM_INVALID); | ||
| 233 | +} | ||
| 234 | + | ||
| 235 | +TEST_F(l2_threshold_backward_test, l2_test_int64_nonzero_threshold_fail) { | ||
| 236 | + auto gradOutputDesc = TensorDesc({10,}, ACL_INT64, ACL_FORMAT_ND); | ||
| 237 | + auto selfDesc = TensorDesc({10,}, ACL_INT64, ACL_FORMAT_ND); | ||
| 238 | + auto scalarDesc = ScalarDesc(1.0f); | ||
| 239 | + auto outDesc = TensorDesc(selfDesc); | ||
| 240 | + auto ut = OP_API_UT(aclnnThresholdBackward, INPUT(gradOutputDesc, selfDesc, scalarDesc), OUTPUT(outDesc)); | ||
| 241 | + uint64_t workspaceSize = 0; | ||
| 242 | + aclnnStatus getWorkspaceResult = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 243 | + EXPECT_EQ(getWorkspaceResult, ACLNN_ERR_PARAM_INVALID); | ||
| 244 | +} | ||
| 245 | + | ||
| 246 | +TEST_F(l2_threshold_backward_test, l2_test_2d_shape_success) { | ||
| 247 | + auto gradOutputDesc = TensorDesc({4, 8}, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 248 | + auto selfDesc = TensorDesc({4, 8}, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 249 | + auto scalarDesc = ScalarDesc(0.5f); | ||
| 250 | + auto outDesc = TensorDesc(selfDesc); | ||
| 251 | + auto ut = OP_API_UT(aclnnThresholdBackward, INPUT(gradOutputDesc, selfDesc, scalarDesc), OUTPUT(outDesc)); | ||
| 252 | + uint64_t workspaceSize = 0; | ||
| 253 | + aclnnStatus getWorkspaceResult = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 254 | + EXPECT_EQ(getWorkspaceResult, ACLNN_SUCCESS); | ||
| 255 | + ut.TestPrecision(); | ||
| 256 | +} | ||
| 257 | + | ||
| 258 | +TEST_F(l2_threshold_backward_test, l2_test_4d_shape_success) { | ||
| 259 | + auto gradOutputDesc = TensorDesc({2, 3, 4, 5}, ACL_FLOAT16, ACL_FORMAT_ND); | ||
| 260 | + auto selfDesc = TensorDesc({2, 3, 4, 5}, ACL_FLOAT16, ACL_FORMAT_ND); | ||
| 261 | + auto scalarDesc = ScalarDesc(0.0f); | ||
| 262 | + auto outDesc = TensorDesc(selfDesc); | ||
| 263 | + auto ut = OP_API_UT(aclnnThresholdBackward, INPUT(gradOutputDesc, selfDesc, scalarDesc), OUTPUT(outDesc)); | ||
| 264 | + uint64_t workspaceSize = 0; | ||
| 265 | + aclnnStatus getWorkspaceResult = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 266 | + EXPECT_EQ(getWorkspaceResult, ACLNN_SUCCESS); | ||
| 267 | + ut.TestPrecision(); | ||
| 268 | +} | ||
| 269 | + | ||
| 270 | +TEST_F(l2_threshold_backward_test, l2_test_8d_shape_success) { | ||
| 271 | + auto gradOutputDesc = TensorDesc({2, 2, 2, 2, 2, 2, 2, 2}, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 272 | + auto selfDesc = TensorDesc({2, 2, 2, 2, 2, 2, 2, 2}, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 273 | + auto scalarDesc = ScalarDesc(0.0f); | ||
| 274 | + auto outDesc = TensorDesc(selfDesc); | ||
| 275 | + auto ut = OP_API_UT(aclnnThresholdBackward, INPUT(gradOutputDesc, selfDesc, scalarDesc), OUTPUT(outDesc)); | ||
| 276 | + uint64_t workspaceSize = 0; | ||
| 277 | + aclnnStatus getWorkspaceResult = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 278 | + EXPECT_EQ(getWorkspaceResult, ACLNN_SUCCESS); | ||
| 279 | +} | ||
| 280 | + | ||
| 281 | +TEST_F(l2_threshold_backward_test, l2_test_large_shape_success) { | ||
| 282 | + auto gradOutputDesc = TensorDesc({1024, 1024}, ACL_FLOAT16, ACL_FORMAT_ND); | ||
| 283 | + auto selfDesc = TensorDesc({1024, 1024}, ACL_FLOAT16, ACL_FORMAT_ND); | ||
| 284 | + auto scalarDesc = ScalarDesc(0.0f); | ||
| 285 | + auto outDesc = TensorDesc(selfDesc); | ||
| 286 | + auto ut = OP_API_UT(aclnnThresholdBackward, INPUT(gradOutputDesc, selfDesc, scalarDesc), OUTPUT(outDesc)); | ||
| 287 | + uint64_t workspaceSize = 0; | ||
| 288 | + aclnnStatus getWorkspaceResult = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 289 | + EXPECT_EQ(getWorkspaceResult, ACLNN_SUCCESS); | ||
| 290 | +} | ||
| 291 | + | ||
| 292 | +TEST_F(l2_threshold_backward_test, l2_test_bf16_threshold_zero_precision) { | ||
| 293 | + auto gradOutputDesc = TensorDesc({10,}, ACL_BF16, ACL_FORMAT_ND); | ||
| 294 | + auto selfDesc = TensorDesc({10,}, ACL_BF16, ACL_FORMAT_ND); | ||
| 295 | + auto scalarDesc = ScalarDesc(0.0f); | ||
| 296 | + auto outDesc = TensorDesc(selfDesc); | ||
| 297 | + auto ut = OP_API_UT(aclnnThresholdBackward, INPUT(gradOutputDesc, selfDesc, scalarDesc), OUTPUT(outDesc)); | ||
| 298 | + uint64_t workspaceSize = 0; | ||
| 299 | + aclnnStatus getWorkspaceResult = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 300 | + EXPECT_EQ(getWorkspaceResult, ACLNN_SUCCESS); | ||
| 301 | + ut.TestPrecision(); | ||
| 302 | +} | ||
| 303 | + | ||
| 304 | +TEST_F(l2_threshold_backward_test, l2_test_uint8_nonzero_threshold_success) { | ||
| 305 | + auto gradOutputDesc = TensorDesc({10,}, ACL_UINT8, ACL_FORMAT_ND); | ||
| 306 | + auto selfDesc = TensorDesc({10,}, ACL_UINT8, ACL_FORMAT_ND); | ||
| 307 | + auto scalarDesc = ScalarDesc(1.0f); | ||
| 308 | + auto outDesc = TensorDesc(selfDesc); | ||
| 309 | + auto ut = OP_API_UT(aclnnThresholdBackward, INPUT(gradOutputDesc, selfDesc, scalarDesc), OUTPUT(outDesc)); | ||
| 310 | + uint64_t workspaceSize = 0; | ||
| 311 | + aclnnStatus getWorkspaceResult = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 312 | + EXPECT_EQ(getWorkspaceResult, ACLNN_SUCCESS); | ||
| 313 | + ut.TestPrecision(); | ||
| 314 | +} | ||
| 315 | + | ||
| 316 | +TEST_F(l2_threshold_backward_test, l2_test_fp32_nonzero_threshold_precision) { | ||
| 317 | + auto gradOutputDesc = TensorDesc({10,}, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 318 | + auto selfDesc = TensorDesc({10,}, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 319 | + auto scalarDesc = ScalarDesc(2.0f); | ||
| 320 | + auto outDesc = TensorDesc(selfDesc); | ||
| 321 | + auto ut = OP_API_UT(aclnnThresholdBackward, INPUT(gradOutputDesc, selfDesc, scalarDesc), OUTPUT(outDesc)); | ||
| 322 | + uint64_t workspaceSize = 0; | ||
| 323 | + aclnnStatus getWorkspaceResult = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 324 | + EXPECT_EQ(getWorkspaceResult, ACLNN_SUCCESS); | ||
| 325 | + ut.TestPrecision(); | ||
| 326 | +} | ||
| 327 | + | ||
| 328 | +TEST_F(l2_threshold_backward_test, l2_test_scalar_tensor_success) { | ||
| 329 | + auto gradOutputDesc = TensorDesc({}, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 330 | + auto selfDesc = TensorDesc({}, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 331 | + auto scalarDesc = ScalarDesc(0.0f); | ||
| 332 | + auto outDesc = TensorDesc(selfDesc); | ||
| 333 | + auto ut = OP_API_UT(aclnnThresholdBackward, INPUT(gradOutputDesc, selfDesc, scalarDesc), OUTPUT(outDesc)); | ||
| 334 | + uint64_t workspaceSize = 0; | ||
| 335 | + aclnnStatus getWorkspaceResult = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 336 | + EXPECT_EQ(getWorkspaceResult, ACLNN_SUCCESS); | ||
| 337 | +} | ||
| 338 | + | ||
| 339 | +TEST_F(l2_threshold_backward_test, l2_test_threshold_nullptr) { | ||
| 340 | + auto gradOutputDesc = TensorDesc({10,}, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 341 | + auto selfDesc = TensorDesc({10,}, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 342 | + auto outDesc = TensorDesc(selfDesc); | ||
| 343 | + auto ut = OP_API_UT(aclnnThresholdBackward, INPUT(gradOutputDesc, selfDesc, (const aclScalar*)nullptr), OUTPUT(outDesc)); | ||
| 344 | + uint64_t workspaceSize = 0; | ||
| 345 | + aclnnStatus getWorkspaceResult = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 346 | + EXPECT_EQ(getWorkspaceResult, ACLNN_ERR_PARAM_NULLPTR); | ||
| 210 | } | 347 | } |
| @@ -0,0 +1,242 @@ | |||
| 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 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | +using namespace ge; | ||
| 20 | + | ||
| 21 | +class ThresholdGradV2DInferShapeTest : public testing::Test { | ||
| 22 | +protected: | ||
| 23 | + static void SetUpTestCase() { | ||
| 24 | + std::cout << "ThresholdGradV2DInferShapeTest SetUp" << std::endl; | ||
| 25 | + } | ||
| 26 | + static void TearDownTestCase() { | ||
| 27 | + std::cout << "ThresholdGradV2DInferShapeTest TearDown" << std::endl; | ||
| 28 | + } | ||
| 29 | +}; | ||
| 30 | + | ||
| 31 | +TEST_F(ThresholdGradV2DInferShapeTest, test_same_shape_fp32) { | ||
| 32 | + auto inferShapeFunc = gert::OpImplRegistry::GetInstance().GetOpImpl("ThresholdGradV2D")->infer_shape; | ||
| 33 | + gert::StorageShape gradOutput_shape = {{16, 16}, {16, 16}}; | ||
| 34 | + gert::StorageShape self_shape = {{16, 16}, {16, 16}}; | ||
| 35 | + gert::StorageShape yShape = {{}, {}}; | ||
| 36 | + auto holder = gert::InferShapeContextFaker() | ||
| 37 | + .NodeIoNum(2, 1) | ||
| 38 | + .IrInstanceNum({1, 1}) | ||
| 39 | + .InputShapes({&gradOutput_shape, &self_shape}) | ||
| 40 | + .OutputShapes({&yShape}) | ||
| 41 | + .NodeInputTd(0, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 42 | + .NodeInputTd(1, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 43 | + .NodeOutputTd(0, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 44 | + .Build(); | ||
| 45 | + ASSERT_EQ(inferShapeFunc(holder.GetContext<gert::InferShapeContext>()), ge::GRAPH_SUCCESS); | ||
| 46 | + auto output_desc = holder.GetContext<gert::InferShapeContext>()->GetOutputShape(0); | ||
| 47 | + gert::Shape expected_output_shape = {16, 16}; | ||
| 48 | + ASSERT_EQ(Ops::Base::ToString(*output_desc), Ops::Base::ToString(expected_output_shape)); | ||
| 49 | +} | ||
| 50 | + | ||
| 51 | +TEST_F(ThresholdGradV2DInferShapeTest, test_broadcast_shape) { | ||
| 52 | + auto inferShapeFunc = gert::OpImplRegistry::GetInstance().GetOpImpl("ThresholdGradV2D")->infer_shape; | ||
| 53 | + gert::StorageShape gradOutput_shape = {{4, 1}, {4, 1}}; | ||
| 54 | + gert::StorageShape self_shape = {{1, 8}, {1, 8}}; | ||
| 55 | + gert::StorageShape yShape = {{}, {}}; | ||
| 56 | + auto holder = gert::InferShapeContextFaker() | ||
| 57 | + .NodeIoNum(2, 1) | ||
| 58 | + .IrInstanceNum({1, 1}) | ||
| 59 | + .InputShapes({&gradOutput_shape, &self_shape}) | ||
| 60 | + .OutputShapes({&yShape}) | ||
| 61 | + .NodeInputTd(0, ge::DT_FLOAT16, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 62 | + .NodeInputTd(1, ge::DT_FLOAT16, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 63 | + .NodeOutputTd(0, ge::DT_FLOAT16, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 64 | + .Build(); | ||
| 65 | + ASSERT_EQ(inferShapeFunc(holder.GetContext<gert::InferShapeContext>()), ge::GRAPH_SUCCESS); | ||
| 66 | + auto output_desc = holder.GetContext<gert::InferShapeContext>()->GetOutputShape(0); | ||
| 67 | + gert::Shape expected_output_shape = {4, 8}; | ||
| 68 | + ASSERT_EQ(Ops::Base::ToString(*output_desc), Ops::Base::ToString(expected_output_shape)); | ||
| 69 | +} | ||
| 70 | + | ||
| 71 | +TEST_F(ThresholdGradV2DInferShapeTest, test_fp16_same_shape) { | ||
| 72 | + auto inferShapeFunc = gert::OpImplRegistry::GetInstance().GetOpImpl("ThresholdGradV2D")->infer_shape; | ||
| 73 | + gert::StorageShape gradOutput_shape = {{32, 64, 128}, {32, 64, 128}}; | ||
| 74 | + gert::StorageShape self_shape = {{32, 64, 128}, {32, 64, 128}}; | ||
| 75 | + gert::StorageShape yShape = {{}, {}}; | ||
| 76 | + auto holder = gert::InferShapeContextFaker() | ||
| 77 | + .NodeIoNum(2, 1) | ||
| 78 | + .IrInstanceNum({1, 1}) | ||
| 79 | + .InputShapes({&gradOutput_shape, &self_shape}) | ||
| 80 | + .OutputShapes({&yShape}) | ||
| 81 | + .NodeInputTd(0, ge::DT_FLOAT16, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 82 | + .NodeInputTd(1, ge::DT_FLOAT16, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 83 | + .NodeOutputTd(0, ge::DT_FLOAT16, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 84 | + .Build(); | ||
| 85 | + ASSERT_EQ(inferShapeFunc(holder.GetContext<gert::InferShapeContext>()), ge::GRAPH_SUCCESS); | ||
| 86 | +} | ||
| 87 | + | ||
| 88 | +TEST_F(ThresholdGradV2DInferShapeTest, test_bf16_same_shape) { | ||
| 89 | + auto inferShapeFunc = gert::OpImplRegistry::GetInstance().GetOpImpl("ThresholdGradV2D")->infer_shape; | ||
| 90 | + gert::StorageShape gradOutput_shape = {{2, 64}, {2, 64}}; | ||
| 91 | + gert::StorageShape self_shape = {{2, 64}, {2, 64}}; | ||
| 92 | + gert::StorageShape yShape = {{}, {}}; | ||
| 93 | + auto holder = gert::InferShapeContextFaker() | ||
| 94 | + .NodeIoNum(2, 1) | ||
| 95 | + .IrInstanceNum({1, 1}) | ||
| 96 | + .InputShapes({&gradOutput_shape, &self_shape}) | ||
| 97 | + .OutputShapes({&yShape}) | ||
| 98 | + .NodeInputTd(0, ge::DT_BF16, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 99 | + .NodeInputTd(1, ge::DT_BF16, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 100 | + .NodeOutputTd(0, ge::DT_BF16, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 101 | + .Build(); | ||
| 102 | + ASSERT_EQ(inferShapeFunc(holder.GetContext<gert::InferShapeContext>()), ge::GRAPH_SUCCESS); | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | +TEST_F(ThresholdGradV2DInferShapeTest, test_int32_same_shape) { | ||
| 106 | + auto inferShapeFunc = gert::OpImplRegistry::GetInstance().GetOpImpl("ThresholdGradV2D")->infer_shape; | ||
| 107 | + gert::StorageShape gradOutput_shape = {{4, 4}, {4, 4}}; | ||
| 108 | + gert::StorageShape self_shape = {{4, 4}, {4, 4}}; | ||
| 109 | + gert::StorageShape yShape = {{}, {}}; | ||
| 110 | + auto holder = gert::InferShapeContextFaker() | ||
| 111 | + .NodeIoNum(2, 1) | ||
| 112 | + .IrInstanceNum({1, 1}) | ||
| 113 | + .InputShapes({&gradOutput_shape, &self_shape}) | ||
| 114 | + .OutputShapes({&yShape}) | ||
| 115 | + .NodeInputTd(0, ge::DT_INT32, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 116 | + .NodeInputTd(1, ge::DT_INT32, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 117 | + .NodeOutputTd(0, ge::DT_INT32, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 118 | + .Build(); | ||
| 119 | + ASSERT_EQ(inferShapeFunc(holder.GetContext<gert::InferShapeContext>()), ge::GRAPH_SUCCESS); | ||
| 120 | +} | ||
| 121 | + | ||
| 122 | +TEST_F(ThresholdGradV2DInferShapeTest, test_int8_same_shape) { | ||
| 123 | + auto inferShapeFunc = gert::OpImplRegistry::GetInstance().GetOpImpl("ThresholdGradV2D")->infer_shape; | ||
| 124 | + gert::StorageShape gradOutput_shape = {{8, 8}, {8, 8}}; | ||
| 125 | + gert::StorageShape self_shape = {{8, 8}, {8, 8}}; | ||
| 126 | + gert::StorageShape yShape = {{}, {}}; | ||
| 127 | + auto holder = gert::InferShapeContextFaker() | ||
| 128 | + .NodeIoNum(2, 1) | ||
| 129 | + .IrInstanceNum({1, 1}) | ||
| 130 | + .InputShapes({&gradOutput_shape, &self_shape}) | ||
| 131 | + .OutputShapes({&yShape}) | ||
| 132 | + .NodeInputTd(0, ge::DT_INT8, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 133 | + .NodeInputTd(1, ge::DT_INT8, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 134 | + .NodeOutputTd(0, ge::DT_INT8, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 135 | + .Build(); | ||
| 136 | + ASSERT_EQ(inferShapeFunc(holder.GetContext<gert::InferShapeContext>()), ge::GRAPH_SUCCESS); | ||
| 137 | +} | ||
| 138 | + | ||
| 139 | +TEST_F(ThresholdGradV2DInferShapeTest, test_uint8_same_shape) { | ||
| 140 | + auto inferShapeFunc = gert::OpImplRegistry::GetInstance().GetOpImpl("ThresholdGradV2D")->infer_shape; | ||
| 141 | + gert::StorageShape gradOutput_shape = {{16}, {16}}; | ||
| 142 | + gert::StorageShape self_shape = {{16}, {16}}; | ||
| 143 | + gert::StorageShape yShape = {{}, {}}; | ||
| 144 | + auto holder = gert::InferShapeContextFaker() | ||
| 145 | + .NodeIoNum(2, 1) | ||
| 146 | + .IrInstanceNum({1, 1}) | ||
| 147 | + .InputShapes({&gradOutput_shape, &self_shape}) | ||
| 148 | + .OutputShapes({&yShape}) | ||
| 149 | + .NodeInputTd(0, ge::DT_UINT8, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 150 | + .NodeInputTd(1, ge::DT_UINT8, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 151 | + .NodeOutputTd(0, ge::DT_UINT8, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 152 | + .Build(); | ||
| 153 | + ASSERT_EQ(inferShapeFunc(holder.GetContext<gert::InferShapeContext>()), ge::GRAPH_SUCCESS); | ||
| 154 | +} | ||
| 155 | + | ||
| 156 | +TEST_F(ThresholdGradV2DInferShapeTest, test_8d_shape) { | ||
| 157 | + auto inferShapeFunc = gert::OpImplRegistry::GetInstance().GetOpImpl("ThresholdGradV2D")->infer_shape; | ||
| 158 | + gert::StorageShape gradOutput_shape = {{1, 2, 3, 4, 5, 6, 7, 8}, {1, 2, 3, 4, 5, 6, 7, 8}}; | ||
| 159 | + gert::StorageShape self_shape = {{1, 2, 3, 4, 5, 6, 7, 8}, {1, 2, 3, 4, 5, 6, 7, 8}}; | ||
| 160 | + gert::StorageShape yShape = {{}, {}}; | ||
| 161 | + auto holder = gert::InferShapeContextFaker() | ||
| 162 | + .NodeIoNum(2, 1) | ||
| 163 | + .IrInstanceNum({1, 1}) | ||
| 164 | + .InputShapes({&gradOutput_shape, &self_shape}) | ||
| 165 | + .OutputShapes({&yShape}) | ||
| 166 | + .NodeInputTd(0, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 167 | + .NodeInputTd(1, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 168 | + .NodeOutputTd(0, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 169 | + .Build(); | ||
| 170 | + ASSERT_EQ(inferShapeFunc(holder.GetContext<gert::InferShapeContext>()), ge::GRAPH_SUCCESS); | ||
| 171 | +} | ||
| 172 | + | ||
| 173 | +TEST_F(ThresholdGradV2DInferShapeTest, test_empty_tensor) { | ||
| 174 | + auto inferShapeFunc = gert::OpImplRegistry::GetInstance().GetOpImpl("ThresholdGradV2D")->infer_shape; | ||
| 175 | + gert::StorageShape gradOutput_shape = {{0, 0}, {0, 0}}; | ||
| 176 | + gert::StorageShape self_shape = {{0, 0}, {0, 0}}; | ||
| 177 | + gert::StorageShape yShape = {{}, {}}; | ||
| 178 | + auto holder = gert::InferShapeContextFaker() | ||
| 179 | + .NodeIoNum(2, 1) | ||
| 180 | + .IrInstanceNum({1, 1}) | ||
| 181 | + .InputShapes({&gradOutput_shape, &self_shape}) | ||
| 182 | + .OutputShapes({&yShape}) | ||
| 183 | + .NodeInputTd(0, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 184 | + .NodeInputTd(1, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 185 | + .NodeOutputTd(0, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 186 | + .Build(); | ||
| 187 | + ASSERT_EQ(inferShapeFunc(holder.GetContext<gert::InferShapeContext>()), ge::GRAPH_SUCCESS); | ||
| 188 | +} | ||
| 189 | + | ||
| 190 | +TEST_F(ThresholdGradV2DInferShapeTest, test_scalar_input) { | ||
| 191 | + auto inferShapeFunc = gert::OpImplRegistry::GetInstance().GetOpImpl("ThresholdGradV2D")->infer_shape; | ||
| 192 | + gert::StorageShape gradOutput_shape = {{}, {}}; | ||
| 193 | + gert::StorageShape self_shape = {{}, {}}; | ||
| 194 | + gert::StorageShape yShape = {{}, {}}; | ||
| 195 | + auto holder = gert::InferShapeContextFaker() | ||
| 196 | + .NodeIoNum(2, 1) | ||
| 197 | + .IrInstanceNum({1, 1}) | ||
| 198 | + .InputShapes({&gradOutput_shape, &self_shape}) | ||
| 199 | + .OutputShapes({&yShape}) | ||
| 200 | + .NodeInputTd(0, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 201 | + .NodeInputTd(1, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 202 | + .NodeOutputTd(0, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 203 | + .Build(); | ||
| 204 | + ASSERT_EQ(inferShapeFunc(holder.GetContext<gert::InferShapeContext>()), ge::GRAPH_SUCCESS); | ||
| 205 | +} | ||
| 206 | + | ||
| 207 | +TEST_F(ThresholdGradV2DInferShapeTest, test_large_shape) { | ||
| 208 | + auto inferShapeFunc = gert::OpImplRegistry::GetInstance().GetOpImpl("ThresholdGradV2D")->infer_shape; | ||
| 209 | + gert::StorageShape gradOutput_shape = {{1024, 1024}, {1024, 1024}}; | ||
| 210 | + gert::StorageShape self_shape = {{1024, 1024}, {1024, 1024}}; | ||
| 211 | + gert::StorageShape yShape = {{}, {}}; | ||
| 212 | + auto holder = gert::InferShapeContextFaker() | ||
| 213 | + .NodeIoNum(2, 1) | ||
| 214 | + .IrInstanceNum({1, 1}) | ||
| 215 | + .InputShapes({&gradOutput_shape, &self_shape}) | ||
| 216 | + .OutputShapes({&yShape}) | ||
| 217 | + .NodeInputTd(0, ge::DT_FLOAT16, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 218 | + .NodeInputTd(1, ge::DT_FLOAT16, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 219 | + .NodeOutputTd(0, ge::DT_FLOAT16, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 220 | + .Build(); | ||
| 221 | + ASSERT_EQ(inferShapeFunc(holder.GetContext<gert::InferShapeContext>()), ge::GRAPH_SUCCESS); | ||
| 222 | +} | ||
| 223 | + | ||
| 224 | +TEST_F(ThresholdGradV2DInferShapeTest, test_broadcast_different_rank) { | ||
| 225 | + auto inferShapeFunc = gert::OpImplRegistry::GetInstance().GetOpImpl("ThresholdGradV2D")->infer_shape; | ||
| 226 | + gert::StorageShape gradOutput_shape = {{2, 3, 4}, {2, 3, 4}}; | ||
| 227 | + gert::StorageShape self_shape = {{4}, {4}}; | ||
| 228 | + gert::StorageShape yShape = {{}, {}}; | ||
| 229 | + auto holder = gert::InferShapeContextFaker() | ||
| 230 | + .NodeIoNum(2, 1) | ||
| 231 | + .IrInstanceNum({1, 1}) | ||
| 232 | + .InputShapes({&gradOutput_shape, &self_shape}) | ||
| 233 | + .OutputShapes({&yShape}) | ||
| 234 | + .NodeInputTd(0, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 235 | + .NodeInputTd(1, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 236 | + .NodeOutputTd(0, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 237 | + .Build(); | ||
| 238 | + ASSERT_EQ(inferShapeFunc(holder.GetContext<gert::InferShapeContext>()), ge::GRAPH_SUCCESS); | ||
| 239 | + auto output_desc = holder.GetContext<gert::InferShapeContext>()->GetOutputShape(0); | ||
| 240 | + gert::Shape expected_output_shape = {2, 3, 4}; | ||
| 241 | + ASSERT_EQ(Ops::Base::ToString(*output_desc), Ops::Base::ToString(expected_output_shape)); | ||
| 242 | +} | ||
| @@ -740,12 +740,12 @@ | |||
| 740 | <tr> | 740 | <tr> |
| 741 | <td>activation</td> | 741 | <td>activation</td> |
| 742 | <td><a href="../../activation/threshold_grad_v2_d/README.md">threshold_grad_v2_d</a></td> | 742 | <td><a href="../../activation/threshold_grad_v2_d/README.md">threshold_grad_v2_d</a></td> |
| 743 | - <td>✗</td> | 743 | + <td>✓</td> |
| 744 | - <td>✗</td> | 744 | + <td>✓</td> |
| 745 | <td>✓</td> | 745 | <td>✓</td> |
| 746 | <td>✗</td> | 746 | <td>✗</td> |
| 747 | <td>AI Core</td> | 747 | <td>AI Core</td> |
| 748 | - <td>该算子暂无Ascend C代码实现,欢迎开发者补充贡献,贡献方式参考<a href="../../CONTRIBUTING.md">贡献指南</a>。</td> | 748 | + <td>完成threshold正向的反向计算:out = (self > threshold) ? gradOutput : 0;threshold==0时等价ReluGrad。</td> |
| 749 | </tr> | 749 | </tr> |
| 750 | <tr> | 750 | <tr> |
| 751 | <td>control</td> | 751 | <td>control</td> |