我想打PAC队的第一次评注 #16
|
|
@ -80,18 +80,50 @@ void BackendCommonOptimization(const std::shared_ptr<session::KernelGraph> &kern
|
|||
MS_LOG(INFO) << "Status record: end common optimization. graph id: " << kernel_graph->graph_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs the final common optimization on a given kernel graph.
|
||||
*
|
||||
* This function conducts a series of optimization passes on the kernel graph. The
|
||||
* passes are bundled together in a PassManager which is then used by the GraphOptimizer
|
||||
* to optimize the graph. After optimizations, the kernel graph's execution order is set
|
||||
* by default. If the dump IR flag is enabled, this function will also dump the intermediate
|
||||
* representation of the optimized kernel graph.
|
||||
*
|
||||
* @param kernel_graph A shared pointer to the kernel graph to be optimized. It is
|
||||
* a data structure representing the computation graph at the kernel level
|
||||
* in the MindSpore computational framework.
|
||||
*
|
||||
* @return void This function does not return any value. It modifies the provided
|
||||
* kernel_graph in-place by applying the specified optimizations.
|
||||
*
|
||||
* @note The function will throw exceptions if kernel_graph is null or if there are issues
|
||||
* accessing the global context (MsContext).
|
||||
*
|
||||
* @see GraphOptimizer, PassManager, MsContext
|
||||
*/
|
||||
void CommonFinalOptimization(const std::shared_ptr<session::KernelGraph> &kernel_graph) {
|
||||
// Check if kernel_graph is null and throw an exception if true.
|
||||
MS_EXCEPTION_IF_NULL(kernel_graph);
|
||||
// Run optimizer passes.
|
||||
|
||||
// Initialize the graph optimizer and the pass manager for final optimization.
|
||||
auto optimizer = std::make_shared<GraphOptimizer>();
|
||||
auto pm = std::make_shared<PassManager>("final_opt");
|
||||
|
||||
// Add optimization passes to the pass manager.
|
||||
pm->AddPass(std::make_shared<OptimizeUpdateState>());
|
||||
pm->AddPass(std::make_shared<AddAkgKernelAttrs>());
|
||||
|
||||
// Attach the pass manager to the graph optimizer.
|
||||
optimizer->AddPassManager(pm);
|
||||
|
||||
// Apply optimization on the kernel graph using the graph optimizer.
|
||||
(void)optimizer->Optimize(kernel_graph);
|
||||
|
||||
// Set the default execution order for the kernel graph.
|
||||
kernel_graph->SetExecOrderByDefault();
|
||||
|
||||
#ifdef ENABLE_DUMP_IR
|
||||
// Dump IR if save_graphs is set.
|
||||
// Check and dump the kernel graph's intermediate representation if the save_graphs flag is enabled.
|
||||
auto context = MsContext::GetInstance();
|
||||
MS_EXCEPTION_IF_NULL(context);
|
||||
const bool save_graphs = context->get_param<bool>(MS_CTX_SAVE_GRAPHS_FLAG);
|
||||
|
|
@ -131,17 +163,54 @@ void CommonUnifyMindIR(const std::shared_ptr<session::KernelGraph> &kernel_graph
|
|||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Applies a pass to add dynamic shape attributes to the given kernel graph.
|
||||
*
|
||||
* This function sets up a GraphOptimizer and PassManager, then applies the
|
||||
* AddDynamicShapeAttr pass on the kernel graph to add necessary dynamic shape
|
||||
* attributes to nodes in the graph.
|
||||
*
|
||||
* @param kernel_graph A shared pointer to the kernel graph to be processed.
|
||||
*
|
||||
* @return void Modifies the provided kernel_graph in-place.
|
||||
*/
|
||||
void AddDynamicShapeAttrPass(const std::shared_ptr<session::KernelGraph> &kernel_graph) {
|
||||
// Initialize the graph optimizer.
|
||||
auto opt = std::make_shared<GraphOptimizer>();
|
||||
|
||||
// Setup a pass manager for adding dynamic shape attributes.
|
||||
auto pm = std::make_shared<PassManager>("add_dynamic_shape_attr");
|
||||
|
||||
// Add the specific pass for adding dynamic shape attributes.
|
||||
pm->AddPass(std::make_shared<AddDynamicShapeAttr>());
|
||||
|
||||
// Add the pass manager to the graph optimizer.
|
||||
opt->AddPassManager(pm);
|
||||
|
||||
// Execute the optimization on the kernel graph.
|
||||
(void)opt->Optimize(kernel_graph);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Eliminates illegal data types from the provided kernel graph.
|
||||
*
|
||||
* This function initializes a GraphOptimizer and PassManager, then applies the
|
||||
* EliminateFuncDataType pass on the kernel graph to remove or replace any
|
||||
* nodes with illegal data types. The kernel graph's execution order is
|
||||
* then set by default. If the dump IR flag is enabled, this function will
|
||||
* also dump the intermediate representation of the kernel graph both before
|
||||
* and after the optimization.
|
||||
*
|
||||
* @param kernel_graph A shared pointer to the kernel graph to be processed.
|
||||
*
|
||||
* @return void Modifies the provided kernel_graph in-place.
|
||||
*/
|
||||
void EliminateIllegalDataTypePass(const std::shared_ptr<session::KernelGraph> &kernel_graph) {
|
||||
// Check if kernel_graph is null and log the start of this pass.
|
||||
MS_EXCEPTION_IF_NULL(kernel_graph);
|
||||
MS_LOG(INFO) << "Start eliminate illegal data type for kernel graph id:" << kernel_graph->graph_id();
|
||||
|
||||
// Check and dump the kernel graph's intermediate representation if the save_graphs flag is enabled.
|
||||
#ifdef ENABLE_DUMP_IR
|
||||
auto context_ptr = MsContext::GetInstance();
|
||||
MS_EXCEPTION_IF_NULL(context_ptr);
|
||||
|
|
@ -152,12 +221,26 @@ void EliminateIllegalDataTypePass(const std::shared_ptr<session::KernelGraph> &k
|
|||
DumpIR(file_name, kernel_graph);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Initialize the graph optimizer.
|
||||
auto opt = std::make_shared<GraphOptimizer>();
|
||||
|
||||
// Setup a pass manager for eliminating illegal data types.
|
||||
auto pm = std::make_shared<PassManager>("common_eliminate_illegal_data_type_pm");
|
||||
|
||||
// Add the specific pass for eliminating illegal data types.
|
||||
pm->AddPass(std::make_shared<EliminateFuncDataType>());
|
||||
|
||||
// Add the pass manager to the graph optimizer.
|
||||
opt->AddPassManager(pm);
|
||||
|
||||
// Execute the optimization on the kernel graph.
|
||||
(void)opt->Optimize(kernel_graph);
|
||||
|
||||
// Set the default execution order for the kernel graph.
|
||||
kernel_graph->SetExecOrderByDefault();
|
||||
|
||||
// Check and dump the kernel graph's intermediate representation post optimization if save_graphs flag is enabled.
|
||||
#ifdef ENABLE_DUMP_IR
|
||||
if (save_graphs) {
|
||||
std::string file_name =
|
||||
|
|
@ -167,9 +250,26 @@ void EliminateIllegalDataTypePass(const std::shared_ptr<session::KernelGraph> &k
|
|||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Converts custom ops related to dynamic shapes in the given kernel graph.
|
||||
*
|
||||
* This function sets up a GraphOptimizer and PassManager, then applies a series
|
||||
* of passes (ConvertCustomOp and LinkCustomOp) on the kernel graph to handle
|
||||
* custom operations related to dynamic shapes. If the dump IR flag is enabled,
|
||||
* this function will also dump the intermediate representation of the kernel
|
||||
* graph both before and after the optimization.
|
||||
*
|
||||
* @param kernel_graph A shared pointer to the kernel graph to be processed.
|
||||
*
|
||||
* @return void Modifies the provided kernel_graph in-place.
|
||||
*/
|
||||
void DynamicShapeConvertPass(const std::shared_ptr<session::KernelGraph> &kernel_graph) {
|
||||
// Check if kernel_graph is null and log the start of this pass.
|
||||
MS_EXCEPTION_IF_NULL(kernel_graph);
|
||||
MS_LOG(INFO) << "Start dynamic shape convert for kernel graph id:" << kernel_graph->graph_id();
|
||||
|
||||
// Check and dump the kernel graph's intermediate representation if save_graphs flag is enabled.
|
||||
#ifdef ENABLE_DUMP_IR
|
||||
auto context_ptr = MsContext::GetInstance();
|
||||
MS_EXCEPTION_IF_NULL(context_ptr);
|
||||
|
|
@ -180,12 +280,24 @@ void DynamicShapeConvertPass(const std::shared_ptr<session::KernelGraph> &kernel
|
|||
DumpIR(file_name, kernel_graph);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Initialize the graph optimizer.
|
||||
auto optimizer = std::make_shared<opt::GraphOptimizer>();
|
||||
|
||||
// Setup a pass manager for dynamic shape conversion.
|
||||
auto dynamic_shape_convert_pm = std::make_shared<opt::PassManager>("dynamic_shape_convert_pm");
|
||||
|
||||
// Add specific passes related to dynamic shape conversion.
|
||||
dynamic_shape_convert_pm->AddPass(std::make_shared<opt::dynamic_shape::ConvertCustomOp>());
|
||||
dynamic_shape_convert_pm->AddPass(std::make_shared<opt::dynamic_shape::LinkCustomOp>());
|
||||
|
||||
// Add the pass manager to the graph optimizer.
|
||||
optimizer->AddPassManager(dynamic_shape_convert_pm);
|
||||
|
||||
// Execute the optimization on the kernel graph.
|
||||
(void)optimizer->Optimize(kernel_graph);
|
||||
|
||||
// Check and dump the kernel graph's intermediate representation post optimization if save_graphs flag is enabled.
|
||||
#ifdef ENABLE_DUMP_IR
|
||||
if (save_graphs) {
|
||||
std::string file_name =
|
||||
|
|
|
|||
|
|
@ -31,10 +31,22 @@ const size_t kSwitchBranchIndex = 2;
|
|||
const size_t kCallArgsIndex = 1;
|
||||
const size_t kPartialArgsIndex = 1;
|
||||
|
||||
/**
|
||||
* @brief Maps the output of a subgraph to its caller node in the main graph.
|
||||
*
|
||||
* This function checks if the given node (`cnode`) is of type `switch` or `call` and then maps
|
||||
* the output of the corresponding subgraph to the `cnode`. This is useful for tracing back
|
||||
* from a subgraph output to its caller in the main graph.
|
||||
*
|
||||
* @param cnode The computation node being checked.
|
||||
* @param out_caller_map Pointer to a map that will hold the relationship between subgraph outputs and their caller nodes.
|
||||
*/
|
||||
void AddOutputAndCallerToMap(const CNodePtr &cnode, mindspore::HashMap<AnfNodePtr, AnfNodePtr> *out_caller_map) {
|
||||
// Ensure the provided node and map are not null.
|
||||
MS_EXCEPTION_IF_NULL(cnode);
|
||||
MS_EXCEPTION_IF_NULL(out_caller_map);
|
||||
auto inputs = cnode->inputs();
|
||||
// Check if the node is a 'switch' type.
|
||||
if (common::AnfAlgo::CheckPrimitiveType(cnode, prim::kPrimSwitch)) {
|
||||
auto partial_node = dyn_cast<CNode>(inputs.at(kSwitchBranchIndex));
|
||||
MS_EXCEPTION_IF_NULL(partial_node);
|
||||
|
|
@ -45,6 +57,7 @@ void AddOutputAndCallerToMap(const CNodePtr &cnode, mindspore::HashMap<AnfNodePt
|
|||
auto switch_subgraph = GetValueNode<FuncGraphPtr>(partial_inputs.at(kPartialArgsIndex));
|
||||
MS_EXCEPTION_IF_NULL(switch_subgraph);
|
||||
(*out_caller_map)[switch_subgraph->output()] = cnode;
|
||||
// Check if the node is a 'call' type.
|
||||
} else if (common::AnfAlgo::CheckPrimitiveType(cnode, prim::kPrimCall)) {
|
||||
auto call_subgraph = GetValueNode<FuncGraphPtr>(inputs.at(kCallArgsIndex));
|
||||
MS_EXCEPTION_IF_NULL(call_subgraph);
|
||||
|
|
@ -52,16 +65,32 @@ void AddOutputAndCallerToMap(const CNodePtr &cnode, mindspore::HashMap<AnfNodePt
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Executes the node pass over the given function graph.
|
||||
*
|
||||
* This function iteratively processes nodes in the `func_graph` and applies transformations,
|
||||
* possibly optimizing or modifying the graph. The changes are tracked, and the function
|
||||
* returns whether the graph was modified or not.
|
||||
*
|
||||
* In the context of MindSpore, this function aids in intermediate optimizations and transformations
|
||||
* during the model compilation phase.
|
||||
*
|
||||
* @param func_graph The function graph (or computational graph) to be processed.
|
||||
* @return A boolean indicating whether the graph was changed during processing.
|
||||
*/
|
||||
bool NodePass::Run(const FuncGraphPtr &func_graph) {
|
||||
// Ensure the function graph is not null.
|
||||
MS_EXCEPTION_IF_NULL(func_graph);
|
||||
FuncGraphManagerPtr manager = func_graph->manager();
|
||||
MS_EXCEPTION_IF_NULL(manager);
|
||||
manager->AddFuncGraph(func_graph);
|
||||
|
||||
// Initializations
|
||||
mindspore::HashMap<AnfNodePtr, AnfNodePtr> subgraph_out_caller_map = {};
|
||||
mindspore::HashSet<AnfNodePtr> seen_node;
|
||||
std::deque<std::pair<AnfNodePtr, FuncGraphPtr>> todo{{func_graph->output(), func_graph}};
|
||||
bool changes = false;
|
||||
// Main loop to process each node in the graph
|
||||
while (!todo.empty()) {
|
||||
AnfNodePtr node = todo.front().first;
|
||||
auto fg = todo.front().second;
|
||||
|
|
|
|||
|
|
@ -44,12 +44,24 @@ void PatternProcessPass::Build() {
|
|||
pattern_ = SexpToNode(DefinePattern(), fg, primitive_vars_.get(), multigraph_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Matches a node in the computation graph against a predefined pattern and processes it accordingly.
|
||||
*
|
||||
* This method attempts to match a given node against a specific pattern. If a match is found, it processes
|
||||
* the node and returns the result.
|
||||
*
|
||||
* @param func_graph The computation graph containing the node.
|
||||
* @param node The node in the computation graph to match against the pattern.
|
||||
* @return Processed node if a match is found; otherwise, nullptr.
|
||||
*/
|
||||
AnfNodePtr PatternProcessPass::Run(const FuncGraphPtr &func_graph, const AnfNodePtr &node) {
|
||||
// Ensure the pattern is built before matching.
|
||||
if (pattern_ == nullptr) {
|
||||
Build();
|
||||
}
|
||||
|
||||
auto primitive = GetCNodePrimitive(pattern_);
|
||||
// Check if the node matches the primitive pattern.
|
||||
if (IsPrimitiveCNode(node, primitive)) {
|
||||
MS_EXCEPTION_IF_NULL(primitive_vars_);
|
||||
MS_EXCEPTION_IF_NULL(equiv_);
|
||||
|
|
@ -62,6 +74,11 @@ AnfNodePtr PatternProcessPass::Run(const FuncGraphPtr &func_graph, const AnfNode
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieves the original nodes that match the primitive variables.
|
||||
*
|
||||
* @return A vector containing the original nodes matched against the primitive variables.
|
||||
*/
|
||||
std::vector<AnfNodePtr> PatternProcessPass::GetOrigNodes() const {
|
||||
std::vector<AnfNodePtr> orig_nodes;
|
||||
for (auto &prim_var : *primitive_vars_) {
|
||||
|
|
@ -78,18 +95,41 @@ std::vector<AnfNodePtr> PatternProcessPass::GetOrigNodes() const {
|
|||
return orig_nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a new CNode with the given inputs and attaches it to the provided computation graph.
|
||||
*
|
||||
* @param inputs The input nodes for the new CNode.
|
||||
* @param fg The computation graph to which the new CNode should be added.
|
||||
* @return The newly created CNode.
|
||||
*/
|
||||
CNodePtr PatternProcessPass::NewCNode(const std::vector<AnfNodePtr> &inputs, const FuncGraphPtr &fg) const {
|
||||
MS_EXCEPTION_IF_NULL(fg);
|
||||
auto orig_nodes = GetOrigNodes();
|
||||
return opt::NewCNode(inputs, fg, orig_nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a new CNode by copying an existing one and attaches it to the provided kernel graph.
|
||||
*
|
||||
* @param cnode The CNode to copy.
|
||||
* @param fg The kernel graph to which the new CNode should be added.
|
||||
* @return The newly created CNode.
|
||||
*/
|
||||
CNodePtr PatternProcessPass::NewCNode(const CNodePtr &cnode, const KernelGraphPtr &fg) const {
|
||||
MS_EXCEPTION_IF_NULL(fg);
|
||||
auto orig_nodes = GetOrigNodes();
|
||||
return opt::NewCNode(cnode, fg, orig_nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Matches a node against another predefined pattern.
|
||||
*
|
||||
* This method checks if a node matches another specific pattern, distinct from the primary pattern.
|
||||
*
|
||||
* @param node The node in the computation graph to match against the secondary pattern.
|
||||
* @param equiv The equivalence mapping of nodes to variables in the primary pattern.
|
||||
* @return True if the node matches the secondary pattern; otherwise, false.
|
||||
*/
|
||||
bool MultipleOutputPatternProcessPass::MatchAnotherPattern(const AnfNodePtr &node, const EquivPtr &equiv) const {
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
MS_EXCEPTION_IF_NULL(equiv);
|
||||
|
|
@ -105,6 +145,11 @@ bool MultipleOutputPatternProcessPass::MatchAnotherPattern(const AnfNodePtr &nod
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieves the original nodes that match both the primary and secondary primitive variables.
|
||||
*
|
||||
* @return A vector containing the original nodes matched against both sets of primitive variables.
|
||||
*/
|
||||
std::vector<AnfNodePtr> MultipleOutputPatternProcessPass::GetOrigNodes() const {
|
||||
std::vector<AnfNodePtr> orig_nodes = PatternProcessPass::GetOrigNodes();
|
||||
for (auto &prim_var : *child_primitive_vars_) {
|
||||
|
|
@ -118,35 +163,75 @@ std::vector<AnfNodePtr> MultipleOutputPatternProcessPass::GetOrigNodes() const {
|
|||
return orig_nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a pass manager to the graph optimizer's list of pass managers.
|
||||
*
|
||||
* A pass manager contains a set of transformation passes to be applied to the computation graph.
|
||||
*
|
||||
* @param pass_manager The pass manager to be added.
|
||||
*/
|
||||
void GraphOptimizer::AddPassManager(const PassManagerPtr &pass_manager) {
|
||||
if (pass_manager != nullptr) {
|
||||
pass_managers_.push_back(pass_manager);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optimizes a given computation graph by applying various transformation passes.
|
||||
*
|
||||
* This method applies a set of transformation passes to the given computation graph, seeking to
|
||||
* optimize its structure and operations. These transformations can include tasks like constant
|
||||
* folding, operation fusion, or simplification. The goal is to generate a more efficient graph
|
||||
* without changing the original semantics. This function can either apply all the transformation
|
||||
* passes once or multiple times based on the `run_only_once` parameter.
|
||||
*
|
||||
* @param func_graph The computation graph that needs to be optimized. Represented as a pointer
|
||||
* to a FuncGraph.
|
||||
* @param run_only_once If true, each transformation pass is applied only once. Otherwise, the
|
||||
* passes may be applied multiple times until no further changes are observed
|
||||
* in the graph.
|
||||
* @return Returns the optimized computation graph.
|
||||
*/
|
||||
FuncGraphPtr GraphOptimizer::Optimize(const FuncGraphPtr &func_graph, bool run_only_once) {
|
||||
// Check if the input computation graph is null. If so, raise an exception.
|
||||
MS_EXCEPTION_IF_NULL(func_graph);
|
||||
|
||||
// Determine if we should run the optimization only once based on the number of pass managers.
|
||||
run_only_once_ = (pass_managers_.size() == 1) ? true : run_only_once;
|
||||
// cppcheck-suppress *
|
||||
|
||||
// Create or retrieve a manager for the computation graph.
|
||||
// The manager is responsible for handling various tasks like graph traversal, node replacement,
|
||||
// and overall graph management.
|
||||
auto manager = Manage(func_graph, true);
|
||||
|
||||
bool changed = true;
|
||||
// Loop until no further changes are observed in the graph.
|
||||
while (changed) {
|
||||
changed = false;
|
||||
// Iterate through all the pass managers and run their associated transformation passes
|
||||
// on the computation graph.
|
||||
for (size_t i = 0; i < pass_managers_.size(); ++i) {
|
||||
const PassManagerPtr &pm = pass_managers_[i];
|
||||
// If a transformation results in a change to the graph, set the `changed` flag to true.
|
||||
if (pm != nullptr && pm->Run(func_graph)) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
// If we're only supposed to run the optimization once, break out of the loop.
|
||||
if (run_only_once_) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// List to hold the collection of computation graphs.
|
||||
std::vector<FuncGraphPtr> func_graphs;
|
||||
func_graphs.push_back(func_graph);
|
||||
|
||||
// Perform a topological sort on the computation graph nodes starting from the return node.
|
||||
// This ensures that the graph nodes are arranged in a specific order based on their dependencies.
|
||||
(void)TopoSort(func_graph->get_return());
|
||||
|
||||
// Return the optimized computation graph.
|
||||
return func_graph;
|
||||
}
|
||||
} // namespace opt
|
||||
|
|
|
|||
|
|
@ -32,135 +32,241 @@ namespace opt {
|
|||
namespace {
|
||||
using KernelWithIndex = std::pair<AnfNodePtr, int64_t>;
|
||||
|
||||
/**
|
||||
* @brief Checks whether a given node should be ignored during optimization.
|
||||
*
|
||||
* This function focuses on nodes of type `kTransDataOpName` and determines if
|
||||
* they should be ignored based on their inputs.
|
||||
*
|
||||
* @param node The node being inspected.
|
||||
* @return True if the node should be ignored, otherwise false.
|
||||
*/
|
||||
bool CheckIgnoreCase(const AnfNodePtr &node) {
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
if (common::AnfAlgo::GetCNodeName(node) != kTransDataOpName) {
|
||||
return false;
|
||||
}
|
||||
auto cnode = node->cast<CNodePtr>();
|
||||
MS_EXCEPTION_IF_NULL(cnode);
|
||||
bool need_ignore = true;
|
||||
auto input_size = cnode->inputs().size() - 1;
|
||||
for (size_t k = 0; k < input_size; ++k) {
|
||||
auto input = common::AnfAlgo::VisitKernelWithReturnType(common::AnfAlgo::GetInputNode(cnode, k), 0).first;
|
||||
if (input != nullptr && input->isa<CNode>()) {
|
||||
need_ignore = false;
|
||||
break;
|
||||
// Ensure the node is not null.
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
|
||||
// Check if the node's operation type is `kTransDataOpName`.
|
||||
if (common::AnfAlgo::GetCNodeName(node) != kTransDataOpName) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return need_ignore;
|
||||
|
||||
// Cast to a computation node for further inspection.
|
||||
auto cnode = node->cast<CNodePtr>();
|
||||
MS_EXCEPTION_IF_NULL(cnode);
|
||||
|
||||
// By default, we consider ignoring the node.
|
||||
bool need_ignore = true;
|
||||
|
||||
// Deduct 1 to ignore self in inputs.
|
||||
auto input_size = cnode->inputs().size() - 1;
|
||||
|
||||
// Loop over all inputs to the computation node.
|
||||
for (size_t k = 0; k < input_size; ++k) {
|
||||
auto input = common::AnfAlgo::VisitKernelWithReturnType(common::AnfAlgo::GetInputNode(cnode, k), 0).first;
|
||||
|
||||
// If any of the inputs are computation nodes, the node shouldn't be ignored.
|
||||
if (input != nullptr && input->isa<CNode>()) {
|
||||
need_ignore = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Return whether or not this node should be ignored.
|
||||
return need_ignore;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Eliminates duplicate 'TupleGetItem' operations in the computational graph.
|
||||
*
|
||||
* This function identifies duplicate 'TupleGetItem' operations in the graph, which have
|
||||
* the same input and index, and replaces duplicates with the original.
|
||||
*
|
||||
* @param graph The target computational graph.
|
||||
* @param manager Manager for handling graph operations.
|
||||
*/
|
||||
void EliminateDuplicatedTupleGetItem(const FuncGraphPtr &graph, const FuncGraphManagerPtr &manager) {
|
||||
MS_EXCEPTION_IF_NULL(graph);
|
||||
MS_EXCEPTION_IF_NULL(manager);
|
||||
// Ensure provided graph and manager are not null.
|
||||
MS_EXCEPTION_IF_NULL(graph);
|
||||
MS_EXCEPTION_IF_NULL(manager);
|
||||
|
||||
// key: (getitem_input, getitem_index), value: getitem_list
|
||||
std::map<KernelWithIndex, std::vector<AnfNodePtr>> getitem_dup_map;
|
||||
const auto &node_list = TopoSort(graph->get_return());
|
||||
for (auto &node : node_list) {
|
||||
if (!node->isa<CNode>() || !IsPrimitiveCNode(node, prim::kPrimTupleGetItem)) {
|
||||
continue;
|
||||
}
|
||||
auto getitem_cnode = node->cast<CNodePtr>();
|
||||
KernelWithIndex input_with_index{getitem_cnode->input(kRealInputNodeIndexInTupleGetItem),
|
||||
GetGetitemIndex(getitem_cnode)};
|
||||
if (getitem_dup_map.count(input_with_index) == 0) {
|
||||
getitem_dup_map.emplace(input_with_index, std::vector<AnfNodePtr>{node});
|
||||
} else {
|
||||
getitem_dup_map[input_with_index].push_back(node);
|
||||
}
|
||||
}
|
||||
// This map will store each unique 'TupleGetItem' operation and its duplicates.
|
||||
std::map<KernelWithIndex, std::vector<AnfNodePtr>> getitem_dup_map;
|
||||
|
||||
// remove duplicated
|
||||
for (auto &item : getitem_dup_map) {
|
||||
auto &getitem_list = item.second;
|
||||
if (getitem_list.size() > 1) {
|
||||
auto first_getitem = getitem_list[0];
|
||||
std::for_each(getitem_list.begin() + 1, getitem_list.end(), [first_getitem, manager](const AnfNodePtr &getitem) {
|
||||
(void)manager->Replace(getitem, first_getitem);
|
||||
});
|
||||
// Sort nodes to ensure we process them in topological order.
|
||||
const auto &node_list = TopoSort(graph->get_return());
|
||||
|
||||
// Process each node in the graph.
|
||||
for (auto &node : node_list) {
|
||||
if (!node->isa<CNode>() || !IsPrimitiveCNode(node, prim::kPrimTupleGetItem)) {
|
||||
continue;
|
||||
}
|
||||
auto getitem_cnode = node->cast<CNodePtr>();
|
||||
|
||||
// Create a key for this 'TupleGetItem' operation based on its input and index.
|
||||
KernelWithIndex input_with_index{getitem_cnode->input(kRealInputNodeIndexInTupleGetItem),
|
||||
GetGetitemIndex(getitem_cnode)};
|
||||
|
||||
// Store the operation in the map.
|
||||
if (getitem_dup_map.count(input_with_index) == 0) {
|
||||
getitem_dup_map.emplace(input_with_index, std::vector<AnfNodePtr>{node});
|
||||
} else {
|
||||
getitem_dup_map[input_with_index].push_back(node);
|
||||
}
|
||||
}
|
||||
|
||||
// For each 'TupleGetItem' operation in the map, if there are duplicates, replace them with the original.
|
||||
for (auto &item : getitem_dup_map) {
|
||||
auto &getitem_list = item.second;
|
||||
if (getitem_list.size() > 1) {
|
||||
auto first_getitem = getitem_list[0];
|
||||
std::for_each(getitem_list.begin() + 1, getitem_list.end(), [first_getitem, manager](const AnfNodePtr &getitem) {
|
||||
(void)manager->Replace(getitem, first_getitem);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
* @brief Checks if the kernel build information of two nodes is equivalent.
|
||||
*
|
||||
* This function examines the kernel build information of the main and another node to determine if they match.
|
||||
* Certain nodes, such as those with operation type `kPrimTensorMove` or `kPrimMemCpyAsync`, are treated as
|
||||
* special cases and are handled differently.
|
||||
*
|
||||
* @param main The main node to compare.
|
||||
* @param node The secondary node to compare.
|
||||
* @return True if the kernel build info of both nodes matches, otherwise false.
|
||||
*/
|
||||
bool BackendCSE::CheckEqualKernelBuildInfo(const AnfNodePtr &main, const AnfNodePtr &node) const {
|
||||
MS_EXCEPTION_IF_NULL(main);
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
if (main->isa<CNode>()) {
|
||||
auto main_name = common::AnfAlgo::GetCNodeName(main);
|
||||
if (main_name == prim::kPrimTensorMove->name() || main_name == prim::kPrimMemCpyAsync->name()) {
|
||||
return false;
|
||||
// Ensure the main and secondary nodes are not null.
|
||||
MS_EXCEPTION_IF_NULL(main);
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
|
||||
// If main is a computation node and has specific operations, return false immediately.
|
||||
if (main->isa<CNode>()) {
|
||||
auto main_name = common::AnfAlgo::GetCNodeName(main);
|
||||
if (main_name == prim::kPrimTensorMove->name() || main_name == prim::kPrimMemCpyAsync->name()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto main_kernel_info = dynamic_cast<device::KernelInfo *>(main->kernel_info());
|
||||
auto node_kernel_info = dynamic_cast<device::KernelInfo *>(node->kernel_info());
|
||||
if (main_kernel_info == nullptr && node_kernel_info == nullptr) {
|
||||
return true;
|
||||
}
|
||||
if (main_kernel_info != nullptr && node_kernel_info != nullptr) {
|
||||
return *main_kernel_info == *node_kernel_info;
|
||||
}
|
||||
return false;
|
||||
|
||||
// Cast the kernel info of both nodes to specific kernel info type.
|
||||
auto main_kernel_info = dynamic_cast<device::KernelInfo *>(main->kernel_info());
|
||||
auto node_kernel_info = dynamic_cast<device::KernelInfo *>(node->kernel_info());
|
||||
|
||||
// Check if both kernel infos are null. If yes, return true.
|
||||
if (main_kernel_info == nullptr && node_kernel_info == nullptr) {
|
||||
return true;
|
||||
}
|
||||
// If both kernel infos are valid, compare them.
|
||||
if (main_kernel_info != nullptr && node_kernel_info != nullptr) {
|
||||
return *main_kernel_info == *node_kernel_info;
|
||||
}
|
||||
|
||||
// If one of the kernel infos is null but the other isn't, return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the inputs of two computation nodes are identical.
|
||||
*
|
||||
* @param main The main computation node to compare.
|
||||
* @param node The secondary computation node to compare.
|
||||
* @return True if the inputs of both computation nodes are the same, otherwise false.
|
||||
*/
|
||||
bool BackendCSE::CheckEqualCnodeInputs(const AnfNodePtr &main, const AnfNodePtr &node) const {
|
||||
auto c_main = main->cast<CNodePtr>();
|
||||
MS_EXCEPTION_IF_NULL(c_main);
|
||||
auto c_node = node->cast<CNodePtr>();
|
||||
MS_EXCEPTION_IF_NULL(c_node);
|
||||
const auto &inp1 = c_main->inputs();
|
||||
const auto &inp2 = c_node->inputs();
|
||||
if (inp1.size() != inp2.size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t j = 0; j < inp1.size(); j++) {
|
||||
auto inp1_j = inp1[j];
|
||||
auto inp2_j = inp2[j];
|
||||
MS_EXCEPTION_IF_NULL(inp1_j);
|
||||
MS_EXCEPTION_IF_NULL(inp2_j);
|
||||
if (!(*inp1_j == *inp2_j)) {
|
||||
return false;
|
||||
// Cast nodes to specific computation node type.
|
||||
auto c_main = main->cast<CNodePtr>();
|
||||
MS_EXCEPTION_IF_NULL(c_main);
|
||||
auto c_node = node->cast<CNodePtr>();
|
||||
MS_EXCEPTION_IF_NULL(c_node);
|
||||
|
||||
// Retrieve the inputs of the computation nodes.
|
||||
const auto &inp1 = c_main->inputs();
|
||||
const auto &inp2 = c_node->inputs();
|
||||
|
||||
// If the number of inputs differ, the nodes are not identical.
|
||||
if (inp1.size() != inp2.size()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
// Compare each input of both computation nodes.
|
||||
for (size_t j = 0; j < inp1.size(); j++) {
|
||||
auto inp1_j = inp1[j];
|
||||
auto inp2_j = inp2[j];
|
||||
MS_EXCEPTION_IF_NULL(inp1_j);
|
||||
MS_EXCEPTION_IF_NULL(inp2_j);
|
||||
if (!(*inp1_j == *inp2_j)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If all inputs match, the nodes are identical.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two value nodes to determine if they hold the same value.
|
||||
*
|
||||
* This function is used to ascertain whether the main and secondary value nodes
|
||||
* encapsulate the same underlying value.
|
||||
*
|
||||
* @param main The main value node for comparison.
|
||||
* @param node The secondary value node for comparison.
|
||||
* @return True if the values in the nodes are the same, otherwise false.
|
||||
*/
|
||||
bool BackendCSE::CheckValueNode(const ValueNodePtr &main, const ValueNodePtr &node) const {
|
||||
MS_EXCEPTION_IF_NULL(main);
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
MS_EXCEPTION_IF_NULL(main);
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
|
||||
auto main_value = main->value();
|
||||
MS_EXCEPTION_IF_NULL(main_value);
|
||||
auto node_value = node->value();
|
||||
MS_EXCEPTION_IF_NULL(node_value);
|
||||
if (main_value->isa<Primitive>() && node_value->isa<Primitive>()) {
|
||||
return false;
|
||||
} else if (main_value->isa<tensor::Tensor>() && node_value->isa<tensor::Tensor>()) {
|
||||
return (AbsOf(main) == AbsOf(node)) && CheckEqualKernelBuildInfo(main, node);
|
||||
}
|
||||
return (AbsOf(main) == AbsOf(node)) && (*main_value == *node_value);
|
||||
auto main_value = main->value();
|
||||
MS_EXCEPTION_IF_NULL(main_value);
|
||||
auto node_value = node->value();
|
||||
MS_EXCEPTION_IF_NULL(node_value);
|
||||
|
||||
// If both values are primitive types, they are not considered equal.
|
||||
if (main_value->isa<Primitive>() && node_value->isa<Primitive>()) {
|
||||
return false;
|
||||
}
|
||||
// Special handling for tensor values.
|
||||
else if (main_value->isa<tensor::Tensor>() && node_value->isa<tensor::Tensor>()) {
|
||||
return (AbsOf(main) == AbsOf(node)) && CheckEqualKernelBuildInfo(main, node);
|
||||
}
|
||||
|
||||
// For other value types, directly compare their values.
|
||||
return (AbsOf(main) == AbsOf(node)) && (*main_value == *node_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if two computation nodes are functionally identical.
|
||||
*
|
||||
* This method performs several checks to determine if the main and secondary computation nodes
|
||||
* can be considered the same in terms of their function within a computational graph.
|
||||
*
|
||||
* @param main The main computation node to compare.
|
||||
* @param node The secondary computation node to compare.
|
||||
* @return True if the computation nodes are functionally identical, otherwise false.
|
||||
*/
|
||||
bool BackendCSE::CheckCNode(const CNodePtr &main, const CNodePtr &node) const {
|
||||
MS_EXCEPTION_IF_NULL(main);
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
MS_EXCEPTION_IF_NULL(main);
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
|
||||
auto context_ptr = MsContext::GetInstance();
|
||||
MS_EXCEPTION_IF_NULL(context_ptr);
|
||||
if (!context_ptr->get_param<bool>(MS_CTX_ENABLE_LOOP_SINK) && CheckIgnoreCase(main)) {
|
||||
return false;
|
||||
}
|
||||
if (HasHiddenSideEffect(main) || HasHiddenSideEffect(node)) {
|
||||
return false;
|
||||
}
|
||||
if (!CheckEqualKernelBuildInfo(main, node)) {
|
||||
return false;
|
||||
}
|
||||
return CheckEqualCnodeInputs(main, node);
|
||||
auto context_ptr = MsContext::GetInstance();
|
||||
MS_EXCEPTION_IF_NULL(context_ptr);
|
||||
|
||||
// Check if loop sink is enabled and if the main node should be ignored.
|
||||
if (!context_ptr->get_param<bool>(MS_CTX_ENABLE_LOOP_SINK) && CheckIgnoreCase(main)) {
|
||||
return false;
|
||||
}
|
||||
// Check for hidden side effects in the main and secondary computation nodes.
|
||||
if (HasHiddenSideEffect(main) || HasHiddenSideEffect(node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare kernel build info and the inputs of the two nodes.
|
||||
return CheckEqualKernelBuildInfo(main, node) && CheckEqualCnodeInputs(main, node);
|
||||
}
|
||||
|
||||
bool BackendCSE::CheckReplace(const AnfNodePtr &main, const AnfNodePtr &node) const {
|
||||
MS_EXCEPTION_IF_NULL(main);
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
|
|
|
|||
|
|
@ -81,15 +81,27 @@ BasePtr AbsOf(const AnfNodePtr &node, bool ignore_fg_abs_tracking_id) {
|
|||
return node_abs;
|
||||
}
|
||||
|
||||
// For a single function graph (fg), this function groups nodes based on their computed hash values
|
||||
// and then attempts to replace duplicate nodes within these groups.
|
||||
// @param fg: The target function graph to process.
|
||||
// @param manager: The manager for this function graph.
|
||||
// @return: Whether the function graph was changed.
|
||||
bool CSE::BuildOrderGroupAndDoReplaceForOneGraph(const FuncGraphPtr &fg, const FuncGraphManagerPtr &manager) const {
|
||||
MS_EXCEPTION_IF_NULL(fg);
|
||||
|
||||
// Lists to store ordering of groups, groupings of nodes based on hash values, and hash values for each node.
|
||||
std::vector<std::size_t> order_group;
|
||||
mindspore::HashMap<std::size_t, std::vector<AnfNodePtr>> groups;
|
||||
mindspore::HashMap<AnfNodePtr, std::size_t> hashes;
|
||||
|
||||
// Topologically sort the nodes in the function graph starting from the return node.
|
||||
std::vector<AnfNodePtr> toposet = TopoSort(fg->get_return());
|
||||
|
||||
// Compute the hash value for each node and group nodes based on these values.
|
||||
for (auto node : toposet) {
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
|
||||
// Skip nodes that have already been hashed.
|
||||
if (hashes.find(node) != hashes.end()) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -99,21 +111,26 @@ bool CSE::BuildOrderGroupAndDoReplaceForOneGraph(const FuncGraphPtr &fg, const F
|
|||
ValueNodePtr value_node = node->cast<ValueNodePtr>();
|
||||
auto value = value_node->value();
|
||||
MS_EXCEPTION_IF_NULL(value);
|
||||
// Combine the hash of the node's value with its abstract hash.
|
||||
h = hash_combine(value->hash(), (AbsOf(value_node, true)->hash()));
|
||||
} else if (node->isa<CNode>()) {
|
||||
auto cnode = node->cast<CNodePtr>();
|
||||
auto &inputs = cnode->inputs();
|
||||
size_t init = 0;
|
||||
// Combine the hash values of all inputs to the compute node.
|
||||
h = std::accumulate(inputs.begin(), inputs.end(), init, [&hashes](std::size_t hash, const AnfNodePtr &node_in) {
|
||||
return hash_combine(hash, hashes[node_in]);
|
||||
});
|
||||
} else if (node->isa<Parameter>()) {
|
||||
// For parameter nodes, use the node's hash.
|
||||
h = node->hash();
|
||||
} else {
|
||||
MS_LOG(ERROR) << "Unknown node type";
|
||||
}
|
||||
|
||||
hashes[node] = h;
|
||||
|
||||
// Group the node based on its hash value.
|
||||
if (groups.find(h) == groups.end()) {
|
||||
std::vector<AnfNodePtr> innervec({node});
|
||||
groups[h] = innervec;
|
||||
|
|
@ -122,94 +139,58 @@ bool CSE::BuildOrderGroupAndDoReplaceForOneGraph(const FuncGraphPtr &fg, const F
|
|||
groups[h].push_back(node);
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to replace nodes within each group.
|
||||
return DoReplace(manager, order_group, &groups);
|
||||
}
|
||||
|
||||
// This function applies the BuildOrderGroupAndDoReplaceForOneGraph operation for all the function graphs
|
||||
// managed by the given manager.
|
||||
// @param manager: The manager for all function graphs to process.
|
||||
// @return: Whether any of the function graphs were changed.
|
||||
bool CSE::BuildOrderGroupAndDoReplace(const FuncGraphManagerPtr manager) const {
|
||||
bool changed = false;
|
||||
|
||||
// Iterate over all function graphs managed by the manager.
|
||||
for (FuncGraphPtr fg : manager->func_graphs()) {
|
||||
// Attempt to replace nodes for the current function graph and update the 'changed' status.
|
||||
changed = BuildOrderGroupAndDoReplaceForOneGraph(fg, manager) || changed;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool CSE::HasHiddenSideEffect(const AnfNodePtr &node) {
|
||||
auto prim = GetCNodePrimitive(node);
|
||||
if (prim == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return prim->HasAttr(GRAPH_FLAG_SIDE_EFFECT_HIDDEN);
|
||||
}
|
||||
|
||||
bool CSE::CheckReplace(const AnfNodePtr &main, const AnfNodePtr &node) const {
|
||||
MS_EXCEPTION_IF_NULL(main);
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
if (main->isa<ValueNode>() && node->isa<ValueNode>()) {
|
||||
auto main_value = GetValueNode(main);
|
||||
auto node_value = GetValueNode(node);
|
||||
return (AbsOf(main, true) == AbsOf(node, true)) && (*main_value == *node_value);
|
||||
} else if (main->isa<CNode>() && node->isa<CNode>()) {
|
||||
auto c_main = main->cast<CNodePtr>();
|
||||
auto c_node = node->cast<CNodePtr>();
|
||||
// Not do cse for the node set recompute before the recompute pass.
|
||||
if (IsSetRecomputed(c_main, c_node)) {
|
||||
return false;
|
||||
}
|
||||
const auto &inputs1 = c_main->inputs();
|
||||
const auto &inputs2 = c_node->inputs();
|
||||
if (inputs1.size() != inputs2.size()) {
|
||||
return false;
|
||||
}
|
||||
// Check inputs, all inputs should equal.
|
||||
for (size_t i = 0; i < inputs1.size(); i++) {
|
||||
auto &input1 = inputs1[i];
|
||||
auto &input2 = inputs2[i];
|
||||
MS_EXCEPTION_IF_NULL(input1);
|
||||
MS_EXCEPTION_IF_NULL(input2);
|
||||
if ((input1 == input2) || (*input1 == *input2)) {
|
||||
continue;
|
||||
}
|
||||
// Handle the case of two different Tensor, but with the same value.
|
||||
if (IsValueNode<tensor::Tensor>(input1) && IsValueNode<tensor::Tensor>(input2)) {
|
||||
auto tensor1 = GetValueNode<tensor::TensorPtr>(input1);
|
||||
auto tensor2 = GetValueNode<tensor::TensorPtr>(input2);
|
||||
if (tensor1->ValueEqual(*tensor2)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// We don't merge primitive cnodes with random effect.
|
||||
return !HasHiddenSideEffect(c_main);
|
||||
}
|
||||
// a parameter node.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Given a list of node groups, this function attempts to replace duplicate nodes within each group.
|
||||
// Nodes are considered duplicates if they compute the same values.
|
||||
// @param manager: The manager for the function graph.
|
||||
// @param order_group: The ordered list of node groups to process.
|
||||
// @param groups: The mapping of group hashes to lists of nodes.
|
||||
// @return: Whether any nodes were replaced.
|
||||
bool CSE::DoReplace(const FuncGraphManagerPtr manager, const std::vector<std::size_t> &order_group,
|
||||
mindspore::HashMap<std::size_t, std::vector<AnfNodePtr>> *groups) const {
|
||||
bool changes = false;
|
||||
std::set<size_t> clear_set;
|
||||
|
||||
// Iterate over each group.
|
||||
for (auto &h : order_group) {
|
||||
std::vector<AnfNodePtr> &group = (*groups)[h];
|
||||
// If there are more than 2 node in that group, they may be same common expression can be eliminated.
|
||||
|
||||
// If there are more than 1 node in the group, they might represent the same computation.
|
||||
if (group.size() > 1) {
|
||||
// Check each node against every other node in the group.
|
||||
for (size_t k = 0; k < group.size() - 1; k++) {
|
||||
AnfNodePtr main = group[k];
|
||||
MS_EXCEPTION_IF_NULL(main);
|
||||
|
||||
// When all node in group has been replaced
|
||||
// or a valuenode node, skip compare in group
|
||||
// Skip nodes that have already been replaced or are value nodes.
|
||||
if ((k + 1 + clear_set.size() == group.size()) || (k > 0 && main->isa<ValueNode>())) {
|
||||
break;
|
||||
}
|
||||
|
||||
// skip node has been replaced
|
||||
if (clear_set.find(k) != clear_set.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compare with rest elements in this group.
|
||||
for (size_t i = k + 1; i < group.size(); i++) {
|
||||
auto node = group[i];
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
|
|
@ -217,17 +198,24 @@ bool CSE::DoReplace(const FuncGraphManagerPtr manager, const std::vector<std::si
|
|||
if (clear_set.find(i) != clear_set.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Nodes must belong to the same function graph.
|
||||
if (main->func_graph() != node->func_graph()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the nodes are equivalent.
|
||||
if (CheckReplace(node, main)) {
|
||||
changes = true;
|
||||
// Optional: Update debug info or dump flags.
|
||||
UpdateDebugInfoAndDumpFlag(main, node);
|
||||
// Replace the node.
|
||||
(void)manager->Replace(node, main);
|
||||
(void)clear_set.insert(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clear_set.clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -235,6 +223,7 @@ bool CSE::DoReplace(const FuncGraphManagerPtr manager, const std::vector<std::si
|
|||
return changes;
|
||||
}
|
||||
|
||||
|
||||
bool CSE::Cse(const FuncGraphPtr root, const FuncGraphManagerPtr manager) const {
|
||||
MS_EXCEPTION_IF_NULL(manager);
|
||||
manager->AddFuncGraph(root);
|
||||
|
|
|
|||
Loading…
Reference in New Issue