我想打PAC队的第一次评注 #16

Open
zbtrs2 wants to merge 43 commits from ssk015/mindspore2022:master into master
14 changed files with 658 additions and 46 deletions
Showing only changes of commit bcff35b77d - Show all commits

View File

@ -126,6 +126,10 @@ bool ConstInputToAttrInfoRegistry::GetRegisterByOpName(const std::string &op_nam
return false;
}
/*
* @brief If the input of cnode is a const tensor, and the index of input is in the input_attrs,
* then set the const tensor to attr of cnode.
*/
void ConstInputToAttr(const CNodePtr &cnode, const mindspore::HashSet<size_t> &input_attrs) {
MS_EXCEPTION_IF_NULL(cnode);
std::vector<AnfNodePtr> new_inputs;
@ -148,6 +152,7 @@ void ConstInputToAttr(const CNodePtr &cnode, const mindspore::HashSet<size_t> &i
input_node = AnfUtils::VisitKernel(input_node, 0).first;
}
if (input_attrs.find(i) != input_attrs.end() && input_node->isa<ValueNode>() && !HasAbstractMonad(input_node)) {
// set const input to primitive attr and erase original const input
auto value_node = input_node->cast<ValueNodePtr>();
MS_EXCEPTION_IF_NULL(value_node);
MS_LOG(DEBUG) << "start erase input[" << i << "] of cnode[" + cnode->DebugString() + "]";

View File

@ -45,12 +45,15 @@ bool AdjustDependForParallelOptimizerRecomputeAllGather::Run(const FuncGraphPtr
if (std::find(parallel_optimizer_recompute_allgather_fusion_ids.begin(),
parallel_optimizer_recompute_allgather_fusion_ids.end(),
fusion_id) == parallel_optimizer_recompute_allgather_fusion_ids.end()) {
// If the fusion id is not in the vector, it means that the allgather node is the first allgather node in the
// fusion group. Leave the fusion group alone till another allgather node in the same fusion group is found.
parallel_optimizer_recompute_allgather_fusion_ids.push_back(fusion_id);
if (recompute_min_fusion_id == 0 || fusion_id < recompute_min_fusion_id) {
recompute_min_fusion_id = fusion_id;
}
parallel_optimizer_recompute_first_fusion_allgathers.push_back(node);
} else {
// Now here's another allgather node in the same fusion group. Handle it.
parallel_optimizer_recompute_allgathers.push_back(node);
}
} else {
@ -73,6 +76,14 @@ bool AdjustDependForParallelOptimizerRecomputeAllGather::Run(const FuncGraphPtr
return AdjustAllgatherDepend(graph, parallel_optimizer_recompute_allgathers);
}
/*
* @brief Increase the fusion id of allgather nodes which need recomputing.
* This step is necessary because we need to separate the nodes that require recomputation from those that don't.
*
* @param parallel_optimizer_recompute_allgathers The allgather nodes which need to be recomputed.
* @param parallel_optimizer_recompute_first_fusion_allgathers The first allgather nodes which need to be recomputed.
*/
void AdjustDependForParallelOptimizerRecomputeAllGather::IncreaseAllgatherFusionId(
const std::vector<AnfNodePtr> &parallel_optimizer_recompute_allgathers,
const std::vector<AnfNodePtr> &parallel_optimizer_recompute_first_fusion_allgathers,
@ -95,6 +106,13 @@ void AdjustDependForParallelOptimizerRecomputeAllGather::IncreaseAllgatherFusion
}
}
/*
* @brief Adjust depend node of recompute allgather nodes.
* It inserts a depend node between the allgather node and the nodes which will be gathered.
*
* @param graph The graph which need to be adjusted.
* @param parallel_optimizer_recompute_allgathers The allgather nodes which need to be recomputed.
*/
bool AdjustDependForParallelOptimizerRecomputeAllGather::AdjustAllgatherDepend(
const FuncGraphPtr &graph, const std::vector<AnfNodePtr> &parallel_optimizer_recompute_allgathers) {
FuncGraphManagerPtr manager = graph->manager();
@ -103,6 +121,7 @@ bool AdjustDependForParallelOptimizerRecomputeAllGather::AdjustAllgatherDepend(
auto cnode = node->cast<CNodePtr>();
auto depend_node = common::AnfAlgo::GetInputNode(cnode, 0);
if (IsPrimitiveCNode(depend_node, prim::kPrimDepend)) {
// depend node is a "depend" primitive
auto depend_cnode = depend_node->cast<CNodePtr>();
AnfNodeIndexSet allgather_node_set = manager->node_users()[cnode];
for (auto &node_pair : allgather_node_set) {
@ -121,6 +140,7 @@ bool AdjustDependForParallelOptimizerRecomputeAllGather::AdjustAllgatherDepend(
}
} else if (IsPrimitiveCNode(depend_node, prim::kPrimCast) &&
IsPrimitiveCNode(common::AnfAlgo::GetInputNode(depend_node->cast<CNodePtr>(), 0), prim::kPrimDepend)) {
// depend node is a "cast" primitive followed by a "depend" primitive
auto cast_cnode = depend_node->cast<CNodePtr>();
auto cast_depend_node = common::AnfAlgo::GetInputNode(cast_cnode, 0);
auto cast_depend_cnode = cast_depend_node->cast<CNodePtr>();

View File

@ -32,6 +32,12 @@ namespace opt {
namespace {
using KernelWithIndex = std::pair<AnfNodePtr, int64_t>;
/**
* @brief Check whether to ignore the current node during CSE process.
*
* @param node The node to check.
* @return true if the node should be ignored, false otherwise.
*/
bool CheckIgnoreCase(const AnfNodePtr &node) {
MS_EXCEPTION_IF_NULL(node);
if (common::AnfAlgo::GetCNodeName(node) != kTransDataOpName) {
@ -51,6 +57,12 @@ bool CheckIgnoreCase(const AnfNodePtr &node) {
return need_ignore;
}
/*
* @brief Eliminate duplicated "tuple_getitem" nodes.
*
* @param graph The graph to be processed.
* @param manager The manager of the graph.
*/
void EliminateDuplicatedTupleGetItem(const FuncGraphPtr &graph, const FuncGraphManagerPtr &manager) {
MS_EXCEPTION_IF_NULL(graph);
MS_EXCEPTION_IF_NULL(manager);
@ -85,6 +97,13 @@ void EliminateDuplicatedTupleGetItem(const FuncGraphPtr &graph, const FuncGraphM
}
} // namespace
/**
* @brief Compare KernelBuildInfo between two nodes.
*
* @param main The primary node for comparison.
* @param node The node to compare with the primary node.
* @return true if the KernelBuildInfo are equal, false otherwise.
*/
bool BackendCSE::CheckEqualKernelBuildInfo(const AnfNodePtr &main, const AnfNodePtr &node) const {
MS_EXCEPTION_IF_NULL(main);
MS_EXCEPTION_IF_NULL(node);
@ -105,6 +124,13 @@ bool BackendCSE::CheckEqualKernelBuildInfo(const AnfNodePtr &main, const AnfNode
return false;
}
/**
* @brief Compare if the inputs of two CNodes are equal.
*
* @param main The primary CNode for comparison.
* @param node The CNode to compare with the primary CNode.
* @return true if inputs are equal, false otherwise.
*/
bool BackendCSE::CheckEqualCnodeInputs(const AnfNodePtr &main, const AnfNodePtr &node) const {
auto c_main = main->cast<CNodePtr>();
MS_EXCEPTION_IF_NULL(c_main);
@ -127,6 +153,13 @@ bool BackendCSE::CheckEqualCnodeInputs(const AnfNodePtr &main, const AnfNodePtr
return true;
}
/**
* @brief Compare if two ValueNodes are equal.
*
* @param main The primary ValueNode for comparison.
* @param node The ValueNode to compare with the primary ValueNode.
* @return true if the ValueNodes are equal, false otherwise.
*/
bool BackendCSE::CheckValueNode(const ValueNodePtr &main, const ValueNodePtr &node) const {
MS_EXCEPTION_IF_NULL(main);
MS_EXCEPTION_IF_NULL(node);
@ -143,6 +176,13 @@ bool BackendCSE::CheckValueNode(const ValueNodePtr &main, const ValueNodePtr &no
return (AbsOf(main) == AbsOf(node)) && (*main_value == *node_value);
}
/**
* @brief Check if two CNodes can be considered equal for the CSE process.
*
* @param main The primary CNode for comparison.
* @param node The CNode to compare with the primary CNode.
* @return true if the CNodes are equal, false otherwise.
*/
bool BackendCSE::CheckCNode(const CNodePtr &main, const CNodePtr &node) const {
MS_EXCEPTION_IF_NULL(main);
MS_EXCEPTION_IF_NULL(node);
@ -161,6 +201,13 @@ bool BackendCSE::CheckCNode(const CNodePtr &main, const CNodePtr &node) const {
return CheckEqualCnodeInputs(main, node);
}
/**
* @brief Verify if one node can be replaced by another in the CSE process.
*
* @param main The node which might replace the other node.
* @param node The node which might be replaced.
* @return true if the nodes can replace each other, false otherwise.
*/
bool BackendCSE::CheckReplace(const AnfNodePtr &main, const AnfNodePtr &node) const {
MS_EXCEPTION_IF_NULL(main);
MS_EXCEPTION_IF_NULL(node);
@ -180,6 +227,12 @@ bool BackendCSE::CheckReplace(const AnfNodePtr &main, const AnfNodePtr &node) co
return false;
}
/*
* @brief Perform CSE and tuple_getitem elimination for one graph.
*
* @param graph The graph to be processed.
* @param manager The manager of the graph.
*/
bool BackendCSE::Cse(const FuncGraphPtr graph, const FuncGraphManagerPtr manager) const {
MS_EXCEPTION_IF_NULL(manager);
auto ret = BuildOrderGroupAndDoReplaceForOneGraph(graph, manager);
@ -189,6 +242,13 @@ bool BackendCSE::Cse(const FuncGraphPtr graph, const FuncGraphManagerPtr manager
return ret;
}
/**
* @brief Execute common subexpression elimination (CSE) on a functional graph.
* Implementations are in BackendCSE.
*
* @param func_graph The functional graph to process.
* @return true if the CSE operation was successful, false otherwise.
*/
bool CommonSubexpressionElimination::Run(const FuncGraphPtr &func_graph) {
MS_EXCEPTION_IF_NULL(func_graph);
auto backend_cse = std::make_shared<BackendCSE>();

View File

@ -24,6 +24,11 @@
namespace mindspore {
namespace opt {
/*
* @brief Convert const input of cnode to attr.
*
* @param node The node which has const input.
*/
const AnfNodePtr ConvertConstInputToAttr::Process(const FuncGraphPtr &, const AnfNodePtr &node,
const EquivPtr &) const {
if (node == nullptr || !AnfUtils::IsRealCNodeKernel(node)) {

View File

@ -27,6 +27,8 @@
namespace mindspore {
namespace opt {
namespace {
// get the real previous cnode; skipping virtual nodes
CNodePtr GetRealPrevCNode(const AnfNodePtr &node, size_t index, std::vector<KernelWithIndex> *pass_vector) {
MS_EXCEPTION_IF_NULL(pass_vector);
if (node == nullptr || !node->isa<CNode>()) {
@ -79,6 +81,7 @@ bool TransDataOpEliminateCondition(const CNodePtr &node1, const CNodePtr &node2)
}
} // namespace
// eliminate redundant op when matching conditions
const AnfNodePtr EliminateRedundantOp::ProcessMatchedNodes(const FuncGraphPtr &func_graph, const CNodePtr &cnode,
const CNodePtr &prev_cnode,
std::vector<KernelWithIndex> *pass_vector) const {
@ -158,7 +161,7 @@ const AnfNodePtr EliminateRedundantOp::DoEliminate(const FuncGraphPtr &func_grap
if (name2 != it->second.first) {
return nullptr;
}
// match condition
// match eliminate condition
auto condition_func = it->second.second;
if (condition_func == nullptr) {
return nullptr;
@ -167,6 +170,7 @@ const AnfNodePtr EliminateRedundantOp::DoEliminate(const FuncGraphPtr &func_grap
return nullptr;
}
// perform elimination
return ProcessMatchedNodes(func_graph, cnode, prev_cnode, &pass_vector);
}

View File

@ -184,6 +184,7 @@ std::vector<tensor::TensorPtr> GetTensorWithoutValueMask(const OpRunInfo &op_run
MS_LOG(EXCEPTION) << "Input tensors size " << input_tensors.size() << " should be equal to tensors mask size "
<< tensors_mask.size();
}
// traverse the tensors_mark to get all tensors without value nodes.
for (size_t index = 0; index < tensors_mask.size(); ++index) {
if (tensors_mask.at(index) != kValueNodeTensorMask) {
(void)tensors_without_value_node.emplace_back(input_tensors.at(index));
@ -191,22 +192,38 @@ std::vector<tensor::TensorPtr> GetTensorWithoutValueMask(const OpRunInfo &op_run
}
return tensors_without_value_node;
}
/**
* @brief Pushes input tensors(such as return variable in previous function) into a vector.
*
* This function takes an input argument `arg` and appends corresponding tensors to the `inputs` vector.
* It handles various input types including tensor pointers, CSR tensors, value tuples, scalars, monads,
* PyObjectRef, and VectorRefPtr.
*
* @param arg The input argument to be pushed.
* @param inputs A pointer to the vector where input tensors will be appended.
*/
void PushInputTensor(const BaseRef &arg, std::vector<tensor::TensorPtr> *inputs) {
MS_EXCEPTION_IF_NULL(inputs);
// Handle tensor pointer
if (utils::isa<tensor::TensorPtr>(arg)) {
auto value = utils::cast<tensor::TensorPtr>(arg);
inputs->push_back(value);
} else if (utils::isa<tensor::CSRTensorPtr>(arg)) {
}
// Handle CSR tensor
else if (utils::isa<tensor::CSRTensorPtr>(arg)) {
auto csr = utils::cast<tensor::CSRTensorPtr>(arg);
MS_EXCEPTION_IF_NULL(csr);
auto csr_values = csr->GetValues();
MS_EXCEPTION_IF_NULL(csr_values);
inputs->push_back(csr_values);
MS_LOG(INFO) << "For CSRTensor, push its values.";
} else if (utils::isa<ValuePtr>(arg)) {
}
// Handle ValuePtr (including ValueTuple, Scalar, and Monad)
else if (utils::isa<ValuePtr>(arg)) {
auto value = utils::cast<ValuePtr>(arg);
MS_EXCEPTION_IF_NULL(value);
if (value->isa<ValueTuple>()) {
auto value_tuple = value->cast<ValueTuplePtr>();
MS_EXCEPTION_IF_NULL(value_tuple);
@ -222,19 +239,26 @@ void PushInputTensor(const BaseRef &arg, std::vector<tensor::TensorPtr> *inputs)
} else {
inputs->push_back(value->cast<tensor::TensorPtr>());
}
} else if (utils::isa<PyObjectRef>(arg)) {
}
// Handle PyObjectRef
else if (utils::isa<PyObjectRef>(arg)) {
auto value = utils::cast<PyObjectRef>(arg).object_;
inputs->push_back(py::cast<tensor::TensorPtr>(value));
} else if (utils::isa<VectorRefPtr>(arg)) {
}
// Handle VectorRefPtr
else if (utils::isa<VectorRefPtr>(arg)) {
const auto &args_new = utils::cast<VectorRef>(arg);
for (const auto &v : args_new) {
PushInputTensor(v, inputs);
}
} else {
}
// Handle unsupported input types
else {
MS_LOG(WARNING) << "Invalid input type.";
}
}
// Insert the front_node related tensor in the input_tensor.
void PushTensor(const VectorRef &args, const std::vector<AnfNodePtr> &parameters, const AnfNodePtr &front_node,
std::vector<tensor::TensorPtr> *input_tensor) {
@ -247,18 +271,43 @@ void PushTensor(const VectorRef &args, const std::vector<AnfNodePtr> &parameters
PushInputTensor(args[position], input_tensor);
}
/**
* @brief Updates the output abstract information in the OpRunInfo structure based on the given KernelGraph.
*
* This function iterates through the execution order of the given KernelGraph and updates the abstract information
* in the provided OpRunInfo structure for the specified operation.
*
* @param kernel_graph The KernelGraph representing the computation graph.
* @param op_run_info A pointer to the OpRunInfo structure to be updated.
*/
void UpdateOutputAbstract(const KernelGraphPtr &kernel_graph, OpRunInfo *op_run_info) {
MS_EXCEPTION_IF_NULL(kernel_graph);
MS_EXCEPTION_IF_NULL(op_run_info);
// Retrieve the list of kernels in the execution order of the KernelGraph
const auto &kernels = kernel_graph->execution_order();
// Iterate through the kernels and update the output abstract information
for (const auto &kernel : kernels) {
MS_EXCEPTION_IF_NULL(kernel);
// Check if the CNode name matches the target operation name
if (common::AnfAlgo::GetCNodeName(kernel) == op_run_info->op_name) {
// Update the abstract information in the OpRunInfo structure
op_run_info->abstract = kernel->abstract();
}
}
}
/**
* @brief Creates an output tensor for a given AnfNode and output index.
*
* This function creates an output tensor for the specified AnfNode and output index. The tensor is initialized with
* the inferred data type and shape of the output, and is associated with the corresponding device tensor.
*
* @param output_node The AnfNode representing the output.
* @param output_index The index of the output in the node.
* @return A pointer to the created output tensor.
*/
TensorPtr CreateOutputTensor(const AnfNodePtr &output_node, size_t output_index) {
MS_EXCEPTION_IF_NULL(output_node);
// Create host tensor, the output tensor should use the infer type, it will be handed correctly by tensor data sync
@ -478,28 +527,44 @@ MindRTBackend::MindRTBackend(const std::string &backend_name, const std::string
runtime::GraphScheduler::GetInstance().Initialize();
}
/**
* Compiles graphs and returns information about the generated actors.
* @param func_graph The function graph to be compiled.
* @return Information about the generated actors.
*/
const ActorInfo &MindRTBackend::CompileGraphs(const FuncGraphPtr &func_graph) {
// Check for null pointers
MS_EXCEPTION_IF_NULL(graph_compiler_);
MS_EXCEPTION_IF_NULL(func_graph);
// Log start of function graph compilation
MS_LOG(INFO) << "Status record: start compile function graph: " << func_graph->ToString();
// Start profiling timer
PROF_START(compile_func_graph);
// Wrap the input function graph to create a preprocessed root graph, which contains funcs each graph has(return a funcptr)
auto root_graph = WrapPrimitives(func_graph);
MS_EXCEPTION_IF_NULL(root_graph);
root_graph_ = root_graph.get();
// Register a summary callback function, which is called in the final stages of summary.
// Register a callback function for summary saving
graph_compiler_->RegisterSummaryCallBackFunc(callbacks::SummarySaveCallback);
// Get execution mode from context
auto context_ptr = MsContext::GetInstance();
MS_EXCEPTION_IF_NULL(context_ptr);
// For debug log
ms_execution_mode_ = context_ptr->get_param<int>(MS_CTX_EXECUTION_MODE);
real_execution_mode_ = ms_execution_mode_;
// Compile root graph.
// Compile the root graph
graph_id_to_device_context_.clear();
func_graph_to_kernel_graph_ids_.clear();
control_nodes_.clear();
auto subgraph_need_compile = CompileGraph(root_graph);
// Compile sub graphs.
// Compile sub graphs if needed
if (subgraph_need_compile) {
MS_EXCEPTION_IF_NULL(root_graph->manager());
FuncGraphSet sub_graphs = root_graph->manager()->func_graphs();
@ -510,27 +575,41 @@ const ActorInfo &MindRTBackend::CompileGraphs(const FuncGraphPtr &func_graph) {
}
}
// Construct the graph compiler info.
// Construct graph compiler info
auto graph_compiler_info = ConstructGraphCompilerInfo(root_graph);
MS_EXCEPTION_IF_NULL(graph_compiler_info);
// If in kgraph mode and there are compiled graphs, transform and schedule actor DAG
if (real_execution_mode_ == kGraphMode && graph_compiler_info->graphs_.size() != 0) {
// Transform graph to actor DAG, and schedule the actor DAG.
const auto &actor_set = runtime::GraphScheduler::GetInstance().Transform(*graph_compiler_info);
runtime::GraphScheduler::GetInstance().Schedule(actor_set);
}
// Retrieve actor information
const ActorInfo &actor_info = graph_compiler_info->name_;
// Store graph compiler info
(void)actor_to_graph_compiler_info_.emplace(graph_compiler_info->name_, std::move(graph_compiler_info));
// End profiling timer
PROF_END(compile_func_graph);
// Reset execution mode if necessary
if (ms_execution_mode_ != real_execution_mode_) {
context_ptr->set_param<int>(MS_CTX_EXECUTION_MODE, ms_execution_mode_);
}
// Log end of function graph compilation and actor information
MS_LOG(INFO) << "Status record: end compile function graph: " << func_graph->ToString()
<< ", produce actor: " << actor_info;
return actor_info;
}
/**
* Compiles the given function graph or its segments, performing necessary partitioning and compilation.
* @param func_graph The function graph or a segment of it to be compiled.
* @return True if the graph was split into segments and compiled separately, false if compiled as a whole.
*/
bool MindRTBackend::CompileGraph(const FuncGraphPtr &func_graph) {
MS_EXCEPTION_IF_NULL(func_graph);
MS_EXCEPTION_IF_NULL(graph_partition_);
@ -559,6 +638,10 @@ bool MindRTBackend::CompileGraph(const FuncGraphPtr &func_graph) {
return true;
}
/**
* Compiles a specific graph segment, which can be a normal segment or a cut node segment.
* @param segment The graph segment to be compiled.
*/
void MindRTBackend::CompileGraph(const GraphSegmentPtr &segment) {
MS_EXCEPTION_IF_NULL(segment);
// Compile the normal nodes, which doesn't contain the cut node.
@ -613,6 +696,22 @@ void MindRTBackend::CompileGraph(const GraphSegmentPtr &segment) {
}
namespace {
/**
* @brief Retrieves input information for the control operation by processing both front and backend CNodes.
*
* This function processes front and backend CNodes of a control operation, extracts the input information, and
* constructs an argument list (args) containing the processed inputs. It also updates the InputTensorInfo structure
* based on the input information.
*
* @param graph_compiler The shared pointer to the GraphCompiler instance.
* @param front_cnode The front-end CNode of the control operation.
* @param backend_cnode The backend CNode of the control operation.
* @param op_output_map The map of KernelWithIndex to tensor::TensorPtr representing operation outputs.
* @param parameter_index The map of AnfNodePtr to index representing parameter nodes.
* @param graph_inputs The vector of tensor::TensorPtr representing graph inputs.
* @param input_tensor_info A pointer to the InputTensorInfo structure to be updated.
* @param args A pointer to the VectorRef where the processed input arguments will be stored.
*/
void GetControlOpInput(const std::shared_ptr<GraphCompiler> &graph_compiler, const CNodePtr &front_cnode,
const CNodePtr &backend_cnode, const std::map<KernelWithIndex, tensor::TensorPtr> &op_output_map,
const std::map<AnfNodePtr, size_t> &parameter_index,
@ -622,20 +721,27 @@ void GetControlOpInput(const std::shared_ptr<GraphCompiler> &graph_compiler, con
MS_EXCEPTION_IF_NULL(backend_cnode);
MS_EXCEPTION_IF_NULL(graph_compiler);
MS_EXCEPTION_IF_NULL(args);
size_t front_index = 0; // Point to front end cnode
size_t back_index = 0; // Point to backend end cnode
size_t args_tuple_num = 0; // Record the input num of maketuple cnode
std::vector<ValuePtr> args_tuple;
// Initialize indices and counters
size_t front_index = 0; // Index pointing to front-end CNode
size_t back_index = 0; // Index pointing to backend CNode
size_t args_tuple_num = 0; // Number of inputs in maketuple CNode
std::vector<ValuePtr> args_tuple; // Temporary storage for inputs in maketuple CNode
auto front_size = front_cnode->inputs().size();
auto back_size = backend_cnode->inputs().size();
// Loop through the inputs of front and backend CNodes
while (front_index + 1 < front_size && back_index + 1 < back_size) {
AnfNodePtr input_node = nullptr;
if (args_tuple_num) {
input_node = backend_cnode->input(back_index + 1);
} else {
input_node = front_cnode->input(front_index + 1);
// Check if the input node is a primitive make tuple node
if (IsPrimitiveCNode(input_node, prim::kPrimMakeTuple)) {
// Hook multi-input or multi-output.
MS_LOG(DEBUG) << "The input node of hook op: " << input_node->DebugString() << " is a make tuple node.";
auto make_tuple = input_node->cast<CNodePtr>();
MS_EXCEPTION_IF_NULL(make_tuple);
@ -643,13 +749,16 @@ void GetControlOpInput(const std::shared_ptr<GraphCompiler> &graph_compiler, con
continue;
}
}
// Hook single-input or single-output.
// Extract the real input node (single-input or single-output)
auto real_input = common::AnfAlgo::VisitKernel(input_node, 0).first;
MS_EXCEPTION_IF_NULL(real_input);
ValuePtr value = nullptr;
if (!real_input->isa<ValueNode>()) {
value = graph_compiler->GetSingleOpInputTensorByIndex(backend_cnode, op_output_map, parameter_index, graph_inputs,
input_tensor_info, back_index);
// Process backend input node that is not a ValueNode
value = graph_compiler->GetSingleOpInputTensorByIndex(backend_cnode, op_output_map, parameter_index,
graph_inputs, input_tensor_info, back_index);
MS_EXCEPTION_IF_NULL(value);
++back_index;
} else {
@ -657,7 +766,9 @@ void GetControlOpInput(const std::shared_ptr<GraphCompiler> &graph_compiler, con
MS_EXCEPTION_IF_NULL(value_node);
value = value_node->value();
MS_EXCEPTION_IF_NULL(value);
if (value->isa<ValueSequence>()) {
// Process ValueSequence input (multi-input or multi-output)
const auto &value_sequeue = value->cast<ValueSequencePtr>();
MS_EXCEPTION_IF_NULL(value_sequeue);
back_index += value_sequeue->size();
@ -665,14 +776,19 @@ void GetControlOpInput(const std::shared_ptr<GraphCompiler> &graph_compiler, con
++back_index;
}
}
// Add value to temporary args_tuple if processing maketuple input
if (args_tuple_num) {
args_tuple.emplace_back(value);
if (args_tuple.size() == args_tuple_num) {
value = std::make_shared<ValueTuple>(args_tuple);
args_tuple_num = 0;
args_tuple.clear();
}
}
// Add value to args if not processing maketuple input
if (!args_tuple_num) {
args->emplace_back(value);
front_index++;
@ -680,35 +796,54 @@ void GetControlOpInput(const std::shared_ptr<GraphCompiler> &graph_compiler, con
}
}
/**
* @brief Converts a PyObject to a tensor::TensorPtr and adds it to the provided vector of tensors.
*
* This function converts a PyObject to a tensor::TensorPtr and appends it to the provided vector of tensors. It
* supports various data types, including tensor::Tensor, py::float_, py::int_, py::list, and py::tuple.
*
* @param input_object The input PyObject to be converted.
* @param tensors A pointer to the vector of tensor::TensorPtr where the converted tensor will be added.
*/
void ConvertPyObjectToTensor(const py::object &input_object, std::vector<tensor::TensorPtr> *tensors) {
MS_EXCEPTION_IF_NULL(tensors);
tensor::TensorPtr tensor_ptr = nullptr;
if (py::isinstance<tensor::Tensor>(input_object)) {
tensor_ptr = py::cast<tensor::TensorPtr>(input_object);
} else if (py::isinstance<py::float_>(input_object)) {
// Convert py::float_ to tensor::Tensor with kFloat32 data type
double input_value = py::cast<py::float_>(input_object);
tensor_ptr = std::make_shared<tensor::Tensor>(input_value, kFloat32);
} else if (py::isinstance<py::int_>(input_object)) {
// Convert py::int_ to tensor::Tensor with kInt64 data type
tensor_ptr = std::make_shared<tensor::Tensor>(py::cast<int64_t>(input_object), kInt64);
} else if (py::isinstance<py::list>(input_object)) {
// Convert py::list to vector of tensors recursively
auto list_inputs = py::cast<py::list>(input_object);
for (size_t i = 0; i < list_inputs.size(); ++i) {
ConvertPyObjectToTensor(list_inputs[i], tensors);
}
return;
} else if (py::isinstance<py::tuple>(input_object)) {
// Convert py::tuple to vector of tensors recursively
auto tuple_inputs = py::cast<py::tuple>(input_object);
for (size_t i = 0; i < tuple_inputs.size(); ++i) {
ConvertPyObjectToTensor(tuple_inputs[i], tensors);
}
return;
} else {
// Unsupported data type
MS_EXCEPTION(TypeError) << "Unreasonable data type: " << input_object.get_type() << ".";
}
// Add the converted tensor to the vector
MS_EXCEPTION_IF_NULL(tensor_ptr);
(void)tensors->emplace_back(tensor_ptr);
}
void RunControlOperator(const std::shared_ptr<GraphCompiler> &graph_compiler, const KernelGraphPtr &graph,
const CNodePtr &kernel, const std::map<KernelWithIndex, tensor::TensorPtr> &op_output_map,
const std::map<AnfNodePtr, size_t> &parameter_index,
@ -778,14 +913,29 @@ void TensorValueToVector(const ValuePtr &value, VectorRef *outputs) {
}
}
/**
* Check if the graph's output is a constant (ValueNode) or a parameter. If it is, there is no need to execute the graph.
*
* @param graph_output The output node of the computation graph.
* @param args Input arguments for the computation graph.
* @param outputs Reference to the vector where the output results will be stored if the graph's output is a constant.
*
* @return True if the graph's output is a constant or a parameter, indicating no need to execute; False otherwise.
*/
bool IsGraphOutputValueNodeOrParameter(const AnfNodePtr &graph_output, const VectorRef &args, VectorRef *outputs) {
MS_EXCEPTION_IF_NULL(graph_output);
MS_EXCEPTION_IF_NULL(outputs);
if (graph_output->isa<ValueNode>()) {
// The graph's output is a constant. No need to execute.
MS_LOG(INFO) << "Graph's output is a constant. No need to execute.";
VectorRef output_tmp;
ValuePtr value = GetValueNode(graph_output);
// Convert the constant tensor value to a vector.
TensorValueToVector(value, &output_tmp);
if (output_tmp.size() == 1) {
*outputs = std::move(output_tmp);
} else if (output_tmp.size() > 1) {
@ -793,33 +943,43 @@ bool IsGraphOutputValueNodeOrParameter(const AnfNodePtr &graph_output, const Vec
} else {
MS_LOG(EXCEPTION) << "Output is empty!";
}
return true;
}
if (graph_output->isa<Parameter>()) {
// The graph's output is a parameter. If all parameters are inputs, no need to execute.
MS_LOG(INFO) << "Graph's output is a parameter. If all params are inputs, no need to execute.";
// Find the right parameter as ret_val.
auto func_graph = graph_output->func_graph();
MS_EXCEPTION_IF_NULL(func_graph);
auto params = func_graph->parameters();
if (args.size() != params.size()) {
MS_LOG(EXCEPTION) << "Input size " << args.size() << " not equal to graph input size " << params.size();
}
auto it = std::find(params.begin(), params.end(), graph_output);
if (it == params.end()) {
MS_EXCEPTION(UnknownError) << "When graph output is Parameter, it should be found in graph parameters";
}
size_t index = it - params.cbegin();
if (index >= args.size()) {
MS_EXCEPTION(UnknownError) << "Index " << index << " equal or larger than args size " << args.size();
}
outputs->emplace_back(args[index]);
return true;
}
return false;
}
} // namespace
void FlatValueTupleValue(const ValuePtrList &value, ValuePtrList *flatted_value) {
@ -862,119 +1022,190 @@ void FlattenValue(const BaseRef &arg, ValuePtrList *flatted_value) {
(void)flatted_value->emplace_back(value);
} else {
FlattenValue(value, flatted_value);
}
}
}
} else {
MS_LOG(EXCEPTION) << "The value input to flatten should only contains be sequence or dictionary, but it is "
<< arg.ToString();
}
}
/**
* Pushes a tensor from the `args` list into the `input_tensor` vector based on the `front_node` and index provided.
*
* @param args A list of input arguments.
* @param parameters A list of AnfNodePtr representing the parameters.
* @param front_node The front node representing the parameter in the subgraph.
* @param index The index of the tensor within the parameter.
* @param input_tensor A vector to store the input tensor.
*/
void PushTupleTensor(const VectorRef &args, const std::vector<AnfNodePtr> &parameters, const AnfNodePtr &front_node,
size_t index, std::vector<tensor::TensorPtr> *input_tensor) {
// Find the position of the `front_node` in the `parameters` list.
const auto &iter = std::find(parameters.begin(), parameters.end(), front_node);
const size_t position = iter - parameters.begin();
// If the parameter is not found in the parameters of the root graph, it means that it is the input of the subgraph,
// and there is no need to input a tensor.
// If the parameter is not found in the parameters of the root graph, it means it's an input of the subgraph.
// In this case, there's no need to input a tensor, so add a nullptr to the `input_tensor` vector.
if (position >= args.size()) {
MS_LOG(INFO) << "Position out of args range, position value is " << position << " and args size is " << args.size()
<< ".";
(void)input_tensor->emplace_back(nullptr);
return;
}
// Flatten the value of the argument at the specified position.
ValuePtrList flatted_value_tuple_value;
FlattenValue(args[position], &flatted_value_tuple_value);
// Check if the index is within the range of the flattened values.
if (index >= flatted_value_tuple_value.size()) {
MS_LOG(EXCEPTION) << "Index out of flatted_value_tuple_value range, index value is " << index
<< " and flatted_value_tuple_value size is " << flatted_value_tuple_value.size() << ".";
}
// Retrieve the tensor input and add it to the `input_tensor` vector.
auto input = flatted_value_tuple_value[index];
MS_EXCEPTION_IF_NULL(input);
auto tensor_input = input->cast<tensor::TensorPtr>();
input_tensor->push_back(tensor_input);
}
/**
* Execute the computation graph represented by a list of KernelGraphPtrs using a single operator approach.
*
* @param graphs A list of KernelGraphPtrs representing the computation graphs.
* @param inputs A list of input tensors for each computation graph.
* @param outputs A reference to the vector where the output results will be stored.
*/
void MindRTBackend::RunGraphBySingleOp(const std::vector<KernelGraphPtr> &graphs,
const std::vector<std::vector<tensor::TensorPtr>> &inputs, VectorRef *outputs) {
// Ensure that previous tasks are finished before starting execution.
WaitTaskFinish();
MS_EXCEPTION_IF_NULL(graph_compiler_);
auto &op_executor = runtime::OpExecutor::GetInstance();
// Register a batch build callback function.
op_executor.Register([this]() { BatchBuildCallback(); });
for (size_t graph_index = 0; graph_index < graphs.size(); ++graph_index) {
const auto &graph = graphs[graph_index];
MS_EXCEPTION_IF_NULL(graph);
// Initialize data structures for managing operator outputs.
std::map<KernelWithIndex, tensor::TensorPtr> op_output_map;
std::map<AnfNodePtr, size_t> parameter_index;
GraphOutputInfo graph_output_info;
graph_output_info.graph_outputs = outputs;
// Get parameter and output indexes for the current graph.
graph_compiler_->GetParamAndOutputIndex(graph, inputs[graph_index], outputs, &parameter_index,
&graph_output_info.output_indexes);
// Initialize a reference count map for CNodes in the current graph.
std::map<KernelWithIndex, size_t> cnode_ref_count;
auto iter = cnode_ref_counts_.find(graph->graph_id());
if (iter == cnode_ref_counts_.end()) {
// Calculate reference counts for CNodes in the graph if not already calculated.
graph_compiler_->CalculateRefCount(graph, &cnode_ref_count);
(void)cnode_ref_counts_.emplace(graph->graph_id(), cnode_ref_count);
} else {
cnode_ref_count = iter->second;
}
// Calculate forward operator output tensor IDs.
graph_compiler_->CalculateForwardOpOutputCount(graph, inputs[graph_index], &forward_op_output_tensor_id_);
for (const auto &kernel : graph->execution_order()) {
InputTensorInfo input_tensor_info;
VectorRef op_outputs;
if (!common::AnfAlgo::IsControlOpExecInBackend(kernel)) {
OpRunInfo op_run_info;
GraphInfo graph_info;
// Get input tensors, operator run info, and graph info for the current kernel.
graph_compiler_->GetSingleOpInputTensors(kernel, op_output_map, parameter_index, inputs[graph_index],
&input_tensor_info);
graph_compiler_->GetSingleOpRunInfoAndGraphInfo(kernel, input_tensor_info, &op_run_info, &graph_info,
&graph_output_info);
// Run the operator and store the outputs.
RunOp(&op_run_info, &op_outputs);
} else {
// If it's a control operator, wait for previous tasks to finish before execution.
WaitTaskFinish();
// Run the control operator and update op outputs.
RunControlOperator(graph_compiler_, graph, kernel, op_output_map, parameter_index, inputs[graph_index],
&input_tensor_info, &op_outputs);
// Execute remaining lazy tasks before PyNative hook exit.
// Execute any remaining lazy tasks before exiting the PyNative hook.
WaitTaskFinish();
}
// Update the reference count for input kernel and manage operator outputs.
graph_compiler_->UpdateRefCount(input_tensor_info.input_kernel, &cnode_ref_count, &op_output_map);
// Recover graph outputs based on operator outputs and reference counts.
graph_output_info.graph_output_tensors.clear();
graph_compiler_->RecoverGraphOutput(kernel, op_outputs, cnode_ref_count, &op_output_map, &graph_output_info);
// Save grad node to Bucket
// Save gradient node addresses to the Bucket if it's a backward graph and not a parallel kernel.
if (graph->is_bprop() && (!common::AnfAlgo::IsControlOpExecInBackend(kernel)) && !kernel->is_parallel()) {
graph_compiler_->AddGradAddrToBucket(graph->graph_id(), graph_output_info.graph_output_tensors);
}
}
// Wait for any pending tasks to finish before moving on to the next graph.
WaitTaskFinish();
// Clear bucket resources every step
// Clear bucket resources at the end of each step.
if (graph->is_bprop()) {
graph_compiler_->ClearAllBucket(graph->graph_id());
}
}
}
/**
* Execute a computation graph using the MindRT backend.
*
* This function is responsible for executing a computation graph represented by the provided `actor_info` and `args`.
* Depending on the execution mode (Pynative or Graph Mode), it processes input tensors and runs the graph. The results
* are constructed in the `outputs` parameter.
*
* @param actor_info Information about the computation graph to be executed.
* @param args Input arguments for the computation graph.
* @param outputs Reference to the vector where the output results will be stored.
*/
void MindRTBackend::RunGraph(const ActorInfo &actor_info, const VectorRef &args, VectorRef *outputs) {
// Ensure the root graph is not null.
MS_EXCEPTION_IF_NULL(root_graph_);
// Check if the root graph output is a value node or parameter; if yes, no execution is needed.
if (IsGraphOutputValueNodeOrParameter(root_graph_->output(), args, outputs)) {
return;
}
// Get the context to check for precompile-only mode.
const auto &context_ptr = MsContext::GetInstance();
MS_EXCEPTION_IF_NULL(context_ptr);
// If in precompile-only mode, stop execution.
if (context_ptr->get_param<bool>(MS_CTX_PRECOMPILE_ONLY)) {
MS_LOG(INFO) << "PrecompileOnly, stop run graph";
return;
}
// Open abstract_lock for dynamic_shape
// Open the abstract lock for dynamic_shape.
AnfUtils::OpenAbstractLock();
MS_LOG(INFO) << "Status record: start run actor: " << actor_info;
// Fetch the graph compiler info.
const auto &graph_iter = actor_to_graph_compiler_info_.find(actor_info);
if (graph_iter == actor_to_graph_compiler_info_.end()) {
@ -988,6 +1219,7 @@ void MindRTBackend::RunGraph(const ActorInfo &actor_info, const VectorRef &args,
WaitTaskFinish();
// Transform args to input tensors.
// Input tensors of the graph.
std::vector<std::vector<tensor::TensorPtr>> input_tensors;
for (const auto &kernel_graph : graph_compiler_info.graphs_) {
@ -1008,16 +1240,18 @@ void MindRTBackend::RunGraph(const ActorInfo &actor_info, const VectorRef &args,
// Input tensors of the control node.
std::vector<tensor::TensorPtr> input_tensor;
MS_EXCEPTION_IF_NULL(graph_compiler_info.control_node_parser_);
// Get inputs of control node which come from the host actor.
// Get inputs of the control node which come from the host actor.
const auto &control_node_parameters = graph_compiler_info.control_node_parser_->control_node_parameters();
for (const auto &parameter : control_node_parameters) {
PushTensor(args, origin_parameters, parameter, &input_tensor);
}
(void)input_tensors.emplace_back(input_tensor);
// Run in the pynative mode.
// Run in pynative mode.
MS_EXCEPTION_IF_NULL(outputs);
// There will be more than one kernel graph in heterogeneous scenario in a ms function of PyNative Mode.
// There will be more than one kernel graph in heterogeneous scenarios in a ms function of PyNative Mode.
if (real_execution_mode_ == kPynativeMode) {
RunGraphBySingleOp(graph_compiler_info.graphs_, input_tensors, outputs);
MS_LOG(INFO) << "Status record: end run actor: " << actor_info;
@ -1033,11 +1267,14 @@ void MindRTBackend::RunGraph(const ActorInfo &actor_info, const VectorRef &args,
MS_EXCEPTION_IF_NULL(graph_compiler_);
graph_compiler_->Summary(graph_compiler_info.graphs_);
bool need_contruct_output = !(distributed::recovery::RecoveryContext::GetInstance()->enable_recovery() &&
// Construct output results.
// Ensure the construction of outputs is needed.
bool need_construct_output = !(distributed::recovery::RecoveryContext::GetInstance()->enable_recovery() &&
distributed::recovery::RecoveryContext::GetInstance()->need_reset());
if (need_contruct_output) {
// Update device address for output node of graph.
// Summary processing will use the output device address, so must be after the summary processing.
if (need_construct_output) {
// Update device address for the output node of the graph.
// Summary processing will use the output device address, so it must be after the summary processing.
actor_set->output_actor_->UpdateOutputDeviceAddress();
// Fetch outputs.
@ -1049,12 +1286,14 @@ void MindRTBackend::RunGraph(const ActorInfo &actor_info, const VectorRef &args,
}
}
// Clear actor data and close the abstract lock for dynamic_shape.
runtime::GraphScheduler::GetInstance().ClearActorData(actor_set);
// Close abstract_lock for dynamic_shape
AnfUtils::CloseAbstractLock();
MS_LOG(INFO) << "Status record: end run actor: " << actor_info;
}
BaseRef MindRTBackend::ConstructOutputByAbstract(const abstract::AbstractBasePtr &abstract,
const std::vector<tensor::TensorPtr> &output_tensors,
size_t *output_position) {
@ -1203,20 +1442,37 @@ void MindRTBackend::SyncStream() {
(void)device_context->SyncStream();
}
/**
* @brief Constructs the information needed for graph compilation and execution in the MindRT backend.
*
* This function collects information about kernel graphs, device contexts, control nodes, and other
* relevant details required for the configuration of a graph compiler. It constructs a `GraphCompilerInfo`
* object to encapsulate this information for further use in graph compilation and execution.
*
* @param root_graph The root function graph to be compiled.
* @return A unique pointer to a `GraphCompilerInfo` object containing the collected information.
*/
std::unique_ptr<GraphCompilerInfo> MindRTBackend::ConstructGraphCompilerInfo(const FuncGraphPtr &root_graph) {
// Check for null inputs
MS_EXCEPTION_IF_NULL(root_graph);
MS_EXCEPTION_IF_NULL(graph_compiler_);
// Initialize data structures to store information
std::vector<KernelGraphPtr> graphs;
std::vector<DeviceContext *> device_contexts;
std::string name = "kernel_graph";
// Loop through graph_id_to_device_context_ and collect graphs and device contexts
for (const auto &graph_id_to_context : graph_id_to_device_context_) {
(void)graphs.emplace_back(graph_compiler_->Fetch(graph_id_to_context.first));
(void)device_contexts.emplace_back(graph_id_to_context.second);
(void)name.append("_").append(std::to_string(graph_id_to_context.first));
}
// Initialize data structures to map function graphs to kernel graphs
FuncGraphToKernelGraphGroup func_graph_to_kernel_graphs;
// Loop through func_graph_to_kernel_graph_ids_ and collect kernel graphs for each function graph
for (const auto &func_graph_to_kernel_graph_ids : func_graph_to_kernel_graph_ids_) {
const auto &func_graph = func_graph_to_kernel_graph_ids.first;
for (const auto &sub_kernel_graphs_ids : func_graph_to_kernel_graph_ids.second) {
@ -1230,9 +1486,11 @@ std::unique_ptr<GraphCompilerInfo> MindRTBackend::ConstructGraphCompilerInfo(con
}
}
// Create a control node parser and parse control nodes
auto parser = std::make_shared<ControlNodeParser>();
parser->Parse(control_nodes_, graphs, device_contexts, root_graph, func_graph_to_kernel_graphs);
// Determine the order of kernel outputs
runtime::KernelMapPosition outputs_order;
const auto &root_output =
common::AnfAlgo::VisitKernelWithReturnType(root_graph->output(), 0, false, {prim::kPrimTupleGetItem}).first;
@ -1247,6 +1505,7 @@ std::unique_ptr<GraphCompilerInfo> MindRTBackend::ConstructGraphCompilerInfo(con
}
}
// Create and return GraphCompilerInfo
std::vector<std::vector<int64_t> *> tensors_mask;
std::vector<std::vector<tensor::TensorPtr> *> input_tensors;
return std::make_unique<GraphCompilerInfo>(graphs, device_contexts, tensors_mask, input_tensors, control_nodes_,
@ -1254,6 +1513,7 @@ std::unique_ptr<GraphCompilerInfo> MindRTBackend::ConstructGraphCompilerInfo(con
runtime::GraphExecutionStrategy::kPipeline);
}
std::unique_ptr<GraphCompilerInfo> MindRTBackend::ConstructGraphCompilerInfo(
const ActorInfo &actor_info, const std::vector<int64_t> *tensors_mask,
const std::vector<tensor::TensorPtr> *input_tensors, bool need_erase) {

View File

@ -54,6 +54,7 @@ enum SwitchCondStatus {
kCondAlreadyRun,
};
// Base Class
class BACKEND_EXPORT Backend {
public:
explicit Backend(const std::string &name);
@ -76,6 +77,7 @@ class BACKEND_EXPORT Backend {
bool is_multi_graph_sink_;
};
// Inherit Ones
class BACKEND_EXPORT MsBackend : public Backend {
public:
MsBackend(const std::string &name, const std::string &target, uint32_t device_id);
@ -102,6 +104,7 @@ class BACKEND_EXPORT MsBackend : public Backend {
mindspore::HashMap<GraphId, LinConvertResult> graph_id_map_;
};
class BACKEND_EXPORT MindRTBackend : public Backend {
public:
MindRTBackend(const std::string &backend_name, const std::string &device_name, uint32_t device_id);

View File

@ -37,6 +37,13 @@ const char kMsVm[] = "vm";
const char kGeVm[] = "ge";
namespace compile {
namespace {
/*
* @brief Get the other target of the graph. Note that a graph can only have two targets.
*
* @param nodes The nodes of the graph.
* @return The other target of the graph.
*/
std::string GetOtherTarget(const std::vector<AnfNodePtr> &nodes) {
auto context_ptr = MsContext::GetInstance();
MS_EXCEPTION_IF_NULL(context_ptr);
@ -54,6 +61,12 @@ std::string GetOtherTarget(const std::vector<AnfNodePtr> &nodes) {
return "";
}
/**
* @brief Calculate the reference count of each node in the graph.
*
* @param graph The graph to be calculated.
* @param nodes_ref The reference count of each node in the graph.
*/
void CalcNodeRefCount(const FuncGraphPtr &graph, std::map<AnfNodePtr, size_t> *nodes_ref) {
MS_EXCEPTION_IF_NULL(graph);
MS_EXCEPTION_IF_NULL(nodes_ref);
@ -85,6 +98,13 @@ void CalcNodeRefCount(const FuncGraphPtr &graph, std::map<AnfNodePtr, size_t> *n
}
}
/*
* @brief Reorder virtual node such as primitive "depend" and "tuple_getitem" to ensure the order of the nodes.
* These virtual nodes will be inserted after their parent node.
*
* @param nodes The nodes to be reordered.
* @param reorder_prim The primitive used to reorder the nodes.
*/
std::vector<AnfNodePtr> ReorderVirtualNode(const std::vector<AnfNodePtr> &nodes, const PrimitivePtr &reorder_prim) {
std::vector<AnfNodePtr> result;
std::map<size_t, std::vector<AnfNodePtr>> insert_positions;
@ -136,6 +156,14 @@ std::vector<AnfNodePtr> ReorderVirtualNode(const std::vector<AnfNodePtr> &nodes,
return result;
}
/*
* @brief Get next nodes to be visited.
*
* @param node The current node.
* @param nodes_ref The reference count of each node in the graph.
* @param result The next nodes to be visited.
* @return The next nodes to be visited.
*/
std::vector<AnfNodePtr> GetNextNodes(const AnfNodePtr &node, std::map<AnfNodePtr, size_t> *nodes_ref,
std::vector<AnfNodePtr> *result) {
MS_EXCEPTION_IF_NULL(node);
@ -226,6 +254,12 @@ struct GraphNodesDependencyInfo {
std::map<AnfNodePtr, std::vector<AnfNodePtr>> output_edges_;
};
/*
* @brief Get the dependency information of all the nodes in the graph.
*
* @param graph The graph to be calculated.
* @return The dependency information of the nodes in the graph.
*/
GraphNodesDependencyInfo GetNodesDependencyInfo(const FuncGraphPtr &graph) {
MS_EXCEPTION_IF_NULL(graph);
GraphNodesDependencyInfo info;
@ -283,6 +317,16 @@ struct VisitNodesInfo {
std::map<AnfNodePtr, AnfNodePtr> seed_cast_next_node_;
};
/*
* @brief Get the visit information of all the nodes in the graph.
* The information includes the nodes of the default target and another specified target,
* and the cast node following a certain node, if exists.
*
* @param dependency_info The dependency information of the nodes in the graph.
* @param default_target The default target of the graph.
* @param other_target The other target of the graph.
* @return The visit information of the nodes in the graph.
*/
VisitNodesInfo GetVisitNodesInfo(const GraphNodesDependencyInfo &dependency_info, const std::string &default_target,
const std::string &other_target) {
VisitNodesInfo result;
@ -366,6 +410,14 @@ void ParallelSortVisitNodeEdges(const std::vector<AnfNodePtr> &output_edges, Gra
}
}
/*
* @brief Sort the nodes of the graph considering multiple targets to facilitate the parallel execution.
*
* @param graph The graph to be sorted.
* @param default_target The default target of the graph.
* @param other_target The other target of the graph.
* @return The sorted nodes of the graph.
*/
std::vector<AnfNodePtr> ParallelSort(const FuncGraphPtr &graph, const std::string &default_target,
const std::string &other_target) {
MS_EXCEPTION_IF_NULL(graph);
@ -408,6 +460,12 @@ std::vector<AnfNodePtr> ParallelSort(const FuncGraphPtr &graph, const std::strin
return result;
}
/*
* @brief Add dependency to segments.
*
* @param graph The graph to be added.
* @param node_to_segment The mapping from node to segment.
*/
void AddSegmentDependency(const FuncGraphPtr &graph, const std::map<AnfNodePtr, GraphSegmentPtr> &node_to_segment) {
MS_EXCEPTION_IF_NULL(graph);
std::stack<AnfNodePtr> to_visit;
@ -550,6 +608,14 @@ struct SplitDynamicNodesHelper {
size_t merge_node_threshold = 6;
};
/*
* @brief Split the nodes into segments according to the dynamic shape.
*
* @param segment_nodes The nodes to be split.
* @param segments The segments after splitting.
* @param node_to_segment The mapping from node to segment.
* @param dynamic_nodes_set The set of nodes with dynamic shape.
*/
void SplitDynamicNodeSegment(const std::vector<AnfNodePtr> &segment_nodes, std::vector<GraphSegmentPtr> *segments,
std::map<AnfNodePtr, GraphSegmentPtr> *node_to_segment,
const std::set<AnfNodePtr> &dynamic_nodes_set) {
@ -590,6 +656,13 @@ void SplitDynamicNodeSegment(const std::vector<AnfNodePtr> &segment_nodes, std::
helper.AddSegments(segments, node_to_segment);
}
/*
* @brief Convert nodes before the cut node to a segment.
*
* @param segment_nodes The nodes to be split.
* @param segments The segments after splitting.
* @param node_to_segment The mapping from node to segment.
*/
void NodesToSegments(const std::vector<AnfNodePtr> &segment_nodes, std::vector<GraphSegmentPtr> *segments,
std::map<AnfNodePtr, GraphSegmentPtr> *node_to_segment) {
if (segment_nodes.empty()) {
@ -624,22 +697,43 @@ void NodesToSegments(const std::vector<AnfNodePtr> &segment_nodes, std::vector<G
GraphPartition::GraphPartition(const std::vector<PrimitivePtr> &cut_list, const std::string &backend_name)
: cut_list_(cut_list), backend_name_(backend_name) {}
/**
* @brief Checks if the given AnfNode is a cut point for partitioning.
*
* This function determines if a given AnfNode should be considered as a cut point for partitioning a graph. It checks
* various conditions, such as the node's type and its association with certain primitives or backends.
*
* @param node The AnfNode to be checked.
* @return True if the node is a cut point, otherwise false.
*/
bool GraphPartition::IsCut(const AnfNodePtr &node) {
MS_EXCEPTION_IF_NULL(node);
// Check if the node is a CNode.
if (node->isa<CNode>()) {
auto cnode = node->cast<CNodePtr>();
auto &inputs = cnode->inputs();
// Ensure the inputs of the apply node are not empty.
if (inputs.empty()) {
MS_LOG(EXCEPTION) << "Inputs of apply node is empty";
}
// Get the first input node.
AnfNodePtr fn = inputs[0];
// Check if the first input is not a ValueNode of Primitive type, indicating a cut point.
if (!IsValueNode<Primitive>(fn)) {
return true;
}
auto node_prim = GetValueNode<PrimitivePtr>(fn);
// Check if the node_prim is in the cut_list, indicating a cut point.
for (auto &prim : cut_list_) {
MS_EXCEPTION_IF_NULL(prim);
if (prim->name() == node_prim->name()) {
// Handle special cases based on the primitive's name.
if (prim->name() == prim::kPrimBpropCut->name()) {
auto ms_context = MsContext::GetInstance();
MS_EXCEPTION_IF_NULL(ms_context);
@ -655,6 +749,8 @@ bool GraphPartition::IsCut(const AnfNodePtr &node) {
return true;
}
}
// Check for backend-specific conditions (e.g., for backend 'kGeVm').
#ifdef ENABLE_D
if (backend_name_ == kGeVm) {
auto name = GetCNodeFuncName(cnode);
@ -665,38 +761,66 @@ bool GraphPartition::IsCut(const AnfNodePtr &node) {
}
#endif
}
// If none of the conditions are met, it's not a cut point.
return false;
}
/**
* @brief Partitions a given FuncGraph into multiple segments.
*
* This function takes a FuncGraph as input and partitions it into multiple segments based on certain criteria. The
* segments are returned as a vector of GraphSegmentPtr.
*
* @param graph The input FuncGraph to be partitioned.
* @param multi_target A pointer to a boolean flag indicating whether the graph contains multiple targets. If not needed,
* you can pass nullptr.
* @return A vector of GraphSegmentPtr representing the partitions of the input FuncGraph.
*/
std::vector<GraphSegmentPtr> GraphPartition::Partition(const FuncGraphPtr &graph, bool *multi_target) {
MS_EXCEPTION_IF_NULL(graph);
// graph->get_return return the CNode object
// TopoSort
auto nodes = TopoSort(graph->get_return());
MS_LOG(DEBUG) << "Split all nodes size:" << nodes.size();
bool contain_multi_target = ContainMultiTarget(nodes);
// Set the 'multi_target' flag if it's provided.
if (multi_target != nullptr) {
*multi_target = contain_multi_target;
}
auto context_ptr = MsContext::GetInstance();
MS_EXCEPTION_IF_NULL(context_ptr);
// may be use loop sink to reduce operations inside the loop
auto enable_loop_sink = context_ptr->get_param<bool>(MS_CTX_ENABLE_LOOP_SINK);
std::string default_target = context_ptr->get_param<std::string>(MS_CTX_DEVICE_TARGET);
// Perform partitioning based on the criteria.
if (contain_multi_target || !enable_loop_sink) {
// parellize or not
if (context_ptr->get_param<bool>(MS_CTX_ENABLE_PARALLEL_SPLIT)) {
auto other_target = GetOtherTarget(nodes);
nodes = ParallelSort(graph, default_target, other_target);
} else {
nodes = SplitSort(graph, default_target);
}
// Reorder the nodes with primitive "depend" and "tuple_getitem" to ensure the correctness of the partitioning.
nodes = ReorderVirtualNode(nodes, prim::kPrimTupleGetItem);
nodes = ReorderVirtualNode(nodes, prim::kPrimDepend);
}
// Initialize data structures to store segments and nodes.
std::vector<GraphSegmentPtr> segments;
std::vector<AnfNodePtr> segment_nodes;
std::map<AnfNodePtr, GraphSegmentPtr> node_to_segment;
std::string last_target;
// Iterate through nodes to create segments.
for (auto &node : nodes) {
MS_EXCEPTION_IF_NULL(node);
if (IsCut(node)) {
NodesToSegments(segment_nodes, &segments, &node_to_segment);
segment_nodes.clear();
@ -707,6 +831,8 @@ std::vector<GraphSegmentPtr> GraphPartition::Partition(const FuncGraphPtr &graph
} else if (node->isa<CNode>()) {
if (contain_multi_target) {
std::string cur_target = GetCNodeTarget(node);
// Check if the target has changed, and if so, start a new segment.
if (cur_target != last_target && !last_target.empty()) {
NodesToSegments(segment_nodes, &segments, &node_to_segment);
segment_nodes.clear();
@ -716,11 +842,15 @@ std::vector<GraphSegmentPtr> GraphPartition::Partition(const FuncGraphPtr &graph
segment_nodes.emplace_back(node);
}
}
MS_LOG(DEBUG) << "Segment size:" << segments.size();
// Add segment dependencies and remove useless ones if multiple targets are present.
if (contain_multi_target) {
AddSegmentDependency(graph, node_to_segment);
RemoveUselessDependency(&segments);
}
return segments;
}
} // namespace compile

View File

@ -38,8 +38,11 @@ class GraphPartition {
std::vector<GraphSegmentPtr> Partition(const FuncGraphPtr &func_graph, bool *multi_target = nullptr);
private:
// To get the point can be cut or not
bool IsCut(const AnfNodePtr &node);
// New Primitives List After cutting
std::vector<PrimitivePtr> cut_list_;
// the key same as the name_ in the backend object
std::string backend_name_;
};

View File

@ -421,15 +421,20 @@ void TraverseGraphMap(
const std::function<std::shared_ptr<FuncGraph>(const PrimitivePtr, const AbstractFunctionPtr)> &get_prim_graph) {
MS_EXCEPTION_IF_NULL(manager_ptr);
MS_EXCEPTION_IF_NULL(tr);
for (const auto &fg : fgs) {
// traverse all funcgraphs
MS_EXCEPTION_IF_NULL(fg);
for (const auto &ct_any : fg->value_nodes()) {
// process all value nodes
AnfNodePtr const_primitive_node = ct_any.first;
if (const_primitive_node != nullptr && IsValueNode<Primitive>(const_primitive_node)) {
auto users = manager_ptr->node_users()[const_primitive_node];
// traverse CNode
for (auto &use : users) {
CNodePtr node = use.first->cast<CNodePtr>();
MS_EXCEPTION_IF_NULL(node);
// coose users use this fg
if (node->func_graph() != fg) {
continue;
}
@ -445,8 +450,10 @@ void TraverseGraphMap(
continue;
}
}
// lambda func here
FuncGraphPtr g = get_prim_graph(GetValueNode<PrimitivePtr>(const_primitive_node),
dyn_cast<AbstractFunction>(const_primitive_node->abstract()));
// The user index "key" Use this node
tr->SetEdge(node, key, NewValueNode(g));
}
}
@ -455,13 +462,29 @@ void TraverseGraphMap(
}
}
/**
* @brief Wraps primitive operations in the given FuncGraph.
*
* This function takes a FuncGraph as input and wraps primitive operations within it. It creates a new FuncGraph for each
* primitive operation and its type, then replaces the original primitive operation with the new FuncGraph call.
*
* @param graph The input FuncGraph to be processed.
* @return The processed FuncGraph with wrapped primitive operations(Prim Graph).
**/
FuncGraphPtr WrapPrimitives(const FuncGraphPtr &graph) {
MS_EXCEPTION_IF_NULL(graph);
// get root graph
FuncGraphManagerPtr manager_ptr = graph->manager();
MS_EXCEPTION_IF_NULL(manager_ptr);
// using MapPrimTypeFuncGraph = std::map<PrimTypePair, FuncGraphPtr>
MapPrimTypeFuncGraph prim_graphs;
// lambda function to get FuncGraphs by Type
auto get_prim_graph = [&prim_graphs](const PrimitivePtr &prim, const AbstractFunctionPtr &type) {
PrimTypePair prim_type = std::make_pair(prim, type);
// If tis type can't be found, create it.
if (prim_graphs.end() == prim_graphs.find(prim_type)) {
FuncGraphPtr g = std::make_shared<FuncGraph>();
std::vector<AnfNodePtr> args;
@ -469,15 +492,18 @@ FuncGraphPtr WrapPrimitives(const FuncGraphPtr &graph) {
MS_EXCEPTION_IF_NULL(prim_ct);
prim_ct->set_abstract(type);
args.push_back(prim_ct);
MS_EXCEPTION_IF_NULL(type);
TypedPrimitiveAbstractClosurePtr tp = dyn_cast<abstract::TypedPrimitiveAbstractClosure>(type->GetUnique());
MS_EXCEPTION_IF_NULL(tp);
MS_EXCEPTION_IF_NULL(g);
// add paras in the para list
for (auto t : tp->args_spec_list()) {
ParameterPtr p = g->add_parameter();
p->set_abstract(t);
args.push_back(p);
}
AnfNodePtr out = g->NewCNode(args);
out->set_abstract(tp->output());
g->set_output(out);
@ -489,12 +515,15 @@ FuncGraphPtr WrapPrimitives(const FuncGraphPtr &graph) {
FuncGraphTransaction tr = manager_ptr->Transact();
auto &fgs = manager_ptr->func_graphs();
// call the lambda function here
TraverseGraphMap(manager_ptr, &tr, fgs, get_prim_graph);
// commit as A FuncGraphTransaction
tr.Commit();
return graph;
}
CompileGraphs::CompileGraphs(const BackendPtr &backend, const std::vector<PrimitivePtr> &cut_list) : backend_(backend) {
MS_EXCEPTION_IF_NULL(backend);
MS_LOG(DEBUG) << "Start vm: " << backend->name();
@ -543,6 +572,7 @@ FinalVMPtr CompileGraphs::CompileAndLink(const FuncGraphPtr &graph) {
Reset();
MS_LOG(DEBUG) << "Begin parameter:" << graph->parameters().size();
// preprocess the graph
FuncGraphPtr prim_graph = WrapPrimitives(graph);
Compile(prim_graph);
MS_EXCEPTION_IF_NULL(prim_graph);

View File

@ -127,6 +127,7 @@ class BACKEND_EXPORT CompileGraphs {
FinalVMPtr CompileAndLink(const FuncGraphPtr &func_graph);
protected:
// A vector stores instructions and their types
InstSet insts_;
mindspore::HashMap<FuncGraphPtr, int64_t> mapping_;
CompileGraphPtr transform_;

View File

@ -441,6 +441,7 @@ GraphId GraphCompiler::CompileGraph(const GraphSegmentPtr &segment, const AnfNod
return graph_id;
}
// Not splited
GraphId GraphCompiler::CompileGraph(const FuncGraphPtr &func_graph, const DeviceContext *device_context) {
MS_EXCEPTION_IF_NULL(session_);
MS_EXCEPTION_IF_NULL(func_graph);

View File

@ -555,28 +555,36 @@ void GraphScheduler::Schedule(const ActorSet *actor_set) {
#endif
}
/**
* Runs the execution of actors within an actor set, following a specified graph execution strategy.
*
* @param actor_set Pointer to the ActorSet containing actors to be executed.
* @param device_contexts A vector of DeviceContext pointers representing the device contexts for execution.
* @param input_tensors A vector of vectors of TensorPtr representing input tensors for each actor.
* @param input_tensors_with_value_node A vector of TensorPtr representing input tensors with value nodes.
* @param strategy The graph execution strategy (e.g., step or pipeline).
*/
void GraphScheduler::Run(ActorSet *const actor_set, const std::vector<DeviceContext *> &device_contexts,
const std::vector<std::vector<TensorPtr>> &input_tensors,
const std::vector<TensorPtr> &input_tensors_with_value_node, GraphExecutionStrategy strategy) {
// Check for null pointers.
MS_EXCEPTION_IF_NULL(actor_set);
MS_EXCEPTION_IF_NULL(actor_set->data_prepare_actor_);
#if !defined(_WIN32) && !defined(_WIN64)
SignalGuard sg(IntHandler);
#endif
// Construct OpContext.
// Initialize OpContext.
OpContext<DeviceTensor> op_context;
std::vector<Promise<int>> result(1);
op_context.sequential_num_ = RandInt::Instance().Get();
op_context.results_ = &result;
// Set OpContext for RPC actors if needed.
#ifdef ENABLE_RPC_ACTOR
// Set OpContext to rpc node scheduler.
auto op_context_setter =
std::make_shared<RpcActorOpContextSetter>(rpc_node_scheduler_.get(), actor_set->rpc_actors_, &op_context);
MS_EXCEPTION_IF_NULL(op_context_setter);
#endif
// Handle single-op execution for specific cases.
if ((strategy == GraphExecutionStrategy::kStep) && IsSingleOpActorSet(actor_set)) {
actor_set->data_prepare_actor_->PrepareData(input_tensors, &op_context, GraphExecutionStrategy::kStep);
MS_EXCEPTION_IF_NULL(actor_set->kernel_actors_[0]);
@ -593,36 +601,42 @@ void GraphScheduler::Run(ActorSet *const actor_set, const std::vector<DeviceCont
ActorDispatcher::Send(actor_set->data_prepare_actor_->GetAID(), &DataPrepareActor::PrepareData, input_tensors,
&op_context, GraphExecutionStrategy::kPipeline);
// Get the run result.
// Wait for the data preparation to complete.
auto result_future = result[0].GetFuture();
result_future.Wait();
MsException::Instance().CheckException();
// Handle potential errors during data preparation.
if (!result_future.IsOK()) {
#ifdef ENABLE_DUMP_IR
mindspore::RDR::TriggerAll();
#endif
// When temporary variable 'op_context' has beed set failed status, the main thread need wait other threads until
// they finish respective task, otherwise segmentation fault will happen when these task access 'op_context',
// because it has been destroyed.
// When the 'op_context' has been set with a failed status, the main thread must wait for other threads to finish
// their respective tasks to avoid segmentation faults, as 'op_context' has been destroyed.
std::mutex mutex;
std::unique_lock<std::mutex> locker(mutex);
std::condition_variable thread_blocker;
const int64_t kTimeToWait = 2;
(void)thread_blocker.wait_for(locker, std::chrono::seconds(kTimeToWait));
// May set exception in the wait time, need throw the exception to avoid affecting the next execution.
// Check for exceptions during the wait time and throw them to avoid affecting the next execution.
MsException::Instance().CheckException();
MS_LOG(EXCEPTION) << op_context.error_info_;
}
// Calculate execution time.
double end_time = GetTime();
const size_t kSecondsToMilliseconds = 1000;
// Set actor execution strategy and timing information.
SetActorExecutionStrategy(actor_set, strategy, (end_time - start_time) * kSecondsToMilliseconds);
#if ((defined ENABLE_CPU) && (!defined _WIN32) && (!defined _WIN64))
// Handle disaster recovery for CPU execution.
DoDisasterRecovery(actor_set->name_);
#endif
}
void GraphScheduler::SetActorExecutionStrategy(ActorSet *const actor_set, GraphExecutionStrategy strategy,
double execution_time) const {
MS_EXCEPTION_IF_NULL(actor_set);

View File

@ -36,6 +36,12 @@ using mindspore::abstract::AbstractBase;
using mindspore::abstract::AbstractFunction;
using mindspore::abstract::AbstractFunctionPtr;
/**
* @brief Check if the given node has recomputed scope.
*
* @param node The node to check.
* @return true if the node has recomputed scope, otherwise false.
*/
bool WithRecomputedScope(const AnfNodePtr &node) {
MS_EXCEPTION_IF_NULL(node);
if (!node->isa<CNode>()) {
@ -45,11 +51,24 @@ bool WithRecomputedScope(const AnfNodePtr &node) {
return full_name_with_scope.find(kAttrRecompute) == 0;
}
/**
* @brief Check if the given nodes are set as recomputed.
*
* @param a The first node.
* @param b The second node.
* @return true if any node is set as recomputed, otherwise false.
*/
bool IsSetRecomputed(const CNodePtr &a, const CNodePtr &b) {
return (WithRecomputedScope(a) && !a->HasAttr(kAttrNeedCseAfterRecompute)) ||
(WithRecomputedScope(b) && !b->HasAttr(kAttrNeedCseAfterRecompute));
}
/**
* @brief Update debug info and dump flag for the given nodes.
*
* @param main The main node.
* @param node The node to check.
*/
void UpdateDebugInfoAndDumpFlag(const AnfNodePtr &main, const AnfNodePtr &node) {
if (main == nullptr || !main->isa<CNode>()) {
return;
@ -61,6 +80,13 @@ void UpdateDebugInfoAndDumpFlag(const AnfNodePtr &main, const AnfNodePtr &node)
main_cnode->AddFusedDebugInfo(node);
}
/**
* @brief Get abstract representation of a node.
*
* @param node The node to process.
* @param ignore_fg_abs_tracking_id Flag to decide if fg_abs_tracking_id should be ignored.
* @return Abstract representation of the node.
*/
BasePtr AbsOf(const AnfNodePtr &node, bool ignore_fg_abs_tracking_id) {
MS_EXCEPTION_IF_NULL(node);
auto node_abs = node->abstract();
@ -81,6 +107,17 @@ BasePtr AbsOf(const AnfNodePtr &node, bool ignore_fg_abs_tracking_id) {
return node_abs;
}
/*
* @brief Build order groups and do replace for one graph.
*
* This function groups all nodes that might have common subexpressions and
* passes them to the DoReplace() function for actual processing. It also
* builds order groups based on the nodes in a single graph.
*
* @param fg The graph to be processed.
* @param manager The manager of the graph.
* @return Returns true if the replace operation was successful, otherwise false.
*/
bool CSE::BuildOrderGroupAndDoReplaceForOneGraph(const FuncGraphPtr &fg, const FuncGraphManagerPtr &manager) const {
MS_EXCEPTION_IF_NULL(fg);
std::vector<std::size_t> order_group;
@ -125,6 +162,16 @@ bool CSE::BuildOrderGroupAndDoReplaceForOneGraph(const FuncGraphPtr &fg, const F
return DoReplace(manager, order_group, &groups);
}
/*
* @brief Builds order groups and executes replacements for all graphs managed by the given manager.
*
* Iterates through all the graphs available in the manager and performs
* order group building and replacements on each of them. The change status
* is aggregated across all the graphs.
*
* @param manager The manager handling the graphs to be processed.
* @return Returns true if there were any changes, otherwise false.
*/
bool CSE::BuildOrderGroupAndDoReplace(const FuncGraphManagerPtr manager) const {
bool changed = false;
for (FuncGraphPtr fg : manager->func_graphs()) {
@ -133,6 +180,15 @@ bool CSE::BuildOrderGroupAndDoReplace(const FuncGraphManagerPtr manager) const {
return changed;
}
/*
* @brief Checks if the node has hidden side effects.
*
* This function checks if the given node has any attributes indicating
* that it has hidden side effects.
*
* @param node The node to be checked.
* @return Returns true if the node has hidden side effects, otherwise false.
*/
bool CSE::HasHiddenSideEffect(const AnfNodePtr &node) {
auto prim = GetCNodePrimitive(node);
if (prim == nullptr) {
@ -141,6 +197,17 @@ bool CSE::HasHiddenSideEffect(const AnfNodePtr &node) {
return prim->HasAttr(GRAPH_FLAG_SIDE_EFFECT_HIDDEN);
}
/*
* @brief Checks whether a node can be replaced by another.
*
* This function performs various checks to determine if the 'main' node can
* be replaced by the 'node'. It takes into account the types and attributes
* of the nodes as well as the values they contain.
*
* @param main The main node that might be replaced.
* @param node The node that is considered as a replacement.
* @return Returns true if 'main' can be replaced by 'node', otherwise false.
*/
bool CSE::CheckReplace(const AnfNodePtr &main, const AnfNodePtr &node) const {
MS_EXCEPTION_IF_NULL(main);
MS_EXCEPTION_IF_NULL(node);
@ -186,6 +253,15 @@ bool CSE::CheckReplace(const AnfNodePtr &main, const AnfNodePtr &node) const {
return false;
}
/*
* @brief Perform the actual replacement step of CSE.
* It inspects all node groups to seek CSE opportunities.
*
* @param manager The manager of the graph.
* @param order_group The order group of the graph.
* @param groups The groups of the graph.
*
*/
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;