LCOV - code coverage report
Current view: top level - ut/compiler/graph/optimize/autofuse/autofuse/pattern_fusion - broadcast_reduce_elimination_pass.cpp Coverage Total Hit
Test: CHG Lines: 100.0 % 27 27
Test Date: 2026-08-20 13:15:03
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /**
       2              :  * Copyright (c) 2026 Huawei Technologies Co., Ltd.
       3              :  * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
       4              :  * CANN Open Software License Agreement Version 2.0 (the "License").
       5              :  * Please refer to the License for details. You may not use this file except in compliance with the License.
       6              :  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
       7              :  * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
       8              :  * See LICENSE in the root of the software repository for the full text of the License.
       9              :  */
      10              : 
      11              : #include "broadcast_reduce_elimination_pass.h"
      12              : #include <vector>
      13              : #include <unordered_set>
      14              : #include "graph/utils/graph_utils.h"
      15              : #include "graph/utils/node_utils.h"
      16              : #include "graph/utils/op_desc_utils.h"
      17              : #include "graph/attribute_group/attr_group_symbolic_desc.h"
      18              : #include "operator_reg.h"
      19              : #include "common/checker.h"
      20              : #include "debug/ge_util.h"
      21              : 
      22              : namespace ge {
      23              : namespace {
      24              : // 广播操作类型
      25              : constexpr auto kOpTypeBroadcastTo = "BroadcastTo";
      26              : constexpr auto kOpTypeFill = "Fill";
      27              : constexpr auto kOpTypeTile = "Tile";
      28              : constexpr auto kOpTypeTileD = "TileD";
      29              : constexpr auto kOpTypeReshape = "Reshape";
      30              : 
      31              : // 归约操作类型(仅支持直接消除的操作,避免数值精度问题)
      32              : // 注意:ReduceSum 和 ReduceProd 不支持,因为:
      33              : // - Sum -> Mul: broadcast_size 很大时 x*size 可能溢出,而原始累加器更鲁棒
      34              : // - Prod -> Pow: Pow 运算比连乘更昂贵且精度波动更大
      35              : const std::unordered_set<std::string> kReduceOpTypes = {"ReduceMax",  "ReduceMin",  "ReduceMean",
      36              :                                                         "ReduceMaxD", "ReduceMinD", "ReduceMeanD"};
      37              : 
      38              : constexpr auto kAttrNameAxes = "axes";
      39              : constexpr auto kAttrNameMultiples = "multiples";
      40              : constexpr auto kAttrNameKeepDims = "keep_dims";
      41              : 
      42              : bool IsReduceNode(const NodePtr &node) {
      43              :   GE_ASSERT_NOTNULL(node);
      44              :   return kReduceOpTypes.count(node->GetType()) > 0UL;
      45              : }
      46              : 
      47              : bool IsBroadcastNode(const NodePtr &node) {
      48              :   GE_ASSERT_NOTNULL(node);
      49              :   const auto &op_type = node->GetType();
      50              :   return op_type == kOpTypeBroadcastTo || op_type == kOpTypeFill || op_type == kOpTypeTile || op_type == kOpTypeTileD;
      51              : }
      52              : 
      53              : // 标准化轴索引(处理负数轴)
      54              : int64_t NormalizeAxis(int64_t axis, int64_t rank) {
      55              :   if (axis < 0) {
      56              :     return axis + rank;
      57              :   }
      58              :   return axis;
      59              : }
      60              : 
      61              : // 获取 BroadcastTo 操作的广播轴(通过 shape 推导)
      62              : // 使用标准广播语义:从右向左对齐
      63              : bool GetBroadcastToAxes(const NodePtr &brc_node, std::vector<int64_t> &brc_axes) {
      64              :   brc_axes.clear();
      65              :   const auto &op_desc = brc_node->GetOpDesc();
      66              :   GE_ASSERT_NOTNULL(op_desc);
      67              : 
      68              :   if (op_desc->GetInputsSize() == 0) {
      69              :     return false;
      70              :   }
      71              : 
      72              :   const auto &input_shape = op_desc->GetInputDesc(0).GetShape().GetDims();
      73              :   const auto &output_shape = op_desc->GetOutputDesc(0).GetShape().GetDims();
      74              :   GE_ASSERT_TRUE(input_shape.size() <= output_shape.size(), "BroadcastTo %s: input rank > output rank",
      75              :                  brc_node->GetNamePtr());
      76              : 
      77              :   // 检查输入 shape 是否有动态维度(输出可以有 -1)
      78              :   // 输入有 -1 时无法确定是否是广播轴,不能优化
      79              :   for (auto dim : input_shape) {
      80              :     if (dim == -1) {
      81              :       GELOGW("BroadcastTo %s has dynamic input shape", brc_node->GetNamePtr());
      82              :       return false;
      83              :     }
      84              :   }
      85              : 
      86              :   // 从右向左对齐(标准广播语义)
      87              :   // 例如:input [c, d] -> output [a, b, c, d]
      88              :   // 实际上是 [1, 1, c, d] -> [a, b, c, d]
      89              :   auto input_rank = static_cast<int64_t>(input_shape.size());
      90              :   auto output_rank = static_cast<int64_t>(output_shape.size());
      91              :   int64_t rank_diff = output_rank - input_rank;
      92              : 
      93              :   for (int64_t i = 0; i < output_rank; ++i) {
      94              :     int64_t in_dim = (i >= rank_diff) ? input_shape[i - rank_diff] : 1;
      95              :     // in_dim=1 且 output_dim!=1 时是广播轴(output_dim 可以是 -1,表示动态广播)
      96              :     if (in_dim == 1 && output_shape[i] != 1) {
      97              :       brc_axes.push_back(i);
      98              :     }
      99              :   }
     100              : 
     101              :   return !brc_axes.empty();
     102              : }
     103              : 
     104              : // 获取 Fill 的广播轴(所有输出维度都是广播轴)
     105              : bool GetFillBroadcastAxes(const NodePtr &fill_node, std::vector<int64_t> &brc_axes) {
     106              :   brc_axes.clear();
     107              :   const auto &op_desc = fill_node->GetOpDesc();
     108              :   GE_ASSERT_NOTNULL(op_desc);
     109              : 
     110              :   if (op_desc->GetInputsSize() < 2UL) {
     111              :     return false;
     112              :   }
     113              : 
     114              :   // 检查 value 输入(index 1)是否有动态 shape
     115              :   // 如果 value shape 包含 -1,无法确定 reshape 目标,不能优化
     116              :   const auto &value_shape = op_desc->GetInputDesc(1).GetShape().GetDims();
     117              :   for (auto dim : value_shape) {
     118              :     if (dim == -1) {
     119              :       GELOGW("Fill %s has dynamic value shape, cannot optimize", fill_node->GetNamePtr());
     120              :       return false;
     121              :     }
     122              :   }
     123              : 
     124              :   const auto &output_shape = op_desc->GetOutputDesc(0).GetShape().GetDims();
     125              :   for (size_t i = 0; i < output_shape.size(); ++i) {
     126              :     brc_axes.push_back(static_cast<int64_t>(i));
     127              :   }
     128              : 
     129              :   return !brc_axes.empty();
     130              : }
     131              : 
     132              : bool ReadIntVecFromInput(const NodePtr &node, const std::string &input_name, std::vector<int64_t> &values) {
     133              :   const auto op = ge::OpDescUtils::CreateOperatorFromNode(node);
     134              :   ge::Tensor tensor;
     135              :   if (op.GetInputConstData(input_name.c_str(), tensor) != ge::SUCCESS) {
     136              :     GELOGD("ReadIntVecFromInput %s: GetInputConstData failed for input '%s'", node->GetNamePtr(), input_name.c_str());
     137              :     return false;
     138              :   }
     139              :   if (tensor.GetData() == nullptr) {
     140              :     GELOGD("ReadIntVecFromInput %s: tensor data is null for input '%s'", node->GetNamePtr(), input_name.c_str());
     141              :     return false;
     142              :   }
     143              :   const auto &dims = tensor.GetTensorDesc().GetShape().GetDims();
     144              :   if (dims.size() > 1U) {
     145              :     GELOGD("ReadIntVecFromInput %s: input '%s' dims size %zu is not scalar or 1D", node->GetNamePtr(),
     146              :            input_name.c_str(), dims.size());
     147              :     return false;
     148              :   }
     149              :   const int64_t num_elems = dims.empty() ? 1L : dims[0];
     150              :   const auto dtype = tensor.GetTensorDesc().GetDataType();
     151              :   for (int64_t i = 0L; i < num_elems; ++i) {
     152              :     if (dtype == ge::DT_INT32) {
     153              :       values.push_back(reinterpret_cast<const int32_t *>(tensor.GetData())[i]);
     154              :     } else if (dtype == ge::DT_INT64) {
     155              :       values.push_back(reinterpret_cast<const int64_t *>(tensor.GetData())[i]);
     156              :     } else {
     157              :       GELOGW("ReadIntVecFromInput %s: unsupported dtype=%d for input '%s'", node->GetNamePtr(),
     158              :              static_cast<int32_t>(dtype), input_name.c_str());
     159              :       return false;
     160              :     }
     161              :   }
     162              :   return true;
     163              : }
     164              : 
     165              : // 获取 Tile 的 multiples(优先属性,fallback 到输入 tensor)
     166              : bool GetTileMultiples(const NodePtr &tile_node, std::vector<int64_t> &multiples) {
     167              :   const auto &op_desc = tile_node->GetOpDesc();
     168              :   if (AttrUtils::GetListInt(op_desc, kAttrNameMultiples, multiples)) {
     169              :     return true;
     170              :   }
     171              :   return ReadIntVecFromInput(tile_node, kAttrNameMultiples, multiples);
     172              : }
     173              : 
     174              : // 获取 Tile 的广播轴(输入维度=1 且 multiples>1 的轴)
     175              : bool GetTileBroadcastAxes(const NodePtr &tile_node, std::vector<int64_t> &brc_axes) {
     176              :   brc_axes.clear();
     177              :   const auto &op_desc = tile_node->GetOpDesc();
     178              :   GE_ASSERT_NOTNULL(op_desc);
     179              : 
     180              :   std::vector<int64_t> multiples;
     181              :   if (!GetTileMultiples(tile_node, multiples)) {
     182              :     GELOGD("Tile %s: cannot get multiples from attr or input", tile_node->GetNamePtr());
     183              :     return false;
     184              :   }
     185              : 
     186              :   const auto &input_shape = op_desc->GetInputDesc(0).GetShape().GetDims();
     187              :   if (multiples.size() != input_shape.size()) {
     188              :     GELOGW("Tile %s: multiples.size() != input_shape.size()", tile_node->GetNamePtr());
     189              :     return false;
     190              :   }
     191              : 
     192              :   // 检查动态 shape
     193              :   for (auto dim : input_shape) {
     194              :     if (dim == -1) {
     195              :       GELOGW("Tile %s has dynamic input shape", tile_node->GetNamePtr());
     196              :       return false;
     197              :     }
     198              :   }
     199              :   for (auto mult : multiples) {
     200              :     if (mult == -1) {
     201              :       GELOGW("Tile %s has dynamic multiples", tile_node->GetNamePtr());
     202              :       return false;
     203              :     }
     204              :   }
     205              : 
     206              :   // 找出广播轴(输入维度=1 且 multiples>1 的轴)
     207              :   for (size_t i = 0; i < input_shape.size(); ++i) {
     208              :     if (input_shape[i] == 1 && multiples[i] > 1) {
     209              :       brc_axes.push_back(static_cast<int64_t>(i));
     210              :     }
     211              :   }
     212              : 
     213              :   return !brc_axes.empty();
     214              : }
     215              : 
     216              : bool GetReduceAttrs(const NodePtr &reduce_node, std::vector<int64_t> &reduce_axes, bool &keep_dims) {
     217              :   reduce_axes.clear();
     218              :   keep_dims = false;
     219              :   const auto &op_desc = reduce_node->GetOpDesc();
     220              :   GE_ASSERT_NOTNULL(op_desc);
     221              :   GE_ASSERT_TRUE(AttrUtils::GetBool(op_desc, kAttrNameKeepDims, keep_dims));
     222              : 
     223              :   if (AttrUtils::GetListInt(op_desc, kAttrNameAxes, reduce_axes)) {
     224              :     return true;
     225              :   }
     226              :   // 非 D 变体:axes 是第二个输入 tensor,动态获取输入名
     227              :   if (reduce_node->GetAllInDataAnchorsSize() >= 2U) {
     228              :     std::string input_name = op_desc->GetInputNameByIndex(1U);
     229              :     if (ReadIntVecFromInput(reduce_node, input_name, reduce_axes)) {
     230              :       return !reduce_axes.empty();
     231              :     }
     232              :   }
     233              : 
     234              :   GELOGW("GetReduceAttrs %s: failed to get reduce axes from attr or input", reduce_node->GetNamePtr());
     235              :   return false;
     236              : }
     237              : 
     238              : // 按 broadcast 类型获取广播轴
     239              : bool GetBroadcastAxes(const NodePtr &brc_node, std::vector<int64_t> &brc_axes) {
     240              :   const auto &brc_type = brc_node->GetType();
     241              : 
     242              :   if (brc_type == kOpTypeBroadcastTo) {
     243              :     return GetBroadcastToAxes(brc_node, brc_axes);
     244              :   } else if (brc_type == kOpTypeFill) {
     245              :     return GetFillBroadcastAxes(brc_node, brc_axes);
     246              :   } else if (brc_type == kOpTypeTile || brc_type == kOpTypeTileD) {
     247              :     return GetTileBroadcastAxes(brc_node, brc_axes);
     248              :   }
     249              : 
     250              :   return false;
     251              : }
     252              : 
     253              : // 优化类型
     254              : enum class EliminationType { kFullElimination, kNoElimination };
     255              : 
     256              : EliminationType AnalyzeBroadcastReduce(const std::vector<int64_t> &brc_axes, const std::vector<int64_t> &reduce_axes) {
     257              :   // broadcast 轴和 reduce 轴必须完全一致:
     258              :   // - reduce 不能覆盖非广播轴(会丢失真实数据计算)
     259              :   // - 所有广播轴都必须被 reduce(否则输出仍含广播维度)
     260              :   const std::unordered_set<int64_t> brc_set(brc_axes.begin(), brc_axes.end());
     261              :   const std::unordered_set<int64_t> reduce_set(reduce_axes.begin(), reduce_axes.end());
     262              :   if (brc_set != reduce_set) {
     263              :     return EliminationType::kNoElimination;
     264              :   }
     265              :   return EliminationType::kFullElimination;
     266              : }
     267              : 
     268              : // 检查是否有其他消费者
     269              : bool HasOtherConsumers(const NodePtr &brc_node, const NodePtr &reduce_node) {
     270              :   for (const auto &out_anchor : brc_node->GetAllOutDataAnchors()) {
     271              :     for (const auto &in_anchor : out_anchor->GetPeerInDataAnchors()) {
     272              :       if (in_anchor->GetOwnerNode() != reduce_node) {
     273              :         return true;
     274              :       }
     275              :     }
     276              :   }
     277              :   return false;
     278              : }
     279              : 
     280              : // 计算 Reduce 输出 shape
     281              : std::vector<int64_t> ComputeReduceOutputShape(const std::vector<int64_t> &broadcast_output_dims,
     282              :                                               const std::vector<int64_t> &reduce_axes, bool keep_dims) {
     283              :   const std::unordered_set<int64_t> reduce_set(reduce_axes.begin(), reduce_axes.end());
     284              :   std::vector<int64_t> output_dims;
     285              :   for (size_t i = 0; i < broadcast_output_dims.size(); ++i) {
     286              :     if (reduce_set.count(static_cast<int64_t>(i)) > 0) {
     287              :       if (keep_dims) {
     288              :         output_dims.push_back(1);
     289              :       }
     290              :     } else {
     291              :       output_dims.push_back(broadcast_output_dims[i]);
     292              :     }
     293              :   }
     294              :   return output_dims;
     295              : }
     296              : 
     297              : // 替换节点并清理
     298              : Status ReplaceAndCleanup(const ComputeGraphPtr &graph, const NodePtr &brc_node, const NodePtr &reduce_node,
     299              :                          const NodePtr &replacement_node) {
     300              :   // 迁移数据边
     301              :   for (const auto &out_anchor : reduce_node->GetAllOutDataAnchors()) {
     302              :     for (const auto &in_anchor : out_anchor->GetPeerInDataAnchors()) {
     303              :       GE_ASSERT_GRAPH_SUCCESS(GraphUtils::RemoveEdge(out_anchor, in_anchor));
     304              :       GE_ASSERT_GRAPH_SUCCESS(GraphUtils::AddEdge(replacement_node->GetOutDataAnchor(0), in_anchor));
     305              :     }
     306              :   }
     307              : 
     308              :   // 迁移 Reduce 节点的入控制边
     309              :   for (const auto &ctrl_in : reduce_node->GetInControlNodes()) {
     310              :     GE_ASSERT_GRAPH_SUCCESS(GraphUtils::RemoveEdge(ctrl_in->GetOutControlAnchor(), reduce_node->GetInControlAnchor()));
     311              :     GE_ASSERT_GRAPH_SUCCESS(
     312              :         GraphUtils::AddEdge(ctrl_in->GetOutControlAnchor(), replacement_node->GetInControlAnchor()));
     313              :   }
     314              : 
     315              :   // 迁移 Broadcast 节点的入控制边(保持执行顺序依赖)
     316              :   for (const auto &ctrl_in : brc_node->GetInControlNodes()) {
     317              :     GE_ASSERT_GRAPH_SUCCESS(GraphUtils::RemoveEdge(ctrl_in->GetOutControlAnchor(), brc_node->GetInControlAnchor()));
     318              :     GE_ASSERT_GRAPH_SUCCESS(
     319              :         GraphUtils::AddEdge(ctrl_in->GetOutControlAnchor(), replacement_node->GetInControlAnchor()));
     320              :   }
     321              : 
     322              :   NodeUtils::UnlinkAll(*brc_node);
     323              :   NodeUtils::UnlinkAll(*reduce_node);
     324              :   GE_ASSERT_GRAPH_SUCCESS(graph->RemoveNode(brc_node));
     325              :   GE_ASSERT_GRAPH_SUCCESS(graph->RemoveNode(reduce_node));
     326              :   return ge::SUCCESS;
     327              : }
     328              : 
     329              : // 创建 Reshape 节点
     330              : NodePtr CreateReshapeNode(const ComputeGraphPtr &graph, const NodePtr &input, const std::vector<int64_t> &target_shape,
     331              :                           const std::string &name) {
     332              :   GeTensorDesc input_desc = input->GetOpDesc()->GetOutputDesc(0);
     333              :   GeTensorDesc output_desc(input_desc);
     334              :   output_desc.SetShape(GeShape(target_shape));
     335            3 :   output_desc.SetOriginShape(GeShape(target_shape));
     336              : 
     337              :   // 输入描述可能携带原始张量的符号化 shape。替换节点是真实的 Reshape,
     338              :   // 因此符号化 shape 的 rank 必须与目标 shape 以及静态 GeShape 保持一致。
     339            3 :   auto symbolic_attr = output_desc.GetOrCreateAttrsGroup<SymbolicDescAttr>();
     340            3 :   GE_ASSERT_NOTNULL(symbolic_attr);
     341            3 :   std::vector<Expression> symbolic_shape;
     342            3 :   symbolic_shape.reserve(target_shape.size());
     343            7 :   for (const auto dim : target_shape) {
     344            4 :     symbolic_shape.emplace_back(Symbol(dim));
     345              :   }
     346            3 :   symbolic_attr->symbolic_tensor.MutableOriginSymbolShape().MutableDims() = symbolic_shape;
     347              : 
     348              :   // Reshape 有两个输入:数据 x 和目标 shape。创建目标 shape 常量并连接到第二个输入。
     349            3 :   const auto shape_tensor = ComGraphMakeShared<GeTensor>();
     350            3 :   GE_ASSERT_NOTNULL(shape_tensor);
     351            3 :   auto &shape_desc = shape_tensor->MutableTensorDesc();
     352            6 :   const GeShape shape_tensor_shape({static_cast<int64_t>(target_shape.size())});
     353            3 :   shape_desc.Update(shape_tensor_shape, FORMAT_ND, DT_INT64);
     354            3 :   shape_desc.SetOriginShape(shape_tensor_shape);
     355            3 :   if (!target_shape.empty()) {
     356            3 :     GE_ASSERT_GRAPH_SUCCESS(shape_tensor->SetData(reinterpret_cast<const uint8_t *>(target_shape.data()),
     357              :                                                   target_shape.size() * sizeof(int64_t)));
     358              :   }
     359            3 :   const auto shape_op_desc = OpDescUtils::CreateConstOpZeroCopy(shape_tensor);
     360            3 :   GE_ASSERT_NOTNULL(shape_op_desc);
     361            3 :   const auto shape_node = graph->AddNode(shape_op_desc);
     362            3 :   GE_ASSERT_NOTNULL(shape_node);
     363              : 
     364            3 :   const auto reshape_op_desc = ComGraphMakeShared<OpDesc>(name, kOpTypeReshape);
     365            3 :   GE_ASSERT_NOTNULL(reshape_op_desc);
     366            9 :   GE_ASSERT_GRAPH_SUCCESS(reshape_op_desc->AddInputDesc("x", input_desc));
     367            9 :   GE_ASSERT_GRAPH_SUCCESS(reshape_op_desc->AddInputDesc("shape", shape_desc));
     368            9 :   GE_ASSERT_GRAPH_SUCCESS(reshape_op_desc->AddOutputDesc("y", output_desc));
     369            3 :   auto reshape_node = graph->AddNode(reshape_op_desc);
     370              :   GE_ASSERT_NOTNULL(reshape_node);
     371            3 :   GE_ASSERT_GRAPH_SUCCESS(GraphUtils::AddEdge(shape_node->GetOutDataAnchor(0), reshape_node->GetInDataAnchor(1)),
     372              :                           "Failed to add shape edge to Reshape node %s", reshape_node->GetNamePtr());
     373              :   GE_ASSERT_GRAPH_SUCCESS(GraphUtils::AddEdge(input->GetOutDataAnchor(0), reshape_node->GetInDataAnchor(0)),
     374              :                           "Failed to add edge to Reshape node %s", reshape_node->GetNamePtr());
     375              : 
     376              :   return reshape_node;
     377              : }
     378              : 
     379              : // 创建替换节点
     380              : NodePtr CreateReplacementNode(const ComputeGraphPtr &graph, const NodePtr &input,
     381              :                               const std::vector<int64_t> &target_shape, const std::string &base_name) {
     382              :   auto input_dims = input->GetOpDesc()->GetOutputDesc(0).GetShape().GetDims();
     383              :   // shape 完全相同,直接用原始输入
     384              :   if (input_dims == target_shape) {
     385              :     return input;
     386              :   }
     387              : 
     388              :   // shape 不同,用 Reshape 节点
     389              :   return CreateReshapeNode(graph, input, target_shape, base_name + "_reshape");
     390              : }
     391              : 
     392              : // 获取 broadcast 的数据输入节点
     393              : NodePtr GetBroadcastInput(const NodePtr &brc_node) {
     394              :   if (brc_node->GetType() == kOpTypeFill) {
     395              :     return NodeUtils::GetInDataNodeByIndex(*brc_node, 1);
     396              :   }
     397              :   return NodeUtils::GetInDataNodeByIndex(*brc_node, 0);
     398              : }
     399              : 
     400              : // 处理 Broadcast + Reduce 优化
     401              : graphStatus ProcessBroadcastReduce(const NodePtr &brc_node, const NodePtr &reduce_node, const ComputeGraphPtr &graph,
     402              :                                    bool &changed) {
     403              :   // 1. 获取广播轴
     404              :   std::vector<int64_t> brc_axes;
     405              :   if (!GetBroadcastAxes(brc_node, brc_axes)) {
     406              :     return GRAPH_SUCCESS;
     407              :   }
     408              : 
     409              :   // 2. 获取归约属性并标准化
     410              :   std::vector<int64_t> reduce_axes;
     411              :   bool keep_dims = false;
     412              :   if (!GetReduceAttrs(reduce_node, reduce_axes, keep_dims)) {
     413              :     return GRAPH_SUCCESS;
     414              :   }
     415              : 
     416              :   int64_t output_rank = brc_node->GetOpDesc()->GetOutputDesc(0).GetShape().GetDims().size();
     417              :   for (auto &axis : reduce_axes) {
     418              :     axis = NormalizeAxis(axis, output_rank);
     419              :   }
     420              : 
     421              :   // 3. 检查是否可优化
     422              :   if (AnalyzeBroadcastReduce(brc_axes, reduce_axes) == EliminationType::kNoElimination) {
     423              :     return GRAPH_SUCCESS;
     424              :   }
     425              : 
     426              :   // 4. 获取广播输入
     427              :   auto brc_input = GetBroadcastInput(brc_node);
     428              :   GE_ASSERT_NOTNULL(brc_input);
     429              : 
     430              :   // 5. 创建替换节点并执行消除
     431              :   auto broadcast_output_dims = brc_node->GetOpDesc()->GetOutputDesc(0).GetShape().GetDims();
     432              :   auto reduce_output_dims = ComputeReduceOutputShape(broadcast_output_dims, reduce_axes, keep_dims);
     433              :   auto replacement_node = CreateReplacementNode(graph, brc_input, reduce_output_dims,
     434              :                                                 std::string(brc_input->GetNamePtr()) + "_" + reduce_node->GetNamePtr());
     435              :   if (!replacement_node) {
     436              :     GELOGW("Failed to create replacement node for %s + %s", brc_node->GetNamePtr(), reduce_node->GetNamePtr());
     437              :     return GRAPH_SUCCESS;
     438              :   }
     439              : 
     440              :   GELOGD("BroadcastReduceElimination: eliminating %s + %s (keep_dims=%d)", brc_node->GetType().c_str(),
     441              :          reduce_node->GetType().c_str(), keep_dims);
     442              :   GE_ASSERT_SUCCESS(ReplaceAndCleanup(graph, brc_node, reduce_node, replacement_node));
     443              :   changed = true;
     444              :   return GRAPH_SUCCESS;
     445              : }
     446              : }  // namespace
     447              : 
     448              : graphStatus BroadcastReduceEliminationPass::Run(const ComputeGraphPtr &graph, bool &changed) const {
     449              :   GE_ASSERT_NOTNULL(graph);
     450              :   std::vector<std::pair<NodePtr, NodePtr> > pairs_to_process;
     451              :   for (const auto &node : graph->GetDirectNode()) {
     452              :     GE_ASSERT_NOTNULL(node);
     453              : 
     454              :     if (!IsReduceNode(node)) {
     455              :       continue;
     456              :     }
     457              : 
     458              :     auto input_node = NodeUtils::GetInDataNodeByIndex(*node, 0);
     459              :     if ((input_node == nullptr) || !IsBroadcastNode(input_node)) {
     460              :       continue;
     461              :     }
     462              : 
     463              :     if (HasOtherConsumers(input_node, node)) {
     464              :       continue;
     465              :     }
     466              : 
     467              :     pairs_to_process.emplace_back(input_node, node);
     468              :   }
     469              : 
     470              :   uint32_t optimized_count = 0U;
     471              :   for (const auto &pair : pairs_to_process) {
     472              :     bool cur_changed = false;
     473              :     auto ret = ProcessBroadcastReduce(pair.first, pair.second, graph, cur_changed);
     474              :     if (ret != GRAPH_SUCCESS) {
     475              :       GELOGW("Failed to process broadcast-reduce pattern for %s + %s", pair.first->GetNamePtr(),
     476              :              pair.second->GetNamePtr());
     477              :       continue;
     478              :     }
     479              : 
     480              :     if (cur_changed) {
     481              :       optimized_count++;
     482              :     }
     483              :   }
     484              : 
     485              :   if (optimized_count > 0U) {
     486              :     changed = true;
     487              :     GELOGI("BroadcastReduceEliminationPass: optimized %u patterns", optimized_count);
     488              :   }
     489              : 
     490              :   return GRAPH_SUCCESS;
     491              : }
     492              : }  // namespace ge
        

Generated by: LCOV version 2.3.2-1