已合并
BatchNormReduce算子在ascend 950 NPU上支持ascendc实现 #8543
yulianjie创建于 8月11日
BatchNormReduce算子在ascend 950 NPU上支持ascendc实现 #8543
已合并
共 33 个文件变更+5480-78
| @@ -4063,32 +4063,6 @@ REG_OP(AdaptiveMaxPool2d) | |||
| 4063 | /** | 4063 | /** |
| 4064 | * @brief Performs reduced batch normalization . | 4064 | * @brief Performs reduced batch normalization . |
| 4065 | 4065 | ||
| 4066 | - * @par Inputs: | ||
| 4067 | - * x: A 4D tensor of type float16 or float32 or bfloat16, with format NHWC or NCHW. | ||
| 4068 | - * Indicates the input tensor, that is, the original data to be normalized. | ||
| 4069 | - | ||
| 4070 | - * @par Outputs: | ||
| 4071 | - * @li sum: A 1D tensor of type float32 for SUM reduced "x". It represents the sum of the input tensor "x" on the C | ||
| 4072 | - axis. | ||
| 4073 | - * The shape of sum is consistent with the C axis of "x". Has the same format as "x". | ||
| 4074 | - * @li square_sum: A 1D tensor of type float32 for SUMSQ reduced "x". It represents the sum of squares of the input | ||
| 4075 | - tensor "x" on the C axis. | ||
| 4076 | - * The shape of sum is consistent with the C axis of "x". Has the same format as "x". \n | ||
| 4077 | - | ||
| 4078 | - * @attention Constraints: | ||
| 4079 | - * This operator is a BatchNorm fusion operator for updating the moving | ||
| 4080 | - * averages for training. | ||
| 4081 | - * This operator is used in conjunction with BNTrainingReduce. | ||
| 4082 | - */ | ||
| 4083 | - REG_OP(BNTrainingReduce) | ||
| 4084 | - .INPUT(x, TensorType({DT_FLOAT16, DT_FLOAT, DT_BF16})) | ||
| 4085 | - .OUTPUT(sum, TensorType({DT_FLOAT})) | ||
| 4086 | - .OUTPUT(square_sum, TensorType({DT_FLOAT})) | ||
| 4087 | - .OP_END_FACTORY_REG(BNTrainingReduce) | ||
| 4088 | - | ||
| 4089 | - /** | ||
| 4090 | - * @brief Performs reduced batch normalization . | ||
| 4091 | - | ||
| 4092 | * @par Inputs: | 4066 | * @par Inputs: |
| 4093 | * x: A 5D tensor of type float16 or float32 or bfloat16, with format NDHWC or NCDHW. | 4067 | * x: A 5D tensor of type float16 or float32 or bfloat16, with format NDHWC or NCDHW. |
| 4094 | * Represents the input tensor in batch normalization training. | 4068 | * Represents the input tensor in batch normalization training. |
| @@ -84,7 +84,7 @@ | |||
| 84 | | [aclnnBatchNormElemtBackward](../../norm/sync_batch_norm_backward_elemt/docs/aclnnBatchNormElemtBackward.md) | aclnnBatchNormElemt的反向计算。用于计算输入张量的元素级梯度,以便在反向传播过程中更新模型参数。 | 默认确定性实现 | 默认确定性实现 | | 84 | | [aclnnBatchNormElemtBackward](../../norm/sync_batch_norm_backward_elemt/docs/aclnnBatchNormElemtBackward.md) | aclnnBatchNormElemt的反向计算。用于计算输入张量的元素级梯度,以便在反向传播过程中更新模型参数。 | 默认确定性实现 | 默认确定性实现 | |
| 85 | | [aclnnBatchNormBackward](../../norm/batch_norm_grad_v3/docs/aclnnBatchNormBackward.md) | [aclnnBatchNorm](../../norm/batch_norm_v3/docs/aclnnBatchNorm.md)的反向传播。用于计算输入张量的梯度,以便在反向传播过程中更新模型参数。 | 默认非确定性实现,支持配置开启 | 默认非确定性实现,支持配置开启 | | 85 | | [aclnnBatchNormBackward](../../norm/batch_norm_grad_v3/docs/aclnnBatchNormBackward.md) | [aclnnBatchNorm](../../norm/batch_norm_v3/docs/aclnnBatchNorm.md)的反向传播。用于计算输入张量的梯度,以便在反向传播过程中更新模型参数。 | 默认非确定性实现,支持配置开启 | 默认非确定性实现,支持配置开启 | |
| 86 | | [aclnnBatchNormGatherStatsWithCounts](../../norm/sync_batch_norm_gather_stats_with_counts/docs/aclnnBatchNormGatherStatsWithCounts.md) | 收集所有device的均值和方差,更新全局的均值和标准差的倒数。 | 默认确定性实现 | 默认确定性实现 | | 86 | | [aclnnBatchNormGatherStatsWithCounts](../../norm/sync_batch_norm_gather_stats_with_counts/docs/aclnnBatchNormGatherStatsWithCounts.md) | 收集所有device的均值和方差,更新全局的均值和标准差的倒数。 | 默认确定性实现 | 默认确定性实现 | |
| 87 | -| [aclnnBatchNormReduce](../../norm/bn_training_reduce/docs/aclnnBatchNormReduce.md) | 对数据做正则化处理的第一步,对数据进行求和及平方和 | 默认非确定性实现,支持配置开启 | - | | 87 | +| [aclnnBatchNormReduce](../../norm/bn_training_reduce/docs/aclnnBatchNormReduce.md) | 对数据做正则化处理的第一步,对数据进行求和及平方和 | 默认非确定性实现,支持配置开启 | 默认非确定性实现,支持配置开启 | |
| 88 | | [aclnnBatchNormReduceBackward](../../norm/sync_batch_norm_backward_reduce/docs/aclnnBatchNormReduceBackward.md) | 主要用于反向传播过程中计算BatchNorm操作的梯度,并进行一些中间结果的规约操作以优化计算效率。 | 默认确定性实现 | 默认确定性实现 | | 88 | | [aclnnBatchNormReduceBackward](../../norm/sync_batch_norm_backward_reduce/docs/aclnnBatchNormReduceBackward.md) | 主要用于反向传播过程中计算BatchNorm操作的梯度,并进行一些中间结果的规约操作以优化计算效率。 | 默认确定性实现 | 默认确定性实现 | |
| 89 | | [aclnnBidirectionLSTM](../../rnn/bidirection_lstm/docs/aclnnBidirectionLSTM.md) | 进行LSTM网络计算,接收输入序列和初始状态,返回输出序列和最终状态。 | - | - | | 89 | | [aclnnBidirectionLSTM](../../rnn/bidirection_lstm/docs/aclnnBidirectionLSTM.md) | 进行LSTM网络计算,接收输入序列和初始状态,返回输出序列和最终状态。 | - | - | |
| 90 | | [aclnnBidirectionLSTMV2](../../rnn/bidirection_lstmv2/docs/aclnnBidirectionLSTMV2.md) | 进行LSTM网络计算,接收输入序列和初始状态,返回输出序列和最终状态。 | - | - | | 90 | | [aclnnBidirectionLSTMV2](../../rnn/bidirection_lstmv2/docs/aclnnBidirectionLSTMV2.md) | 进行LSTM网络计算,接收输入序列和初始状态,返回输出序列和最终状态。 | - | - | |
| @@ -3210,12 +3210,12 @@ | |||
| 3210 | <tr> | 3210 | <tr> |
| 3211 | <td>norm</td> | 3211 | <td>norm</td> |
| 3212 | <td><a href="../../norm/bn_training_reduce/README.md">bn_training_reduce</a></td> | 3212 | <td><a href="../../norm/bn_training_reduce/README.md">bn_training_reduce</a></td> |
| 3213 | - <td>✗</td> | ||
| 3214 | - <td>✗</td> | ||
| 3215 | <td>✓</td> | 3213 | <td>✓</td> |
| 3216 | - <td>✗</td> | 3214 | + <td>✓</td> |
| 3215 | + <td>✓</td> | ||
| 3216 | + <td>✓</td> | ||
| 3217 | <td>AI Core</td> | 3217 | <td>AI Core</td> |
| 3218 | - <td>该算子暂无Ascend C代码实现,欢迎开发者补充贡献,贡献方式参考<a href="../../CONTRIBUTING.md">贡献指南</a>。</td> | 3218 | + <td>对四维NCHW输入的N、H、W轴执行归约,输出每个通道的元素和与平方和。</td> |
| 3219 | </tr> | 3219 | </tr> |
| 3220 | <tr> | 3220 | <tr> |
| 3221 | <td>norm</td> | 3221 | <td>norm</td> |
| @@ -1,18 +1,25 @@ | |||
| 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 | +set(SUPPORT_COMPUTE_UNIT "ascend950") |
| 11 | -if(NOT ENABLE_TEST AND NOT BENCHMARK) | 13 | +set(SUPPORT_TILING_DIR "arch35") |
| 12 | - list(REMOVE_ITEM CURRENT_DIRS tests) | 14 | + |
| 13 | -endif() | 15 | +add_modules_sources( |
| 14 | -foreach(SUB_DIR ${CURRENT_DIRS}) | 16 | + HOSTNAME ${OP_HOST_NAME} |
| 15 | - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUB_DIR}/CMakeLists.txt") | 17 | + MODE PRIVATE |
| 16 | - add_subdirectory(${SUB_DIR}) | 18 | + DIR ${CMAKE_CURRENT_SOURCE_DIR} |
| 17 | - endif() | 19 | + OPTYPE bn_training_reduce |
| 18 | -endforeach() | 20 | + ACLNNTYPE aclnn_exclude |
| 21 | + COMPUTE_UNIT ${SUPPORT_COMPUTE_UNIT} | ||
| 22 | + TILING_DIR ${SUPPORT_TILING_DIR} | ||
| 23 | + DISABLE_IN_OPP TRUE | ||
| 24 | + DEPENDENCIES norm_common batch_norm_v3 | ||
| 25 | +) | ||
| @@ -1,3 +1,94 @@ | |||
| 1 | # BNTrainingReduce | 1 | # BNTrainingReduce |
| 2 | 2 | ||
| 3 | -本目录仅包含BNTrainingReduce算子对应的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 | +- 算子功能:对四维NCHW或NHWC输入的N、H、W轴执行归约,输出每个通道的元素和与平方和。 | ||
| 17 | +- 计算公式: | ||
| 18 | + | ||
| 19 | + $$ | ||
| 20 | + sum_c = \sum_{n,h,w}x_{n,c,h,w} | ||
| 21 | + $$ | ||
| 22 | + | ||
| 23 | + $$ | ||
| 24 | + square\_sum_c = \sum_{n,h,w}x_{n,c,h,w}^{2} | ||
| 25 | + $$ | ||
| 26 | + | ||
| 27 | +## 参数说明 | ||
| 28 | + | ||
| 29 | +<table style="table-layout: fixed; width: 1576px"><colgroup> | ||
| 30 | +<col style="width: 170px"> | ||
| 31 | +<col style="width: 170px"> | ||
| 32 | +<col style="width: 520px"> | ||
| 33 | +<col style="width: 300px"> | ||
| 34 | +<col style="width: 170px"> | ||
| 35 | +</colgroup> | ||
| 36 | +<thead> | ||
| 37 | + <tr> | ||
| 38 | + <th>参数名</th> | ||
| 39 | + <th>输入/输出/属性</th> | ||
| 40 | + <th>描述</th> | ||
| 41 | + <th>数据类型</th> | ||
| 42 | + <th>数据格式</th> | ||
| 43 | + </tr></thead> | ||
| 44 | +<tbody> | ||
| 45 | + <tr> | ||
| 46 | + <td>x</td> | ||
| 47 | + <td>输入</td> | ||
| 48 | + <td>待统计的四维训练激活。</td> | ||
| 49 | + <td>FLOAT16、BFLOAT16、FLOAT</td> | ||
| 50 | + <td>NCHW、NHWC</td> | ||
| 51 | + </tr> | ||
| 52 | + <tr> | ||
| 53 | + <td>sum</td> | ||
| 54 | + <td>输出</td> | ||
| 55 | + <td>沿N、H、W轴归约得到的每通道元素和。</td> | ||
| 56 | + <td>FLOAT</td> | ||
| 57 | + <td>ND</td> | ||
| 58 | + </tr> | ||
| 59 | + <tr> | ||
| 60 | + <td>square_sum</td> | ||
| 61 | + <td>输出</td> | ||
| 62 | + <td>沿N、H、W轴归约得到的每通道平方和。</td> | ||
| 63 | + <td>FLOAT</td> | ||
| 64 | + <td>ND</td> | ||
| 65 | + </tr> | ||
| 66 | +</tbody></table> | ||
| 67 | + | ||
| 68 | +## 约束说明 | ||
| 69 | + | ||
| 70 | +- x必须为四维NCHW或NHWC张量,通道轴分别为第1维或第3维。 | ||
| 71 | +- sum和square_sum必须为一维ND张量,长度等于x的C维,数据类型固定为FLOAT。 | ||
| 72 | +- FLOAT16和BFLOAT16输入按FLOAT精度执行平方与累加。 | ||
| 73 | +- 算子无属性,支持空Tensor;归约集合为空时输出为零或空向量。 | ||
| 74 | + | ||
| 75 | +## 调用说明 | ||
| 76 | + | ||
| 77 | +<table><thead> | ||
| 78 | + <tr> | ||
| 79 | + <th>调用方式</th> | ||
| 80 | + <th>调用样例</th> | ||
| 81 | + <th>说明</th> | ||
| 82 | + </tr></thead> | ||
| 83 | +<tbody> | ||
| 84 | + <tr> | ||
| 85 | + <td>aclnn调用</td> | ||
| 86 | + <td><a href="./examples/test_aclnn_batch_norm_reduce.cpp">test_aclnn_batch_norm_reduce</a></td> | ||
| 87 | + <td>通过aclnnBatchNormReduce两段式接口调用BNTrainingReduce算子。</td> | ||
| 88 | + </tr> | ||
| 89 | + <tr> | ||
| 90 | + <td>图模式调用</td> | ||
| 91 | + <td><a href="./examples/test_geir_bn_training_reduce.cpp">test_geir_bn_training_reduce</a></td> | ||
| 92 | + <td>通过算子IR构图并调用BNTrainingReduce算子。</td> | ||
| 93 | + </tr> | ||
| 94 | +</tbody></table> | ||
| @@ -5,7 +5,7 @@ | |||
| 5 | ## 产品支持情况 | 5 | ## 产品支持情况 |
| 6 | 6 | ||
| 7 | <!-- npu="950" id1 --> | 7 | <!-- npu="950" id1 --> |
| 8 | -- <term>Ascend 950PR/Ascend 950DT</term>:不支持 | 8 | +- <term>Ascend 950PR/Ascend 950DT</term>:支持 |
| 9 | <!-- end id1 --> | 9 | <!-- end id1 --> |
| 10 | <!-- npu="A3" id2 --> | 10 | <!-- npu="A3" id2 --> |
| 11 | - <term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term>:支持 | 11 | - <term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term>:支持 |
| @@ -0,0 +1,158 @@ | |||
| 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 | + | ||
| 17 | + do { \ | ||
| 18 | + if (!(cond)) { \ | ||
| 19 | + return_expr; \ | ||
| 20 | + } \ | ||
| 21 | + } while (0) | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + do { \ | ||
| 25 | + printf(message, ##__VA_ARGS__); \ | ||
| 26 | + } while (0) | ||
| 27 | + | ||
| 28 | +int64_t GetShapeSize(const std::vector<int64_t>& shape) | ||
| 29 | +{ | ||
| 30 | + int64_t shape_size = 1; | ||
| 31 | + for (auto i : shape) { | ||
| 32 | + shape_size *= i; | ||
| 33 | + } | ||
| 34 | + return shape_size; | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +int Init(int32_t deviceId, aclrtStream* stream) | ||
| 38 | +{ | ||
| 39 | + // 固定写法,资源初始化 | ||
| 40 | + auto ret = aclInit(nullptr); | ||
| 41 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret); | ||
| 42 | + ret = aclrtSetDevice(deviceId); | ||
| 43 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret); | ||
| 44 | + ret = aclrtCreateStream(stream); | ||
| 45 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret); | ||
| 46 | + | ||
| 47 | + return 0; | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +template <typename T> | ||
| 51 | +int CreateAclTensor(const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr, | ||
| 52 | + aclDataType dataType, aclTensor** tensor, aclFormat format) | ||
| 53 | +{ | ||
| 54 | + auto size = GetShapeSize(shape) * sizeof(T); | ||
| 55 | + // 调用aclrtMalloc申请device侧内存 | ||
| 56 | + auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 57 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret); | ||
| 58 | + | ||
| 59 | + // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上 | ||
| 60 | + ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 61 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret); | ||
| 62 | + | ||
| 63 | + // 计算连续tensor的strides | ||
| 64 | + std::vector<int64_t> strides(shape.size(), 1); | ||
| 65 | + for (int64_t i = static_cast<int64_t>(shape.size()) - 2; i >= 0; --i) { | ||
| 66 | + strides[i] = shape[i + 1] * strides[i + 1]; | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + // 调用aclCreateTensor接口创建aclTensor | ||
| 70 | + *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, format, shape.data(), | ||
| 71 | + shape.size(), *deviceAddr); | ||
| 72 | + CHECK_RET(*tensor != nullptr, LOG_PRINT("aclCreateTensor failed\n"); return -1); | ||
| 73 | + return 0; | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +int main(int argc, char* argv[]) | ||
| 77 | +{ | ||
| 78 | + // 1. (固定写法)device/stream初始化,参考acl API手册 | ||
| 79 | + // 根据自己的实际device填写deviceId | ||
| 80 | + int32_t deviceId = 0; | ||
| 81 | + aclrtStream stream; | ||
| 82 | + auto ret = Init(deviceId, &stream); | ||
| 83 | + // check根据自己的需要处理 | ||
| 84 | + CHECK_RET(ret == 0, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret); | ||
| 85 | + | ||
| 86 | + // 2. 构造输入与输出,需要根据API的接口自定义构造 | ||
| 87 | + void* xDeviceAddr = nullptr; | ||
| 88 | + void* sumDeviceAddr = nullptr; | ||
| 89 | + void* squareSumDeviceAddr = nullptr; | ||
| 90 | + aclTensor* x = nullptr; | ||
| 91 | + aclTensor* sum = nullptr; | ||
| 92 | + aclTensor* squareSum = nullptr; | ||
| 93 | + | ||
| 94 | + std::vector<int64_t> xShape = {1, 2, 3, 4}; | ||
| 95 | + std::vector<int64_t> sumShape = {2}; | ||
| 96 | + std::vector<int64_t> squareSumShape = {2}; | ||
| 97 | + std::vector<float> xHostData(24, 1); | ||
| 98 | + std::vector<float> sumHostData(2, 0); | ||
| 99 | + std::vector<float> squareSumHostData(2, 0); | ||
| 100 | + | ||
| 101 | + // 创建x aclTensor | ||
| 102 | + ret = CreateAclTensor(xHostData, xShape, &xDeviceAddr, aclDataType::ACL_FLOAT, &x, aclFormat::ACL_FORMAT_NCHW); | ||
| 103 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 104 | + // 创建sum aclTensor | ||
| 105 | + ret = CreateAclTensor(sumHostData, sumShape, &sumDeviceAddr, aclDataType::ACL_FLOAT, &sum, | ||
| 106 | + aclFormat::ACL_FORMAT_ND); | ||
| 107 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 108 | + // 创建squareSum aclTensor | ||
| 109 | + ret = CreateAclTensor(squareSumHostData, squareSumShape, &squareSumDeviceAddr, aclDataType::ACL_FLOAT, &squareSum, | ||
| 110 | + aclFormat::ACL_FORMAT_ND); | ||
| 111 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 112 | + // 3. 调用CANN算子库API,需要修改为具体的API | ||
| 113 | + uint64_t workspaceSize = 0; | ||
| 114 | + aclOpExecutor* executor; | ||
| 115 | + // 调用aclnnBatchNormReduce第一段接口 | ||
| 116 | + ret = aclnnBatchNormReduceGetWorkspaceSize(x, sum, squareSum, &workspaceSize, &executor); | ||
| 117 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnBatchNormReduceGetWorkspaceSize failed. ERROR: %d\n", ret); | ||
| 118 | + return ret); | ||
| 119 | + // 根据第一段接口计算出的workspaceSize申请device内存 | ||
| 120 | + void* workspaceAddr = nullptr; | ||
| 121 | + if (workspaceSize > 0) { | ||
| 122 | + ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 123 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret); | ||
| 124 | + } | ||
| 125 | + // 调用aclnnBatchNormReduce第二段接口 | ||
| 126 | + ret = aclnnBatchNormReduce(workspaceAddr, workspaceSize, executor, stream); | ||
| 127 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnBatchNormReduce failed. ERROR: %d\n", ret); return ret); | ||
| 128 | + // 4. (固定写法)同步等待任务执行结束 | ||
| 129 | + ret = aclrtSynchronizeStream(stream); | ||
| 130 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret); | ||
| 131 | + // 5. 获取输出的值,将device侧内存上的结果拷贝至host侧,需要根据具体API的接口定义修改 | ||
| 132 | + auto size = GetShapeSize(sumShape); | ||
| 133 | + std::vector<float> resultData(size, 0); | ||
| 134 | + ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), sumDeviceAddr, | ||
| 135 | + size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST); | ||
| 136 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret); | ||
| 137 | + | ||
| 138 | + for (int64_t i = 0; i < size; i++) { | ||
| 139 | + LOG_PRINT("result[%ld] is: %f\n", i, resultData[i]); | ||
| 140 | + } | ||
| 141 | + | ||
| 142 | + // 6. 释放aclTensor,需要根据具体API的接口定义修改 | ||
| 143 | + aclDestroyTensor(x); | ||
| 144 | + aclDestroyTensor(sum); | ||
| 145 | + aclDestroyTensor(squareSum); | ||
| 146 | + | ||
| 147 | + // 7. 释放device资源,需要根据具体API的接口定义修改 | ||
| 148 | + aclrtFree(xDeviceAddr); | ||
| 149 | + aclrtFree(sumDeviceAddr); | ||
| 150 | + aclrtFree(squareSumDeviceAddr); | ||
| 151 | + if (workspaceSize > 0) { | ||
| 152 | + aclrtFree(workspaceAddr); | ||
| 153 | + } | ||
| 154 | + aclrtDestroyStream(stream); | ||
| 155 | + aclrtResetDevice(deviceId); | ||
| 156 | + aclFinalize(); | ||
| 157 | + return 0; | ||
| 158 | +} | ||
| @@ -0,0 +1,919 @@ | |||
| 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 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | +// The selected standard domain header owns BNTrainingReduce. Data remains in | ||
| 35 | +// ops_proto_legacy.h in this CANN release, so keep its tiny construction class | ||
| 36 | +// test-local instead of including the duplicate-heavy compatibility header. | ||
| 37 | +namespace ge { | ||
| 38 | +REG_OP(Data).INPUT(x, TensorType::ALL()).OUTPUT(y, TensorType::ALL()).ATTR(index, Int, 0).OP_END_FACTORY_REG(Data) | ||
| 39 | +} // namespace ge | ||
| 40 | + | ||
| 41 | +namespace { | ||
| 42 | + | ||
| 43 | +constexpr float kRtol = 1.0e-4F; | ||
| 44 | +constexpr float kAtol = 1.0e-4F; | ||
| 45 | + | ||
| 46 | +struct CaseDef { | ||
| 47 | + std::string id; | ||
| 48 | + std::string category; | ||
| 49 | + std::string mode; | ||
| 50 | + std::string capabilities; | ||
| 51 | + std::string dtype; | ||
| 52 | + std::string inputFormat; | ||
| 53 | + std::string graphShape; | ||
| 54 | + std::string feedShapes; | ||
| 55 | + std::string pattern; | ||
| 56 | + std::string expectation; | ||
| 57 | + std::string acceptance; | ||
| 58 | +}; | ||
| 59 | + | ||
| 60 | +struct InputData { | ||
| 61 | + ge::Tensor tensor; | ||
| 62 | + std::vector<float> quantized; | ||
| 63 | +}; | ||
| 64 | + | ||
| 65 | +struct Expected { | ||
| 66 | + std::vector<float> sum; | ||
| 67 | + std::vector<float> squareSum; | ||
| 68 | +}; | ||
| 69 | + | ||
| 70 | +struct Observed { | ||
| 71 | + bool readable = false; | ||
| 72 | + bool valuePass = false; | ||
| 73 | + std::vector<float> sum; | ||
| 74 | + std::vector<float> squareSum; | ||
| 75 | +}; | ||
| 76 | + | ||
| 77 | +struct GraphBundle { | ||
| 78 | + ge::Graph graph; | ||
| 79 | + ge::Operator reduce; | ||
| 80 | + std::string node; | ||
| 81 | +}; | ||
| 82 | + | ||
| 83 | +std::vector<std::string> Split(const std::string& text, char delimiter) | ||
| 84 | +{ | ||
| 85 | + std::vector<std::string> fields; | ||
| 86 | + std::stringstream stream(text); | ||
| 87 | + std::string field; | ||
| 88 | + while (std::getline(stream, field, delimiter)) { | ||
| 89 | + fields.push_back(field); | ||
| 90 | + } | ||
| 91 | + if (!text.empty() && text.back() == delimiter) { | ||
| 92 | + fields.emplace_back(); | ||
| 93 | + } | ||
| 94 | + return fields; | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +bool LoadCases(const std::string& path, std::vector<CaseDef>& cases) | ||
| 98 | +{ | ||
| 99 | + std::ifstream input(path); | ||
| 100 | + if (!input.is_open()) { | ||
| 101 | + std::fprintf(stderr, "GEIR-INFRA manifest=%s reason=open_failed value=FAIL\n", path.c_str()); | ||
| 102 | + return false; | ||
| 103 | + } | ||
| 104 | + std::string line; | ||
| 105 | + if (!std::getline(input, line)) { | ||
| 106 | + return false; | ||
| 107 | + } | ||
| 108 | + const std::string expectedHeader = "id\tcategory\tmode\tcapabilities\tdtype\tinput_format\tgraph_shape\tfeed_" | ||
| 109 | + "shapes\tpattern\texpectation\tacceptance"; | ||
| 110 | + if (line != expectedHeader) { | ||
| 111 | + std::fprintf(stderr, "GEIR-INFRA manifest=%s reason=bad_header value=FAIL\n", path.c_str()); | ||
| 112 | + return false; | ||
| 113 | + } | ||
| 114 | + std::map<std::string, bool> ids; | ||
| 115 | + while (std::getline(input, line)) { | ||
| 116 | + if (line.empty()) { | ||
| 117 | + continue; | ||
| 118 | + } | ||
| 119 | + const auto fields = Split(line, '\t'); | ||
| 120 | + if (fields.size() != 11U || fields[0].empty() || ids[fields[0]]) { | ||
| 121 | + std::fprintf(stderr, "GEIR-INFRA manifest=%s reason=bad_or_duplicate_row row=%s value=FAIL\n", path.c_str(), | ||
| 122 | + line.c_str()); | ||
| 123 | + return false; | ||
| 124 | + } | ||
| 125 | + ids[fields[0]] = true; | ||
| 126 | + cases.push_back({fields[0], fields[1], fields[2], fields[3], fields[4], fields[5], fields[6], fields[7], | ||
| 127 | + fields[8], fields[9], fields[10]}); | ||
| 128 | + } | ||
| 129 | + return !cases.empty(); | ||
| 130 | +} | ||
| 131 | + | ||
| 132 | +std::vector<int64_t> ParseShape(const std::string& text) | ||
| 133 | +{ | ||
| 134 | + if (text == "UNKNOWN_RANK") { | ||
| 135 | + return ge::UNKNOWN_RANK; | ||
| 136 | + } | ||
| 137 | + if (text.size() < 2U || text.front() != '[' || text.back() != ']') { | ||
| 138 | + return {}; | ||
| 139 | + } | ||
| 140 | + const std::string inner = text.substr(1, text.size() - 2U); | ||
| 141 | + if (inner.empty()) { | ||
| 142 | + return {}; | ||
| 143 | + } | ||
| 144 | + std::vector<int64_t> dims; | ||
| 145 | + for (const auto& field : Split(inner, ',')) { | ||
| 146 | + dims.push_back(std::stoll(field)); | ||
| 147 | + } | ||
| 148 | + return dims; | ||
| 149 | +} | ||
| 150 | + | ||
| 151 | +std::string ShapeString(const std::vector<int64_t>& dims) | ||
| 152 | +{ | ||
| 153 | + if (dims == ge::UNKNOWN_RANK) { | ||
| 154 | + return "UNKNOWN_RANK"; | ||
| 155 | + } | ||
| 156 | + std::ostringstream out; | ||
| 157 | + out << '['; | ||
| 158 | + for (size_t i = 0; i < dims.size(); ++i) { | ||
| 159 | + if (i != 0U) { | ||
| 160 | + out << ','; | ||
| 161 | + } | ||
| 162 | + out << dims[i]; | ||
| 163 | + } | ||
| 164 | + out << ']'; | ||
| 165 | + return out.str(); | ||
| 166 | +} | ||
| 167 | + | ||
| 168 | +std::string ShapeString(const ge::Shape& shape) { return ShapeString(shape.GetDims()); } | ||
| 169 | + | ||
| 170 | +size_t Numel(const std::vector<int64_t>& shape) | ||
| 171 | +{ | ||
| 172 | + if (shape.empty()) { | ||
| 173 | + return 1U; | ||
| 174 | + } | ||
| 175 | + size_t count = 1U; | ||
| 176 | + for (const int64_t dim : shape) { | ||
| 177 | + if (dim <= 0) { | ||
| 178 | + return 0U; | ||
| 179 | + } | ||
| 180 | + count *= static_cast<size_t>(dim); | ||
| 181 | + } | ||
| 182 | + return count; | ||
| 183 | +} | ||
| 184 | + | ||
| 185 | +ge::DataType ParseDtype(const std::string& dtype) | ||
| 186 | +{ | ||
| 187 | + if (dtype == "F16") { | ||
| 188 | + return ge::DT_FLOAT16; | ||
| 189 | + } | ||
| 190 | + if (dtype == "BF16") { | ||
| 191 | + return ge::DT_BF16; | ||
| 192 | + } | ||
| 193 | + if (dtype == "I32") { | ||
| 194 | + return ge::DT_INT32; | ||
| 195 | + } | ||
| 196 | + return ge::DT_FLOAT; | ||
| 197 | +} | ||
| 198 | + | ||
| 199 | +const char* DtypeName(ge::DataType dtype) | ||
| 200 | +{ | ||
| 201 | + switch (dtype) { | ||
| 202 | + case ge::DT_FLOAT16: | ||
| 203 | + return "FLOAT16"; | ||
| 204 | + case ge::DT_BF16: | ||
| 205 | + return "BFLOAT16"; | ||
| 206 | + case ge::DT_FLOAT: | ||
| 207 | + return "FLOAT32"; | ||
| 208 | + case ge::DT_INT32: | ||
| 209 | + return "INT32"; | ||
| 210 | + case ge::DT_UNDEFINED: | ||
| 211 | + return "UNDEFINED"; | ||
| 212 | + default: | ||
| 213 | + return "OTHER"; | ||
| 214 | + } | ||
| 215 | +} | ||
| 216 | + | ||
| 217 | +ge::Format ParseFormat(const std::string& format) { return format == "NHWC" ? ge::FORMAT_NHWC : ge::FORMAT_NCHW; } | ||
| 218 | + | ||
| 219 | +const char* FormatName(ge::Format format) | ||
| 220 | +{ | ||
| 221 | + if (format == ge::FORMAT_ND) { | ||
| 222 | + return "ND"; | ||
| 223 | + } | ||
| 224 | + if (format == ge::FORMAT_NCHW) { | ||
| 225 | + return "NCHW"; | ||
| 226 | + } | ||
| 227 | + if (format == ge::FORMAT_NHWC) { | ||
| 228 | + return "NHWC"; | ||
| 229 | + } | ||
| 230 | + return "OTHER"; | ||
| 231 | +} | ||
| 232 | + | ||
| 233 | +uint32_t RoundRightShiftToEven(uint32_t value, uint32_t shift) | ||
| 234 | +{ | ||
| 235 | + const uint32_t truncated = value >> shift; | ||
| 236 | + const uint32_t remainder = value & ((1U << shift) - 1U); | ||
| 237 | + const uint32_t halfway = 1U << (shift - 1U); | ||
| 238 | + return truncated + static_cast<uint32_t>(remainder > halfway || (remainder == halfway && (truncated & 1U) != 0U)); | ||
| 239 | +} | ||
| 240 | + | ||
| 241 | +aclFloat16 FloatToFloat16(float value) | ||
| 242 | +{ | ||
| 243 | + uint32_t bits = 0U; | ||
| 244 | + std::memcpy(&bits, &value, sizeof(bits)); | ||
| 245 | + const uint32_t sign = (bits >> 16U) & 0x8000U; | ||
| 246 | + const uint32_t exponent = (bits >> 23U) & 0xFFU; | ||
| 247 | + const uint32_t mantissa = bits & 0x7FFFFFU; | ||
| 248 | + if (exponent == 0xFFU) { | ||
| 249 | + return static_cast<aclFloat16>(sign | 0x7C00U | (mantissa == 0U ? 0U : 0x0200U)); | ||
| 250 | + } | ||
| 251 | + if (exponent == 0U) { | ||
| 252 | + return static_cast<aclFloat16>(sign); | ||
| 253 | + } | ||
| 254 | + | ||
| 255 | + int32_t halfExponent = static_cast<int32_t>(exponent) - 127 + 15; | ||
| 256 | + if (halfExponent <= 0) { | ||
| 257 | + if (halfExponent < -10) { | ||
| 258 | + return static_cast<aclFloat16>(sign); | ||
| 259 | + } | ||
| 260 | + const uint32_t rounded = RoundRightShiftToEven(mantissa | 0x800000U, 14U - halfExponent); | ||
| 261 | + return static_cast<aclFloat16>(sign | rounded); | ||
| 262 | + } | ||
| 263 | + if (halfExponent >= 31) { | ||
| 264 | + return static_cast<aclFloat16>(sign | 0x7C00U); | ||
| 265 | + } | ||
| 266 | + | ||
| 267 | + uint32_t roundedMantissa = RoundRightShiftToEven(mantissa, 13U); | ||
| 268 | + if (roundedMantissa == 0x400U) { | ||
| 269 | + roundedMantissa = 0U; | ||
| 270 | + ++halfExponent; | ||
| 271 | + if (halfExponent >= 31) { | ||
| 272 | + return static_cast<aclFloat16>(sign | 0x7C00U); | ||
| 273 | + } | ||
| 274 | + } | ||
| 275 | + return static_cast<aclFloat16>(sign | (static_cast<uint32_t>(halfExponent) << 10U) | roundedMantissa); | ||
| 276 | +} | ||
| 277 | + | ||
| 278 | +float Float16ToFloat(aclFloat16 value) | ||
| 279 | +{ | ||
| 280 | + const uint32_t sign = (static_cast<uint32_t>(value) & 0x8000U) << 16U; | ||
| 281 | + uint32_t exponent = (static_cast<uint32_t>(value) >> 10U) & 0x1FU; | ||
| 282 | + uint32_t mantissa = static_cast<uint32_t>(value) & 0x03FFU; | ||
| 283 | + uint32_t bits = sign; | ||
| 284 | + if (exponent == 0U && mantissa != 0U) { | ||
| 285 | + int32_t normalizedExponent = -14; | ||
| 286 | + while ((mantissa & 0x0400U) == 0U) { | ||
| 287 | + mantissa <<= 1U; | ||
| 288 | + --normalizedExponent; | ||
| 289 | + } | ||
| 290 | + mantissa &= 0x03FFU; | ||
| 291 | + bits |= static_cast<uint32_t>(normalizedExponent + 127) << 23U; | ||
| 292 | + bits |= mantissa << 13U; | ||
| 293 | + } else if (exponent == 0x1FU) { | ||
| 294 | + bits |= 0x7F800000U | (mantissa << 13U); | ||
| 295 | + } else if (exponent != 0U) { | ||
| 296 | + bits |= (exponent - 15U + 127U) << 23U; | ||
| 297 | + bits |= mantissa << 13U; | ||
| 298 | + } | ||
| 299 | + float result = 0.0F; | ||
| 300 | + std::memcpy(&result, &bits, sizeof(result)); | ||
| 301 | + return result; | ||
| 302 | +} | ||
| 303 | + | ||
| 304 | +uint16_t FloatToBfloat16(float value) | ||
| 305 | +{ | ||
| 306 | + uint32_t bits = 0U; | ||
| 307 | + std::memcpy(&bits, &value, sizeof(bits)); | ||
| 308 | + const uint32_t roundingBias = 0x7FFFU + ((bits >> 16U) & 1U); | ||
| 309 | + return static_cast<uint16_t>((bits + roundingBias) >> 16U); | ||
| 310 | +} | ||
| 311 | + | ||
| 312 | +float Bfloat16ToFloat(uint16_t value) | ||
| 313 | +{ | ||
| 314 | + uint32_t bits = static_cast<uint32_t>(value) << 16U; | ||
| 315 | + float result = 0.0F; | ||
| 316 | + std::memcpy(&result, &bits, sizeof(result)); | ||
| 317 | + return result; | ||
| 318 | +} | ||
| 319 | + | ||
| 320 | +std::vector<float> GenerateValues(const std::vector<int64_t>& shape, const std::string& pattern) | ||
| 321 | +{ | ||
| 322 | + const size_t count = Numel(shape); | ||
| 323 | + std::vector<float> values(count, 0.0F); | ||
| 324 | + for (size_t i = 0; i < count; ++i) { | ||
| 325 | + const int64_t signedIndex = static_cast<int64_t>(i % 17U) - 8; | ||
| 326 | + values[i] = static_cast<float>(signedIndex) * 0.125F; | ||
| 327 | + } | ||
| 328 | + if (pattern == "zero") { | ||
| 329 | + std::fill(values.begin(), values.end(), 0.0F); | ||
| 330 | + } else if (pattern == "alternate") { | ||
| 331 | + for (size_t i = 0; i < count; ++i) { | ||
| 332 | + values[i] = (i % 2U == 0U) ? 16.0F : -16.0F; | ||
| 333 | + } | ||
| 334 | + } else if (pattern == "60000") { | ||
| 335 | + std::fill(values.begin(), values.end(), 60000.0F); | ||
| 336 | + } else if (pattern == "1e20") { | ||
| 337 | + std::fill(values.begin(), values.end(), 1.0e20F); | ||
| 338 | + } else if (pattern == "partition" && shape.size() == 4U) { | ||
| 339 | + const int64_t n = shape[0]; | ||
| 340 | + const int64_t c = shape[1]; | ||
| 341 | + const int64_t h = shape[2]; | ||
| 342 | + const int64_t w = shape[3]; | ||
| 343 | + for (int64_t ni = 0; ni < n; ++ni) { | ||
| 344 | + for (int64_t ci = 0; ci < c; ++ci) { | ||
| 345 | + for (int64_t hi = 0; hi < h; ++hi) { | ||
| 346 | + for (int64_t wi = 0; wi < w; ++wi) { | ||
| 347 | + const size_t offset = static_cast<size_t>(((ni * c + ci) * h + hi) * w + wi); | ||
| 348 | + const int64_t code = ((ni % 2) * 13 + ci * 7 + hi * 3 + wi) % 19 - 9; | ||
| 349 | + values[offset] = static_cast<float>(code) * 0.0625F; | ||
| 350 | + } | ||
| 351 | + } | ||
| 352 | + } | ||
| 353 | + } | ||
| 354 | + } | ||
| 355 | + | ||
| 356 | + if (shape.size() == 4U && count != 0U && shape[1] > 0) { | ||
| 357 | + const int64_t channel = shape[1] > 1 ? 1 : 0; | ||
| 358 | + const size_t first = static_cast<size_t>(channel * shape[2] * shape[3]); | ||
| 359 | + if (pattern == "nan") { | ||
| 360 | + values[first] = std::numeric_limits<float>::quiet_NaN(); | ||
| 361 | + } else if (pattern == "posinf") { | ||
| 362 | + values[first] = std::numeric_limits<float>::infinity(); | ||
| 363 | + } else if (pattern == "posneginf") { | ||
| 364 | + values[first] = std::numeric_limits<float>::infinity(); | ||
| 365 | + if (first + 1U < values.size()) { | ||
| 366 | + values[first + 1U] = -std::numeric_limits<float>::infinity(); | ||
| 367 | + } | ||
| 368 | + } | ||
| 369 | + } | ||
| 370 | + return values; | ||
| 371 | +} | ||
| 372 | + | ||
| 373 | +InputData MakeInput(const CaseDef& test, const std::vector<int64_t>& shape) | ||
| 374 | +{ | ||
| 375 | + const ge::DataType dtype = ParseDtype(test.dtype); | ||
| 376 | + std::vector<float> source = GenerateValues(shape, test.pattern); | ||
| 377 | + std::vector<uint8_t> bytes; | ||
| 378 | + std::vector<float> quantized(source.size()); | ||
| 379 | + if (dtype == ge::DT_FLOAT16) { | ||
| 380 | + std::vector<aclFloat16> raw(source.size()); | ||
| 381 | + for (size_t i = 0; i < source.size(); ++i) { | ||
| 382 | + raw[i] = FloatToFloat16(source[i]); | ||
| 383 | + quantized[i] = Float16ToFloat(raw[i]); | ||
| 384 | + } | ||
| 385 | + bytes.resize(raw.size() * sizeof(aclFloat16)); | ||
| 386 | + if (!bytes.empty()) { | ||
| 387 | + std::memcpy(bytes.data(), raw.data(), bytes.size()); | ||
| 388 | + } | ||
| 389 | + } else if (dtype == ge::DT_BF16) { | ||
| 390 | + std::vector<uint16_t> raw(source.size()); | ||
| 391 | + for (size_t i = 0; i < source.size(); ++i) { | ||
| 392 | + raw[i] = FloatToBfloat16(source[i]); | ||
| 393 | + quantized[i] = Bfloat16ToFloat(raw[i]); | ||
| 394 | + } | ||
| 395 | + bytes.resize(raw.size() * sizeof(uint16_t)); | ||
| 396 | + if (!bytes.empty()) { | ||
| 397 | + std::memcpy(bytes.data(), raw.data(), bytes.size()); | ||
| 398 | + } | ||
| 399 | + } else if (dtype == ge::DT_INT32) { | ||
| 400 | + std::vector<int32_t> raw(source.size()); | ||
| 401 | + for (size_t i = 0; i < source.size(); ++i) { | ||
| 402 | + raw[i] = static_cast<int32_t>(source[i]); | ||
| 403 | + quantized[i] = static_cast<float>(raw[i]); | ||
| 404 | + } | ||
| 405 | + bytes.resize(raw.size() * sizeof(int32_t)); | ||
| 406 | + if (!bytes.empty()) { | ||
| 407 | + std::memcpy(bytes.data(), raw.data(), bytes.size()); | ||
| 408 | + } | ||
| 409 | + } else { | ||
| 410 | + quantized = source; | ||
| 411 | + bytes.resize(source.size() * sizeof(float)); | ||
| 412 | + if (!bytes.empty()) { | ||
| 413 | + std::memcpy(bytes.data(), source.data(), bytes.size()); | ||
| 414 | + } | ||
| 415 | + } | ||
| 416 | + | ||
| 417 | + ge::TensorDesc desc(ge::Shape(shape), ParseFormat(test.inputFormat), dtype); | ||
| 418 | + desc.SetPlacement(ge::kPlacementHost); | ||
| 419 | + desc.SetRealDimCnt(static_cast<int64_t>(shape.size())); | ||
| 420 | + return {ge::Tensor(desc, bytes), std::move(quantized)}; | ||
| 421 | +} | ||
| 422 | + | ||
| 423 | +Expected ComputeExpected(const std::vector<float>& input, const std::vector<int64_t>& shape) | ||
| 424 | +{ | ||
| 425 | + Expected expected; | ||
| 426 | + if (shape.size() != 4U || shape[1] < 0) { | ||
| 427 | + return expected; | ||
| 428 | + } | ||
| 429 | + const int64_t n = shape[0]; | ||
| 430 | + const int64_t c = shape[1]; | ||
| 431 | + const int64_t h = shape[2]; | ||
| 432 | + const int64_t w = shape[3]; | ||
| 433 | + expected.sum.assign(static_cast<size_t>(c), 0.0F); | ||
| 434 | + expected.squareSum.assign(static_cast<size_t>(c), 0.0F); | ||
| 435 | + for (int64_t ni = 0; ni < n; ++ni) { | ||
| 436 | + for (int64_t ci = 0; ci < c; ++ci) { | ||
| 437 | + for (int64_t hi = 0; hi < h; ++hi) { | ||
| 438 | + for (int64_t wi = 0; wi < w; ++wi) { | ||
| 439 | + const size_t offset = static_cast<size_t>(((ni * c + ci) * h + hi) * w + wi); | ||
| 440 | + const float value = input[offset]; | ||
| 441 | + expected.sum[static_cast<size_t>(ci)] += value; | ||
| 442 | + expected.squareSum[static_cast<size_t>(ci)] += value * value; | ||
| 443 | + } | ||
| 444 | + } | ||
| 445 | + } | ||
| 446 | + } | ||
| 447 | + return expected; | ||
| 448 | +} | ||
| 449 | + | ||
| 450 | +bool Close(float actual, float expected, float& absoluteError) | ||
| 451 | +{ | ||
| 452 | + if (std::isnan(expected)) { | ||
| 453 | + return std::isnan(actual); | ||
| 454 | + } | ||
| 455 | + if (std::isinf(expected)) { | ||
| 456 | + return std::isinf(actual) && std::signbit(actual) == std::signbit(expected); | ||
| 457 | + } | ||
| 458 | + if (!std::isfinite(actual)) { | ||
| 459 | + return false; | ||
| 460 | + } | ||
| 461 | + absoluteError = std::fabs(actual - expected); | ||
| 462 | + return absoluteError <= kAtol + kRtol * std::fabs(expected); | ||
| 463 | +} | ||
| 464 | + | ||
| 465 | +std::string OutputShapes(const std::vector<ge::Tensor>& outputs) | ||
| 466 | +{ | ||
| 467 | + if (outputs.size() != 2U) { | ||
| 468 | + return "COUNT_" + std::to_string(outputs.size()); | ||
| 469 | + } | ||
| 470 | + return "{" + ShapeString(outputs[0].GetTensorDesc().GetShape()) + "," + | ||
| 471 | + ShapeString(outputs[1].GetTensorDesc().GetShape()) + "}"; | ||
| 472 | +} | ||
| 473 | + | ||
| 474 | +std::string OutputDtypes(const std::vector<ge::Tensor>& outputs) | ||
| 475 | +{ | ||
| 476 | + if (outputs.size() != 2U) { | ||
| 477 | + return "COUNT_" + std::to_string(outputs.size()); | ||
| 478 | + } | ||
| 479 | + return "{" + std::string(DtypeName(outputs[0].GetTensorDesc().GetDataType())) + "," + | ||
| 480 | + DtypeName(outputs[1].GetTensorDesc().GetDataType()) + "}"; | ||
| 481 | +} | ||
| 482 | + | ||
| 483 | +std::string OutputFormats(const std::vector<ge::Tensor>& outputs) | ||
| 484 | +{ | ||
| 485 | + if (outputs.size() != 2U) { | ||
| 486 | + return "COUNT_" + std::to_string(outputs.size()); | ||
| 487 | + } | ||
| 488 | + return "{" + std::string(FormatName(outputs[0].GetTensorDesc().GetFormat())) + "," + | ||
| 489 | + FormatName(outputs[1].GetTensorDesc().GetFormat()) + "}"; | ||
| 490 | +} | ||
| 491 | + | ||
| 492 | +Observed ValidateOutputs(const CaseDef& test, const std::vector<int64_t>& feedShape, const Expected& expected, | ||
| 493 | + const std::vector<ge::Tensor>& outputs, std::string& maxErrors, std::string& actualNumel) | ||
| 494 | +{ | ||
| 495 | + Observed observed; | ||
| 496 | + if (outputs.size() != 2U || feedShape.size() != 4U) { | ||
| 497 | + maxErrors = "{NA,NA}"; | ||
| 498 | + actualNumel = "{NA,NA}"; | ||
| 499 | + return observed; | ||
| 500 | + } | ||
| 501 | + const int64_t channel = feedShape[1]; | ||
| 502 | + const std::vector<int64_t> expectedShape = {channel}; | ||
| 503 | + bool metadataPass = true; | ||
| 504 | + for (const auto& output : outputs) { | ||
| 505 | + const ge::TensorDesc desc = output.GetTensorDesc(); | ||
| 506 | + metadataPass = metadataPass && desc.GetShape().GetDims() == expectedShape; | ||
| 507 | + metadataPass = metadataPass && desc.GetDataType() == ge::DT_FLOAT; | ||
| 508 | + metadataPass = metadataPass && desc.GetFormat() == ge::FORMAT_ND; | ||
| 509 | + metadataPass = metadataPass && output.GetSize() == static_cast<size_t>(channel) * sizeof(float); | ||
| 510 | + } | ||
| 511 | + | ||
| 512 | + observed.sum.resize(static_cast<size_t>(channel)); | ||
| 513 | + observed.squareSum.resize(static_cast<size_t>(channel)); | ||
| 514 | + if (channel > 0) { | ||
| 515 | + if (outputs[0].GetData() == nullptr || outputs[1].GetData() == nullptr) { | ||
| 516 | + maxErrors = "{NA,NA}"; | ||
| 517 | + actualNumel = "{0,0}"; | ||
| 518 | + return observed; | ||
| 519 | + } | ||
| 520 | + std::memcpy(observed.sum.data(), outputs[0].GetData(), observed.sum.size() * sizeof(float)); | ||
| 521 | + std::memcpy(observed.squareSum.data(), outputs[1].GetData(), observed.squareSum.size() * sizeof(float)); | ||
| 522 | + } | ||
| 523 | + observed.readable = true; | ||
| 524 | + | ||
| 525 | + float maxSumError = 0.0F; | ||
| 526 | + float maxSquareError = 0.0F; | ||
| 527 | + bool valuesPass = expected.sum.size() == observed.sum.size() && | ||
| 528 | + expected.squareSum.size() == observed.squareSum.size(); | ||
| 529 | + for (size_t i = 0; valuesPass && i < observed.sum.size(); ++i) { | ||
| 530 | + float error = 0.0F; | ||
| 531 | + valuesPass = Close(observed.sum[i], expected.sum[i], error); | ||
| 532 | + maxSumError = std::max(maxSumError, error); | ||
| 533 | + error = 0.0F; | ||
| 534 | + valuesPass = valuesPass && Close(observed.squareSum[i], expected.squareSum[i], error); | ||
| 535 | + maxSquareError = std::max(maxSquareError, error); | ||
| 536 | + if (test.pattern == "zero") { | ||
| 537 | + valuesPass = valuesPass && observed.sum[i] == 0.0F && observed.squareSum[i] == 0.0F; | ||
| 538 | + } | ||
| 539 | + if (test.pattern == "alternate" && std::isfinite(observed.squareSum[i])) { | ||
| 540 | + valuesPass = valuesPass && observed.squareSum[i] >= 0.0F; | ||
| 541 | + } | ||
| 542 | + } | ||
| 543 | + observed.valuePass = metadataPass && valuesPass; | ||
| 544 | + std::ostringstream errors; | ||
| 545 | + errors << '{' << maxSumError << ',' << maxSquareError << '}'; | ||
| 546 | + maxErrors = errors.str(); | ||
| 547 | + actualNumel = "{" + std::to_string(outputs[0].GetSize() / sizeof(float)) + "," + | ||
| 548 | + std::to_string(outputs[1].GetSize() / sizeof(float)) + "}"; | ||
| 549 | + return observed; | ||
| 550 | +} | ||
| 551 | + | ||
| 552 | +bool IsUnknownRank(const ge::TensorDesc& desc) { return desc.GetShape().GetDims() == ge::UNKNOWN_RANK; } | ||
| 553 | + | ||
| 554 | +bool PrintAndCheckShapeInit(const std::string& caseId, const GraphBundle& bundle) | ||
| 555 | +{ | ||
| 556 | + const ge::TensorDesc sumDesc = bundle.reduce.GetOutputDescByName("sum"); | ||
| 557 | + const ge::TensorDesc squareDesc = bundle.reduce.GetOutputDescByName("square_sum"); | ||
| 558 | + const bool valid = IsUnknownRank(sumDesc) && IsUnknownRank(squareDesc) && sumDesc.GetDataType() == ge::DT_FLOAT && | ||
| 559 | + squareDesc.GetDataType() == ge::DT_FLOAT; | ||
| 560 | + std::printf("SHAPE-INIT case=%s outputs={sum:{shape:%s,dtype:%s},square_sum:{shape:%s,dtype:%s}} value=%s\n", | ||
| 561 | + caseId.c_str(), ShapeString(sumDesc.GetShape()).c_str(), DtypeName(sumDesc.GetDataType()), | ||
| 562 | + ShapeString(squareDesc.GetShape()).c_str(), DtypeName(squareDesc.GetDataType()), | ||
| 563 | + valid ? "PASS" : "FAIL"); | ||
| 564 | + return valid; | ||
| 565 | +} | ||
| 566 | + | ||
| 567 | +GraphBundle BuildGraph(const CaseDef& test) | ||
| 568 | +{ | ||
| 569 | + GraphBundle bundle; | ||
| 570 | + bundle.node = test.id == "route-target" ? "bn_training_reduce_route" : "bn_training_reduce_" + test.id; | ||
| 571 | + bundle.graph = ge::Graph((bundle.node + "_graph").c_str()); | ||
| 572 | + auto reduce = ge::op::BNTrainingReduce(bundle.node.c_str()); | ||
| 573 | + const ge::TensorDesc outDesc(ge::Shape(ge::UNKNOWN_RANK), ge::FORMAT_ND, ge::DT_FLOAT); | ||
| 574 | + reduce.update_output_desc_sum(outDesc); | ||
| 575 | + reduce.update_output_desc_square_sum(outDesc); | ||
| 576 | + | ||
| 577 | + std::vector<ge::Operator> inputs; | ||
| 578 | + const std::vector<int64_t> graphShape = test.mode == "missing-input" ? std::vector<int64_t>{1, 1, 1, 1} : | ||
| 579 | + ParseShape(test.graphShape); | ||
| 580 | + ge::TensorDesc xDesc(ge::Shape(graphShape), ParseFormat(test.inputFormat), ParseDtype(test.dtype)); | ||
| 581 | + xDesc.SetRealDimCnt(graphShape == ge::UNKNOWN_RANK ? 0 : static_cast<int64_t>(graphShape.size())); | ||
| 582 | + auto data = ge::op::Data((bundle.node + "_x").c_str()).set_attr_index(0); | ||
| 583 | + data.update_input_desc_x(xDesc); | ||
| 584 | + data.update_output_desc_y(xDesc); | ||
| 585 | + reduce.set_input_x(data); | ||
| 586 | + reduce.update_input_desc_x(xDesc); | ||
| 587 | + bundle.graph.AddOp(data); | ||
| 588 | + inputs.push_back(data); | ||
| 589 | + bundle.graph.AddOp(reduce); | ||
| 590 | + const std::vector<std::pair<ge::Operator, std::vector<size_t>>> outputs = {{reduce, {0U, 1U}}}; | ||
| 591 | + if (!inputs.empty()) { | ||
| 592 | + bundle.graph.SetInputs(inputs); | ||
| 593 | + } | ||
| 594 | + bundle.graph.SetOutputs(outputs); | ||
| 595 | + bundle.reduce = reduce; | ||
| 596 | + return bundle; | ||
| 597 | +} | ||
| 598 | + | ||
| 599 | +const char* GraphMode(const CaseDef& test) | ||
| 600 | +{ | ||
| 601 | + return test.mode == "dynamic" || test.mode == "unknown-rank" ? "dynamic" : "static"; | ||
| 602 | +} | ||
| 603 | + | ||
| 604 | +std::string Sanitize(std::string text) | ||
| 605 | +{ | ||
| 606 | + if (text.size() > 160U) { | ||
| 607 | + text.resize(160U); | ||
| 608 | + } | ||
| 609 | + for (char& ch : text) { | ||
| 610 | + const bool safe = (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || | ||
| 611 | + ch == '_' || ch == '-' || ch == '.'; | ||
| 612 | + if (!safe) { | ||
| 613 | + ch = '_'; | ||
| 614 | + } | ||
| 615 | + } | ||
| 616 | + return text.empty() ? "NONE" : text; | ||
| 617 | +} | ||
| 618 | + | ||
| 619 | +std::string CurrentError() | ||
| 620 | +{ | ||
| 621 | + const ge::AscendString message = ge::GEGetErrorMsgV2(); | ||
| 622 | + return message.GetString() == nullptr ? "NONE" : Sanitize(message.GetString()); | ||
| 623 | +} | ||
| 624 | + | ||
| 625 | +std::string ClassifyError(const std::string& raw) | ||
| 626 | +{ | ||
| 627 | + std::string lower = raw; | ||
| 628 | + std::transform(lower.begin(), lower.end(), lower.begin(), | ||
| 629 | + [](unsigned char ch) { return static_cast<char>(std::tolower(ch)); }); | ||
| 630 | + if (lower.find("dtype") != std::string::npos || lower.find("data_type") != std::string::npos || | ||
| 631 | + lower.find("datatype") != std::string::npos) { | ||
| 632 | + return "dtype_not_supported"; | ||
| 633 | + } | ||
| 634 | + if (lower.find("format") != std::string::npos || lower.find("shape") != std::string::npos || | ||
| 635 | + lower.find("rank") != std::string::npos || lower.find("dim") != std::string::npos) { | ||
| 636 | + return "shape_mismatch"; | ||
| 637 | + } | ||
| 638 | + if (lower.find("null") != std::string::npos || lower.find("missing") != std::string::npos || | ||
| 639 | + lower.find("input") != std::string::npos || lower.find("anchor") != std::string::npos || | ||
| 640 | + lower.find("edge") != std::string::npos) { | ||
| 641 | + return "null_input"; | ||
| 642 | + } | ||
| 643 | + return "unclassified"; | ||
| 644 | +} | ||
| 645 | + | ||
| 646 | +Observed RunPositiveFeed(ge::Session& session, uint32_t gid, const CaseDef& test, const GraphBundle& bundle, | ||
| 647 | + const std::vector<int64_t>& feedShape, const std::string& feedLabel, | ||
| 648 | + const std::string& runMode, bool& runCompleted) | ||
| 649 | +{ | ||
| 650 | + const InputData input = MakeInput(test, feedShape); | ||
| 651 | + const Expected expected = ComputeExpected(input.quantized, feedShape); | ||
| 652 | + std::vector<ge::Tensor> outputs; | ||
| 653 | + const ge::Status status = session.RunGraph(gid, {input.tensor}, outputs); | ||
| 654 | + runCompleted = status == ge::SUCCESS; | ||
| 655 | + std::string maxErrors = "{NA,NA}"; | ||
| 656 | + std::string actualNumel = "{NA,NA}"; | ||
| 657 | + Observed observed; | ||
| 658 | + if (runCompleted) { | ||
| 659 | + observed = ValidateOutputs(test, feedShape, expected, outputs, maxErrors, actualNumel); | ||
| 660 | + } | ||
| 661 | + const std::string runError = runCompleted ? "NONE" : CurrentError(); | ||
| 662 | + const int64_t channel = feedShape.size() == 4U ? feedShape[1] : -1; | ||
| 663 | + std::printf("GRAPH-RUN mode=%s case=%s node=%s gid=%u feed=%s expected_shape={[%ld],[%ld]} actual_shape=%s " | ||
| 664 | + "expected_dtype={FLOAT32,FLOAT32} actual_dtype=%s expected_format={ND,ND} actual_format=%s " | ||
| 665 | + "expected_numel={%ld,%ld} actual_numel=%s max_abs=%s rtol=%.1e atol=%.1e status=%u error=%s value=%s\n", | ||
| 666 | + runMode.c_str(), feedLabel.c_str(), bundle.node.c_str(), gid, ShapeString(feedShape).c_str(), channel, | ||
| 667 | + channel, OutputShapes(outputs).c_str(), OutputDtypes(outputs).c_str(), OutputFormats(outputs).c_str(), | ||
| 668 | + channel, channel, actualNumel.c_str(), maxErrors.c_str(), kRtol, kAtol, status, runError.c_str(), | ||
| 669 | + observed.valuePass ? "PASS" : "FAIL"); | ||
| 670 | + return observed; | ||
| 671 | +} | ||
| 672 | + | ||
| 673 | +bool CheckPartitionInvariant(const Observed& whole, const Observed& half, float& maxError) | ||
| 674 | +{ | ||
| 675 | + if (!whole.readable || !half.readable || whole.sum.size() != half.sum.size()) { | ||
| 676 | + return false; | ||
| 677 | + } | ||
| 678 | + bool pass = true; | ||
| 679 | + maxError = 0.0F; | ||
| 680 | + for (size_t i = 0; i < whole.sum.size(); ++i) { | ||
| 681 | + float error = 0.0F; | ||
| 682 | + pass = pass && Close(whole.sum[i], half.sum[i] * 2.0F, error); | ||
| 683 | + maxError = std::max(maxError, error); | ||
| 684 | + error = 0.0F; | ||
| 685 | + pass = pass && Close(whole.squareSum[i], half.squareSum[i] * 2.0F, error); | ||
| 686 | + maxError = std::max(maxError, error); | ||
| 687 | + } | ||
| 688 | + return pass; | ||
| 689 | +} | ||
| 690 | + | ||
| 691 | +bool RunAcceptCase(ge::Session& session, uint32_t gid, const CaseDef& test, bool& infraFailure) | ||
| 692 | +{ | ||
| 693 | + GraphBundle bundle = BuildGraph(test); | ||
| 694 | + const char* graphMode = GraphMode(test); | ||
| 695 | + const ge::Status addStatus = session.AddGraph(gid, bundle.graph, std::map<ge::AscendString, ge::AscendString>{}); | ||
| 696 | + std::printf("GRAPH-ADD mode=%s case=%s node=%s gid=%u declared_input_shape=%s status=%u\n", graphMode, | ||
| 697 | + test.id.c_str(), bundle.node.c_str(), gid, test.graphShape.c_str(), addStatus); | ||
| 698 | + if (addStatus != ge::SUCCESS) { | ||
| 699 | + std::printf("GRAPH-RUN mode=%s case=%s node=%s gid=%u actual_stage=ADD_GRAPH status=%u error=%s value=FAIL\n", | ||
| 700 | + graphMode, test.id.c_str(), bundle.node.c_str(), gid, addStatus, CurrentError().c_str()); | ||
| 701 | + return false; | ||
| 702 | + } | ||
| 703 | + if (!PrintAndCheckShapeInit(test.id, bundle)) { | ||
| 704 | + infraFailure = true; | ||
| 705 | + } | ||
| 706 | + | ||
| 707 | + const auto feedFields = Split(test.feedShapes, ';'); | ||
| 708 | + bool pass = true; | ||
| 709 | + std::vector<Observed> observations; | ||
| 710 | + for (size_t i = 0; i < feedFields.size(); ++i) { | ||
| 711 | + bool completed = false; | ||
| 712 | + const std::string label = feedFields.size() == 1U ? test.id : test.id + "#feed" + std::to_string(i); | ||
| 713 | + observations.push_back( | ||
| 714 | + RunPositiveFeed(session, gid, test, bundle, ParseShape(feedFields[i]), label, graphMode, completed)); | ||
| 715 | + pass = pass && completed && observations.back().valuePass; | ||
| 716 | + } | ||
| 717 | + if (test.mode == "partition") { | ||
| 718 | + float maxError = 0.0F; | ||
| 719 | + const bool invariantPass = observations.size() == 2U && | ||
| 720 | + CheckPartitionInvariant(observations[0], observations[1], maxError); | ||
| 721 | + std::printf("INVARIANT case=%s name=batch_partition_additivity actual=whole_vs_two_halves max_abs=%g " | ||
| 722 | + "rtol=%.1e atol=%.1e value=%s\n", | ||
| 723 | + test.id.c_str(), maxError, kRtol, kAtol, invariantPass ? "PASS" : "FAIL"); | ||
| 724 | + pass = pass && invariantPass; | ||
| 725 | + } | ||
| 726 | + session.RemoveGraph(gid); | ||
| 727 | + return pass; | ||
| 728 | +} | ||
| 729 | + | ||
| 730 | +bool RunRejectCase(ge::Session& session, uint32_t gid, const CaseDef& test, bool& infraFailure) | ||
| 731 | +{ | ||
| 732 | + GraphBundle bundle = BuildGraph(test); | ||
| 733 | + const char* graphMode = GraphMode(test); | ||
| 734 | + const ge::Status addStatus = session.AddGraph(gid, bundle.graph, std::map<ge::AscendString, ge::AscendString>{}); | ||
| 735 | + std::printf("GRAPH-ADD mode=%s case=%s node=%s gid=%u declared_input_shape=%s status=%u\n", graphMode, | ||
| 736 | + test.id.c_str(), bundle.node.c_str(), gid, test.graphShape.c_str(), addStatus); | ||
| 737 | + std::string stage = "ADD_GRAPH"; | ||
| 738 | + ge::Status status = addStatus; | ||
| 739 | + std::string rawError = CurrentError(); | ||
| 740 | + if (addStatus == ge::SUCCESS) { | ||
| 741 | + if (!PrintAndCheckShapeInit(test.id, bundle)) { | ||
| 742 | + infraFailure = true; | ||
| 743 | + } | ||
| 744 | + stage = "RUN_GRAPH"; | ||
| 745 | + std::vector<ge::Tensor> feeds; | ||
| 746 | + if (test.mode != "missing-input") { | ||
| 747 | + feeds.push_back(MakeInput(test, ParseShape(test.feedShapes)).tensor); | ||
| 748 | + } | ||
| 749 | + std::vector<ge::Tensor> outputs; | ||
| 750 | + status = session.RunGraph(gid, feeds, outputs); | ||
| 751 | + rawError = CurrentError(); | ||
| 752 | + session.RemoveGraph(gid); | ||
| 753 | + } | ||
| 754 | + const std::string actualError = status == ge::SUCCESS ? "accepted" : ClassifyError(rawError); | ||
| 755 | + const bool pass = status != ge::SUCCESS && actualError == test.expectation; | ||
| 756 | + std::printf( | ||
| 757 | + "GRAPH-REJECT case=%s node=%s inputs={shape:%s,format:%s,dtype:%s} actual_stage=%s status=%u value=%s\n", | ||
| 758 | + test.id.c_str(), bundle.node.c_str(), test.graphShape.c_str(), test.inputFormat.c_str(), test.dtype.c_str(), | ||
| 759 | + stage.c_str(), status, pass ? "PASS" : "FAIL"); | ||
| 760 | + std::printf("REJECT-DETAIL case=%s expected_error=%s actual_error=%s raw_error=%s\n", test.id.c_str(), | ||
| 761 | + test.expectation.c_str(), actualError.c_str(), rawError.c_str()); | ||
| 762 | + return pass; | ||
| 763 | +} | ||
| 764 | + | ||
| 765 | +bool RunRuntimeInputContractCase(ge::Session& session, uint32_t gid, const CaseDef& test, bool& infraFailure) | ||
| 766 | +{ | ||
| 767 | + GraphBundle bundle = BuildGraph(test); | ||
| 768 | + const char* graphMode = GraphMode(test); | ||
| 769 | + const ge::Status addStatus = session.AddGraph(gid, bundle.graph, std::map<ge::AscendString, ge::AscendString>{}); | ||
| 770 | + std::printf("GRAPH-ADD mode=%s case=%s node=%s gid=%u declared_input_shape=%s status=%u\n", graphMode, | ||
| 771 | + test.id.c_str(), bundle.node.c_str(), gid, test.graphShape.c_str(), addStatus); | ||
| 772 | + std::string stage = "ADD_GRAPH"; | ||
| 773 | + ge::Status status = addStatus; | ||
| 774 | + std::string rawError = CurrentError(); | ||
| 775 | + if (addStatus == ge::SUCCESS) { | ||
| 776 | + if (!PrintAndCheckShapeInit(test.id, bundle)) { | ||
| 777 | + infraFailure = true; | ||
| 778 | + } | ||
| 779 | + stage = "RUN_GRAPH"; | ||
| 780 | + std::vector<ge::Tensor> outputs; | ||
| 781 | + status = session.RunGraph(gid, {}, outputs); | ||
| 782 | + rawError = CurrentError(); | ||
| 783 | + session.RemoveGraph(gid); | ||
| 784 | + } | ||
| 785 | + const std::string actualError = status == ge::SUCCESS ? "accepted" : ClassifyError(rawError); | ||
| 786 | + const bool pass = status != ge::SUCCESS && actualError == test.expectation; | ||
| 787 | + std::printf("RUNTIME-INPUT-CONTRACT case=%s node=%s graph=LEGAL_DATA_TO_OP runtime_feeds=EMPTY actual_stage=%s " | ||
| 788 | + "status=%u expected_error=%s actual_error=%s kernel_start_policy=ALLOWED value=%s\n", | ||
| 789 | + test.id.c_str(), bundle.node.c_str(), stage.c_str(), status, test.expectation.c_str(), | ||
| 790 | + actualError.c_str(), pass ? "PASS" : "FAIL"); | ||
| 791 | + std::printf("RUNTIME-CONTRACT-DETAIL case=%s raw_error=%s\n", test.id.c_str(), rawError.c_str()); | ||
| 792 | + return pass; | ||
| 793 | +} | ||
| 794 | + | ||
| 795 | +bool InitializeGe() | ||
| 796 | +{ | ||
| 797 | + const std::map<ge::AscendString, ge::AscendString> options = {{"ge.exec.deviceId", "0"}, {"ge.graphRunMode", "1"}}; | ||
| 798 | + const ge::Status status = ge::GEInitialize(options); | ||
| 799 | + if (status != ge::SUCCESS) { | ||
| 800 | + std::fprintf(stderr, "GEIR-INFRA stage=GEInitialize status=%u error=%s value=FAIL\n", status, | ||
| 801 | + CurrentError().c_str()); | ||
| 802 | + return false; | ||
| 803 | + } | ||
| 804 | + return true; | ||
| 805 | +} | ||
| 806 | + | ||
| 807 | +int RunRoute() | ||
| 808 | +{ | ||
| 809 | + if (!InitializeGe()) { | ||
| 810 | + return 2; | ||
| 811 | + } | ||
| 812 | + ge::Session session(std::map<ge::AscendString, ge::AscendString>{}); | ||
| 813 | + CaseDef route = {"route-target", "positive", "static", "route", "F32", "NCHW", | ||
| 814 | + "[1,1,1,1]", "[1,1,1,1]", "finite", "oracle", "accept"}; | ||
| 815 | + GraphBundle bundle = BuildGraph(route); | ||
| 816 | + constexpr uint32_t gid = 7000U; | ||
| 817 | + const ge::Status addStatus = session.AddGraph(gid, bundle.graph, std::map<ge::AscendString, ge::AscendString>{}); | ||
| 818 | + std::printf("GRAPH-ADD mode=route signature=F32_NCHW_1x1x1x1 node=%s gid=%u declared_input_shape=%s status=%u\n", | ||
| 819 | + bundle.node.c_str(), gid, route.graphShape.c_str(), addStatus); | ||
| 820 | + int probeExit = 0; | ||
| 821 | + bool businessPass = false; | ||
| 822 | + if (addStatus != ge::SUCCESS) { | ||
| 823 | + probeExit = 3; | ||
| 824 | + std::printf("GRAPH-RUN mode=route case=route-target node=%s gid=%u actual_stage=ADD_GRAPH status=%u " | ||
| 825 | + "error=%s value=FAIL\n", | ||
| 826 | + bundle.node.c_str(), gid, addStatus, CurrentError().c_str()); | ||
| 827 | + } else if (!PrintAndCheckShapeInit("route-target", bundle)) { | ||
| 828 | + probeExit = 4; | ||
| 829 | + } else { | ||
| 830 | + bool completed = false; | ||
| 831 | + const Observed observed = RunPositiveFeed(session, gid, route, bundle, {1, 1, 1, 1}, "route-target", "route", | ||
| 832 | + completed); | ||
| 833 | + if (!completed || !observed.readable) { | ||
| 834 | + probeExit = 5; | ||
| 835 | + } | ||
| 836 | + businessPass = observed.valuePass; | ||
| 837 | + session.RemoveGraph(gid); | ||
| 838 | + } | ||
| 839 | + const ge::Status finalizeStatus = ge::GEFinalize(); | ||
| 840 | + if (finalizeStatus != ge::SUCCESS && probeExit == 0) { | ||
| 841 | + probeExit = 6; | ||
| 842 | + } | ||
| 843 | + std::printf("ROUTE-PROBE op=BNTrainingReduce node=%s status=DONE value=%s probe_exit=%d\n", bundle.node.c_str(), | ||
| 844 | + businessPass ? "PASS" : "FAIL", probeExit); | ||
| 845 | + return probeExit; | ||
| 846 | +} | ||
| 847 | + | ||
| 848 | +int RunSelected(const std::vector<CaseDef>& cases, const std::string& selectedId, bool printSummary) | ||
| 849 | +{ | ||
| 850 | + if (!InitializeGe()) { | ||
| 851 | + return 2; | ||
| 852 | + } | ||
| 853 | + ge::Session session(std::map<ge::AscendString, ge::AscendString>{}); | ||
| 854 | + int total = 0; | ||
| 855 | + int passed = 0; | ||
| 856 | + bool infraFailure = false; | ||
| 857 | + uint32_t gid = 8000U; | ||
| 858 | + for (const auto& test : cases) { | ||
| 859 | + if (!selectedId.empty() && test.id != selectedId) { | ||
| 860 | + continue; | ||
| 861 | + } | ||
| 862 | + ++total; | ||
| 863 | + bool pass = false; | ||
| 864 | + if (test.acceptance == "reject") { | ||
| 865 | + pass = RunRejectCase(session, gid, test, infraFailure); | ||
| 866 | + } else if (test.acceptance == "runtime-contract") { | ||
| 867 | + pass = RunRuntimeInputContractCase(session, gid, test, infraFailure); | ||
| 868 | + } else { | ||
| 869 | + pass = RunAcceptCase(session, gid, test, infraFailure); | ||
| 870 | + } | ||
| 871 | + std::printf("CASE %s %s\n", test.id.c_str(), pass ? "PASS" : "FAIL"); | ||
| 872 | + if (pass) { | ||
| 873 | + ++passed; | ||
| 874 | + } | ||
| 875 | + ++gid; | ||
| 876 | + } | ||
| 877 | + const ge::Status finalizeStatus = ge::GEFinalize(); | ||
| 878 | + if (finalizeStatus != ge::SUCCESS) { | ||
| 879 | + infraFailure = true; | ||
| 880 | + } | ||
| 881 | + if (printSummary) { | ||
| 882 | + std::printf("GEIR-SUMMARY total=%d pass=%d fail=%d skip=0\n", total, passed, total - passed); | ||
| 883 | + } | ||
| 884 | + if (total == 0) { | ||
| 885 | + std::fprintf(stderr, "GEIR-INFRA selected=%s reason=no_matching_case value=FAIL\n", selectedId.c_str()); | ||
| 886 | + return 2; | ||
| 887 | + } | ||
| 888 | + return infraFailure ? 2 : 0; | ||
| 889 | +} | ||
| 890 | + | ||
| 891 | +} // namespace | ||
| 892 | + | ||
| 893 | +int main(int argc, char** argv) | ||
| 894 | +{ | ||
| 895 | + if (argc < 2) { | ||
| 896 | + return RunRoute(); | ||
| 897 | + } | ||
| 898 | + if (std::string(argv[1]) == "--route") { | ||
| 899 | + return RunRoute(); | ||
| 900 | + } | ||
| 901 | + std::string manifest; | ||
| 902 | + std::string selected; | ||
| 903 | + bool printSummary = true; | ||
| 904 | + if (std::string(argv[1]) == "--test" && argc == 3) { | ||
| 905 | + manifest = argv[2]; | ||
| 906 | + } else if (std::string(argv[1]) == "--case" && argc == 4) { | ||
| 907 | + selected = argv[2]; | ||
| 908 | + manifest = argv[3]; | ||
| 909 | + printSummary = false; | ||
| 910 | + } else { | ||
| 911 | + std::fprintf(stderr, "usage: %s {--route|--test|--case ID} cases.tsv\n", argv[0]); | ||
| 912 | + return 2; | ||
| 913 | + } | ||
| 914 | + std::vector<CaseDef> cases; | ||
| 915 | + if (!LoadCases(manifest, cases)) { | ||
| 916 | + return 2; | ||
| 917 | + } | ||
| 918 | + return RunSelected(cases, selected, printSummary); | ||
| 919 | +} | ||
| @@ -0,0 +1,919 @@ | |||
| 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 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | +// The selected standard domain header owns BNTrainingReduce. Data remains in | ||
| 35 | +// ops_proto_legacy.h in this CANN release, so keep its tiny construction class | ||
| 36 | +// test-local instead of including the duplicate-heavy compatibility header. | ||
| 37 | +namespace ge { | ||
| 38 | +REG_OP(Data).INPUT(x, TensorType::ALL()).OUTPUT(y, TensorType::ALL()).ATTR(index, Int, 0).OP_END_FACTORY_REG(Data) | ||
| 39 | +} // namespace ge | ||
| 40 | + | ||
| 41 | +namespace { | ||
| 42 | + | ||
| 43 | +constexpr float kRtol = 1.0e-4F; | ||
| 44 | +constexpr float kAtol = 1.0e-4F; | ||
| 45 | + | ||
| 46 | +struct CaseDef { | ||
| 47 | + std::string id; | ||
| 48 | + std::string category; | ||
| 49 | + std::string mode; | ||
| 50 | + std::string capabilities; | ||
| 51 | + std::string dtype; | ||
| 52 | + std::string inputFormat; | ||
| 53 | + std::string graphShape; | ||
| 54 | + std::string feedShapes; | ||
| 55 | + std::string pattern; | ||
| 56 | + std::string expectation; | ||
| 57 | + std::string acceptance; | ||
| 58 | +}; | ||
| 59 | + | ||
| 60 | +struct InputData { | ||
| 61 | + ge::Tensor tensor; | ||
| 62 | + std::vector<float> quantized; | ||
| 63 | +}; | ||
| 64 | + | ||
| 65 | +struct Expected { | ||
| 66 | + std::vector<float> sum; | ||
| 67 | + std::vector<float> squareSum; | ||
| 68 | +}; | ||
| 69 | + | ||
| 70 | +struct Observed { | ||
| 71 | + bool readable = false; | ||
| 72 | + bool valuePass = false; | ||
| 73 | + std::vector<float> sum; | ||
| 74 | + std::vector<float> squareSum; | ||
| 75 | +}; | ||
| 76 | + | ||
| 77 | +struct GraphBundle { | ||
| 78 | + ge::Graph graph; | ||
| 79 | + ge::Operator reduce; | ||
| 80 | + std::string node; | ||
| 81 | +}; | ||
| 82 | + | ||
| 83 | +std::vector<std::string> Split(const std::string& text, char delimiter) | ||
| 84 | +{ | ||
| 85 | + std::vector<std::string> fields; | ||
| 86 | + std::stringstream stream(text); | ||
| 87 | + std::string field; | ||
| 88 | + while (std::getline(stream, field, delimiter)) { | ||
| 89 | + fields.push_back(field); | ||
| 90 | + } | ||
| 91 | + if (!text.empty() && text.back() == delimiter) { | ||
| 92 | + fields.emplace_back(); | ||
| 93 | + } | ||
| 94 | + return fields; | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +bool LoadCases(const std::string& path, std::vector<CaseDef>& cases) | ||
| 98 | +{ | ||
| 99 | + std::ifstream input(path); | ||
| 100 | + if (!input.is_open()) { | ||
| 101 | + std::fprintf(stderr, "GEIR-INFRA manifest=%s reason=open_failed value=FAIL\n", path.c_str()); | ||
| 102 | + return false; | ||
| 103 | + } | ||
| 104 | + std::string line; | ||
| 105 | + if (!std::getline(input, line)) { | ||
| 106 | + return false; | ||
| 107 | + } | ||
| 108 | + const std::string expectedHeader = "id\tcategory\tmode\tcapabilities\tdtype\tinput_format\tgraph_shape\tfeed_" | ||
| 109 | + "shapes\tpattern\texpectation\tacceptance"; | ||
| 110 | + if (line != expectedHeader) { | ||
| 111 | + std::fprintf(stderr, "GEIR-INFRA manifest=%s reason=bad_header value=FAIL\n", path.c_str()); | ||
| 112 | + return false; | ||
| 113 | + } | ||
| 114 | + std::map<std::string, bool> ids; | ||
| 115 | + while (std::getline(input, line)) { | ||
| 116 | + if (line.empty()) { | ||
| 117 | + continue; | ||
| 118 | + } | ||
| 119 | + const auto fields = Split(line, '\t'); | ||
| 120 | + if (fields.size() != 11U || fields[0].empty() || ids[fields[0]]) { | ||
| 121 | + std::fprintf(stderr, "GEIR-INFRA manifest=%s reason=bad_or_duplicate_row row=%s value=FAIL\n", path.c_str(), | ||
| 122 | + line.c_str()); | ||
| 123 | + return false; | ||
| 124 | + } | ||
| 125 | + ids[fields[0]] = true; | ||
| 126 | + cases.push_back({fields[0], fields[1], fields[2], fields[3], fields[4], fields[5], fields[6], fields[7], | ||
| 127 | + fields[8], fields[9], fields[10]}); | ||
| 128 | + } | ||
| 129 | + return !cases.empty(); | ||
| 130 | +} | ||
| 131 | + | ||
| 132 | +std::vector<int64_t> ParseShape(const std::string& text) | ||
| 133 | +{ | ||
| 134 | + if (text == "UNKNOWN_RANK") { | ||
| 135 | + return ge::UNKNOWN_RANK; | ||
| 136 | + } | ||
| 137 | + if (text.size() < 2U || text.front() != '[' || text.back() != ']') { | ||
| 138 | + return {}; | ||
| 139 | + } | ||
| 140 | + const std::string inner = text.substr(1, text.size() - 2U); | ||
| 141 | + if (inner.empty()) { | ||
| 142 | + return {}; | ||
| 143 | + } | ||
| 144 | + std::vector<int64_t> dims; | ||
| 145 | + for (const auto& field : Split(inner, ',')) { | ||
| 146 | + dims.push_back(std::stoll(field)); | ||
| 147 | + } | ||
| 148 | + return dims; | ||
| 149 | +} | ||
| 150 | + | ||
| 151 | +std::string ShapeString(const std::vector<int64_t>& dims) | ||
| 152 | +{ | ||
| 153 | + if (dims == ge::UNKNOWN_RANK) { | ||
| 154 | + return "UNKNOWN_RANK"; | ||
| 155 | + } | ||
| 156 | + std::ostringstream out; | ||
| 157 | + out << '['; | ||
| 158 | + for (size_t i = 0; i < dims.size(); ++i) { | ||
| 159 | + if (i != 0U) { | ||
| 160 | + out << ','; | ||
| 161 | + } | ||
| 162 | + out << dims[i]; | ||
| 163 | + } | ||
| 164 | + out << ']'; | ||
| 165 | + return out.str(); | ||
| 166 | +} | ||
| 167 | + | ||
| 168 | +std::string ShapeString(const ge::Shape& shape) { return ShapeString(shape.GetDims()); } | ||
| 169 | + | ||
| 170 | +size_t Numel(const std::vector<int64_t>& shape) | ||
| 171 | +{ | ||
| 172 | + if (shape.empty()) { | ||
| 173 | + return 1U; | ||
| 174 | + } | ||
| 175 | + size_t count = 1U; | ||
| 176 | + for (const int64_t dim : shape) { | ||
| 177 | + if (dim <= 0) { | ||
| 178 | + return 0U; | ||
| 179 | + } | ||
| 180 | + count *= static_cast<size_t>(dim); | ||
| 181 | + } | ||
| 182 | + return count; | ||
| 183 | +} | ||
| 184 | + | ||
| 185 | +ge::DataType ParseDtype(const std::string& dtype) | ||
| 186 | +{ | ||
| 187 | + if (dtype == "F16") { | ||
| 188 | + return ge::DT_FLOAT16; | ||
| 189 | + } | ||
| 190 | + if (dtype == "BF16") { | ||
| 191 | + return ge::DT_BF16; | ||
| 192 | + } | ||
| 193 | + if (dtype == "I32") { | ||
| 194 | + return ge::DT_INT32; | ||
| 195 | + } | ||
| 196 | + return ge::DT_FLOAT; | ||
| 197 | +} | ||
| 198 | + | ||
| 199 | +const char* DtypeName(ge::DataType dtype) | ||
| 200 | +{ | ||
| 201 | + switch (dtype) { | ||
| 202 | + case ge::DT_FLOAT16: | ||
| 203 | + return "FLOAT16"; | ||
| 204 | + case ge::DT_BF16: | ||
| 205 | + return "BFLOAT16"; | ||
| 206 | + case ge::DT_FLOAT: | ||
| 207 | + return "FLOAT32"; | ||
| 208 | + case ge::DT_INT32: | ||
| 209 | + return "INT32"; | ||
| 210 | + case ge::DT_UNDEFINED: | ||
| 211 | + return "UNDEFINED"; | ||
| 212 | + default: | ||
| 213 | + return "OTHER"; | ||
| 214 | + } | ||
| 215 | +} | ||
| 216 | + | ||
| 217 | +ge::Format ParseFormat(const std::string& format) { return format == "NHWC" ? ge::FORMAT_NHWC : ge::FORMAT_NCHW; } | ||
| 218 | + | ||
| 219 | +const char* FormatName(ge::Format format) | ||
| 220 | +{ | ||
| 221 | + if (format == ge::FORMAT_ND) { | ||
| 222 | + return "ND"; | ||
| 223 | + } | ||
| 224 | + if (format == ge::FORMAT_NCHW) { | ||
| 225 | + return "NCHW"; | ||
| 226 | + } | ||
| 227 | + if (format == ge::FORMAT_NHWC) { | ||
| 228 | + return "NHWC"; | ||
| 229 | + } | ||
| 230 | + return "OTHER"; | ||
| 231 | +} | ||
| 232 | + | ||
| 233 | +uint32_t RoundRightShiftToEven(uint32_t value, uint32_t shift) | ||
| 234 | +{ | ||
| 235 | + const uint32_t truncated = value >> shift; | ||
| 236 | + const uint32_t remainder = value & ((1U << shift) - 1U); | ||
| 237 | + const uint32_t halfway = 1U << (shift - 1U); | ||
| 238 | + return truncated + static_cast<uint32_t>(remainder > halfway || (remainder == halfway && (truncated & 1U) != 0U)); | ||
| 239 | +} | ||
| 240 | + | ||
| 241 | +aclFloat16 FloatToFloat16(float value) | ||
| 242 | +{ | ||
| 243 | + uint32_t bits = 0U; | ||
| 244 | + std::memcpy(&bits, &value, sizeof(bits)); | ||
| 245 | + const uint32_t sign = (bits >> 16U) & 0x8000U; | ||
| 246 | + const uint32_t exponent = (bits >> 23U) & 0xFFU; | ||
| 247 | + const uint32_t mantissa = bits & 0x7FFFFFU; | ||
| 248 | + if (exponent == 0xFFU) { | ||
| 249 | + return static_cast<aclFloat16>(sign | 0x7C00U | (mantissa == 0U ? 0U : 0x0200U)); | ||
| 250 | + } | ||
| 251 | + if (exponent == 0U) { | ||
| 252 | + return static_cast<aclFloat16>(sign); | ||
| 253 | + } | ||
| 254 | + | ||
| 255 | + int32_t halfExponent = static_cast<int32_t>(exponent) - 127 + 15; | ||
| 256 | + if (halfExponent <= 0) { | ||
| 257 | + if (halfExponent < -10) { | ||
| 258 | + return static_cast<aclFloat16>(sign); | ||
| 259 | + } | ||
| 260 | + const uint32_t rounded = RoundRightShiftToEven(mantissa | 0x800000U, 14U - halfExponent); | ||
| 261 | + return static_cast<aclFloat16>(sign | rounded); | ||
| 262 | + } | ||
| 263 | + if (halfExponent >= 31) { | ||
| 264 | + return static_cast<aclFloat16>(sign | 0x7C00U); | ||
| 265 | + } | ||
| 266 | + | ||
| 267 | + uint32_t roundedMantissa = RoundRightShiftToEven(mantissa, 13U); | ||
| 268 | + if (roundedMantissa == 0x400U) { | ||
| 269 | + roundedMantissa = 0U; | ||
| 270 | + ++halfExponent; | ||
| 271 | + if (halfExponent >= 31) { | ||
| 272 | + return static_cast<aclFloat16>(sign | 0x7C00U); | ||
| 273 | + } | ||
| 274 | + } | ||
| 275 | + return static_cast<aclFloat16>(sign | (static_cast<uint32_t>(halfExponent) << 10U) | roundedMantissa); | ||
| 276 | +} | ||
| 277 | + | ||
| 278 | +float Float16ToFloat(aclFloat16 value) | ||
| 279 | +{ | ||
| 280 | + const uint32_t sign = (static_cast<uint32_t>(value) & 0x8000U) << 16U; | ||
| 281 | + uint32_t exponent = (static_cast<uint32_t>(value) >> 10U) & 0x1FU; | ||
| 282 | + uint32_t mantissa = static_cast<uint32_t>(value) & 0x03FFU; | ||
| 283 | + uint32_t bits = sign; | ||
| 284 | + if (exponent == 0U && mantissa != 0U) { | ||
| 285 | + int32_t normalizedExponent = -14; | ||
| 286 | + while ((mantissa & 0x0400U) == 0U) { | ||
| 287 | + mantissa <<= 1U; | ||
| 288 | + --normalizedExponent; | ||
| 289 | + } | ||
| 290 | + mantissa &= 0x03FFU; | ||
| 291 | + bits |= static_cast<uint32_t>(normalizedExponent + 127) << 23U; | ||
| 292 | + bits |= mantissa << 13U; | ||
| 293 | + } else if (exponent == 0x1FU) { | ||
| 294 | + bits |= 0x7F800000U | (mantissa << 13U); | ||
| 295 | + } else if (exponent != 0U) { | ||
| 296 | + bits |= (exponent - 15U + 127U) << 23U; | ||
| 297 | + bits |= mantissa << 13U; | ||
| 298 | + } | ||
| 299 | + float result = 0.0F; | ||
| 300 | + std::memcpy(&result, &bits, sizeof(result)); | ||
| 301 | + return result; | ||
| 302 | +} | ||
| 303 | + | ||
| 304 | +uint16_t FloatToBfloat16(float value) | ||
| 305 | +{ | ||
| 306 | + uint32_t bits = 0U; | ||
| 307 | + std::memcpy(&bits, &value, sizeof(bits)); | ||
| 308 | + const uint32_t roundingBias = 0x7FFFU + ((bits >> 16U) & 1U); | ||
| 309 | + return static_cast<uint16_t>((bits + roundingBias) >> 16U); | ||
| 310 | +} | ||
| 311 | + | ||
| 312 | +float Bfloat16ToFloat(uint16_t value) | ||
| 313 | +{ | ||
| 314 | + uint32_t bits = static_cast<uint32_t>(value) << 16U; | ||
| 315 | + float result = 0.0F; | ||
| 316 | + std::memcpy(&result, &bits, sizeof(result)); | ||
| 317 | + return result; | ||
| 318 | +} | ||
| 319 | + | ||
| 320 | +std::vector<float> GenerateValues(const std::vector<int64_t>& shape, const std::string& pattern) | ||
| 321 | +{ | ||
| 322 | + const size_t count = Numel(shape); | ||
| 323 | + std::vector<float> values(count, 0.0F); | ||
| 324 | + for (size_t i = 0; i < count; ++i) { | ||
| 325 | + const int64_t signedIndex = static_cast<int64_t>(i % 17U) - 8; | ||
| 326 | + values[i] = static_cast<float>(signedIndex) * 0.125F; | ||
| 327 | + } | ||
| 328 | + if (pattern == "zero") { | ||
| 329 | + std::fill(values.begin(), values.end(), 0.0F); | ||
| 330 | + } else if (pattern == "alternate") { | ||
| 331 | + for (size_t i = 0; i < count; ++i) { | ||
| 332 | + values[i] = (i % 2U == 0U) ? 16.0F : -16.0F; | ||
| 333 | + } | ||
| 334 | + } else if (pattern == "60000") { | ||
| 335 | + std::fill(values.begin(), values.end(), 60000.0F); | ||
| 336 | + } else if (pattern == "1e20") { | ||
| 337 | + std::fill(values.begin(), values.end(), 1.0e20F); | ||
| 338 | + } else if (pattern == "partition" && shape.size() == 4U) { | ||
| 339 | + const int64_t n = shape[0]; | ||
| 340 | + const int64_t c = shape[1]; | ||
| 341 | + const int64_t h = shape[2]; | ||
| 342 | + const int64_t w = shape[3]; | ||
| 343 | + for (int64_t ni = 0; ni < n; ++ni) { | ||
| 344 | + for (int64_t ci = 0; ci < c; ++ci) { | ||
| 345 | + for (int64_t hi = 0; hi < h; ++hi) { | ||
| 346 | + for (int64_t wi = 0; wi < w; ++wi) { | ||
| 347 | + const size_t offset = static_cast<size_t>(((ni * c + ci) * h + hi) * w + wi); | ||
| 348 | + const int64_t code = ((ni % 2) * 13 + ci * 7 + hi * 3 + wi) % 19 - 9; | ||
| 349 | + values[offset] = static_cast<float>(code) * 0.0625F; | ||
| 350 | + } | ||
| 351 | + } | ||
| 352 | + } | ||
| 353 | + } | ||
| 354 | + } | ||
| 355 | + | ||
| 356 | + if (shape.size() == 4U && count != 0U && shape[1] > 0) { | ||
| 357 | + const int64_t channel = shape[1] > 1 ? 1 : 0; | ||
| 358 | + const size_t first = static_cast<size_t>(channel * shape[2] * shape[3]); | ||
| 359 | + if (pattern == "nan") { | ||
| 360 | + values[first] = std::numeric_limits<float>::quiet_NaN(); | ||
| 361 | + } else if (pattern == "posinf") { | ||
| 362 | + values[first] = std::numeric_limits<float>::infinity(); | ||
| 363 | + } else if (pattern == "posneginf") { | ||
| 364 | + values[first] = std::numeric_limits<float>::infinity(); | ||
| 365 | + if (first + 1U < values.size()) { | ||
| 366 | + values[first + 1U] = -std::numeric_limits<float>::infinity(); | ||
| 367 | + } | ||
| 368 | + } | ||
| 369 | + } | ||
| 370 | + return values; | ||
| 371 | +} | ||
| 372 | + | ||
| 373 | +InputData MakeInput(const CaseDef& test, const std::vector<int64_t>& shape) | ||
| 374 | +{ | ||
| 375 | + const ge::DataType dtype = ParseDtype(test.dtype); | ||
| 376 | + std::vector<float> source = GenerateValues(shape, test.pattern); | ||
| 377 | + std::vector<uint8_t> bytes; | ||
| 378 | + std::vector<float> quantized(source.size()); | ||
| 379 | + if (dtype == ge::DT_FLOAT16) { | ||
| 380 | + std::vector<aclFloat16> raw(source.size()); | ||
| 381 | + for (size_t i = 0; i < source.size(); ++i) { | ||
| 382 | + raw[i] = FloatToFloat16(source[i]); | ||
| 383 | + quantized[i] = Float16ToFloat(raw[i]); | ||
| 384 | + } | ||
| 385 | + bytes.resize(raw.size() * sizeof(aclFloat16)); | ||
| 386 | + if (!bytes.empty()) { | ||
| 387 | + std::memcpy(bytes.data(), raw.data(), bytes.size()); | ||
| 388 | + } | ||
| 389 | + } else if (dtype == ge::DT_BF16) { | ||
| 390 | + std::vector<uint16_t> raw(source.size()); | ||
| 391 | + for (size_t i = 0; i < source.size(); ++i) { | ||
| 392 | + raw[i] = FloatToBfloat16(source[i]); | ||
| 393 | + quantized[i] = Bfloat16ToFloat(raw[i]); | ||
| 394 | + } | ||
| 395 | + bytes.resize(raw.size() * sizeof(uint16_t)); | ||
| 396 | + if (!bytes.empty()) { | ||
| 397 | + std::memcpy(bytes.data(), raw.data(), bytes.size()); | ||
| 398 | + } | ||
| 399 | + } else if (dtype == ge::DT_INT32) { | ||
| 400 | + std::vector<int32_t> raw(source.size()); | ||
| 401 | + for (size_t i = 0; i < source.size(); ++i) { | ||
| 402 | + raw[i] = static_cast<int32_t>(source[i]); | ||
| 403 | + quantized[i] = static_cast<float>(raw[i]); | ||
| 404 | + } | ||
| 405 | + bytes.resize(raw.size() * sizeof(int32_t)); | ||
| 406 | + if (!bytes.empty()) { | ||
| 407 | + std::memcpy(bytes.data(), raw.data(), bytes.size()); | ||
| 408 | + } | ||
| 409 | + } else { | ||
| 410 | + quantized = source; | ||
| 411 | + bytes.resize(source.size() * sizeof(float)); | ||
| 412 | + if (!bytes.empty()) { | ||
| 413 | + std::memcpy(bytes.data(), source.data(), bytes.size()); | ||
| 414 | + } | ||
| 415 | + } | ||
| 416 | + | ||
| 417 | + ge::TensorDesc desc(ge::Shape(shape), ParseFormat(test.inputFormat), dtype); | ||
| 418 | + desc.SetPlacement(ge::kPlacementHost); | ||
| 419 | + desc.SetRealDimCnt(static_cast<int64_t>(shape.size())); | ||
| 420 | + return {ge::Tensor(desc, bytes), std::move(quantized)}; | ||
| 421 | +} | ||
| 422 | + | ||
| 423 | +Expected ComputeExpected(const std::vector<float>& input, const std::vector<int64_t>& shape) | ||
| 424 | +{ | ||
| 425 | + Expected expected; | ||
| 426 | + if (shape.size() != 4U || shape[1] < 0) { | ||
| 427 | + return expected; | ||
| 428 | + } | ||
| 429 | + const int64_t n = shape[0]; | ||
| 430 | + const int64_t c = shape[1]; | ||
| 431 | + const int64_t h = shape[2]; | ||
| 432 | + const int64_t w = shape[3]; | ||
| 433 | + expected.sum.assign(static_cast<size_t>(c), 0.0F); | ||
| 434 | + expected.squareSum.assign(static_cast<size_t>(c), 0.0F); | ||
| 435 | + for (int64_t ni = 0; ni < n; ++ni) { | ||
| 436 | + for (int64_t ci = 0; ci < c; ++ci) { | ||
| 437 | + for (int64_t hi = 0; hi < h; ++hi) { | ||
| 438 | + for (int64_t wi = 0; wi < w; ++wi) { | ||
| 439 | + const size_t offset = static_cast<size_t>(((ni * c + ci) * h + hi) * w + wi); | ||
| 440 | + const float value = input[offset]; | ||
| 441 | + expected.sum[static_cast<size_t>(ci)] += value; | ||
| 442 | + expected.squareSum[static_cast<size_t>(ci)] += value * value; | ||
| 443 | + } | ||
| 444 | + } | ||
| 445 | + } | ||
| 446 | + } | ||
| 447 | + return expected; | ||
| 448 | +} | ||
| 449 | + | ||
| 450 | +bool Close(float actual, float expected, float& absoluteError) | ||
| 451 | +{ | ||
| 452 | + if (std::isnan(expected)) { | ||
| 453 | + return std::isnan(actual); | ||
| 454 | + } | ||
| 455 | + if (std::isinf(expected)) { | ||
| 456 | + return std::isinf(actual) && std::signbit(actual) == std::signbit(expected); | ||
| 457 | + } | ||
| 458 | + if (!std::isfinite(actual)) { | ||
| 459 | + return false; | ||
| 460 | + } | ||
| 461 | + absoluteError = std::fabs(actual - expected); | ||
| 462 | + return absoluteError <= kAtol + kRtol * std::fabs(expected); | ||
| 463 | +} | ||
| 464 | + | ||
| 465 | +std::string OutputShapes(const std::vector<ge::Tensor>& outputs) | ||
| 466 | +{ | ||
| 467 | + if (outputs.size() != 2U) { | ||
| 468 | + return "COUNT_" + std::to_string(outputs.size()); | ||
| 469 | + } | ||
| 470 | + return "{" + ShapeString(outputs[0].GetTensorDesc().GetShape()) + "," + | ||
| 471 | + ShapeString(outputs[1].GetTensorDesc().GetShape()) + "}"; | ||
| 472 | +} | ||
| 473 | + | ||
| 474 | +std::string OutputDtypes(const std::vector<ge::Tensor>& outputs) | ||
| 475 | +{ | ||
| 476 | + if (outputs.size() != 2U) { | ||
| 477 | + return "COUNT_" + std::to_string(outputs.size()); | ||
| 478 | + } | ||
| 479 | + return "{" + std::string(DtypeName(outputs[0].GetTensorDesc().GetDataType())) + "," + | ||
| 480 | + DtypeName(outputs[1].GetTensorDesc().GetDataType()) + "}"; | ||
| 481 | +} | ||
| 482 | + | ||
| 483 | +std::string OutputFormats(const std::vector<ge::Tensor>& outputs) | ||
| 484 | +{ | ||
| 485 | + if (outputs.size() != 2U) { | ||
| 486 | + return "COUNT_" + std::to_string(outputs.size()); | ||
| 487 | + } | ||
| 488 | + return "{" + std::string(FormatName(outputs[0].GetTensorDesc().GetFormat())) + "," + | ||
| 489 | + FormatName(outputs[1].GetTensorDesc().GetFormat()) + "}"; | ||
| 490 | +} | ||
| 491 | + | ||
| 492 | +Observed ValidateOutputs(const CaseDef& test, const std::vector<int64_t>& feedShape, const Expected& expected, | ||
| 493 | + const std::vector<ge::Tensor>& outputs, std::string& maxErrors, std::string& actualNumel) | ||
| 494 | +{ | ||
| 495 | + Observed observed; | ||
| 496 | + if (outputs.size() != 2U || feedShape.size() != 4U) { | ||
| 497 | + maxErrors = "{NA,NA}"; | ||
| 498 | + actualNumel = "{NA,NA}"; | ||
| 499 | + return observed; | ||
| 500 | + } | ||
| 501 | + const int64_t channel = feedShape[1]; | ||
| 502 | + const std::vector<int64_t> expectedShape = {channel}; | ||
| 503 | + bool metadataPass = true; | ||
| 504 | + for (const auto& output : outputs) { | ||
| 505 | + const ge::TensorDesc desc = output.GetTensorDesc(); | ||
| 506 | + metadataPass = metadataPass && desc.GetShape().GetDims() == expectedShape; | ||
| 507 | + metadataPass = metadataPass && desc.GetDataType() == ge::DT_FLOAT; | ||
| 508 | + metadataPass = metadataPass && desc.GetFormat() == ge::FORMAT_ND; | ||
| 509 | + metadataPass = metadataPass && output.GetSize() == static_cast<size_t>(channel) * sizeof(float); | ||
| 510 | + } | ||
| 511 | + | ||
| 512 | + observed.sum.resize(static_cast<size_t>(channel)); | ||
| 513 | + observed.squareSum.resize(static_cast<size_t>(channel)); | ||
| 514 | + if (channel > 0) { | ||
| 515 | + if (outputs[0].GetData() == nullptr || outputs[1].GetData() == nullptr) { | ||
| 516 | + maxErrors = "{NA,NA}"; | ||
| 517 | + actualNumel = "{0,0}"; | ||
| 518 | + return observed; | ||
| 519 | + } | ||
| 520 | + std::memcpy(observed.sum.data(), outputs[0].GetData(), observed.sum.size() * sizeof(float)); | ||
| 521 | + std::memcpy(observed.squareSum.data(), outputs[1].GetData(), observed.squareSum.size() * sizeof(float)); | ||
| 522 | + } | ||
| 523 | + observed.readable = true; | ||
| 524 | + | ||
| 525 | + float maxSumError = 0.0F; | ||
| 526 | + float maxSquareError = 0.0F; | ||
| 527 | + bool valuesPass = expected.sum.size() == observed.sum.size() && | ||
| 528 | + expected.squareSum.size() == observed.squareSum.size(); | ||
| 529 | + for (size_t i = 0; valuesPass && i < observed.sum.size(); ++i) { | ||
| 530 | + float error = 0.0F; | ||
| 531 | + valuesPass = Close(observed.sum[i], expected.sum[i], error); | ||
| 532 | + maxSumError = std::max(maxSumError, error); | ||
| 533 | + error = 0.0F; | ||
| 534 | + valuesPass = valuesPass && Close(observed.squareSum[i], expected.squareSum[i], error); | ||
| 535 | + maxSquareError = std::max(maxSquareError, error); | ||
| 536 | + if (test.pattern == "zero") { | ||
| 537 | + valuesPass = valuesPass && observed.sum[i] == 0.0F && observed.squareSum[i] == 0.0F; | ||
| 538 | + } | ||
| 539 | + if (test.pattern == "alternate" && std::isfinite(observed.squareSum[i])) { | ||
| 540 | + valuesPass = valuesPass && observed.squareSum[i] >= 0.0F; | ||
| 541 | + } | ||
| 542 | + } | ||
| 543 | + observed.valuePass = metadataPass && valuesPass; | ||
| 544 | + std::ostringstream errors; | ||
| 545 | + errors << '{' << maxSumError << ',' << maxSquareError << '}'; | ||
| 546 | + maxErrors = errors.str(); | ||
| 547 | + actualNumel = "{" + std::to_string(outputs[0].GetSize() / sizeof(float)) + "," + | ||
| 548 | + std::to_string(outputs[1].GetSize() / sizeof(float)) + "}"; | ||
| 549 | + return observed; | ||
| 550 | +} | ||
| 551 | + | ||
| 552 | +bool IsUnknownRank(const ge::TensorDesc& desc) { return desc.GetShape().GetDims() == ge::UNKNOWN_RANK; } | ||
| 553 | + | ||
| 554 | +bool PrintAndCheckShapeInit(const std::string& caseId, const GraphBundle& bundle) | ||
| 555 | +{ | ||
| 556 | + const ge::TensorDesc sumDesc = bundle.reduce.GetOutputDescByName("sum"); | ||
| 557 | + const ge::TensorDesc squareDesc = bundle.reduce.GetOutputDescByName("square_sum"); | ||
| 558 | + const bool valid = IsUnknownRank(sumDesc) && IsUnknownRank(squareDesc) && sumDesc.GetDataType() == ge::DT_FLOAT && | ||
| 559 | + squareDesc.GetDataType() == ge::DT_FLOAT; | ||
| 560 | + std::printf("SHAPE-INIT case=%s outputs={sum:{shape:%s,dtype:%s},square_sum:{shape:%s,dtype:%s}} value=%s\n", | ||
| 561 | + caseId.c_str(), ShapeString(sumDesc.GetShape()).c_str(), DtypeName(sumDesc.GetDataType()), | ||
| 562 | + ShapeString(squareDesc.GetShape()).c_str(), DtypeName(squareDesc.GetDataType()), | ||
| 563 | + valid ? "PASS" : "FAIL"); | ||
| 564 | + return valid; | ||
| 565 | +} | ||
| 566 | + | ||
| 567 | +GraphBundle BuildGraph(const CaseDef& test) | ||
| 568 | +{ | ||
| 569 | + GraphBundle bundle; | ||
| 570 | + bundle.node = test.id == "route-target" ? "bn_training_reduce_route" : "bn_training_reduce_" + test.id; | ||
| 571 | + bundle.graph = ge::Graph((bundle.node + "_graph").c_str()); | ||
| 572 | + auto reduce = ge::op::BNTrainingReduce(bundle.node.c_str()); | ||
| 573 | + const ge::TensorDesc outDesc(ge::Shape(ge::UNKNOWN_RANK), ge::FORMAT_ND, ge::DT_FLOAT); | ||
| 574 | + reduce.update_output_desc_sum(outDesc); | ||
| 575 | + reduce.update_output_desc_square_sum(outDesc); | ||
| 576 | + | ||
| 577 | + std::vector<ge::Operator> inputs; | ||
| 578 | + const std::vector<int64_t> graphShape = test.mode == "missing-input" ? std::vector<int64_t>{1, 1, 1, 1} : | ||
| 579 | + ParseShape(test.graphShape); | ||
| 580 | + ge::TensorDesc xDesc(ge::Shape(graphShape), ParseFormat(test.inputFormat), ParseDtype(test.dtype)); | ||
| 581 | + xDesc.SetRealDimCnt(graphShape == ge::UNKNOWN_RANK ? 0 : static_cast<int64_t>(graphShape.size())); | ||
| 582 | + auto data = ge::op::Data((bundle.node + "_x").c_str()).set_attr_index(0); | ||
| 583 | + data.update_input_desc_x(xDesc); | ||
| 584 | + data.update_output_desc_y(xDesc); | ||
| 585 | + reduce.set_input_x(data); | ||
| 586 | + reduce.update_input_desc_x(xDesc); | ||
| 587 | + bundle.graph.AddOp(data); | ||
| 588 | + inputs.push_back(data); | ||
| 589 | + bundle.graph.AddOp(reduce); | ||
| 590 | + const std::vector<std::pair<ge::Operator, std::vector<size_t>>> outputs = {{reduce, {0U, 1U}}}; | ||
| 591 | + if (!inputs.empty()) { | ||
| 592 | + bundle.graph.SetInputs(inputs); | ||
| 593 | + } | ||
| 594 | + bundle.graph.SetOutputs(outputs); | ||
| 595 | + bundle.reduce = reduce; | ||
| 596 | + return bundle; | ||
| 597 | +} | ||
| 598 | + | ||
| 599 | +const char* GraphMode(const CaseDef& test) | ||
| 600 | +{ | ||
| 601 | + return test.mode == "dynamic" || test.mode == "unknown-rank" ? "dynamic" : "static"; | ||
| 602 | +} | ||
| 603 | + | ||
| 604 | +std::string Sanitize(std::string text) | ||
| 605 | +{ | ||
| 606 | + if (text.size() > 160U) { | ||
| 607 | + text.resize(160U); | ||
| 608 | + } | ||
| 609 | + for (char& ch : text) { | ||
| 610 | + const bool safe = (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || | ||
| 611 | + ch == '_' || ch == '-' || ch == '.'; | ||
| 612 | + if (!safe) { | ||
| 613 | + ch = '_'; | ||
| 614 | + } | ||
| 615 | + } | ||
| 616 | + return text.empty() ? "NONE" : text; | ||
| 617 | +} | ||
| 618 | + | ||
| 619 | +std::string CurrentError() | ||
| 620 | +{ | ||
| 621 | + const ge::AscendString message = ge::GEGetErrorMsgV2(); | ||
| 622 | + return message.GetString() == nullptr ? "NONE" : Sanitize(message.GetString()); | ||
| 623 | +} | ||
| 624 | + | ||
| 625 | +std::string ClassifyError(const std::string& raw) | ||
| 626 | +{ | ||
| 627 | + std::string lower = raw; | ||
| 628 | + std::transform(lower.begin(), lower.end(), lower.begin(), | ||
| 629 | + [](unsigned char ch) { return static_cast<char>(std::tolower(ch)); }); | ||
| 630 | + if (lower.find("dtype") != std::string::npos || lower.find("data_type") != std::string::npos || | ||
| 631 | + lower.find("datatype") != std::string::npos) { | ||
| 632 | + return "dtype_not_supported"; | ||
| 633 | + } | ||
| 634 | + if (lower.find("format") != std::string::npos || lower.find("shape") != std::string::npos || | ||
| 635 | + lower.find("rank") != std::string::npos || lower.find("dim") != std::string::npos) { | ||
| 636 | + return "shape_mismatch"; | ||
| 637 | + } | ||
| 638 | + if (lower.find("null") != std::string::npos || lower.find("missing") != std::string::npos || | ||
| 639 | + lower.find("input") != std::string::npos || lower.find("anchor") != std::string::npos || | ||
| 640 | + lower.find("edge") != std::string::npos) { | ||
| 641 | + return "null_input"; | ||
| 642 | + } | ||
| 643 | + return "unclassified"; | ||
| 644 | +} | ||
| 645 | + | ||
| 646 | +Observed RunPositiveFeed(ge::Session& session, uint32_t gid, const CaseDef& test, const GraphBundle& bundle, | ||
| 647 | + const std::vector<int64_t>& feedShape, const std::string& feedLabel, | ||
| 648 | + const std::string& runMode, bool& runCompleted) | ||
| 649 | +{ | ||
| 650 | + const InputData input = MakeInput(test, feedShape); | ||
| 651 | + const Expected expected = ComputeExpected(input.quantized, feedShape); | ||
| 652 | + std::vector<ge::Tensor> outputs; | ||
| 653 | + const ge::Status status = session.RunGraph(gid, {input.tensor}, outputs); | ||
| 654 | + runCompleted = status == ge::SUCCESS; | ||
| 655 | + std::string maxErrors = "{NA,NA}"; | ||
| 656 | + std::string actualNumel = "{NA,NA}"; | ||
| 657 | + Observed observed; | ||
| 658 | + if (runCompleted) { | ||
| 659 | + observed = ValidateOutputs(test, feedShape, expected, outputs, maxErrors, actualNumel); | ||
| 660 | + } | ||
| 661 | + const std::string runError = runCompleted ? "NONE" : CurrentError(); | ||
| 662 | + const int64_t channel = feedShape.size() == 4U ? feedShape[1] : -1; | ||
| 663 | + std::printf("GRAPH-RUN mode=%s case=%s node=%s gid=%u feed=%s expected_shape={[%ld],[%ld]} actual_shape=%s " | ||
| 664 | + "expected_dtype={FLOAT32,FLOAT32} actual_dtype=%s expected_format={ND,ND} actual_format=%s " | ||
| 665 | + "expected_numel={%ld,%ld} actual_numel=%s max_abs=%s rtol=%.1e atol=%.1e status=%u error=%s value=%s\n", | ||
| 666 | + runMode.c_str(), feedLabel.c_str(), bundle.node.c_str(), gid, ShapeString(feedShape).c_str(), channel, | ||
| 667 | + channel, OutputShapes(outputs).c_str(), OutputDtypes(outputs).c_str(), OutputFormats(outputs).c_str(), | ||
| 668 | + channel, channel, actualNumel.c_str(), maxErrors.c_str(), kRtol, kAtol, status, runError.c_str(), | ||
| 669 | + observed.valuePass ? "PASS" : "FAIL"); | ||
| 670 | + return observed; | ||
| 671 | +} | ||
| 672 | + | ||
| 673 | +bool CheckPartitionInvariant(const Observed& whole, const Observed& half, float& maxError) | ||
| 674 | +{ | ||
| 675 | + if (!whole.readable || !half.readable || whole.sum.size() != half.sum.size()) { | ||
| 676 | + return false; | ||
| 677 | + } | ||
| 678 | + bool pass = true; | ||
| 679 | + maxError = 0.0F; | ||
| 680 | + for (size_t i = 0; i < whole.sum.size(); ++i) { | ||
| 681 | + float error = 0.0F; | ||
| 682 | + pass = pass && Close(whole.sum[i], half.sum[i] * 2.0F, error); | ||
| 683 | + maxError = std::max(maxError, error); | ||
| 684 | + error = 0.0F; | ||
| 685 | + pass = pass && Close(whole.squareSum[i], half.squareSum[i] * 2.0F, error); | ||
| 686 | + maxError = std::max(maxError, error); | ||
| 687 | + } | ||
| 688 | + return pass; | ||
| 689 | +} | ||
| 690 | + | ||
| 691 | +bool RunAcceptCase(ge::Session& session, uint32_t gid, const CaseDef& test, bool& infraFailure) | ||
| 692 | +{ | ||
| 693 | + GraphBundle bundle = BuildGraph(test); | ||
| 694 | + const char* graphMode = GraphMode(test); | ||
| 695 | + const ge::Status addStatus = session.AddGraph(gid, bundle.graph, std::map<ge::AscendString, ge::AscendString>{}); | ||
| 696 | + std::printf("GRAPH-ADD mode=%s case=%s node=%s gid=%u declared_input_shape=%s status=%u\n", graphMode, | ||
| 697 | + test.id.c_str(), bundle.node.c_str(), gid, test.graphShape.c_str(), addStatus); | ||
| 698 | + if (addStatus != ge::SUCCESS) { | ||
| 699 | + std::printf("GRAPH-RUN mode=%s case=%s node=%s gid=%u actual_stage=ADD_GRAPH status=%u error=%s value=FAIL\n", | ||
| 700 | + graphMode, test.id.c_str(), bundle.node.c_str(), gid, addStatus, CurrentError().c_str()); | ||
| 701 | + return false; | ||
| 702 | + } | ||
| 703 | + if (!PrintAndCheckShapeInit(test.id, bundle)) { | ||
| 704 | + infraFailure = true; | ||
| 705 | + } | ||
| 706 | + | ||
| 707 | + const auto feedFields = Split(test.feedShapes, ';'); | ||
| 708 | + bool pass = true; | ||
| 709 | + std::vector<Observed> observations; | ||
| 710 | + for (size_t i = 0; i < feedFields.size(); ++i) { | ||
| 711 | + bool completed = false; | ||
| 712 | + const std::string label = feedFields.size() == 1U ? test.id : test.id + "#feed" + std::to_string(i); | ||
| 713 | + observations.push_back( | ||
| 714 | + RunPositiveFeed(session, gid, test, bundle, ParseShape(feedFields[i]), label, graphMode, completed)); | ||
| 715 | + pass = pass && completed && observations.back().valuePass; | ||
| 716 | + } | ||
| 717 | + if (test.mode == "partition") { | ||
| 718 | + float maxError = 0.0F; | ||
| 719 | + const bool invariantPass = observations.size() == 2U && | ||
| 720 | + CheckPartitionInvariant(observations[0], observations[1], maxError); | ||
| 721 | + std::printf("INVARIANT case=%s name=batch_partition_additivity actual=whole_vs_two_halves max_abs=%g " | ||
| 722 | + "rtol=%.1e atol=%.1e value=%s\n", | ||
| 723 | + test.id.c_str(), maxError, kRtol, kAtol, invariantPass ? "PASS" : "FAIL"); | ||
| 724 | + pass = pass && invariantPass; | ||
| 725 | + } | ||
| 726 | + session.RemoveGraph(gid); | ||
| 727 | + return pass; | ||
| 728 | +} | ||
| 729 | + | ||
| 730 | +bool RunRejectCase(ge::Session& session, uint32_t gid, const CaseDef& test, bool& infraFailure) | ||
| 731 | +{ | ||
| 732 | + GraphBundle bundle = BuildGraph(test); | ||
| 733 | + const char* graphMode = GraphMode(test); | ||
| 734 | + const ge::Status addStatus = session.AddGraph(gid, bundle.graph, std::map<ge::AscendString, ge::AscendString>{}); | ||
| 735 | + std::printf("GRAPH-ADD mode=%s case=%s node=%s gid=%u declared_input_shape=%s status=%u\n", graphMode, | ||
| 736 | + test.id.c_str(), bundle.node.c_str(), gid, test.graphShape.c_str(), addStatus); | ||
| 737 | + std::string stage = "ADD_GRAPH"; | ||
| 738 | + ge::Status status = addStatus; | ||
| 739 | + std::string rawError = CurrentError(); | ||
| 740 | + if (addStatus == ge::SUCCESS) { | ||
| 741 | + if (!PrintAndCheckShapeInit(test.id, bundle)) { | ||
| 742 | + infraFailure = true; | ||
| 743 | + } | ||
| 744 | + stage = "RUN_GRAPH"; | ||
| 745 | + std::vector<ge::Tensor> feeds; | ||
| 746 | + if (test.mode != "missing-input") { | ||
| 747 | + feeds.push_back(MakeInput(test, ParseShape(test.feedShapes)).tensor); | ||
| 748 | + } | ||
| 749 | + std::vector<ge::Tensor> outputs; | ||
| 750 | + status = session.RunGraph(gid, feeds, outputs); | ||
| 751 | + rawError = CurrentError(); | ||
| 752 | + session.RemoveGraph(gid); | ||
| 753 | + } | ||
| 754 | + const std::string actualError = status == ge::SUCCESS ? "accepted" : ClassifyError(rawError); | ||
| 755 | + const bool pass = status != ge::SUCCESS && actualError == test.expectation; | ||
| 756 | + std::printf( | ||
| 757 | + "GRAPH-REJECT case=%s node=%s inputs={shape:%s,format:%s,dtype:%s} actual_stage=%s status=%u value=%s\n", | ||
| 758 | + test.id.c_str(), bundle.node.c_str(), test.graphShape.c_str(), test.inputFormat.c_str(), test.dtype.c_str(), | ||
| 759 | + stage.c_str(), status, pass ? "PASS" : "FAIL"); | ||
| 760 | + std::printf("REJECT-DETAIL case=%s expected_error=%s actual_error=%s raw_error=%s\n", test.id.c_str(), | ||
| 761 | + test.expectation.c_str(), actualError.c_str(), rawError.c_str()); | ||
| 762 | + return pass; | ||
| 763 | +} | ||
| 764 | + | ||
| 765 | +bool RunRuntimeInputContractCase(ge::Session& session, uint32_t gid, const CaseDef& test, bool& infraFailure) | ||
| 766 | +{ | ||
| 767 | + GraphBundle bundle = BuildGraph(test); | ||
| 768 | + const char* graphMode = GraphMode(test); | ||
| 769 | + const ge::Status addStatus = session.AddGraph(gid, bundle.graph, std::map<ge::AscendString, ge::AscendString>{}); | ||
| 770 | + std::printf("GRAPH-ADD mode=%s case=%s node=%s gid=%u declared_input_shape=%s status=%u\n", graphMode, | ||
| 771 | + test.id.c_str(), bundle.node.c_str(), gid, test.graphShape.c_str(), addStatus); | ||
| 772 | + std::string stage = "ADD_GRAPH"; | ||
| 773 | + ge::Status status = addStatus; | ||
| 774 | + std::string rawError = CurrentError(); | ||
| 775 | + if (addStatus == ge::SUCCESS) { | ||
| 776 | + if (!PrintAndCheckShapeInit(test.id, bundle)) { | ||
| 777 | + infraFailure = true; | ||
| 778 | + } | ||
| 779 | + stage = "RUN_GRAPH"; | ||
| 780 | + std::vector<ge::Tensor> outputs; | ||
| 781 | + status = session.RunGraph(gid, {}, outputs); | ||
| 782 | + rawError = CurrentError(); | ||
| 783 | + session.RemoveGraph(gid); | ||
| 784 | + } | ||
| 785 | + const std::string actualError = status == ge::SUCCESS ? "accepted" : ClassifyError(rawError); | ||
| 786 | + const bool pass = status != ge::SUCCESS && actualError == test.expectation; | ||
| 787 | + std::printf("RUNTIME-INPUT-CONTRACT case=%s node=%s graph=LEGAL_DATA_TO_OP runtime_feeds=EMPTY actual_stage=%s " | ||
| 788 | + "status=%u expected_error=%s actual_error=%s kernel_start_policy=ALLOWED value=%s\n", | ||
| 789 | + test.id.c_str(), bundle.node.c_str(), stage.c_str(), status, test.expectation.c_str(), | ||
| 790 | + actualError.c_str(), pass ? "PASS" : "FAIL"); | ||
| 791 | + std::printf("RUNTIME-CONTRACT-DETAIL case=%s raw_error=%s\n", test.id.c_str(), rawError.c_str()); | ||
| 792 | + return pass; | ||
| 793 | +} | ||
| 794 | + | ||
| 795 | +bool InitializeGe() | ||
| 796 | +{ | ||
| 797 | + const std::map<ge::AscendString, ge::AscendString> options = {{"ge.exec.deviceId", "0"}, {"ge.graphRunMode", "1"}}; | ||
| 798 | + const ge::Status status = ge::GEInitialize(options); | ||
| 799 | + if (status != ge::SUCCESS) { | ||
| 800 | + std::fprintf(stderr, "GEIR-INFRA stage=GEInitialize status=%u error=%s value=FAIL\n", status, | ||
| 801 | + CurrentError().c_str()); | ||
| 802 | + return false; | ||
| 803 | + } | ||
| 804 | + return true; | ||
| 805 | +} | ||
| 806 | + | ||
| 807 | +int RunRoute() | ||
| 808 | +{ | ||
| 809 | + if (!InitializeGe()) { | ||
| 810 | + return 2; | ||
| 811 | + } | ||
| 812 | + ge::Session session(std::map<ge::AscendString, ge::AscendString>{}); | ||
| 813 | + CaseDef route = {"route-target", "positive", "static", "route", "F32", "NCHW", | ||
| 814 | + "[1,1,1,1]", "[1,1,1,1]", "finite", "oracle", "accept"}; | ||
| 815 | + GraphBundle bundle = BuildGraph(route); | ||
| 816 | + constexpr uint32_t gid = 7000U; | ||
| 817 | + const ge::Status addStatus = session.AddGraph(gid, bundle.graph, std::map<ge::AscendString, ge::AscendString>{}); | ||
| 818 | + std::printf("GRAPH-ADD mode=route signature=F32_NCHW_1x1x1x1 node=%s gid=%u declared_input_shape=%s status=%u\n", | ||
| 819 | + bundle.node.c_str(), gid, route.graphShape.c_str(), addStatus); | ||
| 820 | + int probeExit = 0; | ||
| 821 | + bool businessPass = false; | ||
| 822 | + if (addStatus != ge::SUCCESS) { | ||
| 823 | + probeExit = 3; | ||
| 824 | + std::printf("GRAPH-RUN mode=route case=route-target node=%s gid=%u actual_stage=ADD_GRAPH status=%u " | ||
| 825 | + "error=%s value=FAIL\n", | ||
| 826 | + bundle.node.c_str(), gid, addStatus, CurrentError().c_str()); | ||
| 827 | + } else if (!PrintAndCheckShapeInit("route-target", bundle)) { | ||
| 828 | + probeExit = 4; | ||
| 829 | + } else { | ||
| 830 | + bool completed = false; | ||
| 831 | + const Observed observed = RunPositiveFeed(session, gid, route, bundle, {1, 1, 1, 1}, "route-target", "route", | ||
| 832 | + completed); | ||
| 833 | + if (!completed || !observed.readable) { | ||
| 834 | + probeExit = 5; | ||
| 835 | + } | ||
| 836 | + businessPass = observed.valuePass; | ||
| 837 | + session.RemoveGraph(gid); | ||
| 838 | + } | ||
| 839 | + const ge::Status finalizeStatus = ge::GEFinalize(); | ||
| 840 | + if (finalizeStatus != ge::SUCCESS && probeExit == 0) { | ||
| 841 | + probeExit = 6; | ||
| 842 | + } | ||
| 843 | + std::printf("ROUTE-PROBE op=BNTrainingReduce node=%s status=DONE value=%s probe_exit=%d\n", bundle.node.c_str(), | ||
| 844 | + businessPass ? "PASS" : "FAIL", probeExit); | ||
| 845 | + return probeExit; | ||
| 846 | +} | ||
| 847 | + | ||
| 848 | +int RunSelected(const std::vector<CaseDef>& cases, const std::string& selectedId, bool printSummary) | ||
| 849 | +{ | ||
| 850 | + if (!InitializeGe()) { | ||
| 851 | + return 2; | ||
| 852 | + } | ||
| 853 | + ge::Session session(std::map<ge::AscendString, ge::AscendString>{}); | ||
| 854 | + int total = 0; | ||
| 855 | + int passed = 0; | ||
| 856 | + bool infraFailure = false; | ||
| 857 | + uint32_t gid = 8000U; | ||
| 858 | + for (const auto& test : cases) { | ||
| 859 | + if (!selectedId.empty() && test.id != selectedId) { | ||
| 860 | + continue; | ||
| 861 | + } | ||
| 862 | + ++total; | ||
| 863 | + bool pass = false; | ||
| 864 | + if (test.acceptance == "reject") { | ||
| 865 | + pass = RunRejectCase(session, gid, test, infraFailure); | ||
| 866 | + } else if (test.acceptance == "runtime-contract") { | ||
| 867 | + pass = RunRuntimeInputContractCase(session, gid, test, infraFailure); | ||
| 868 | + } else { | ||
| 869 | + pass = RunAcceptCase(session, gid, test, infraFailure); | ||
| 870 | + } | ||
| 871 | + std::printf("CASE %s %s\n", test.id.c_str(), pass ? "PASS" : "FAIL"); | ||
| 872 | + if (pass) { | ||
| 873 | + ++passed; | ||
| 874 | + } | ||
| 875 | + ++gid; | ||
| 876 | + } | ||
| 877 | + const ge::Status finalizeStatus = ge::GEFinalize(); | ||
| 878 | + if (finalizeStatus != ge::SUCCESS) { | ||
| 879 | + infraFailure = true; | ||
| 880 | + } | ||
| 881 | + if (printSummary) { | ||
| 882 | + std::printf("GEIR-SUMMARY total=%d pass=%d fail=%d skip=0\n", total, passed, total - passed); | ||
| 883 | + } | ||
| 884 | + if (total == 0) { | ||
| 885 | + std::fprintf(stderr, "GEIR-INFRA selected=%s reason=no_matching_case value=FAIL\n", selectedId.c_str()); | ||
| 886 | + return 2; | ||
| 887 | + } | ||
| 888 | + return infraFailure ? 2 : 0; | ||
| 889 | +} | ||
| 890 | + | ||
| 891 | +} // namespace | ||
| 892 | + | ||
| 893 | +int main(int argc, char** argv) | ||
| 894 | +{ | ||
| 895 | + if (argc < 2) { | ||
| 896 | + return RunRoute(); | ||
| 897 | + } | ||
| 898 | + if (std::string(argv[1]) == "--route") { | ||
| 899 | + return RunRoute(); | ||
| 900 | + } | ||
| 901 | + std::string manifest; | ||
| 902 | + std::string selected; | ||
| 903 | + bool printSummary = true; | ||
| 904 | + if (std::string(argv[1]) == "--test" && argc == 3) { | ||
| 905 | + manifest = argv[2]; | ||
| 906 | + } else if (std::string(argv[1]) == "--case" && argc == 4) { | ||
| 907 | + selected = argv[2]; | ||
| 908 | + manifest = argv[3]; | ||
| 909 | + printSummary = false; | ||
| 910 | + } else { | ||
| 911 | + std::fprintf(stderr, "usage: %s {--route|--test|--case ID} cases.tsv\n", argv[0]); | ||
| 912 | + return 2; | ||
| 913 | + } | ||
| 914 | + std::vector<CaseDef> cases; | ||
| 915 | + if (!LoadCases(manifest, cases)) { | ||
| 916 | + return 2; | ||
| 917 | + } | ||
| 918 | + return RunSelected(cases, selected, printSummary); | ||
| 919 | +} | ||
Rnorm/bn_training_reduce/op_host/op_api/aclnn_batch_norm_reduce.cpp→norm/bn_training_reduce/op_api/aclnn_batch_norm_reduce.cpp+20-14
| @@ -64,8 +64,8 @@ static bool CheckFormat(const aclTensor* x, const aclTensor* sum, const aclTenso | |||
| 64 | auto xFormat = x->GetStorageFormat(); | 64 | auto xFormat = x->GetStorageFormat(); |
| 65 | auto sumFormat = sum->GetStorageFormat(); | 65 | auto sumFormat = sum->GetStorageFormat(); |
| 66 | auto squareSumFormat = squareSum->GetStorageFormat(); | 66 | auto squareSumFormat = squareSum->GetStorageFormat(); |
| 67 | - if (xFormat != op::Format::FORMAT_NCHW) { | 67 | + if (xFormat != op::Format::FORMAT_NCHW && xFormat != op::Format::FORMAT_NHWC) { |
| 68 | - OP_LOGE(ACLNN_ERR_PARAM_INVALID, "Format of x only supports [NCHW], but format is [%s].", | 68 | + OP_LOGE(ACLNN_ERR_PARAM_INVALID, "Format of x only supports [NCHW, NHWC], but format is [%s].", |
| 69 | op::ToString(xFormat).GetString()); | 69 | op::ToString(xFormat).GetString()); |
| 70 | return false; | 70 | return false; |
| 71 | } | 71 | } |
| @@ -129,10 +129,13 @@ static aclnnStatus CheckParams(const aclTensor* x, aclTensor* sum, aclTensor* sq | |||
| 129 | return ACLNN_SUCCESS; | 129 | return ACLNN_SUCCESS; |
| 130 | } | 130 | } |
| 131 | 131 | ||
| 132 | -const aclTensor* ResizeTo4D(const aclTensor* input, aclOpExecutor* executor) | 132 | +const aclTensor* ResizeTo4D(const aclTensor* input, op::Format format, aclOpExecutor* executor) |
| 133 | { | 133 | { |
| 134 | - const int64_t appendDim[] = {0, 2, 3}; | 134 | + const int64_t appendDimNchw[] = {0, 2, 3}; |
| 135 | - aclIntArray* newShape = executor->AllocIntArray(appendDim, sizeof(appendDim) / sizeof(int64_t)); | 135 | + const int64_t appendDimNhwc[] = {0, 1, 2}; |
| 136 | + const int64_t* appendDim = format == op::Format::FORMAT_NHWC ? appendDimNhwc : appendDimNchw; | ||
| 137 | + constexpr size_t appendDimNum = sizeof(appendDimNchw) / sizeof(appendDimNchw[0]); | ||
| 138 | + aclIntArray* newShape = executor->AllocIntArray(appendDim, appendDimNum); | ||
| 136 | 139 | ||
| 137 | auto inputUnsqueeze = l0op::UnsqueezeNd(input, newShape, executor); | 140 | auto inputUnsqueeze = l0op::UnsqueezeNd(input, newShape, executor); |
| 138 | if (inputUnsqueeze == nullptr) { | 141 | if (inputUnsqueeze == nullptr) { |
| @@ -141,16 +144,19 @@ const aclTensor* ResizeTo4D(const aclTensor* input, aclOpExecutor* executor) | |||
| 141 | auto formatTensor = executor == nullptr ? const_cast<aclTensor*>(inputUnsqueeze) : | 144 | auto formatTensor = executor == nullptr ? const_cast<aclTensor*>(inputUnsqueeze) : |
| 142 | executor->CreateView(inputUnsqueeze, inputUnsqueeze->GetViewShape(), | 145 | executor->CreateView(inputUnsqueeze, inputUnsqueeze->GetViewShape(), |
| 143 | inputUnsqueeze->GetViewOffset()); | 146 | inputUnsqueeze->GetViewOffset()); |
| 144 | - formatTensor->SetViewFormat(Format::FORMAT_NCHW); | 147 | + formatTensor->SetViewFormat(format); |
| 145 | - formatTensor->SetOriginalFormat(Format::FORMAT_NCHW); | 148 | + formatTensor->SetOriginalFormat(format); |
| 146 | - formatTensor->SetStorageFormat(Format::FORMAT_NCHW); | 149 | + formatTensor->SetStorageFormat(format); |
| 147 | return formatTensor; | 150 | return formatTensor; |
| 148 | } | 151 | } |
| 149 | 152 | ||
| 150 | const aclTensor* ResizeTo1D(const aclTensor* input, aclOpExecutor* executor) | 153 | const aclTensor* ResizeTo1D(const aclTensor* input, aclOpExecutor* executor) |
| 151 | { | 154 | { |
| 152 | - const int64_t removeDim[] = {0, 2, 3}; | 155 | + const int64_t removeDimNchw[] = {0, 2, 3}; |
| 153 | - aclIntArray* newShape = executor->AllocIntArray(removeDim, sizeof(removeDim) / sizeof(int64_t)); | 156 | + const int64_t removeDimNhwc[] = {0, 1, 2}; |
| 157 | + const int64_t* removeDim = input->GetStorageFormat() == op::Format::FORMAT_NHWC ? removeDimNhwc : removeDimNchw; | ||
| 158 | + constexpr size_t removeDimNum = sizeof(removeDimNchw) / sizeof(removeDimNchw[0]); | ||
| 159 | + aclIntArray* newShape = executor->AllocIntArray(removeDim, removeDimNum); | ||
| 154 | 160 | ||
| 155 | auto inputSqueeze = l0op::SqueezeNd(input, newShape, executor); | 161 | auto inputSqueeze = l0op::SqueezeNd(input, newShape, executor); |
| 156 | if (inputSqueeze == nullptr) { | 162 | if (inputSqueeze == nullptr) { |
| @@ -193,10 +199,10 @@ aclnnStatus aclnnBatchNormReduceGetWorkspaceSize(const aclTensor* x, aclTensor* | |||
| 193 | auto xContiguous = l0op::Contiguous(x, uniqueExecutor.get()); | 199 | auto xContiguous = l0op::Contiguous(x, uniqueExecutor.get()); |
| 194 | CHECK_RET(xContiguous != nullptr, ACLNN_ERR_INNER_NULLPTR); | 200 | CHECK_RET(xContiguous != nullptr, ACLNN_ERR_INNER_NULLPTR); |
| 195 | 201 | ||
| 196 | - auto sumNCHW = ResizeTo4D(sum, uniqueExecutor.get()); | 202 | + auto sum4D = ResizeTo4D(sum, xContiguous->GetStorageFormat(), uniqueExecutor.get()); |
| 197 | - CHECK_RET(sumNCHW != nullptr, ACLNN_ERR_INNER_NULLPTR); | 203 | + CHECK_RET(sum4D != nullptr, ACLNN_ERR_INNER_NULLPTR); |
| 198 | 204 | ||
| 199 | - std::array<aclTensor*, TENSOR_NUM> sumTensor = l0op::BNTrainingReduce(xContiguous, sumNCHW->GetViewShape(), | 205 | + std::array<aclTensor*, TENSOR_NUM> sumTensor = l0op::BNTrainingReduce(xContiguous, sum4D->GetViewShape(), |
| 200 | uniqueExecutor.get()); | 206 | uniqueExecutor.get()); |
| 201 | 207 | ||
| 202 | auto sumND = ResizeTo1D(sumTensor[0], uniqueExecutor.get()); | 208 | auto sumND = ResizeTo1D(sumTensor[0], uniqueExecutor.get()); |
| @@ -225,4 +231,4 @@ aclnnStatus aclnnBatchNormReduce(void* workspace, uint64_t workspaceSize, aclOpE | |||
| 225 | 231 | ||
| 226 | 232 | ||
| 227 | } | 233 | } |
| 228 | -#endif | 234 | +#endif |
Rnorm/bn_training_reduce/op_host/op_api/aclnn_batch_norm_reduce.h→norm/bn_training_reduce/op_api/aclnn_batch_norm_reduce.h+0-0
文件重命名但无更改。
| @@ -0,0 +1,39 @@ | |||
| 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 | +using namespace ge; | ||
| 15 | + | ||
| 16 | +namespace ops { | ||
| 17 | +static ge::graphStatus InferDataType4BNTrainingReduce(gert::InferDataTypeContext* context) | ||
| 18 | +{ | ||
| 19 | + if (context == nullptr) { | ||
| 20 | + return GRAPH_FAILED; | ||
| 21 | + } | ||
| 22 | + const DataType xDataType = context->GetInputDataType(0); | ||
| 23 | + if (xDataType != DT_FLOAT16 && xDataType != DT_BF16 && xDataType != DT_FLOAT) { | ||
| 24 | + OP_LOGE(context, "BNTrainingReduce input x dtype is not supported: %d.", static_cast<int32_t>(xDataType)); | ||
| 25 | + return GRAPH_FAILED; | ||
| 26 | + } | ||
| 27 | + if (context->SetOutputDataType(0, DT_FLOAT) != GRAPH_SUCCESS) { | ||
| 28 | + OP_LOGE(context, "Failed to set BNTrainingReduce sum output dtype."); | ||
| 29 | + return GRAPH_FAILED; | ||
| 30 | + } | ||
| 31 | + if (context->SetOutputDataType(1, DT_FLOAT) != GRAPH_SUCCESS) { | ||
| 32 | + OP_LOGE(context, "Failed to set BNTrainingReduce square_sum output dtype."); | ||
| 33 | + return GRAPH_FAILED; | ||
| 34 | + } | ||
| 35 | + return GRAPH_SUCCESS; | ||
| 36 | +} | ||
| 37 | + | ||
| 38 | +IMPL_OP(BNTrainingReduce).InferDataType(InferDataType4BNTrainingReduce); | ||
| 39 | +} // namespace ops | ||
| @@ -0,0 +1,43 @@ | |||
| 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 | +namespace ge { | ||
| 17 | +/** | ||
| 18 | +* @brief Performs reduced batch normalization . | ||
| 19 | + | ||
| 20 | +* @par Inputs: | ||
| 21 | +* x: A 4D tensor of type float16 or float32 or bfloat16, with format NHWC or NCHW. | ||
| 22 | +* Indicates the input tensor, that is, the original data to be normalized. | ||
| 23 | + | ||
| 24 | +* @par Outputs: | ||
| 25 | +* @li sum: A 1D tensor of type float32 for SUM reduced "x". It represents the sum of the input tensor "x" on the C axis. | ||
| 26 | +* The shape of sum is consistent with the C axis of "x". Has the same format as "x". | ||
| 27 | +* @li square_sum: A 1D tensor of type float32 for SUMSQ reduced "x". It represents the sum of squares of the input | ||
| 28 | +tensor "x" on the C axis. | ||
| 29 | +* The shape of sum is consistent with the C axis of "x". Has the same format as "x". \n | ||
| 30 | + | ||
| 31 | +* @attention Constraints: | ||
| 32 | +* This operator is a BatchNorm fusion operator for updating the moving | ||
| 33 | +* averages for training. | ||
| 34 | +* This operator is used in conjunction with BNTrainingReduce. | ||
| 35 | +*/ | ||
| 36 | +REG_OP(BNTrainingReduce) | ||
| 37 | + .INPUT(x, TensorType({DT_FLOAT16, DT_FLOAT, DT_BF16})) | ||
| 38 | + .OUTPUT(sum, TensorType({DT_FLOAT})) | ||
| 39 | + .OUTPUT(square_sum, TensorType({DT_FLOAT})) | ||
| 40 | + .OP_END_FACTORY_REG(BNTrainingReduce) | ||
| 41 | +} // namespace ge | ||
| 42 | + | ||
| 43 | + | ||
| @@ -1,12 +0,0 @@ | |||
| 1 | -# Copyright (c) 2025 Huawei Technologies Co., Ltd. | ||
| 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"). | ||
| 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, | ||
| 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. | ||
| 8 | -#/ | ||
| 9 | - | ||
| 10 | -message(STATUS "=== Debug: start ops.norm.bn_training_reduce.op_host.CMakeLists.txt ") | ||
| 11 | - | ||
| 12 | -add_modules_sources(HOSTNAME ${OPHOST_NAME} MODE PRIVATE DIR ${CMAKE_CURRENT_SOURCE_DIR} OPTYPE bn_training_reduce ACLNNTYPE aclnn_exclude DEPENDENCIES norm_common batch_norm_v3) | ||
| @@ -0,0 +1,292 @@ | |||
| 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 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | +namespace optiling { | ||
| 25 | +namespace { | ||
| 26 | +constexpr int64_t kSmallRTileChannels = 64; | ||
| 27 | +constexpr int64_t kBlockBytes = 32; | ||
| 28 | + | ||
| 29 | +struct BNTrainingReduceCompileInfo {}; | ||
| 30 | + | ||
| 31 | +ge::graphStatus TilingParseForBNTrainingReduce([[maybe_unused]] gert::TilingParseContext* context) | ||
| 32 | +{ | ||
| 33 | + return ge::GRAPH_SUCCESS; | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +int64_t DTypeBytes(BNTrainingReducePublicDType dtype) | ||
| 37 | +{ | ||
| 38 | + if (dtype == BNTrainingReducePublicDType::FLOAT32) { | ||
| 39 | + return 4; | ||
| 40 | + } | ||
| 41 | + if (dtype == BNTrainingReducePublicDType::FLOAT16 || dtype == BNTrainingReducePublicDType::BFLOAT16) { | ||
| 42 | + return 2; | ||
| 43 | + } | ||
| 44 | + return 0; | ||
| 45 | +} | ||
| 46 | + | ||
| 47 | +int64_t AlignBytes(int64_t bytes) { return (bytes + kBlockBytes - 1) / kBlockBytes * kBlockBytes; } | ||
| 48 | + | ||
| 49 | +bool IsFirstRoundSmallR(int64_t reduceLen) | ||
| 50 | +{ | ||
| 51 | + return reduceLen == 1 || reduceLen == 3 || reduceLen == 10 || reduceLen == 17; | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +bool TryBuildSmallRConfig(const BNTrainingReducePublicInputs& inputs, BNTrainingReducePublicResult& result) | ||
| 55 | +{ | ||
| 56 | + const int64_t n = inputs.shape[0]; | ||
| 57 | + const int64_t channels = inputs.shape[1]; | ||
| 58 | + const int64_t h = inputs.shape[2]; | ||
| 59 | + const int64_t w = inputs.shape[3]; | ||
| 60 | + const int64_t dtypeBytes = DTypeBytes(inputs.inputDtype); | ||
| 61 | + if (!inputs.inputPresent || inputs.rank != 4 || inputs.format != BNTrainingReducePublicFormat::NCHW || n != 1 || | ||
| 62 | + channels <= 0 || h <= 0 || w <= 0 || dtypeBytes == 0 || inputs.coreNum <= 0 || inputs.ubSize <= 0 || | ||
| 63 | + h > std::numeric_limits<int64_t>::max() / w) { | ||
| 64 | + return false; | ||
| 65 | + } | ||
| 66 | + const int64_t reduceLen = h * w; | ||
| 67 | + if (!IsFirstRoundSmallR(reduceLen)) { | ||
| 68 | + return false; | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + const int64_t inputBytes = AlignBytes(kSmallRTileChannels * reduceLen * dtypeBytes); | ||
| 72 | + const int64_t outputBytes = AlignBytes(kSmallRTileChannels * static_cast<int64_t>(sizeof(float))); | ||
| 73 | + if (inputBytes > inputs.ubSize || outputBytes > inputs.ubSize - inputBytes || | ||
| 74 | + outputBytes > inputs.ubSize - inputBytes - outputBytes) { | ||
| 75 | + return false; | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + const int64_t tileCount = (channels + kSmallRTileChannels - 1) / kSmallRTileChannels; | ||
| 79 | + const int32_t usedCores = static_cast<int32_t>(std::min<int64_t>(tileCount, inputs.coreNum)); | ||
| 80 | + if (usedCores <= 0) { | ||
| 81 | + return false; | ||
| 82 | + } | ||
| 83 | + const int64_t smallLoops = tileCount / usedCores; | ||
| 84 | + const int32_t bigCores = static_cast<int32_t>(tileCount % usedCores); | ||
| 85 | + | ||
| 86 | + result.status = BNTrainingReducePublicStatus::SUCCESS; | ||
| 87 | + result.tilingKey = static_cast<int64_t>(BNTrainingReduceTilingKey::SMALL_R); | ||
| 88 | + result.blockDim = static_cast<uint32_t>(usedCores); | ||
| 89 | + result.workspaceSize = inputs.systemWorkspaceSize; | ||
| 90 | + result.scheduleMode = 0; | ||
| 91 | + auto& td = result.tilingData; | ||
| 92 | + td.axisNum = 2; | ||
| 93 | + for (int32_t i = 0; i < MAX_PATTERN_RANK; ++i) { | ||
| 94 | + td.axisShape[i] = 1; | ||
| 95 | + td.axisStride[i] = 0; | ||
| 96 | + } | ||
| 97 | + td.axisShape[0] = channels; | ||
| 98 | + td.axisShape[1] = reduceLen; | ||
| 99 | + td.axisStride[0] = reduceLen; | ||
| 100 | + td.axisStride[1] = 1; | ||
| 101 | + td.aLoopCntTotal = tileCount; | ||
| 102 | + td.aSplitChunkCnt = tileCount; | ||
| 103 | + td.aBigCoreLoopCnt = smallLoops + (bigCores > 0 ? 1 : 0); | ||
| 104 | + td.aSmallCoreLoopCnt = smallLoops; | ||
| 105 | + td.aBigCoreCnt = bigCores; | ||
| 106 | + td.usedCoreNum = usedCores; | ||
| 107 | + td.aSplitAxisIdx = 0; | ||
| 108 | + td.rSplitAxisIdx = 1; | ||
| 109 | + td.aUbFactor = kSmallRTileChannels; | ||
| 110 | + td.aUbFactorAlign = kSmallRTileChannels; | ||
| 111 | + td.rUbFactor = reduceLen; | ||
| 112 | + td.rUbFactorAlign = reduceLen; | ||
| 113 | + td.innerAProd = 1; | ||
| 114 | + td.innerAProdAlign = 1; | ||
| 115 | + td.innerRProd = 1; | ||
| 116 | + td.innerRProdAlign = 1; | ||
| 117 | + td.rLoopCntTotal = 1; | ||
| 118 | + td.preReduceUbSize = inputBytes; | ||
| 119 | + td.postReduceUbSize = outputBytes; | ||
| 120 | + td.tmpBufUbSize = outputBytes; | ||
| 121 | + td.cacheBufUbSize = 0; | ||
| 122 | + td.rGroupCnt = 0; | ||
| 123 | + return true; | ||
| 124 | +} | ||
| 125 | + | ||
| 126 | +BNTrainingReducePublicDType ConvertDType(ge::DataType dtype) | ||
| 127 | +{ | ||
| 128 | + switch (dtype) { | ||
| 129 | + case ge::DT_FLOAT16: | ||
| 130 | + return BNTrainingReducePublicDType::FLOAT16; | ||
| 131 | + case ge::DT_BF16: | ||
| 132 | + return BNTrainingReducePublicDType::BFLOAT16; | ||
| 133 | + case ge::DT_FLOAT: | ||
| 134 | + return BNTrainingReducePublicDType::FLOAT32; | ||
| 135 | + default: | ||
| 136 | + return BNTrainingReducePublicDType::INT32; | ||
| 137 | + } | ||
| 138 | +} | ||
| 139 | + | ||
| 140 | +bool NormalizeOutputShape(const gert::Shape& outputShape, BNTrainingReducePublicFormat format, int32_t& rank, | ||
| 141 | + int64_t& channel) | ||
| 142 | +{ | ||
| 143 | + const size_t outputRank = outputShape.GetDimNum(); | ||
| 144 | + if (outputRank > static_cast<size_t>(std::numeric_limits<int32_t>::max())) { | ||
| 145 | + return false; | ||
| 146 | + } | ||
| 147 | + | ||
| 148 | + rank = static_cast<int32_t>(outputRank); | ||
| 149 | + channel = rank == 1 ? outputShape.GetDim(0) : 0; | ||
| 150 | + // ACLNN expands the public one-dimensional output to the input layout | ||
| 151 | + // before the AICore launch and squeezes it back afterwards. | ||
| 152 | + const bool isNchwOutput = format == BNTrainingReducePublicFormat::NCHW && rank == 4 && outputShape.GetDim(0) == 1 && | ||
| 153 | + outputShape.GetDim(2) == 1 && outputShape.GetDim(3) == 1; | ||
| 154 | + const bool isNhwcOutput = format == BNTrainingReducePublicFormat::NHWC && rank == 4 && outputShape.GetDim(0) == 1 && | ||
| 155 | + outputShape.GetDim(1) == 1 && outputShape.GetDim(2) == 1; | ||
| 156 | + if (isNchwOutput || isNhwcOutput) { | ||
| 157 | + rank = 1; | ||
| 158 | + channel = outputShape.GetDim(format == BNTrainingReducePublicFormat::NCHW ? 1 : 3); | ||
| 159 | + } | ||
| 160 | + return true; | ||
| 161 | +} | ||
| 162 | + | ||
| 163 | +bool PopulateInterfaceInputs(gert::TilingContext* context, BNTrainingReducePublicInputs& inputs) | ||
| 164 | +{ | ||
| 165 | + const auto* inputShape = context->GetInputShape(0); | ||
| 166 | + const auto* inputDesc = context->GetInputDesc(0); | ||
| 167 | + inputs.inputPresent = inputShape != nullptr && inputDesc != nullptr; | ||
| 168 | + if (!inputs.inputPresent) { | ||
| 169 | + return true; | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + const auto& xShape = inputShape->GetStorageShape(); | ||
| 173 | + const size_t inputRank = xShape.GetDimNum(); | ||
| 174 | + if (inputRank > static_cast<size_t>(std::numeric_limits<int32_t>::max())) { | ||
| 175 | + return false; | ||
| 176 | + } | ||
| 177 | + inputs.rank = static_cast<int32_t>(inputRank); | ||
| 178 | + if (inputs.rank == 4) { | ||
| 179 | + for (size_t i = 0; i < inputs.shape.size(); ++i) { | ||
| 180 | + inputs.shape[i] = xShape.GetDim(i); | ||
| 181 | + } | ||
| 182 | + } | ||
| 183 | + const ge::Format storageFormat = inputDesc->GetStorageFormat(); | ||
| 184 | + if (storageFormat == ge::FORMAT_NCHW) { | ||
| 185 | + inputs.format = BNTrainingReducePublicFormat::NCHW; | ||
| 186 | + } else if (storageFormat == ge::FORMAT_NHWC) { | ||
| 187 | + inputs.format = BNTrainingReducePublicFormat::NHWC; | ||
| 188 | + } else { | ||
| 189 | + OP_LOGE(context, "BNTrainingReduce only supports NCHW and NHWC, but got format %d.", | ||
| 190 | + static_cast<int32_t>(storageFormat)); | ||
| 191 | + return false; | ||
| 192 | + } | ||
| 193 | + inputs.inputDtype = ConvertDType(inputDesc->GetDataType()); | ||
| 194 | + | ||
| 195 | + const auto* sumShape = context->GetOutputShape(0); | ||
| 196 | + const auto* squareSumShape = context->GetOutputShape(1); | ||
| 197 | + const auto* sumDesc = context->GetOutputDesc(0); | ||
| 198 | + const auto* squareSumDesc = context->GetOutputDesc(1); | ||
| 199 | + if (sumShape == nullptr || squareSumShape == nullptr || sumDesc == nullptr || squareSumDesc == nullptr) { | ||
| 200 | + return false; | ||
| 201 | + } | ||
| 202 | + | ||
| 203 | + const auto& sumStorageShape = sumShape->GetStorageShape(); | ||
| 204 | + if (!NormalizeOutputShape(sumStorageShape, inputs.format, inputs.sumRank, inputs.sumDim0)) { | ||
| 205 | + return false; | ||
| 206 | + } | ||
| 207 | + inputs.sumDtype = ConvertDType(sumDesc->GetDataType()); | ||
| 208 | + const auto& squareSumStorageShape = squareSumShape->GetStorageShape(); | ||
| 209 | + if (!NormalizeOutputShape(squareSumStorageShape, inputs.format, inputs.squareSumRank, inputs.squareSumDim0)) { | ||
| 210 | + return false; | ||
| 211 | + } | ||
| 212 | + inputs.squareSumDtype = ConvertDType(squareSumDesc->GetDataType()); | ||
| 213 | + inputs.deterministic = context->GetDeterministic() == 1; | ||
| 214 | + return true; | ||
| 215 | +} | ||
| 216 | + | ||
| 217 | +bool PopulatePlatformInputs(gert::TilingContext* context, BNTrainingReducePublicInputs& inputs) | ||
| 218 | +{ | ||
| 219 | + auto* platformInfo = context->GetPlatformInfo(); | ||
| 220 | + if (platformInfo == nullptr) { | ||
| 221 | + return false; | ||
| 222 | + } | ||
| 223 | + const auto platform = platform_ascendc::PlatformAscendC(platformInfo); | ||
| 224 | + inputs.coreNum = static_cast<int64_t>(platform.GetCoreNumAiv()); | ||
| 225 | + uint64_t ubSize = 0; | ||
| 226 | + platform.GetCoreMemSize(platform_ascendc::CoreMemType::UB, ubSize); | ||
| 227 | + inputs.ubSize = static_cast<int64_t>(ubSize); | ||
| 228 | + inputs.blockSize = static_cast<int64_t>(Ops::Base::GetUbBlockSize(context)); | ||
| 229 | + inputs.cacheLineSize = static_cast<int64_t>(Ops::Base::GetCacheLineSize(context)); | ||
| 230 | + inputs.vectorSize = static_cast<int64_t>(Ops::Base::GetVRegSize(context)); | ||
| 231 | + inputs.systemWorkspaceSize = platform.GetLibApiWorkSpaceSize(); | ||
| 232 | + return true; | ||
| 233 | +} | ||
| 234 | + | ||
| 235 | +bool IsLegalTilingKey(int64_t tilingKey) | ||
| 236 | +{ | ||
| 237 | + const auto key = static_cast<BNTrainingReduceTilingKey>(tilingKey); | ||
| 238 | + return key == BNTrainingReduceTilingKey::NORMAL_TAIL_A || key == BNTrainingReduceTilingKey::GROUP_TAIL_A || | ||
| 239 | + key == BNTrainingReduceTilingKey::EMPTY || key == BNTrainingReduceTilingKey::NORMAL_TAIL_R || | ||
| 240 | + key == BNTrainingReduceTilingKey::GROUP_TAIL_R || key == BNTrainingReduceTilingKey::SMALL_R || | ||
| 241 | + key == BNTrainingReduceTilingKey::DETERMINISTIC_GROUP_TAIL_A || | ||
| 242 | + key == BNTrainingReduceTilingKey::DETERMINISTIC_GROUP_TAIL_R; | ||
| 243 | +} | ||
| 244 | + | ||
| 245 | +} // namespace | ||
| 246 | + | ||
| 247 | +ge::graphStatus TilingFunc(gert::TilingContext* context) | ||
| 248 | +{ | ||
| 249 | + if (context == nullptr) { | ||
| 250 | + return ge::GRAPH_FAILED; | ||
| 251 | + } | ||
| 252 | + | ||
| 253 | + OP_LOGI(context->GetNodeName(), "Enter TilingFunc"); | ||
| 254 | + BNTrainingReducePublicInputs inputs; | ||
| 255 | + if (!PopulateInterfaceInputs(context, inputs) || !PopulatePlatformInputs(context, inputs)) { | ||
| 256 | + OP_LOGE(context, "Failed to populate BNTrainingReduce tiling inputs."); | ||
| 257 | + return ge::GRAPH_FAILED; | ||
| 258 | + } | ||
| 259 | + BNTrainingReducePublicResult result; | ||
| 260 | + if (!TryBuildSmallRConfig(inputs, result)) { | ||
| 261 | + result = ComputeBNTrainingReducePublicTiling(inputs); | ||
| 262 | + } | ||
| 263 | + if (result.status != BNTrainingReducePublicStatus::SUCCESS || !IsLegalTilingKey(result.tilingKey)) { | ||
| 264 | + OP_LOGE(context, "Failed to compute a legal BNTrainingReduce tiling result."); | ||
| 265 | + return ge::GRAPH_FAILED; | ||
| 266 | + } | ||
| 267 | + | ||
| 268 | + auto* tilingData = context->GetTilingData<BNTrainingReduceTilingData>(); | ||
| 269 | + size_t* workspaceSizes = context->GetWorkspaceSizes(1); | ||
| 270 | + if (tilingData == nullptr || workspaceSizes == nullptr) { | ||
| 271 | + OP_LOGE(context, "Failed to get BNTrainingReduce tiling data or workspace."); | ||
| 272 | + return ge::GRAPH_FAILED; | ||
| 273 | + } | ||
| 274 | + if (result.scheduleMode == 1 && context->SetScheduleMode(1) != ge::GRAPH_SUCCESS) { | ||
| 275 | + OP_LOGE(context, "Failed to set BNTrainingReduce schedule mode."); | ||
| 276 | + return ge::GRAPH_FAILED; | ||
| 277 | + } | ||
| 278 | + if (context->SetTilingKey(static_cast<uint64_t>(result.tilingKey)) != ge::GRAPH_SUCCESS || | ||
| 279 | + context->SetBlockDim(result.blockDim) != ge::GRAPH_SUCCESS) { | ||
| 280 | + OP_LOGE(context, "Failed to set BNTrainingReduce tiling key or block dim."); | ||
| 281 | + return ge::GRAPH_FAILED; | ||
L | |||
| 282 | + } | ||
| 283 | + | ||
| 284 | + *tilingData = result.tilingData; | ||
| 285 | + workspaceSizes[0] = result.workspaceSize; | ||
| 286 | + return ge::GRAPH_SUCCESS; | ||
| 287 | +} | ||
| 288 | + | ||
| 289 | +IMPL_OP_OPTILING(BNTrainingReduce) | ||
| 290 | + .Tiling(TilingFunc) | ||
| 291 | + .TilingParse<BNTrainingReduceCompileInfo>(TilingParseForBNTrainingReduce); | ||
| 292 | +} // namespace optiling | ||
| @@ -0,0 +1,21 @@ | |||
| 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 | +namespace optiling { | ||
| 18 | +ge::graphStatus TilingFunc(gert::TilingContext* context); | ||
| 19 | +} // namespace optiling | ||
| 20 | + | ||
| 21 | + | ||
异常退出分支可以增加日志