天天向上队——pipeline文件夹注释 #26

Open
WEI_4614 wants to merge 26 commits from WEI_4614/mindspore2022:comp into master
21 changed files with 1661 additions and 354 deletions

View File

@ -94,6 +94,9 @@ void UpdateFuncGraphParameter(const FuncGraphPtr &func_graph) {
}
func_graph->set_parameters(new_paras);
}
//The function of this code is to update the parameters in the passed function graph
// leaving only the parameters that have no default value and meet certain conditions,
//and update the new parameter list to the function graph
bool IsDynamicShapeGraph(const FuncGraphPtr &func_graph) {
MS_EXCEPTION_IF_NULL(func_graph);
@ -141,6 +144,7 @@ void TaskEmitActionForMindRT(const ResourcePtr &res) {
res->SetResult(kOutput, actor_info);
}
// Get the graph information, construct the pointer of the execution function, execute the graph and return the result
void ExecuteActionForMindRT(const ResourcePtr &res) {
MS_EXCEPTION_IF_NULL(res);
const auto actor_info = res->GetResult(kOutput).cast<compile::ActorInfo>();
@ -206,6 +210,7 @@ void ModifyOutputNode(const FuncGraphPtr &func_graph) {
func_graph->set_output(merge_node);
// Clear
func_graph->set_modify_output(true);
func_graph->ClearUsedForwardNodes();
}
@ -228,6 +233,7 @@ abstract::AnalysisResult AbstractAnalyze(const ResourcePtr &resource, const Func
MS_EXCEPTION_IF_NULL(node);
// Handle previous inferred value for CNode if is loaded from MindIR
if (resource->is_load()) {
// If the primitive is not defined in front end, keep the inferred value loaded from MindIR.
auto primitive = GetCNodePrimitive(node);
@ -241,9 +247,9 @@ abstract::AnalysisResult AbstractAnalyze(const ResourcePtr &resource, const Func
}
const AbstractBasePtr &prev_inferred = node->abstract();
// Keep previous inferred value for ValueNode if the inferred value is not AbstractFunction.
// Keep previous inferred value for ValueNode if the inferred value is not AbstractFunction
if (!node->isa<ValueNode>() || (prev_inferred != nullptr && prev_inferred->isa<abstract::AbstractFunction>())) {
// Reset tuple/list abstract use flags.
// Reset tuple/list abstract use flags
if (enable_eliminate_unused_element && prev_inferred != nullptr &&
prev_inferred->isa<abstract::AbstractSequence>()) {
SetSequenceNodeElementsUseFlags(node, nullptr);
@ -322,6 +328,7 @@ const FuncGraphPtr GetLoadedGraph(const ResourcePtr &res) {
MS_LOG(EXCEPTION) << "The loaded sub graph currently should be less than 2, but got " << loaded_graph_num;
}
// Check that the root diagram input shape and type are consistent with the loaded diagram
void CheckRootInputShapeAndType(const ResourcePtr &res, const FuncGraphPtr &loaded_graph) {
MS_EXCEPTION_IF_NULL(res);
auto manager = res->manager();
@ -374,6 +381,9 @@ void CheckRootInputShapeAndType(const ResourcePtr &res, const FuncGraphPtr &load
}
}
// Parsing a Python object into a graph includes the process of obtaining a source input object, initializing the parser environment,
// setting up Python paths, converting an input object into a graph, creating a top-level graph, updating the parser and manager, and returning true values
bool ParseAction(const ResourcePtr &res) {
MS_EXCEPTION_IF_NULL(res);
TraceManager::OpenRecordDebugInfoFlag();
@ -419,6 +429,7 @@ bool ParseAction(const ResourcePtr &res) {
// This step do this optimize: graph1(x){xx(fv1),xxx(fv2)}, graph2(x){xxx(fv3),xxx(fv4)}->
// graph1(x){base_graph(x, fv1, fv2)}, graph1(x){base_graph(x, fv3, fv4)}, base_graph(x, fv...){xxx,xxx}
// all obj_map's graph shared base_graph
bool CombineLikeGraphs(const ResourcePtr &res) {
MS_EXCEPTION_IF_NULL(res);
auto &obj_map = parse::data_converter::GetObjGraphs();
@ -555,6 +566,10 @@ bool EliminateUnusedParameterAction(const ResourcePtr &res) {
return true;
}
// Perform graph abstraction and specialization operations, including obtaining graph objects, parameter specification lists, parallel context objects,
// initializing shape information, obtaining originally loaded graph, processing default parameters, performing abstract analysis,
// updating top-level graph, specializing graph, removing unused nodes, checking input shapes and types, updating graph parameters, and so on.
bool AbstractSpecializeAction(const ResourcePtr &res) {
MS_EXCEPTION_IF_NULL(res);
if (res->func_graph() == nullptr) {
@ -587,14 +602,11 @@ bool AbstractSpecializeAction(const ResourcePtr &res) {
}
// Analyze
AnalysisResult result = AbstractAnalyze(res, func_graph, args_spec);
// The top graph may be replaced by infer, update the top graph when the infer is done
parse::Parser::UpdateTopFuncGraph(result.context->func_graph());
// Specialize
FuncGraphPtr new_fg = ProgramSpecialize(res, result.context->func_graph(), result.context);
res->set_func_graph(new_fg);
// Remove unused nodes in cnode order list, this is prepared for auto-monad.
if (new_fg) {
new_fg->EraseUnusedNodeInOrder();
@ -710,6 +722,8 @@ bool CheckGraphOutputConstOrParameter(const FuncGraphPtr &func_graph) {
return false;
}
// Eliminate forward CNode nodes in Pynative mode, including obtaining graph actuator and Pynative actuator instance, checking execution mode, obtaining process phase,
// processing derived graph and forward process, running gradient calculation and replacing forward node, setting forward eliminating flag, setting gradient graph, modifying output node, etc.
bool EliminateForwardCNode(const ResourcePtr &res) {
// This function only works in Pynative mode. The func_graph is decorated by ms_function.
if (MsContext::GetInstance()->get_param<int>(MS_CTX_EXECUTION_MODE) == kGraphMode) {
@ -721,6 +735,7 @@ bool EliminateForwardCNode(const ResourcePtr &res) {
auto phase = graph_executor->phase();
MS_LOG(DEBUG) << "The phase of current pipeline graph is: " << phase;
// Exporting graph in PyNative mode or only running forward process no need to do this action.
auto pynative_exec = pynative::PynativeExecutor::GetInstance();
if (phase.find("export") == 0 || !pynative_exec->grad_flag()) {
MS_LOG(DEBUG) << "When exporting graph or only running forward process, no need to eliminate forward cnode.";
@ -762,6 +777,7 @@ bool EliminateAdRelatedSpecialOpNode(const ResourcePtr &res) {
return EliminateAdRelatedSpecialOpOptPass(res);
}
// The process of determining whether indirect calls exist includes traversing all nodes, determining Partial, Switch, SwitchLayer, and Call nodes, printing log information, and returning true or false values.
bool HasIncorporateCall(const std::vector<AnfNodePtr> &all_nodes) {
for (const auto &node : all_nodes) {
if (!node->isa<CNode>()) {
@ -872,6 +888,7 @@ void SetRunMode(const FuncGraphPtr &func_graph, compile::Backend *backend_ptr) {
const auto &all_nodes = TopoSort(func_graph->return_node(), SuccDeeperSimple, AlwaysInclude);
// GPU/CPU no need set any context.
if (!ExistTarget(all_nodes, kAscendDevice)) {
return;
}
@ -924,12 +941,15 @@ void SetRunMode(const FuncGraphPtr &func_graph, compile::Backend *backend_ptr) {
return;
}
// GRAPH | normal network and if/for/switch scenario etc : MultiGraph path in MindRT.
// GRAPH | normal network and if/for/switch scenario etc : MultiGraph path in Mind
MS_LOG(INFO) << "Run graph mode with multigraph sink.";
set_ctx(true, true, true);
return;
}
// Set the running mode according to the function graph properties, execution mode, device target, and back-end policy,
//and set the corresponding flag bit and print log information according to the conditions
void OriginSetRunMode(const ResourcePtr &res) {
FuncGraphPtr func_graph = res->func_graph();
MS_EXCEPTION_IF_NULL(func_graph);
@ -964,6 +984,7 @@ void OriginSetRunMode(const ResourcePtr &res) {
}
}
// Perform task launch operations and set the run mode and corresponding graph compilation operations by calling different functions.
bool TaskEmitAction(const ResourcePtr &res) {
MS_EXCEPTION_IF_NULL(res);
FuncGraphPtr func_graph = res->func_graph();
@ -989,6 +1010,7 @@ bool TaskEmitAction(const ResourcePtr &res) {
!is_parallel;
if (context_ptr->get_param<bool>(MS_CTX_ENABLE_MINDRT) && common::GetEnv("DISABLE_ASCEND_MINDRT") != "1") {
// Run in GRAPH_MODE if the func_graph is ms_function or the func_graph contain multi-subgraph.
if (pynative_switch_to_graph_mode) {
context_ptr->set_param<int>(MS_CTX_EXECUTION_MODE, kGraphMode);
MS_LOG(INFO) << "PyNative graph Compile and Run in GRAPH_MODE";
@ -1002,6 +1024,7 @@ bool TaskEmitAction(const ResourcePtr &res) {
MS_EXCEPTION_IF_NULL(bc_ptr);
std::string backend = context_ptr->backend_policy();
// The graph compiling of mindRT.
if ((backend == kMsConvert) && context_ptr->get_param<bool>(MS_CTX_ENABLE_MINDRT)) {
TaskEmitActionForMindRT(res);
if (pynative_switch_to_graph_mode) {
@ -1010,7 +1033,7 @@ bool TaskEmitAction(const ResourcePtr &res) {
return true;
}
// The graph compiling of control sink.
// The graph compiling of control sink
if (IsCtrlSink() && backend == kMsConvert) {
auto graph_id = bc_ptr->CompileGraph(NOT_NULL(func_graph));
res->SetResult(kOutput, graph_id);
@ -1037,12 +1060,14 @@ bool ExecuteAction(const ResourcePtr &res) {
}
std::string backend = MsContext::GetInstance()->backend_policy();
// The graph running of mindRT.
if ((backend == kMsConvert) && MsContext::GetInstance()->get_param<bool>(MS_CTX_ENABLE_MINDRT)) {
ExecuteActionForMindRT(res);
return true;
}
// The graph running of control sink.
if (IsCtrlSink() && backend == kMsConvert) {
auto graph_id = res->GetResult(kOutput).cast<GraphId>();
std::shared_ptr<compile::Backend> bc_ptr = res->GetResult(kBackend).cast<std::shared_ptr<compile::Backend>>();
@ -1097,6 +1122,8 @@ bool StartPSServerAction(const ResourcePtr &res) {
return true;
}
// Initialize the server according to the configuration parameters and run the server.
bool StartServerAction(const ResourcePtr &res) {
MS_EXCEPTION_IF_NULL(res);
FuncGraphPtr func_graph = res->func_graph();
@ -1107,6 +1134,7 @@ bool StartServerAction(const ResourcePtr &res) {
// Update model threshold is a certain ratio of start_fl_job threshold.
// update_model_threshold = start_fl_job_threshold * update_model_ratio.
size_t start_fl_job_threshold = ps::PSContext::instance()->start_fl_job_threshold();
float update_model_ratio = ps::PSContext::instance()->update_model_ratio();
size_t update_model_threshold = static_cast<size_t>(std::ceil(start_fl_job_threshold * update_model_ratio));
@ -1216,6 +1244,7 @@ bool DistributedSplitAction(const ResourcePtr &res) {
// that will result in a synchronization error due to different executing order.
// Here we temporarily avoid the problem by skipping valuenode merging used by parallel related primitive,
// the final solution will be proposed later as a parallel feature.
bool KeepValueNodeDuplication(const AnfNodePtr &value_node, const ResourcePtr &res) {
MS_EXCEPTION_IF_NULL(res);
MS_EXCEPTION_IF_NULL(res->manager());
@ -1248,6 +1277,7 @@ bool RemoveValueNodeDuplicationsAction(const ResourcePtr &res) {
}
auto manager = res->manager();
// Remove duplicated value nodes, due to replace operation, can't use reference.
auto value_nodes = func_graph->value_nodes();
HashCache hash_cache;
HashValue hashes;
@ -1266,6 +1296,8 @@ bool ValidateAction(const ResourcePtr &res) { return ValidatePass(res); }
bool GeSpecializedAction(const ResourcePtr &res) { return GeSpecializedPass(res); }
// Based on the MindIR model information in the resource pointer, convert it to FuncGraphPtr and set it in the resource pointer.
bool SetMindIRGraphAction(const ResourcePtr &res) {
MS_EXCEPTION_IF_NULL(res);
res->set_is_load(true);
@ -1322,6 +1354,7 @@ bool SetMindIRGraphAction(const ResourcePtr &res) {
if (!is_equal_input_args) {
// Use InferMindir which will find c++ infer in eval_map and backend_eval_map;
(void)InferMindir(res->func_graph(), args_spec_list, true);
}
return true;
@ -1342,6 +1375,7 @@ bool PreAdActionPyStub(const ResourcePtr &res) {
return true;
}
// Run the Python optimization procedure on the computation graph associated with the resource pointer
bool OptActionVmPyStub(const ResourcePtr &res) {
if (ActionPyStub(res, opt::python_pass::Phase::OPT)) {
if (opt::python_pass::PyPassManager::GetInstance()->ShouldRenorm()) {
@ -1384,12 +1418,12 @@ bool OptActionGePyStub(const ResourcePtr &res) {
return true;
}
// Returns a vector containing multiple Actionitems
static std::vector<ActionItem> CommonPipeline() {
std::vector<ActionItem> actions;
// Parse the python ast to ANF graph
(void)actions.emplace_back(std::make_pair("parse", ParseAction));
// Resolve the python func
(void)actions.emplace_back(std::make_pair("symbol_resolve", SymbolResolveAction));
@ -1434,18 +1468,13 @@ std::vector<ActionItem> VmPipeline(const ResourcePtr &resource) {
// If enable compilation cache and the cache is read successfully, only do the backend actions.
if (!resource->EnableCompileCache() || resource->func_graph() == nullptr) {
actions = CommonPipeline();
// Optimize
(void)actions.emplace_back(std::make_pair("optimize", VmOptimizeAction));
// Add opt-stage python pass stub
(void)actions.emplace_back(std::make_pair("py_opt", OptActionVmPyStub));
(void)actions.emplace_back(std::make_pair("auto_monad_reorder", OrderEnforceAction));
// Eliminate forward cnode for grad graph
(void)actions.emplace_back(std::make_pair("eliminate_forward_cnode", EliminateForwardCNode));
// Eliminate the virtual mirror node
(void)actions.emplace_back(std::make_pair("eliminate_ad_related_special_op_node", EliminateAdRelatedSpecialOpNode));
@ -1468,7 +1497,6 @@ std::vector<ActionItem> VmPipeline(const ResourcePtr &resource) {
#endif
// Compile the ANF graph
(void)actions.emplace_back(std::make_pair("task_emit", TaskEmitAction));
// Execute the graph
(void)actions.emplace_back(std::make_pair("execute", ExecuteAction));

View File

@ -47,6 +47,8 @@ constexpr char kRolePServer[] = "pserver_";
constexpr char kRolePScheduler[] = "pscheduler_";
constexpr char kGroupCkptFileName[] = "group.ckpt";
// Get cache path defined by user.
// The cache path is in MsContext.
std::string GetUserDefinedCachePath() {
auto user_defined_path = MsContext::GetInstance()->get_param<std::string>(MS_CTX_COMPILE_CACHE_PATH);
if (!user_defined_path.empty()) {
@ -68,6 +70,9 @@ std::string GetCompileCacheDir() {
return compile_cache_dir;
}
// Get current role.
// Not support for windows.
// Roles: kRoleServer, kRolePServer, kRolePScheduler
std::string GetRole() {
#if ((defined ENABLE_CPU) && (!defined _WIN32))
const std::string &server_mode = ps::PSContext::instance()->server_mode();
@ -110,6 +115,7 @@ std::string GetDepFilesHashPath() {
std::string GetGroupCkptSavePath() { return GetCompileCacheDir() + "/" + kGroupCkptFileName; }
// Get hash code of compiled dependency files.
std::string GetCompileDepFilesHash(const py::list &dep_files) {
MS_LOG(DEBUG) << "Dependency files size: " << dep_files.size();
std::vector<std::string> dep_files_path;
@ -251,6 +257,8 @@ bool CompileCacheManager::CheckDepFilesHashConsistency() {
return true;
}
// Load and return the cached function graph based on parallel mode and compilation cache information.
// If loading fails, perform all compilation operations and return a null pointer
FuncGraphPtr CompileCacheManager::GetCachedFuncGraph(const FuncGraphManagerPtr &manager, const py::dict &weights,
const std::string &queue_name) {
// Determine whether to load parallel information.

View File

@ -541,3 +541,6 @@ PYBIND11_MODULE(_c_expression, m) {
#endif
(void)m.def("_ms_memory_recycle", &mindspore::pipeline::MemoryRecycle, "Recycle memory used by mindspore.");
}
//This file is mainly used to initialize functions
//and facilitate direct calls to subsequent files.

View File

@ -42,6 +42,7 @@ FunctionBlock::FunctionBlock(const Parser &parser) : parser_(parser) {
void FunctionBlock::AddPrevBlock(const FunctionBlockPtr &block) { prev_blocks_.push_back(block.get()); }
// Determine whether a node can be isolated based on its type, name information, and whether it has side effects.
static bool CanBeIsolatedNode(const std::string &var_name, const AnfNodePtr &node) {
auto cnode = dyn_cast<CNode>(node);
if (cnode == nullptr || cnode->inputs().empty()) {
@ -107,6 +108,8 @@ void FunctionBlock::WriteVariable(const std::string &var_name, const AnfNodePtr
}
}
// Based on the given variable name, in the assigned_ Vars_ Find the corresponding node in and
// return the value of that node as a local variable. At the same time, mark that the variable has been used.
AnfNodePtr FunctionBlock::ReadLocalVariable(const std::string &var_name) {
auto found = assigned_vars_.find(var_name);
if (found != assigned_vars_.end()) {
@ -268,6 +271,7 @@ AnfNodePtr FunctionBlock::HandleNamespaceInfo(const py::tuple &info) {
return GetResolveNode(info);
}
// Process built-in namespace information and add it to global variables.
AnfNodePtr FunctionBlock::HandleBuiltinNamespaceInfo(const py::tuple &info) {
constexpr size_t closure_info_size = 2;
constexpr size_t namespace_info_size = 4;
@ -336,6 +340,7 @@ AnfNodePtr FunctionBlock::MakeResolveSymbol(const std::string &value) {
}
}
// Create a parsing operation and return the parsing node for subsequent processing and use
AnfNodePtr FunctionBlock::MakeResolveOperation(const std::string &value) {
auto ast = parser_.ast();
MS_EXCEPTION_IF_NULL(ast);
@ -395,6 +400,8 @@ void FunctionBlock::SetPhiArgument(const ParameterPtr &phi) {
}
}
// Search for and replace nodes in the preceding block, find a replacement node that meets the condition,
// and return it. Otherwise, return a null pointer
AnfNodePtr FunctionBlock::SearchReplaceNode(const std::string &var, const ParameterPtr &phi) {
AnfNodePtr arg_node = nullptr;
MS_LOG(DEBUG) << "Prev_blocks size: " << prev_blocks_.size();
@ -622,6 +629,8 @@ void FunctionBlock::FindIsolatedNodes() {
void FunctionBlock::AddIsolatedNode(const AnfNodePtr &target) { isolated_nodes_.add(target); }
// Before returning the function block, add isolated nodes to the dependency and create a new depend_ node.
// The node replaces the original output node, achieving the effect of adding dependency on the new state node on the original output node.
void FunctionBlock::AttachIsolatedNodesBeforeReturn() {
if (isolated_nodes_.empty()) {
return;

View File

@ -130,6 +130,8 @@ void Parser::CleanParserResource() {
ScopeManager::GetInstance().ClearScope();
}
// This function is used to check for missing return statements in the function graph
// and throw an exception when missing statements are found.
void CheckFuncReturn(const FuncGraphPtr &fn, const std::shared_ptr<ParseFunctionAst> &ast) {
// Check whether the functions referred by this function and itself are missing 'return' statement
auto manager = Manage(fn, false);
@ -137,6 +139,8 @@ void CheckFuncReturn(const FuncGraphPtr &fn, const std::shared_ptr<ParseFunction
for (const auto &func_graph : manager->func_graphs()) {
MS_EXCEPTION_IF_NULL(func_graph);
if (func_graph->get_return() != nullptr) {
// If not null, it indicates that the function graph already has a return statement,
// skipping the processing of the current function graph.
continue;
}
py::object node = ast->GetAstNode();
@ -152,6 +156,8 @@ void CheckFuncReturn(const FuncGraphPtr &fn, const std::shared_ptr<ParseFunction
}
}
// Find free variables in the function graph and return information about these free variables.
// Free variables refer to input objects that reference other function graphs in the current function graph.
std::vector<std::pair<CNodePtr, size_t>> GetFreeVariable(const FuncGraphPtr &func_graph) {
// Considering the performance, we didn't use Manager here.
std::vector<std::pair<CNodePtr, size_t>> free_variables;
@ -183,6 +189,11 @@ std::vector<std::pair<CNodePtr, size_t>> GetFreeVariable(const FuncGraphPtr &fun
return free_variables;
}
// This function is used to elevate the free variable of the scrolling function to
// its caller's parameter list, and modify the input parameter nodes in the referenced
// function graph and the input nodes in the calling node. This process,
// also known as free variable lifting, can eliminate dependencies between function graphs
// and achieve better reusability between them.
void Parser::LiftRolledBodyGraphFV() {
for (auto &rolled_call_pair : rolled_body_calls_) {
auto rolled_call_cnode = rolled_call_pair.first;
@ -204,6 +215,11 @@ void Parser::LiftRolledBodyGraphFV() {
}
}
// This function is used to elevate a free variable in a conditional statement branch
// to its caller's parameter list, and modify the input parameter nodes and call nodes
// in the true and false branch function graphs. This process is similar to the free variable
// enhancement of rolling functions, which can eliminate dependencies between function
// graphs and improve code reusability.
void Parser::LiftIfBranchGraphFV() {
for (auto &branch_call_tuple : if_branch_calls_) {
auto call_cnode = std::get<0>(branch_call_tuple);
@ -245,6 +261,11 @@ void Parser::LiftIfBranchGraphFV() {
}
namespace {
// This function converts the first half of the parallel call into a call to the
// intermediate graph, and achieves the conversion process by creating a
// new input node list and updating the output nodes of the first half of the
// call graph. This process may involve graph optimization or automatic parallelization
// in deep learning frameworks.
void TransformParallelCallFormerToMiddle(const FuncGraphPtr &former_call_graph, const FuncGraphPtr &latter_call_graph,
size_t middle_graph_output_cnode_size, bool use_arguments_pack) {
// The 'former_graph_output' is middle graph call.
@ -264,6 +285,12 @@ void TransformParallelCallFormerToMiddle(const FuncGraphPtr &former_call_graph,
former_call_graph->set_output(new_output);
}
// This function converts the call to the middle graph into a call to the second half,
// determines whether parameter packaging (tuples) is needed based on the number
// of input parameters, adjusts the output of the middle graph based on the existence
// of dependent nodes, and updates the output of the second half call graph.
// This process may be used in areas such as graph optimization or automatic parallelization
// in deep learning frameworks.
bool TransformParallelCallMiddleToLatter(const FuncGraphPtr &middle_call_graph,
const CNodePtr &middle_graph_output_cnode,
const AnfNodePtr &middle_graph_dependency_node,
@ -301,6 +328,11 @@ bool IsDependOfIsolatedNodes(const AnfNodePtr &node) {
return sort_rhs_first;
}
// This function is used to obtain the actual output nodes of the intermediate graph.
// If the output node of the middle graph is a null pointer, an exception is thrown.
// If the output node is a Dependent node, obtain the actual output node and dependent
// node, and return them. This function may be used in scenarios such as graph
// optimization or graph transformation in deep learning frameworks.
std::pair<CNodePtr, AnfNodePtr> GetRealMiddleOutputNodes(const FuncGraphPtr &middle_call_graph) {
auto middle_graph_output = middle_call_graph->output();
if (middle_graph_output == nullptr) {
@ -374,6 +406,12 @@ void Parser::TransformParallelCall() {
LiftRolledBodyGraphFV();
}
// Parse functions in Python code and convert them into FuncGraph objects.
// In the parsing process, first determine whether the function type is FunctionDef
// or Lambda based on the node type in AST, and then call the corresponding parsing
// functions for parsing. After parsing is completed, a series of post-processing is required,
// including removing irrelevant Phi functions, checking function return values, and concurrently
// calling and replacing nodes. Finally, the parsed FuncGraph object is returned.
FuncGraphPtr Parser::ParseFuncGraph() {
// Get ast FunctionDef node
py::object node = ast_->GetAstNode();
@ -418,6 +456,10 @@ AnfNodePtr GetMixedPrecisionCastHelp(const FuncGraphPtr &func_graph, const AnfNo
return cast;
}
// This function generates function parameter nodes based on the attribute information of function nodes,
// and stores parameter names and node objects in the function block object block.
// When generating parameter nodes, it will determine whether there are variable length parameters and
// keyword parameters, as well as handle other parameter related information
void Parser::GenerateArgsNodeForFunction(const FunctionBlockPtr &block, const py::object &fn_node) {
py::object func_args = python_adapter::GetPyObjAttr(fn_node, "args");
py::object var_arg_node = python_adapter::GetPyObjAttr(func_args, "vararg");
@ -453,6 +495,9 @@ void Parser::GenerateArgsNodeForFunction(const FunctionBlockPtr &block, const py
}
}
// Generate default values for function parameters and store them in the function
// graph object. When generating default values, it will determine whether the
// default values for the parsing target type and parameters are None and process them separately.
void Parser::GenerateArgsDefaultValueForFunction(const FunctionBlockPtr &block, const py::object &fn_node) {
MS_EXCEPTION_IF_NULL(block);
py::list defaults = ast_->GetArgsDefaultValues(fn_node);
@ -493,6 +538,7 @@ ScopePtr Parser::GetScopeForParseFunction() {
return scope;
}
// Parse and generate function nodes.
FunctionBlockPtr Parser::ParseDefFunction(const py::object &node, const FunctionBlockPtr &block) {
ScopePtr scope = GetScopeForParseFunction();
// The node created in the parsefunction context, will inherit the scope created using scope_guard
@ -559,6 +605,7 @@ FunctionBlockPtr Parser::ParseDefFunction(const py::object &node, const Function
return func_block;
}
// Parse and generate lambda function nodes.
FunctionBlockPtr Parser::ParseLambdaFunction(const py::object &node, const FunctionBlockPtr &block) {
MS_EXCEPTION_IF_NULL(ast_);
ScopePtr scope = GetScopeForParseFunction();
@ -731,6 +778,7 @@ void Parser::UpdateBlockPyParams(const FunctionBlockPtr &block, const FunctionBl
block->UpdateLocalPyParam(keys, values);
}
// Used to generate conditional blocks, including true_block and false_block.
void Parser::MakeConditionBlocks(const FunctionBlockPtr &pre_block, const FunctionBlockPtr &true_block,
const FunctionBlockPtr &false_block) {
MS_EXCEPTION_IF_NULL(true_block);
@ -1002,6 +1050,7 @@ std::vector<AnfNodePtr> Parser::ParseException(const FunctionBlockPtr &block, co
return node_inputs;
}
// Parse the function call in the raise statement and return the parsing result
std::vector<AnfNodePtr> Parser::ParseRaiseCall(const FunctionBlockPtr &block, const py::object &node) {
MS_LOG(DEBUG) << "Process ast Call, the current node is raise.";
// Process function call
@ -1095,6 +1144,9 @@ AnfNodePtr Parser::GenerateAnfNodeForCall(const FunctionBlockPtr &block, const A
return call_anf_node;
}
// Parse the parameters of the function call and store the parsing results in packed_ Arguments
// and groups_ Arguments. At the same time, the function also returns a Boolean value of need_ Unpack,
// indicating whether the parameter needs to be unpacked.
bool Parser::ParseArgsInCall(const FunctionBlockPtr &block, const py::list &args, bool *need_fallback,
std::vector<AnfNodePtr> *packed_arguments, std::vector<AnfNodePtr> *group_arguments) {
MS_LOG(DEBUG) << "Process ast args in call";
@ -1124,6 +1176,9 @@ bool Parser::ParseArgsInCall(const FunctionBlockPtr &block, const py::list &args
return need_unpack;
}
// Parse keyword parameters in function calls and store the parsing results in
// packed_ Arguments. Meanwhile, the function returns a Boolean value of need_ Unpack,
// indicating whether the parameter needs to be unpacked.
bool Parser::ParseKeywordsInCall(const FunctionBlockPtr &block, const py::object &node,
std::vector<AnfNodePtr> *packed_arguments) {
MS_LOG(DEBUG) << "Process ast key words in call";
@ -1252,6 +1307,23 @@ AnfNodePtr Parser::ParseCompare(const FunctionBlockPtr &block, const py::object
return new_node;
}
// This is a function that parses Boolean operations in Python syntax.
// The function parameters include block representing the current function
// block, value_ List represents the list of Boolean operation nodes processed,
// and mode represents the type of Boolean operation (and/or).
// When there is only one node in the node list, directly call the ParseExprNode()
// function to parse the node and return it.
// When there are multiple nodes in the node list, the first node is removed, and
// the remaining nodes rest to form a new list. Then, create two new function blocks true_ Block
// and false_ Block and hijack the tracker TraceGuard in two separate blocks to record its call stack information.
//Next, call the MakeConditionBlocks() function to create a condition block and set it,
// and then determine the Boolean operation type to select sub blocks b1 and b2. For
// the and operation, reset_ Node wrapped in b1, test_ Node wrapped in b2; For the
// or operation, test_ Node wrapped in b1, rest_ Node is wrapped in b2. Rest_ The node
// is obtained by recursively calling the ProcessBoolOpValueList() function.
//Finally, use the conditional node prim:: kPrimSwitch to convert cond_ Node as the
// branching condition, set true_ Block and false_ Run two function blocks as branches and
// switch them_ Add app to block_ In fg. Finally, switch_ The app is returned as the output of the function block.
AnfNodePtr Parser::ProcessBoolOpValueList(const FunctionBlockPtr &block, const py::list &value_list, AstSubType mode) {
// If there is only one bool op now
MS_EXCEPTION_IF_NULL(block);
@ -2317,6 +2389,7 @@ void Parser::HandleAssignSubscript(const FunctionBlockPtr &block, const py::obje
block->WriteVariable(var_name, setitem_app);
}
// Choose appropriate processing methods to handle assignment statements based on the different types of target objects
void Parser::WriteAssignVars(const FunctionBlockPtr &block, const py::object &target_object,
const AnfNodePtr &value_node) {
MS_EXCEPTION_IF_NULL(value_node);
@ -2409,6 +2482,7 @@ bool Parser::IsTensorType(const AnfNodePtr &node, const std::string &script_text
return false;
}
// Create an interpretation node and handle global and local parameters.
AnfNodePtr Parser::MakeInterpretNode(const FunctionBlockPtr &block, const AnfNodePtr &value_node,
const string &script_text) {
MS_EXCEPTION_IF_NULL(block);

View File

@ -33,6 +33,9 @@ static const std::set<std::string> unchanged_named_primitive = {
parse::NAMED_PRIMITIVE_ATTRIBUTE, parse::NAMED_PRIMITIVE_NAMECONSTANT, parse::NAMED_PRIMITIVE_CONSTANT,
parse::NAMED_PRIMITIVE_NUM, parse::NAMED_PRIMITIVE_STR};
// Resolve the name of the node based on the incoming node object and
// type, and return the name. It can be used to process and distinguish
// different types of nodes during the parsing process.
std::string DynamicParser::ParseNodeName(const std::shared_ptr<parse::ParseFunctionAst> &ast, const py::object &node,
parse::AstMainType type) {
MS_EXCEPTION_IF_NULL(ast);
@ -63,6 +66,9 @@ void DynamicParser::ParseInputArgs(const std::shared_ptr<parse::ParseFunctionAst
}
}
// Parse the expression in the if/while statement and obtain the name of the variable
// that needs further processing. It can be used to further parse the conditional judgment
// of if/while statements and extract variable information to adapt to different contextual requirements
bool DynamicParser::ParseIfWhileExprNode(const std::shared_ptr<parse::ParseFunctionAst> &ast, const py::object &node) {
MS_LOG(DEBUG) << "Parse if/while expr";
py::object test_node = python_adapter::GetPyObjAttr(node, parse::NAMED_PRIMITIVE_TEST);
@ -112,6 +118,9 @@ bool DynamicParser::ParseIfWhileExprNode(const std::shared_ptr<parse::ParseFunct
return false;
}
// Analyze the expression in the assignment statement and determine if further
// processing is necessary. It can be used to identify variable information that needs
// to be retained and process it accordingly according to different contextual requirements.
bool DynamicParser::ParseAssignExprNode(const std::shared_ptr<parse::ParseFunctionAst> &ast, const py::object &node) {
MS_LOG(DEBUG) << "Parse assign expr";
py::object value_node = python_adapter::GetPyObjAttr(node, parse::NAMED_PRIMITIVE_VALUE);
@ -140,6 +149,9 @@ bool DynamicParser::ParseAssignExprNode(const std::shared_ptr<parse::ParseFuncti
return false;
}
// Parse the expression in an augmented assignment statement and determine if further processing
// is required. It compares the value of assign_prim with the values in the given string vector
// compare_prim to determine if a specific operation needs to be performed.
bool DynamicParser::ParseAugAssignExprNode(const std::shared_ptr<parse::ParseFunctionAst> &, const py::object &node,
const std::vector<std::string> &compare_prim) {
MS_LOG(DEBUG) << "Parse augassign expr";
@ -168,6 +180,9 @@ bool DynamicParser::ParseAugAssignExprNode(const std::shared_ptr<parse::ParseFun
return ret;
}
// this function parses the body of a for expression, iterates over the nodes in the body,
// and checks if any of them are assignment expressions. It returns true if an assignment
// expression is found, otherwise false.
bool DynamicParser::ParseForExprNode(const std::shared_ptr<parse::ParseFunctionAst> &ast, const py::object &node) {
MS_LOG(DEBUG) << "Parse for expr";
py::object body_node = python_adapter::GetPyObjAttr(node, parse::NAMED_PRIMITIVE_BODY);
@ -188,6 +203,9 @@ bool DynamicParser::ParseForExprNode(const std::shared_ptr<parse::ParseFunctionA
return false;
}
// this function parses the body context of a function or cell by iterating over the nodes
// in the body and calling specific parsing functions based on the node type. It returns
// true if any dynamic expressions are found, otherwise false.
bool DynamicParser::ParseBodyContext(const std::shared_ptr<parse::ParseFunctionAst> &ast, const py::object &fn_node,
const std::vector<std::string> &compare_prim) {
MS_EXCEPTION_IF_NULL(ast);
@ -230,6 +248,8 @@ std::string DynamicParser::GetCellInfo(const py::object &cell) {
return "";
}
// IsDynamicCell checks whether a cell contains any dynamic expressions by creating
// an AST for the cell's code and parsing its input arguments and body context
bool DynamicParser::IsDynamicCell(const py::object &cell) {
std::string cell_info = GetCellInfo(cell);
if (ignore_judge_dynamic_cell.find(cell_info) != ignore_judge_dynamic_cell.end()) {

View File

@ -48,6 +48,7 @@ std::string ReplaceSpecialChar(const std::string &str) {
}
return oss.str();
}
//What this code does is replace "<" in the string with """ and ">" with """ to get a new string
struct AnfDumpHandlerRegister {
AnfDumpHandlerRegister() {
@ -77,6 +78,10 @@ abstract::AbstractBasePtr ClassObject::ToAbstract() {
auto func_ptr = std::make_shared<abstract::PrimitiveAbstractClosure>(prim::kPrimMakeRecord);
return std::make_shared<abstract::PartialAbstractClosure>(func_ptr, args_spec_list);
}
//What this code does is create an abstract object and return its pointer.
//An abstract object consists of a PartialAbstractClosure
//that contains a PrimitiveAbstractClosure object
//and a parameter list containing an AbstractScalar object as part of the application
static inline bool IsSupportedCreateInstanceType(const py::object &obj) {
py::module mod = python_adapter::GetPyModule(PYTHON_MOD_PARSE_MODULE);
@ -87,6 +92,9 @@ static inline bool IsSupportedCreateInstanceType(const py::object &obj) {
}
return res.cast<bool>();
}
//The purpose of this code is to call a function in Python to determine
// whether a given Python object is a type that supports creating instances,
//and return the result
abstract::AbstractBasePtr ClassType::ToAbstract() {
auto abs_scalar =
@ -194,6 +202,9 @@ void BroadenCNodeAbstract(const FuncGraphPtr &func_graph) {
}
}
}
//The function of this code is to expand the CNode node in the given function graph,
// implement the extension by calling the Broaden function,
//and update the abstract properties of the node
void ConvertLoadedGraph(const FuncGraphPtr &func_graph, const ValuePtr &value) {
if (!value->isa<FuncGraph>()) {
@ -224,6 +235,8 @@ void ConvertLoadedGraph(const FuncGraphPtr &func_graph, const ValuePtr &value) {
resolved_graph->set_parameters(input_params);
BroadenCNodeAbstract(resolved_graph);
}
//The purpose of this code is to convert the loaded subgraph object into the corresponding FuncGraph object,
//and update the parameter information and expand the abstract information
bool ResolveObjectToNode(const FuncGraphPtr &func_graph, const py::object &obj, AnfNodePtr *const node) {
AnfNodePtr output = nullptr;
@ -266,6 +279,10 @@ bool ResolveObjectToNode(const FuncGraphPtr &func_graph, const py::object &obj,
*node = output;
return true;
}
//The function of this code is to parse the incoming Python object
//into the corresponding AnfNodePtr node,
// and perform parameter parsing, creating CNode or value nodes
//according to different situations, and converting data types
bool IsAllFuncInValueSequence(const std::vector<ValuePtr> &value_vec) {
if (value_vec.empty()) {
@ -285,6 +302,9 @@ bool IsAllFuncInValueSequence(const std::vector<ValuePtr> &value_vec) {
}
return true;
}
//The purpose of this code is to determine
//whether the element types in the incoming value_vec are all FuncGraph or Primitive objects.
//Returns false if there are non-eligible elements.
AnfNodePtr TransformToMakeTupleNodes(const FuncGraphManagerPtr &manager, const FuncGraphPtr &func_graph,
const std::vector<ValuePtr> &value_vec) {
@ -310,8 +330,10 @@ AnfNodePtr TransformToMakeTupleNodes(const FuncGraphManagerPtr &manager, const F
auto cnode = func_graph->NewCNode(std::move(nodes));
return cnode;
}
//The function of this code is to convert the incoming value_vec into a MakeTuple node,
//and create and add nodes to the node vector nodes according to different situations
// Transform the ValueTuple or ValueList of graph/primitive node to make tuple of const graph/primitive node
// Transform the ValueTuple or ValueList of graph/primitive node to make tuple of const graph/primitive node
bool TransformVectorFuncValueNode(const FuncGraphManagerPtr &manager, const FuncGraphPtr &func_graph,
const ValueNodePtr &value_node, AnfNodePtr *const transformed) {
MS_EXCEPTION_IF_NULL(value_node);
@ -320,14 +342,14 @@ bool TransformVectorFuncValueNode(const FuncGraphManagerPtr &manager, const Func
return false;
}
// (1) The celllist or ordered_cell will be parsed as valuetuple of const graph in it,
// So if has graph in list, try to replace the node with make tuple of graph value node.
// We do this because the graph manager won't investigate the graph inside valuetuple,
// change the vector of graph to be make_tuple of graph value node.
// (2) the primitive valuetuple or valuelist may encounter to abstract error, make it all
// independent nodes.
// (1) The celllist or ordered_cell will be parsed as valuetuple of const graph in it,
// So if has graph in list, try to replace the node with make tuple of graph value node.
// We do this because the graph manager won't investigate the graph inside valuetuple,
// change the vector of graph to be make_tuple of graph value node.
// (2) the primitive valuetuple or valuelist may encounter to abstract error, make it all
// independent nodes.
auto node_tuple_graphs = TransformToMakeTupleNodes(manager, func_graph, value_vec);
// Replace the ret ptr to be make tuple of graph value node
// Replace the ret ptr to be make tuple of graph value node
*transformed = node_tuple_graphs;
return true;
@ -348,16 +370,16 @@ AnfNodePtr ResolveObjectAndAddToManager(const FuncGraphManagerPtr &manager, cons
manager->AddFuncGraph(new_fg);
}
// If the constant node is constant of vector of graph, add graph to manager.
// If the constant node is constant of vector of graph, add graph to manager.
if (IsValueNode<ValueTuple>(resolved_node) || IsValueNode<ValueList>(resolved_node)) {
(void)TransformVectorFuncValueNode(manager, node->func_graph(), resolved_node->cast<ValueNodePtr>(),
&resolved_node);
}
return resolved_node;
}
} // namespace
} // namespace
// Get python object with index from a list or the whole list if the index is not fixed.
// Get python object with index from a list or the whole list if the index is not fixed.
py::object GetObjectFromSequence(const NameSpacePtr &name_space, const SymbolPtr &symbol, const AnfNodePtr &node,
const AnfNodePtr &index_node) {
MS_EXCEPTION_IF_NULL(node);
@ -375,7 +397,7 @@ py::object GetObjectFromSequence(const NameSpacePtr &name_space, const SymbolPtr
// Index is not fixed, return the whole list.
return obj;
}
// It index is a value node, get the item of index directly.
// It index is a value node, get the item of index directly.
const std::string fn = PYTHON_MOD_GET_ITEM_FROM_SEQUENCE;
const std::string module = "mindspore._extends.parse.parser";
auto index = imm_value->value();
@ -511,6 +533,10 @@ bool IsGetItemCNode(const AnfNodePtr &node) {
constexpr auto prim_index = 0;
return IsResolveNodeWithGetItem(cnode->input(prim_index));
}
//The purpose of this code is to determine
//whether the incoming node is a GetItem node,
//by checking the node type, the number of inputs,
//and the node that parses the GetItem. Returns true if all conditions are met. Otherwise, false is returned.
AnfNodePtr ResolveMsClassWithAttr(const FuncGraphManagerPtr &manager, const MsClassObjectPtr &ms_class,
const std::string &attr, const AnfNodePtr &node) {
@ -551,6 +577,8 @@ bool ResolveFuncGraph(const FuncGraphPtr &func_graph, const pipeline::ResourceBa
MS_LOG(ERROR) << "func_graph or resource is null";
return false;
}
//What this code does is parse the incoming func_graph
//and print an error message and return false if the parameter is invalid
opt::irpass::ResolveIRPassLib irpass;
opt::OptimizerPtr opt_resolve =
opt::Optimizer::MakeOptimizer("opt_resolve", res, GetOptResolvePasses(irpass), false, false, false);

View File

@ -90,6 +90,9 @@ bool SimplifyDataStructuresPass(const ResourcePtr &res) {
UpdateArgsSpec(func_graph, res);
return true;
}
//Optimization of Simplified Data Structures PASS aims to
//optimize and simplify data structures in function graphs
//to improve compute performance and reduce memory footprint
bool TransformTopGraphPass(const ResourcePtr &res) {
MS_EXCEPTION_IF_NULL(res);
@ -109,6 +112,10 @@ bool TransformTopGraphPass(const ResourcePtr &res) {
}
return true;
}
//The transformation pass of the top-level function graph
//mainly performs conversion operations on the tuple inputs
//that may exist in the function graph,
//splitting the tuple parameters into a single parameter for subsequent optimization and processing
bool CleanAfterOptAPass(const ResourcePtr &res) {
MS_EXCEPTION_IF_NULL(res);
@ -118,6 +125,11 @@ bool CleanAfterOptAPass(const ResourcePtr &res) {
UpdateArgsSpec(func_graph, res);
return true;
}
//The purpose of cleaning up the optimized pass is to
//do some additional processing and cleaning on the optimized function graph
//to improve the readability and execution efficiency of the code.
//This pass may perform some cleaning operations according to the specific optimization technique
//to ensure that the structure and logic of the function graph are correct and optimal.
FuncGraphPtr PrimBpOptPassStep1(const opt::irpass::OptimizeIRPassLib &irpass, const ResourcePtr &res) {
MS_EXCEPTION_IF_NULL(res);
@ -125,6 +137,8 @@ FuncGraphPtr PrimBpOptPassStep1(const opt::irpass::OptimizeIRPassLib &irpass, co
opt::OptPassConfig pynative_eliminate = opt::OptPassConfig({
irpass.pynative_eliminate_,
});
//Provides a flexible way to configure and organize the execution of optimized passes,
//selecting the required passes and setting their parameters according to your needs
opt::OptPassConfig switch_simplify = opt::OptPassConfig({
irpass.switch_simplify_,
@ -144,6 +158,9 @@ FuncGraphPtr PrimBpOptPassStep1(const opt::irpass::OptimizeIRPassLib &irpass, co
};
return func_graph;
}
//By defining different pass groupings and organizing them in the desired order,
//complex optimization logic can be implemented
//and multiple rounds of optimization of the function graph can be performed to achieve the final result.
FuncGraphPtr PrimBpOptPassStep2(const opt::irpass::OptimizeIRPassLib &irpass, const ResourcePtr &res) {
MS_EXCEPTION_IF_NULL(res);
@ -162,10 +179,10 @@ FuncGraphPtr PrimBpOptPassStep2(const opt::irpass::OptimizeIRPassLib &irpass, co
auto re_auto_monadwrapper = [](const FuncGraphPtr &root, const opt::OptimizerPtr &) -> bool {
return ReAutoMonad(root);
};
OptPassGroupMap map({{"ad_renormalize", opt::OptPassConfig::Renormalize()},
{"ad_inline", inline_opt},
{"ad_special_op_simplify", special_op_simplify},
{"auto_monad_grad", opt::OptPassConfig(re_auto_monadwrapper)}});
OptPassGroupMap map({{"ad_renormalize", opt::OptPassConfig::Renormalize()},//An optimized pass configuration object for renormalization
{"ad_inline", inline_opt},//Used for inline optimization
{"ad_special_op_simplify", special_op_simplify},//Simplified optimization for performing special operations
{"auto_monad_grad", opt::OptPassConfig(re_auto_monadwrapper)}});//Used to automatically process Monad in automatic differentiation and generate gradient functions.
auto prim_bprop_opt_step_2 = opt::Optimizer::MakeOptimizer("prim_bprop_opt_step_2", res, map);
FuncGraphPtr func_graph = res->func_graph();
@ -209,6 +226,10 @@ FuncGraphPtr BpropGraphFinalOptPass(const ResourcePtr &res) {
});
(void)map.emplace_back(std::make_pair("environ_eliminate", environ_eliminate));
}
//Three additional pass groupings are dynamically added to the map
//as needed for appropriate optimization steps in subsequent optimization processes.
//This gives you the flexibility to configure and adjust optimization processes
// to your specific needs for better performance and results.
auto bprop_graph_final_opt = opt::Optimizer::MakeOptimizer("bprop_graph_final_opt", res, map);
FuncGraphPtr func_graph = res->func_graph();
@ -232,6 +253,7 @@ bool parallel_mode() {
std::string parallel_mode = parallel::ParallelContext::GetInstance()->parallel_mode();
return (parallel_mode == parallel::kAutoParallel) || (parallel_mode == parallel::kSemiAutoParallel);
}
//Determine whether you are currently in parallel mode
void AddParallelRenormalize(OptPassGroupMap *map_a) {
if (parallel_mode()) {
@ -242,6 +264,9 @@ void AddParallelRenormalize(OptPassGroupMap *map_a) {
}
}
}
//The purpose of this code is to find the optimization step group "meta_fg_expand"
// based on whether it is currently in parallel mode,
//and insert a parallel optimization step group named "parallel_renormalize" before the combination.
opt::OptPassConfig GetOptPassA1(const opt::irpass::OptimizeIRPassLib &irpass) {
return opt::OptPassConfig({
@ -304,6 +329,9 @@ opt::OptPassConfig GetGeTensorArrayPass(const opt::irpass::OptimizeIRPassLib &ir
irpass.ge_tensor_array_cast_index_,
});
}
//What this code does is create a function called GetGeTensorArrayPass
//that encapsulates two pass functions
//and returns an optimized pass configuration object containing both passes
OptPassGroupMap GetOptPassesA(const opt::irpass::OptimizeIRPassLib &irpass) {
opt::OptPassConfig a_1 = GetOptPassA1(irpass);
@ -391,6 +419,9 @@ OptPassGroupMap GetA1A2(const opt::irpass::OptimizeIRPassLib &irpass) {
OptPassGroupMap a1_a2(opt_a.begin(), opt_a.begin() + a1_a2_len);
return a1_a2;
}
//What this code does is create a function named GetA1A2
//that extracts a pass configuration combination named a1_a2
//that contains the first 9 pass configurations from the pass function obtained from irpass
OptPassGroupMap GetOptPassesAfterCconv(const opt::irpass::OptimizeIRPassLib &irpass) {
opt::OptPassConfig c_1 = opt::OptPassConfig({
@ -495,6 +526,7 @@ OptPassGroupMap GetOptPassesPynativeElim(const opt::irpass::OptimizeIRPassLib &i
});
return map;
}
//Create an optimized pass configuration combination map that contains a pass
OptPassGroupMap GetOptPassesC(const opt::irpass::OptimizeIRPassLib &) {
return OptPassGroupMap({{"renormalize", opt::OptPassConfig::Renormalize()}});
@ -508,6 +540,12 @@ OptPassGroupMap GetControlPhases(const opt::irpass::OptimizeIRPassLib &) {
});
return map;
}
//What this code does is create an optimized pass configuration combination map with two passes.
//One of the passes has the name "control_group"
//and the corresponding configuration is a control flow optimization pass;
//The other pass has the name "renormalize",
//which corresponds to a renormalized pass.
// The function returns this configuration combination as a result.
OptPassGroupMap GetGeSpecializedPhases() {
opt::OptPassConfig ge_ta_size_group = opt::OptPassConfig(opt::irpass::GeTensorArrayPrepare());
@ -519,6 +557,12 @@ OptPassGroupMap GetGeSpecializedPhases() {
});
return map;
}
//What this code does is create an optimized pass configuration combination map with two passes.
//One of the passes is named "ge_ta_size_group",
//and the corresponding configuration is to handle the pass of GeTensorArrayPrepare;
//The other pass, named "ge_ta_passes",
//corresponds to a set of passes used to optimize GeTensorArray.
//The function returns this configuration combination as a result
OptPassGroupMap GetOptPynativeGradEpiloguePhases(const opt::irpass::OptimizeIRPassLib &irpass) {
auto opt_a = GetOptPassesA(irpass);
@ -578,6 +622,7 @@ void ReclaimOptimizer() {
}
g_pass_opts.clear();
}
//Free up optimizer-related assets
bool OptPassGroup(const ResourcePtr &res, const std::string &name) {
MS_EXCEPTION_IF_NULL(res);
@ -585,6 +630,11 @@ bool OptPassGroup(const ResourcePtr &res, const std::string &name) {
MS_LOG(ERROR) << "Opt passes int64_t error";
return false;
}
//This code completes the null determination of resources
// and determines whether the function graph is empty,
//if the function graph is empty,
//it prints an error message and returns false,
//otherwise continue the subsequent optimization pass operation
FuncGraphPtr func_graph = res->func_graph();
MS_LOG(DEBUG) << "Start " << name << " func graph:" << func_graph->ToString() << ", "
@ -622,6 +672,8 @@ bool SliceRecomputeActivationPass(const ResourcePtr &res) {
opt::SliceRecomputedActivationNodes(res->func_graph());
return true;
}
//The function of this code is to perform the SliceRecomputeActivation optimization operation
//on the function graph in the passed resource and return the optimization execution result.
bool CommOpAddAttrs(const ResourcePtr &res) {
MS_EXCEPTION_IF_NULL(res);
@ -693,6 +745,8 @@ bool CconvPass(const ResourcePtr &res) {
res->set_func_graph(new_fg);
return true;
}
//The function of this code is to clone the function graph in the incoming resource
//and update the cloned function graph to the resource
bool PipelineSplitPass(const ResourcePtr &res) { return PipelineSplit(res); }

View File

@ -135,6 +135,9 @@ std::string GetBaseNameForIR(int64_t stage_idx, const std::string &action_name)
oss << std::setfill('0') << std::setw(spaces) << stage_idx << "_" << action_name;
return oss.str();
}
//The definition of an anonymous namespace
//The namespace contains an implementation of the function GetBaseNameForIR
//Based on the given stage index and action name, a baseline name is generated for IR
#endif
bool CheckAllTensor(const ValueTuplePtr &value_tuple) {
@ -147,6 +150,12 @@ bool CheckAllTensor(const ValueTuplePtr &value_tuple) {
}
return true;
}
//A function called CheckAllTensor is defined
//Determines whether a value tuple object contains all tensors
//If there are non-tensor elements in the value tuple,
//or if the element itself is not a value tuple or MetaTensor type
//The function returns false
//The function returns true only if all elements are tensors.
AbstractBasePtr ArgsToAbstract(const ValuePtr &value, bool enable_tuple_broaden = false) {
MS_EXCEPTION_IF_NULL(value);
@ -156,13 +165,22 @@ AbstractBasePtr ArgsToAbstract(const ValuePtr &value, bool enable_tuple_broaden
return abstract::FromValue(value, broaden);
}
//A function called ArgsToAbstract is defined
//The purpose of this function is to convert the given ValuePtr object into an AbstractBasePtr object
//The judgment logic inside the function determines whether a type extension operation
//is required based on different types and conditions
//The final return is the converted AbstractBasePtr object
bool CheckArgValid(const py::handle &arg) {
if (py::isinstance<py::list>(arg) || py::isinstance<py::tuple>(arg)) {
auto vector_arg = py::cast<py::list>(arg);
return std::all_of(vector_arg.begin(), vector_arg.end(), CheckArgValid);
}
//A function called CheckArgValid is defined
//What this function does is check if a given Python object is legitimate
//If the object is a list or tuple, each element in it is checked recursively
//If the object is not a list or tuple, the judgment false is returned directly
if (py::isinstance<py::dict>(arg)) {
auto dict_arg = py::cast<py::dict>(arg);
return std::all_of(dict_arg.begin(), dict_arg.end(), [](const auto &pair) { return CheckArgValid(pair.second); });
@ -184,7 +202,8 @@ bool CheckArgValid(const py::handle &arg) {
"For more details, please refer to the FAQ at https://www.mindspore.cn.";
}
}
//What this code does is check if a given object is of type Tensor or not
//Special handling of boolean tensors prints a warning message
return py::isinstance<py::int_>(arg) || py::isinstance<py::float_>(arg) || py::isinstance<py::none>(arg) ||
py::isinstance<Number>(arg) ||
((py::isinstance<Tensor>(arg) || py::isinstance<CSRTensor>(arg) || py::isinstance<COOTensor>(arg)) &&
@ -215,6 +234,9 @@ void SetLoopCount(const ResourcePtr &resource) {
MS_LOG(INFO) << "Change vm_loop_flag to " << resource->vm_loop_flag() << ", set loop_size to " << loop_size;
}
}
//Set the number of cycles and the vm_loop_flag flag bit based on the current operating environment
//These flag bits are passed to the virtual machine engine
//You can use these flag bits in the virtual machine engine to control how the graph performs
std::map<string, string> GenerateJitConfigMap(const py::dict &jit_config) {
std::map<string, string> ret{};
@ -225,6 +247,8 @@ std::map<string, string> GenerateJitConfigMap(const py::dict &jit_config) {
}
return ret;
}
//What this code does is convert the given dictionary jit_config of type py::dict to a key-value pair mapping of type std::map
//and returns the mapping result
void RecordInitStatus() {
static bool printed = false;
@ -233,6 +257,9 @@ void RecordInitStatus() {
printed = true;
}
}
//A function is defined, RecordInitStatus
//Record the system status during the system initialization phase
//Ensure that the status is logged only once
void RecordExitStatus() { MS_LOG(INFO) << "Status record: system exit."; }
} // namespace
@ -258,6 +285,10 @@ void CheckArgsValid(const py::object &source_obj, const py::tuple &args) {
}
}
}
//A function is defined, CheckArgsValid
//Check the validity of the input parameters
//and throw a type error exception when there are invalid parameters
py::object GraphExecutorPy::GenerateArgumentsKey(const py::tuple &args, bool enable_tuple_broaden) {
MS_LOG(DEBUG) << "GenerateArgumentsKey args size:" << args.size();
@ -277,6 +308,7 @@ py::object GraphExecutorPy::GenerateArgumentsKey(const py::tuple &args, bool ena
}
// If cache matched no need CheckArgsValid
auto iter = g_args_cache.find(args_spec);
if (iter != g_args_cache.end()) {
return py::int_(iter->second);
@ -287,14 +319,18 @@ py::object GraphExecutorPy::GenerateArgumentsKey(const py::tuple &args, bool ena
MS_LOG(INFO) << "Generate a new compile key for new args, key: " << key_counter;
return py::int_(key_counter++);
}
//A simple caching mechanism is implemented
py::bool_ VerifyInputSignature(const py::list &input_signature, const py::tuple &inputs) {
MS_LOG(DEBUG) << "Verify args size:" << inputs.size();
if (inputs.size() != input_signature.size()) {
MS_LOG(ERROR) << "Signature size not equal to args size";
return false;
}
//A function VerifyInputSignature is implemented
//The log message outputs the number of input parameters
//in order to debug and troubleshoot errors
size_t count = 0;
for (auto arg_obj : inputs) {
if (py::isinstance<Tensor>(arg_obj)) {
@ -304,6 +340,10 @@ py::bool_ VerifyInputSignature(const py::list &input_signature, const py::tuple
MS_LOG(ERROR) << "Verify Tensor error, get ptr is null";
return false;
}
//This code implements a one-by-one traversal of the input parameters
//and verify that each parameter is of type Tensor
//Any parameter that is not of a Tensor type will cause validation to fail
auto sig = input_signature[count].cast<std::shared_ptr<MetaTensor>>();
ShapeVector sig_shape = sig->shape();
TypePtr sig_type = sig->Dtype();
@ -325,7 +365,11 @@ py::bool_ VerifyInputSignature(const py::list &input_signature, const py::tuple
return true;
}
//Validation of the data type of each input Tensor is implemented one by one
//The data type is compared to the data type specified in the signature
//If the data type of any input Tensor does not match the signature
//the signature verification fails
ResourcePtr GraphExecutorPy::GetResource(const std::string &phase) {
MS_LOG(DEBUG) << "Phase size:" << info_.size();
if (info_.count(phase) == 0) {
@ -341,6 +385,9 @@ FuncGraphPtr GraphExecutorPy::GetFuncGraph(const std::string &phase) {
}
return info_[phase]->func_graph;
}
//The corresponding resources are obtained according to the given phase
//If a given stage exists in a info_ map container
//the corresponding resource is returned
FuncGraphPtr GraphExecutorPy::GetGradGraph(const std::string &phase) {
if (phase.empty()) {
@ -349,13 +396,15 @@ FuncGraphPtr GraphExecutorPy::GetGradGraph(const std::string &phase) {
if (info_.count(phase) == 0) {
MS_LOG(EXCEPTION) << "No phase in executor:" << phase;
}
auto execute_info = info_[phase];
MS_EXCEPTION_IF_NULL(execute_info);
auto grad_graph = execute_info->grad_graph;
MS_EXCEPTION_IF_NULL(grad_graph);
return grad_graph;
}
//The corresponding gradient map is obtained according to the given phase
//The corresponding gradient map can only be obtained
//if a given stage is present in the actuator
void GraphExecutorPy::SetGradGraph(const FuncGraphPtr &grad_graph, const std::string &phase) {
if (phase.empty()) {
@ -383,6 +432,11 @@ compile::VmEvalFuncPtr GraphExecutorPy::GetVmEvalFunc(const std::string &phase)
MS_LOG(ERROR) << "GetVmEvalFunc vm model can't find kOutput:" << kOutput;
return nullptr;
}
//Implements obtaining the corresponding VmEvalFunc according to a given phase
//Start by getting a resource pointer for a given stage
//Then check if the resource contains a result with the name kOutput
//And the type of the result is compile::VmEvalFuncPtr
//If the condition is met, the result is returned
bool GraphExecutorPy::HasCompiled(const std::string &phase) const {
if (info_.count(phase) == 0) {
@ -426,6 +480,11 @@ py::bytes GraphExecutorPy::GetFuncGraphProto(const std::string &phase, const std
MS_LOG(EXCEPTION) << "Unknown ir type: " << ir_type;
}
//Implements serialization strings that obtain the corresponding function graph
//according to the given phase and IR type (ir_type).
//First get the function graph pointer corresponding to the given stage
//It is then processed differently depending on the IR type
//Finally, the corresponding function graph serialized string is returned
py::bytes GraphExecutorPy::GetOptimizeGraphProto(const std::string &phase) {
if (info_.count(phase) == 0) {
@ -442,6 +501,12 @@ py::bytes GraphExecutorPy::GetOptimizeGraphProto(const std::string &phase) {
}
return proto_str;
}
//A serialized string for the function graph that obtains the optimized graph
//according to a given phase is implemented
//First check if a given stage is present in the actuator
//Then get the function graph pointer of the optimized graph
//by calling the optimize_graph method of the resource
//Finally, the function graph is serialized to a string and returned
void GraphExecutorPy::SetJitConfig(const py::dict &jit_config) { jit_config_ = GenerateJitConfigMap(jit_config); }
@ -455,6 +520,10 @@ py::dict GraphExecutorPy::GetParallelGraphInfo(const std::string &phase) {
return mindspore::parallel::GetParallelCNodeInfoFromGraph(graph);
}
//This code implements the functions of setting the JIT configuration
//and obtaining information about the parallel graph
//The SetJitConfig method converts the incoming Python dictionary into an internal configuration map
//The GetParallelGraphInfo method gets the function graph based on the given stage name
py::dict GraphExecutorPy::GetParameterLayout(const std::string &phase) {
MS_LOG(DEBUG) << "GetParameterLayout!";
@ -466,6 +535,8 @@ py::dict GraphExecutorPy::GetParameterLayout(const std::string &phase) {
}
return mindspore::parallel::GetParameterLayoutFromGraph(graph);
}
//The function is to obtain parameter layout information based on the given phase phase
//and return it as a Python dictionary
py::dict GraphExecutorPy::GetCNodeStrategy(const std::string &phase) {
MS_LOG(DEBUG) << "GetCNodeStrategy!";
@ -481,11 +552,14 @@ py::list GraphExecutorPy::GetParallelParameterNameList(const std::string &phase)
}
return mindspore::parallel::GetParallelParameterNameListFromGraph(graph);
}
//The function is to get a list of parallel parameter names based on the given phase phase
void GraphExecutorPy::SetCNodeStrategy(const std::string &name, const parallel::Strategys &strategy) {
MS_LOG(DEBUG) << "SetCNodeStrategy!";
stra_dict_[phase_][py::str(name)] = strategy;
}
//The function is to store the parallel policy strategy corresponding to the given node name
//in the stra_dict_ member variable of the executor object
size_t GraphExecutorPy::GetNumOpsInfo(const std::string &phase) {
MS_LOG(DEBUG) << "GetNumOpsInfo!";
@ -530,6 +604,9 @@ void GraphExecutorPy::DelNetRes(const py::set &id) {
}
#endif
}
//This method is mainly used to delete specified network resources
//and reset the number of iterations after deletion
void GraphExecutorPy::DelOneNetRes(const py::handle &py_phase) {
if (!pybind11::isinstance<py::str>(py_phase)) {
MS_LOG(ERROR) << "Expect string phase, but got " << py::str(py_phase);
@ -547,6 +624,8 @@ void GraphExecutorPy::DelOneNetRes(const py::handle &py_phase) {
MS_LOG(DEBUG) << "Delete phase: " << phase << ", info size: " << info_.size();
}
}
//The DeleteSource method deletes network resources
//and related information at a specified stage and outputs some related log information.
void GraphExecutorPy::ClearRes() {
MS_LOG(INFO) << "Clean executor resource!";
@ -638,6 +717,10 @@ std::map<std::string, std::pair<PrimitivePyAdapterPtr, std::string>> GraphExecut
return !(IsPrimitiveCNode(node, prim::kPrimConv2D) || IsPrimitiveCNode(node, prim::kPrimMatMul) ||
IsPrimitiveCNode(node, prim::kPrimDepthwiseConv2dNative));
};
//:kPrimConv2D:Determine whether it is a convolution node.
//kPrimMatMul:Determines whether it is a matrix multiplication node
//kPrimDepthwiseConv2dNative:Determines whether it is a deeply separable convolution node
//You can filter out nodes of the specified type
std::vector<AnfNodePtr> nodes = DeepScopedGraphSearchWithFilter(func_graph->get_return(), AlwaysInclude, filter);
auto is_quant_cnode = [](const AnfNodePtr &node) {
return IsPrimitiveCNode(node, prim::kPrimFakeQuantPerLayer) ||
@ -645,6 +728,12 @@ std::map<std::string, std::pair<PrimitivePyAdapterPtr, std::string>> GraphExecut
IsPrimitiveCNode(node, prim::kPrimFakeLearnedScaleQuantPerLayer) ||
IsPrimitiveCNode(node, prim::kPrimFakeLearnedScaleQuantPerChannel);
};
//Determine whether the node is a specific quantization operation type
//kPrimFakeQuantPerLayer:Quantization operation nodes for each layer
//kPrimFakeQuantPerChannel:Quantization operation nodes per channel
//kPrimFakeLearnedScaleQuantPerLayer:Learning scaling quantization operation nodes for each layer
//kPrimFakeLearnedScaleQuantPerChannel:Learning scaling quantization operation nodes per channel
const size_t root_node_size = 3;
const size_t weight_index = 2;
for (const auto &node : nodes) {
@ -701,6 +790,9 @@ void GraphExecutorPy::SaveCompiledGraph(const std::string &phase) {
} else {
MS_LOG(DEBUG) << "Save model parallel parameter layout graph null!";
}
//If there is no result with a key value of kStepParallelGraph in the res object
//a DEBUG level log is output,
//indicating that the model parallel parameter layout graph is empty
MS_LOG(INFO) << "End save compiled func graph!";
}
@ -712,6 +804,8 @@ void GraphExecutorPy::GetGeBackendPolicy() const {
MS_LOG(EXCEPTION) << backend << " backend policy is not supported under ge backend!";
}
}
//This code is used to check if the current backend policy is GE
//and if not, throw an exception
bool IsPhaseExportAir(const std::string &phase) {
auto phase_to_export = "export.air";
@ -740,17 +834,22 @@ std::vector<ActionItem> GetPipeline(const ResourcePtr &resource, const std::stri
ps::PSContext::instance()->is_server()) {
return ServerPipeline(resource);
}
//It determines whether the server mode is federated learning mode or mixed mode
//And whether the current process is a server process
if (ps::PSContext::instance()->is_server()) {
resource->SetResult(kBackend, compile::CreateBackend());
return PServerPipeline(resource);
}
//Determines whether the current process is a server process
if (ps::PSContext::instance()->is_scheduler()) {
return PSchedulerPipeline(resource);
}
//Determines whether the current process is a scheduler process
if (distributed::cluster::ClusterContext::instance()->initialized()) {
auto node = distributed::cluster::ClusterContext::instance()->node();
MS_EXCEPTION_IF_NULL(node);
MS_LOG(INFO) << "Cluster is initialized. This node role is " << node->role();
//Determines whether the cluster environment is initialized
switch (node->role()) {
case ps::core::NodeRole::SERVER:
return PServerPipeline(resource);
@ -771,6 +870,8 @@ std::vector<ActionItem> GetPipeline(const ResourcePtr &resource, const std::stri
}
return GePipeline();
}
//Select different pipeline functions according to different conditions and parameters,
//and return the corresponding results
void GraphExecutorPy::InitCompileCacheInfo(const ResourcePtr &resource, const std::string &phase) {
// The compilation cache only support for training cell or ms_function currently.
@ -965,6 +1066,9 @@ void CacheValidateFuncGraph(const ResourcePtr &resource) {
MsProfile::StatTime("SaveCacheFuncGraph", t2 - t1);
#endif
}
//When the compilation cache function is enabled,
//the computational graph is cached and verified,
//and performance statistics can optionally be recorded
void CheckInterpretNodeLineInfos() {
auto &line_infos = InterpretNodeRecorder::GetInstance().LineInfos();
@ -983,6 +1087,9 @@ void CheckInterpretNodeLineInfos() {
MS_LOG(INFO) << ss.str();
InterpretNodeRecorder::GetInstance().Clear();
}
//Check the line information for the interpretation node,
//and if there is line information,
//print out the code that runs in the JIT fallback and empty the line information for the interpreter node logger
#ifdef ENABLE_DUMP_IR
void RDRRecordGraph(const size_t action_index, const size_t action_size, const std::string &filename,
@ -1007,6 +1114,8 @@ void RDRRecordGraph(const size_t action_index, const size_t action_size, const s
}
}
#endif
//The function is to record the function graph in the pipeline
//using the RDR recorder with macro definition turned on
#ifdef ENABLE_DUMP_IR
void RecordIR(const size_t action_index, const size_t action_size, const std::string &action_name,
@ -1015,19 +1124,22 @@ void RecordIR(const size_t action_index, const size_t action_size, const std::st
*user_graph = graph;
std::string base_name = GetBaseNameForIR(SizeToLong(action_index), action_name);
// Generate IR file in human readable format
// Generate IR file in human readable format
if (action_index == action_size - 1) {
DumpIR(base_name + ".ir", graph, false, kWholeStack);
} else {
DumpIR(base_name + ".ir", graph, false, kTopStack);
}
// Generate IR file in a heavily commented format, which can also be reloaded
// Generate IR file in a heavily commented format, which can also be reloaded
ExportIR(base_name + ".dat", graph);
// Generate IR file in dot format, which can be converted to svg file using graphviz dot command
draw::Draw(base_name + ".dot", graph);
}
}
#endif
//This function generates IR files in different formats
//according to the current action index and action name
//to record the IR representation of the function graph if the conditions are met
#ifndef ENABLE_SECURITY
void SaveGraphForReadability(const std::string &action_name, const FuncGraphPtr graph, const ResourcePtr resource) {
@ -1108,7 +1220,7 @@ bool Pipeline::NeedCreateBackend() {
return std::any_of(actions_.begin(), actions_.end(),
[](const ActionItem &action) { return action.first == "task_emit" || action.first == "execute"; });
}
//If there is an action for some condition, the function returns a true value
void ProcessVmArgInner(const py::tuple &args, const ResourcePtr &res, VectorRef *const arg_list) {
MS_EXCEPTION_IF_NULL(arg_list);
std::size_t size = args.size();
@ -1156,7 +1268,8 @@ void ProcessVmArgInner(const py::tuple &args, const ResourcePtr &res, VectorRef
void GraphExecutorPy::ProcessVmArg(const py::tuple &args, const std::string &phase, VectorRef *const arg_list) {
ProcessVmArgInner(args, GetResource(phase), arg_list);
}
//The ProcessVmArg function implements the processing of args
//and stores the processed results in arg_list.
#ifdef ENABLE_DEBUGGER
void GraphExecutorPy::TerminateDebugger() {
if (Common::GetDebugTerminate()) {
@ -1166,6 +1279,8 @@ void GraphExecutorPy::TerminateDebugger() {
}
}
#endif
//With the debugger enabled, the appropriate debugger termination logic
//can be executed when the program terminates
py::object GraphExecutorPy::Run(const py::tuple &args, const py::object &phase_obj) {
// Mindspore debugger notify main thread to exit after one step, and will not run next step
@ -1218,8 +1333,8 @@ py::object GraphExecutorPy::Run(const py::tuple &args, const py::object &phase_o
if (vm_loop_flag) {
vm_loop = loop_size;
} else {
// Set the loop size in config if graphs nums is 1(is_loop_sin=True), then there will be a loop embrace
// 'Execute(graph)' in GPUSession.
// Set the loop size in config if graphs nums is 1(is_loop_sin=True), then there will be a loop embrace
// 'Execute(graph)' in GPUSession.
ConfigManager::GetInstance().set_gpu_loopsink_size(loop_size);
}
MS_LOG(INFO) << "VM loop size " << vm_loop << ", loopsink size " << vm_loop;
@ -1233,7 +1348,7 @@ py::object GraphExecutorPy::Run(const py::tuple &args, const py::object &phase_o
}
MS_LOG(DEBUG) << "Run end";
return ret;
} // namespace pipeline
} // namespace pipeline
FuncGraphPtr GraphExecutorPy::BuildGraph(const py::dict &init_params, const std::string &phase,
const py::object &broadcast_params) const {
@ -1243,6 +1358,8 @@ FuncGraphPtr GraphExecutorPy::BuildGraph(const py::dict &init_params, const std:
return nullptr;
#endif
}
//Selectively builds different types of graphs based on the macro definition
//and returns the corresponding graph objects.
void GraphExecutorPy::UpdataParamNodeDefaultInput(
const std::string &phase, const std::unordered_map<std::string, tensor::TensorPtr> &params_value) {
@ -1261,6 +1378,8 @@ void GraphExecutorPy::UpdataParamNodeDefaultInput(
}
}
}
//You can update the default input for parameter nodes in a dynamic graph,
// providing default input when you run the graph
void GraphExecutorPy::RunInitGraph(const py::dict &init_params, const std::string &phase) const {
#ifdef ENABLE_D
@ -1272,6 +1391,8 @@ void GraphExecutorPy::RunInitGraph(const py::dict &init_params, const std::strin
}
#endif
}
//According to the situation defined by the macro and the setting of the back-end policy,
//the corresponding initialization diagram operation is executed
void GraphExecutorPy::PyExePath(const py::object &py_exe_path) {
if (!py::isinstance<py::str>(py_exe_path)) {
@ -1281,6 +1402,7 @@ void GraphExecutorPy::PyExePath(const py::object &py_exe_path) {
auto ms_context = MsContext::GetInstance();
ms_context->set_param<std::string>(MS_CTX_PYTHON_EXE_PATH, py_exe_path_s);
}
//You can set the global Python executable path parameter
void GraphExecutorPy::KernelBuildServerDir(const py::object &kernel_build_server_dir) {
if (!py::isinstance<py::str>(kernel_build_server_dir)) {
@ -1290,6 +1412,7 @@ void GraphExecutorPy::KernelBuildServerDir(const py::object &kernel_build_server
auto ms_context = MsContext::GetInstance();
ms_context->set_param<std::string>(MS_CTX_KERNEL_BUILD_SERVER_DIR, kernel_build_server_dir_s);
}
//You can set the global kernel build server directory parameter
bool InitExecDataset(const std::string &queue_name, int64_t iter_num, int64_t batch_size,
const std::vector<TypePtr> &types, const std::vector<std::vector<int64_t>> &shapes,
@ -1432,6 +1555,8 @@ void InitHccl() {
return;
}
#endif
//According to the situation defined by the macro and the setting of the backend policy,
//perform the corresponding HCCL initialization operation
mindspore::python_adapter::set_python_env_flag(true);
uint32_t device_id = ms_context->get_param<uint32_t>(MS_CTX_DEVICE_ID);
@ -1486,6 +1611,8 @@ void FinalizeHccl() {
device::DeviceContextManager::GetInstance().ClearDeviceContexts();
device::KernelRuntimeManager::Instance().ClearRuntimeResource();
}
//Depending on the macro definition and the settings of the backend policy,
//perform the corresponding HCCL termination operation or resource release operation
uint32_t GetHcclRankId() {
uint32_t rank_id = 0;
@ -1495,6 +1622,8 @@ uint32_t GetHcclRankId() {
}
return rank_id;
}
//You can get the rank id of the current HCCL
// and return the default value if the acquisition fails
uint32_t GetHcclRankSize() {
uint32_t rank_size = 0;
@ -1504,6 +1633,8 @@ uint32_t GetHcclRankSize() {
}
return rank_size;
}
//You can get the rank number of the current HCCL
//and return the default value if the acquisition fails
void ExportGraph(const std::string &file_name, const std::string &, const std::string &phase) {
#ifdef ENABLE_D
@ -1512,6 +1643,7 @@ void ExportGraph(const std::string &file_name, const std::string &, const std::s
MS_EXCEPTION(ValueError) << "Only support export file in 'AIR' format with Ascend backend.";
#endif
}
//Perform the appropriate diagram export operation according to the situation defined by the macro
FuncGraphPtr LoadMindIR(const std::string &file_name, char *dec_key, const size_t key_len,
const std::string &dec_mode) {
@ -1535,12 +1667,13 @@ void ReleaseGeTsd() {
(void)context::CloseTsd(context_ptr, true);
}
}
//Free up resources related to GE and TSD
void InitPipeline() {
// set python env flag
// set python env flag
RecordInitStatus();
mindspore::python_adapter::set_python_env_flag(true);
// open tsd before ge initialize
// open tsd before ge initialize
auto ms_context = MsContext::GetInstance();
MS_EXCEPTION_IF_NULL(ms_context);
if (!context::OpenTsd(ms_context)) {
@ -1555,6 +1688,8 @@ void FinalizeBackend() {
(void)context::FinalizeGe(context_ptr);
(void)context::CloseTsd(context_ptr);
}
//Complete the termination of the backend,
//including the termination of GE and the shutdown of TSD
void MemoryRecycle() {
#ifdef ENABLE_DUMP_IR
@ -1568,8 +1703,8 @@ void MemoryRecycle() {
abstract::AnalysisResultCacheMgr::GetInstance().Clear();
abstract::AnalysisContext::ClearContext();
g_args_cache.clear();
// clean static variable to prevent from crash. As static variable is released after
// Python threads is released.
// clean static variable to prevent from crash. As static variable is released after
// Python threads is released.
parse::data_converter::ClearObjectCache();
parse::Parser::CleanParserResource();
parse::CleanDataClassToClassMap();
@ -1580,7 +1715,7 @@ void MemoryRecycle() {
void ClearResAtexit() {
MS_LOG(INFO) << "Pipeline clear all resource";
runtime::OpExecutor::GetInstance().WorkerJoin();
// When the python process exits, the kernels on the device may not have finished executing.
// When the python process exits, the kernels on the device may not have finished executing.
device::KernelRuntimeManager::Instance().WaitTaskFinishOnDevice();
device::DeviceContextManager::GetInstance().WaitTaskFinishOnDevice();
@ -1720,6 +1855,8 @@ py::bytes PyEncrypt(char *plain_data, size_t plain_len, char *key, size_t key_le
auto py_encrypt_data = py::bytes(reinterpret_cast<char *>(encrypt_data.get()), encrypt_len);
return py_encrypt_data;
}
//You can implement that the specified plaintext data is encrypted with the given key
//and encryption mode, and then the encrypted data is returned as a Python byte object
py::bytes PyDecrypt(const std::string &encrypt_data_path, char *key, size_t key_len, const std::string &dec_mode) {
size_t decrypt_len;
@ -1732,6 +1869,8 @@ py::bytes PyDecrypt(const std::string &encrypt_data_path, char *key, size_t key_
auto py_decrypt_data = py::bytes(reinterpret_cast<char *>(decrypt_data.get()), decrypt_len);
return py_decrypt_data;
}
//You can implement the specified encrypted data to be decrypted with the given key and decryption mode,
//and then return the decrypted data as a Python byte object
bool PyIsCipherFile(const std::string &file_path) { return mindspore::IsCipherFile(file_path); }
} // namespace pipeline

View File

@ -58,9 +58,11 @@ void DoExecNonInputGraph(const std::string &phase) {
MS_LOG(ERROR) << "Can not found GraphRunner";
return;
}
//It provides a basic framework for performing non-input graph calculations,
//and the specific calculation logic is implemented in subsequent code
{
// Release GIL before calling into (potentially long-running) C++ code
// Release GIL before calling into (potentially long-running) C++ code
py::gil_scoped_release release;
Status ret = graph_runner->RunGraph(run_options, ge_tensors, &ge_outputs);
if (ret != Status::SUCCESS) {
@ -73,6 +75,7 @@ void DoExecNonInputGraph(const std::string &phase) {
void SetGeOption(const std::map<std::string, std::string> &options) {
ConfigManager::GetInstance().set_ge_initialize_options(options);
}
//We can flexibly set GE's initialization parameters to meet different needs
Status CreateSessionAndGraphRunner(bool is_training = true) {
std::shared_ptr<ge::Session> sess = DfGraphManager::GetInstance().GetGeSession();
@ -98,6 +101,9 @@ Status CreateSessionAndGraphRunner(bool is_training = true) {
DfGraphManager::GetInstance().SetGraphRunner(graph_runner);
return Status::SUCCESS;
}
//The role of this code is to create session and graph runner objects,
//and configure the corresponding options,
//which provides the infrastructure for the calculation process of the model
bool InitExecDatasetGe(const std::string &queue_name, int64_t size, int64_t batch_size,
const std::vector<TypePtr> &types, const std::vector<std::vector<int64_t>> &shapes,
@ -108,11 +114,15 @@ bool InitExecDatasetGe(const std::string &queue_name, int64_t size, int64_t batc
});
ConfigManager::GetInstance().set_dataset_mode(DatasetMode::DS_SINK_MODE);
//Set the dataset mode to Data Drop Mode
ConfigManager::GetInstance().set_iter_num(queue_name, size);
//Set the number of iterations
ConfigManager::GetInstance().set_dataset_phase(phase);
//Set up the dataset stage
DatasetGraphParam param(queue_name, size, batch_size, ge_types, shapes, input_indexes);
ConfigManager::GetInstance().set_dataset_param(param);
//Set some specific configurations for the dataset
if (transform::BuildDatasetGraph(param, phase) != transform::SUCCESS) {
MS_LOG(ERROR) << "Build dateset graph failed.";
@ -169,6 +179,8 @@ void ConvertObjectToTensors(const py::dict &dict, TensorOrderMap *const tensors)
(void)tensors->emplace(name, tensor);
}
}
//By processing key-value pairs in the Python dictionary one by one,
//they are converted into tensors and stored in TensorOrderMap for later use
bool AddDFGraph(const std::map<std::string, ExecutorInfoPtr> &info, const py::dict &init_params,
const std::string &phase, const py::object &broadcast_params) {
@ -211,15 +223,15 @@ bool AddDFGraph(const std::map<std::string, ExecutorInfoPtr> &info, const py::di
}
#ifdef ENABLE_DUMP_IR
if (MsContext::GetInstance()->get_param<bool>(MS_CTX_SAVE_GRAPHS_FLAG)) {
converter.DrawComputeGraph(GetSaveGraphsPathName("ge_graph.dot")); // for debug
converter.DrawInitGraph(GetSaveGraphsPathName("init_graph.dot")); // for debug
converter.DrawSaveCheckpointGraph(GetSaveGraphsPathName("save_checkpoint_graph.dot")); // for debug
converter.DrawComputeGraph(GetSaveGraphsPathName("ge_graph.dot")); // for debug
converter.DrawInitGraph(GetSaveGraphsPathName("init_graph.dot")); // for debug
converter.DrawSaveCheckpointGraph(GetSaveGraphsPathName("save_checkpoint_graph.dot")); // for debug
}
#endif
std::string init_graph = "init_subgraph." + net_id;
std::string checkpoint_name = "save." + net_id;
if (phase.find("train") != std::string::npos) {
(void)DfGraphManager::GetInstance().AddGraph(phase, converter.GetComputeGraph(), {{"ge.exec.variable_acc", "1"}});
(void)DfGraphManager::GetInstance().AddGraph(phase, converter.GetComputeGraph(), {{"ge.exec.variable_acc", "1"}});//Add additional properties to the graph
} else {
(void)DfGraphManager::GetInstance().AddGraph(phase, converter.GetComputeGraph());
}
@ -246,6 +258,8 @@ FuncGraphPtr BuildDFGraph(const std::map<std::string, ExecutorInfoPtr> &info, co
DumpIR("anf_graph.ir", anf_graph, true);
}
#endif
//Computational graphs can be saved in the form of images
//and texts for subsequent visualization, analysis, and debugging
if (!AddDFGraph(info, init_params, phase, broadcast_params)) {
MS_LOG(ERROR) << "GenConvertor failed";
@ -300,7 +314,7 @@ void RunGEInitGraph(const py::dict &init_params, const std::string &phase) {
MS_LOG(EXCEPTION) << "Can not found GraphRunner.";
}
{
// Release GIL before calling into (potentially long-running) C++ code
// Release GIL before calling into (potentially long-running) C++ code
py::gil_scoped_release release;
Status ret = graph_runner->RunGraph(run_options, ge_tensors, &ge_outputs);
if (ret != Status::SUCCESS) {
@ -329,6 +343,9 @@ py::object ExtractGeneralCnodeRet(const AbstractBasePtr &cnode_data, const py::t
MS_LOG(EXCEPTION) << "The number of elements in the outputs : " << data.size()
<< " less than the number of elements required. ";
}
//This code is used to check whether the abstract tensor data output
//by the compute node is available and determine
//whether the amount of output data is consistent with the required quantity.
BaseShapePtr shape = cnode_data->BuildShape();
if (!shape->isa<abstract::Shape>()) {
@ -337,7 +354,7 @@ py::object ExtractGeneralCnodeRet(const AbstractBasePtr &cnode_data, const py::t
auto shape_me = shape->cast<abstract::ShapePtr>()->shape();
auto shape_ge = py::cast<Tensor &>(data[*count]).shape();
if (shape_ge != shape_me) { // dynamic shape
if (shape_ge != shape_me) { // dynamic shape
MS_LOG(WARNING) << "The shape of the " << *count << "th tensor returned: " << shape_ge
<< " is not the same as the shape of the tensor derived: " << shape_me;
}
@ -350,6 +367,7 @@ py::object ExtractGeneralCnodeRet(const AbstractBasePtr &cnode_data, const py::t
<< "only be a tensor or a tuple of tensor, but got " << cnode_data->BuildValue()->ToString()
<< ".";
}
//Used to check whether the data type of the compute node output is an abstract tuple
auto data_tp = cnode_data->cast<AbstractTuplePtr>();
auto elements = data_tp->elements();
size_t size = data_tp->size();
@ -380,6 +398,7 @@ py::object StructureOutput(const AnfNodePtr &output_node, const py::tuple &data,
MS_LOG(EXCEPTION) << "The final anf graph could only have constant, parameter, and operator, but got "
<< output_node->ToString();
}
//Used to check whether the final output of the graph is constant, parameter, or operator
if (output_c->IsApply(prim::kPrimMakeTuple)) {
auto input_list = output_c->inputs();
@ -413,7 +432,7 @@ std::shared_ptr<py::object> DoExecGraph(const FuncGraphPtr &graph, const std::ve
}
{
// Release GIL before calling into (potentially long-running) C++ code
// Release GIL before calling into (potentially long-running) C++ code
py::gil_scoped_release release;
MS_LOG(DEBUG) << "Run graph begin, inputs size is: " << inputs.size();
Status ret = graph_runner->RunGraph(run_options, ge_tensors, &ge_outputs);
@ -447,7 +466,7 @@ std::shared_ptr<py::object> DoExecGraph(const FuncGraphPtr &graph, const std::ve
void ProcessGeArg(const std::map<std::string, ExecutorInfoPtr> &info, const py::tuple &args, const std::string &phase,
std::vector<tensor::TensorPtr> *inputs) {
// check the arg and use the GraphExecutorPy args
// check the arg and use the GraphExecutorPy args
std::size_t size = args.size();
if (info.count(phase) == 0) {
@ -459,8 +478,8 @@ void ProcessGeArg(const std::map<std::string, ExecutorInfoPtr> &info, const py::
MS_LOG(EXCEPTION) << "The real arg num : size = " << size << ". graph_arg_size = " << arg_size;
}
// process the first args of tensor
// only in dataset normal(non-sink) mode, fp_bp graph need input tensors
// process the first args of tensor
// only in dataset normal(non-sink) mode, fp_bp graph need input tensors
if (ConfigManager::GetInstance().dataset_mode() == DS_NORMAL_MODE) {
for (std::size_t i = 0; i < size; i++) {
ValuePtr converted = nullptr;
@ -492,7 +511,7 @@ py::object ExecDFGraph(const std::map<std::string, ExecutorInfoPtr> &info, const
FuncGraphPtr anf_graph = info.at(phase)->func_graph;
std::shared_ptr<py::object> ret_val = std::make_shared<py::object>();
// We will not execute graph when output is constant or just input itself.
// We will not execute graph when output is constant or just input itself.
if (IsGraphOutputValueNodeOrParameter(info.at(phase)->func_graph->output(), args, ret_val)) {
ConfigManager::GetInstance().ResetConfig();
return *ret_val;
@ -517,6 +536,10 @@ void ExportDFGraph(const std::string &file_name, const std::string &phase) {
MS_LOG(ERROR) << "Get graph form DfGraphManager failed!";
return;
}
//It is mainly used to export deep learning framework diagrams to disk files
//for use by other modules or tools.
//You need to obtain the corresponding DfGraphWrapperPtr object
//through DfGraphManager and then export it through this object
transform::DfGraphPtr ge_graph = wrap_ptr->graph_ptr_;
if (ge_graph == nullptr) {
@ -529,5 +552,5 @@ void ExportDFGraph(const std::string &file_name, const std::string &phase) {
}
MS_LOG(INFO) << "Export air model finish.";
}
} // namespace pipeline
} // namespace mindspore
} // namespace pipeline
} // namespace mindspore

View File

@ -49,6 +49,9 @@ std::string GetWorldGroup() {
}
return world_group;
}
//It is mainly used to obtain the communication groups used in the current running environment
// for parallel operations in scenarios such as distributed training.
//Depending on the back-end device, the corresponding communication group name is returned
static int64_t GetRank() {
auto ms_context = MsContext::GetInstance();
@ -64,6 +67,10 @@ static int64_t GetRank() {
}
return global_rank;
}
//It is mainly used to obtain the global ranking of the current process in distributed training.
//First, check whether the global ranking has been set, and if not,
//get the ranking of the current process in the distribution group through the CommManager.
//The rank is then converted to the correct data type and the global rank is returned
static int64_t InferStage(int64_t rank_id, int64_t stage_num, int64_t device_num) {
if (stage_num == 0) {
@ -76,6 +83,12 @@ static int64_t InferStage(int64_t rank_id, int64_t stage_num, int64_t device_num
auto per_stage_rank_num = device_num / stage_num;
return rank_id / per_stage_rank_num;
}
//This function is mainly used to infer the stage of the current process
// based on the given number of stages, the number of devices,
//and the ranking of the current process.
//It calculates the stage it is in by dividing the ranking of the current process
//by the number of rankings for each stage,
//thus determining the stage to which the current process belongs.
static bool HasVirtualDataset(const std::vector<AnfNodePtr> &all_nodes) {
for (auto &node : all_nodes) {
@ -86,7 +99,11 @@ static bool HasVirtualDataset(const std::vector<AnfNodePtr> &all_nodes) {
}
return false;
}
//This function is primarily used to check for the presence of a virtual dataset operation in a given node list.
//It determines whether a node is a virtual dataset operation
//by iterating through each node in the list.
//Returns true if a dummy dataset operation is found; Otherwise, false is returned.
static CNodePtr CreateTupleGetItem(const AnfNodePtr &node, size_t index, const FuncGraphPtr &func_graph) {
MS_EXCEPTION_IF_NULL(node);
MS_EXCEPTION_IF_NULL(func_graph);
@ -105,6 +122,10 @@ static CNodePtr CreateTupleGetItem(const AnfNodePtr &node, size_t index, const F
tuple_get_item->set_abstract(tuple_get_item_abstract);
return tuple_get_item;
}
//This function is primarily used to create an tuple_get_item operation
//that gets the element of the specified subscript from a tuple type node.
// It implements the function of fetching the specified subscript element in the tuple
//by creating a new tuple_get_item operation and setting its input node and Abstract.
static CNodePtr CreateVirtualDataset(const FuncGraphPtr &func_graph) {
mindspore::parallel::OperatorAttrs attrs;
@ -128,6 +149,11 @@ static CNodePtr CreateVirtualDataset(const FuncGraphPtr &func_graph) {
virtual_dataset_node->set_abstract(std::make_shared<abstract::AbstractTuple>(abstract_list));
return virtual_dataset_node;
}
//This function is mainly used to create a virtual dataset (VirtualDataset) operation.
// It implements the function of creating a virtual dataset operation
//by creating a ValueNode node and corresponding parameter list,
//and then creating a new virtual dataset operation CNode with these parameters,
//and setting its in_forward_flag and Abstract.
static std::set<FuncGraphPtr> FindForwardGraph(const FuncGraphPtr &root, const std::vector<AnfNodePtr> &all_nodes) {
std::set<FuncGraphPtr> graph_sets;
@ -176,6 +202,7 @@ static std::set<FuncGraphPtr> FindForwardGraph(const FuncGraphPtr &root, const s
}
return graph_sets;
}
//Used to find the forward graph associated with a given root node
static void InsertVirtualDataset(const FuncGraphPtr &root, const std::vector<AnfNodePtr> &all_nodes) {
MS_EXCEPTION_IF_NULL(root);
@ -219,6 +246,7 @@ static void InsertVirtualDataset(const FuncGraphPtr &root, const std::vector<Anf
}
}
}
//You can automatically insert a VirtualDataset node to control data parallelism
void GenerateDefaultStrategy(const ValueNodePtr &axes, const std::vector<AnfNodePtr> &nodes, const int64_t device_num,
std::vector<std::vector<int64_t>> *default_strategy) {
@ -240,6 +268,8 @@ void GenerateDefaultStrategy(const ValueNodePtr &axes, const std::vector<AnfNode
i += 1;
}
}
//You can quickly generate default policies to control data parallelism
//based on device_num specified default_strategy and number of devices
bool CheckLayout(const ValueNodePtr &axes, bool *need_default_strategy, size_t *axes_size) {
auto strategies = axes->value()->cast<ValueTuplePtr>()->value();
@ -262,22 +292,25 @@ bool CheckLayout(const ValueNodePtr &axes, bool *need_default_strategy, size_t *
}
return true;
}
//You can check whether the layout meets your requirements
//and determine if you need a default policy
bool IsElementWiseNode(const CNodePtr &cnode) {
auto prim = GetCNodePrimitive(cnode);
MS_EXCEPTION_IF_NULL(prim);
return ELEMENT_WISE_NODE_.find(prim->name()) != ELEMENT_WISE_NODE_.end();
}
//You can determine whether a node is an element-by-element operation node
void HandleStrategyForOneHot(std::vector<ValuePtr> *strategy) {
// onehot needs to set layout for output, modify the strategy with an additional dimension
// onehot needs to set layout for output, modify the strategy with an additional dimension
auto input_strategy = GetValue<std::vector<int64_t>>(strategy->at(0));
input_strategy.push_back(1);
strategy->at(0) = MakeValue(input_strategy);
}
void HandleStrategyForMatMul(std::vector<ValuePtr> *strategy, const CNodePtr &cnode) {
// handle strategy for matmul to deal with corresponding dimension
// handle strategy for matmul to deal with corresponding dimension
auto left_matrix_strategy = GetValue<std::vector<int64_t>>(strategy->at(0));
auto right_matrix_strategy = GetValue<std::vector<int64_t>>(strategy->at(1));
auto index_a = left_matrix_strategy.size() - 1;
@ -342,6 +375,8 @@ void HandleSpecialStrategy(std::vector<ValuePtr> *strategy, const CNodePtr &cnod
HandleStrategyForElementWiseNode(strategy, cnode);
}
}
//You can use the corresponding data parallelism strategy
//according to the special node type
void GetInputNodes(const FuncGraphPtr &func_graph, std::vector<AnfNodePtr> *input_nodes) {
auto parameters = func_graph->parameters();
@ -352,6 +387,8 @@ void GetInputNodes(const FuncGraphPtr &func_graph, std::vector<AnfNodePtr> *inpu
input_nodes->push_back(parameter);
}
}
//You can get the input nodes in the function graph
//except for the parameter nodes named "u" and "io"
void GetOutputNodes(const FuncGraphPtr &func_graph, std::vector<AnfNodePtr> *output_nodes) {
auto return_node = func_graph->get_return();
@ -368,6 +405,9 @@ void GetOutputNodes(const FuncGraphPtr &func_graph, std::vector<AnfNodePtr> *out
}
}
}
//You can get the output node in the function graph,
//that is, the input node of the non-Depend child node
//or the MakeTuple node of the return node
bool CheckDeviceNum(const std::vector<std::vector<int64_t>> &strategies, const int64_t &device_num) {
for (size_t i = 0; i < strategies.size(); ++i) {
@ -387,6 +427,8 @@ bool CheckDeviceNum(const std::vector<std::vector<int64_t>> &strategies, const i
}
return true;
}
//It is mainly used in distributed training to check
// whether the number of devices meets the requirements of each policy
void SetOutputLayout(const FuncGraphPtr &func_graph, const AnfNodePtr &out_strategy, const int64_t &device_num) {
auto out_strategy_tuple = out_strategy->cast<ValueNodePtr>();
@ -419,6 +461,9 @@ void SetOutputLayout(const FuncGraphPtr &func_graph, const AnfNodePtr &out_strat
<< " is not equal to out_strategy dimension: " << output_strategy[i].size() << " at index "
<< i;
}
//It is mainly used in distributed training to check
//whether the shape dimension of the output node matches the expected policy dimension.
//If it doesn't match, it can result in incorrect or incomplete data distribution for distributed training
std::vector<ValuePtr> elements;
elements.push_back(MakeValue(output_strategy[i]));
auto prim = GetCNodePrimitive(node);
@ -451,6 +496,8 @@ std::vector<ValuePtr> GetStrategyElements(const CNodePtr &cnode, const std::vect
}
}
return elements;
//It is mainly used to process the input information of the model
//and generate corresponding policy information according to the situation
}
void SetInputLayout(const FuncGraphPtr &func_graph, const AnfNodePtr &in_strategy, const int64_t &device_num) {
@ -506,6 +553,7 @@ void SetInputLayout(const FuncGraphPtr &func_graph, const AnfNodePtr &in_strateg
attrs_temp[parallel::IN_STRATEGY] = strategy;
(void)prim->SetAttrs(attrs_temp);
}
//It is mainly used to input policy information for some special computing node settings
}
void SetStrategyForShard(const FuncGraphPtr &root, const std::vector<AnfNodePtr> &all_nodes,
@ -526,6 +574,9 @@ void SetStrategyForShard(const FuncGraphPtr &root, const std::vector<AnfNodePtr>
}
}
}
//This code snippet finds the nodes of the shard operation
//and sets the layout strategy of the input and output
//by iterating through the nodes in the function graph
// Only auto_parallel and semi_auto_parallel support PipelineSplit
bool PipelineSplit(const ResourcePtr &res) {

View File

@ -40,6 +40,12 @@ void TryToDoReplace(FuncGraphManager *const manager, const AnfNodePtr &node, Has
// Calculate hash value.
size_t h;
//What this code does is try to replace a node.
//It first excludes the case where the value node is a function graph,
//then gets the value object of the node and attempts to replace the node.
//Specifically, the code calculates the hash value of the node
//and makes node replacement or caching based on the hash value
auto hash_iter = hash_value->find(node);
if (hash_iter == hash_value->end()) {
h = hash_combine(to_check_value->hash(), (opt::AbsOf(node)->hash()));
@ -47,13 +53,21 @@ void TryToDoReplace(FuncGraphManager *const manager, const AnfNodePtr &node, Has
} else {
h = hash_iter->second;
}
//What this code does is perform different operations
//depending on whether the node has a hash value or not.
//If the node does not have a hash,
//a new hash value is calculated and stored in a hash table;
//If the node already has a hash, it is taken out directly and stored in the variable h.
auto bucket_iter = hash_cache->find(h);
if (bucket_iter == hash_cache->end()) {
// Meet for the first time, add bucket.
// Meet for the first time, add bucket.
(*hash_cache)[h] = {node};
return;
}
//The function of this code is to find the corresponding cache bucket
//according to the hash value of the node,
//and if it is not found, create a new cache bucket and add the node to it
auto &bucket = bucket_iter->second;
// Check if need to replace node with value node already met.
@ -75,6 +89,12 @@ void TryToDoReplace(FuncGraphManager *const manager, const AnfNodePtr &node, Has
return;
}
}
//The function of this code is to compare
//whether the values of two nodes are equal,
//and if they are, replace the nodes; Otherwise, do nothing.
//The specific value comparison method depends on the type of node,
// for nodes of type tensor::Tensor, call the ValueEqual() function to compare whether their values are equal,
//for other types of nodes, use the operator "==" to compare
// Meet for the first time, append node to bucket.
bucket.emplace_back(node);

View File

@ -285,7 +285,8 @@ Resource::Resource(const py::object &obj)
: engine_(std::make_shared<abstract::AnalysisEngine>(abstract::GetPrimEvaluatorConstructors(), manager_)),
source_input_(obj),
is_cleaned_(false) {}
// The constructor initializes several member variables of the class
//assign initial values to those member variables
Resource::~Resource() {
MS_LOG(DEBUG) << "Resource clear";
@ -325,6 +326,9 @@ Any GetMethodOrAttr(const string &name, const TypeId &type_id, const BuiltInType
}
return method->second;
}
// This function is used to get a method or property
//of the specified name and type from the method_map
//If no matching method or property is found, an empty Any object is returned.
bool Resource::IsTypeInBuiltInMap(const TypeId &type) {
TypeId type_id = NormalizeTypeId(type);
@ -339,12 +343,18 @@ bool Resource::IsTypeInBuiltInMap(const TypeId &type) {
}
return true;
}
// This function is used to determine whether a given type exists
//in the mapping table of built-in methods and properties
//Returns true if present, false otherwise
Any Resource::GetMethodPtr(const TypeId &type, const std::string &name) {
TypeId type_id = NormalizeTypeId(type);
const BuiltInTypeMap &method_map = GetMethodMap();
return GetMethodOrAttr(name, type_id, method_map);
}
//This function is used to get the method pointer of the specified name and type
//from the built-in method mapping table and return it with the Any type.
//If no matching method is found, an empty Any object is returned
Any Resource::GetAttrPtr(const TypeId &type, const std::string &name) {
TypeId type_id = NormalizeTypeId(type);
@ -371,6 +381,11 @@ void Resource::GetCompileCacheResource(const py::list &compile_cache_dep_files,
func_graph_ = compile_cache_manager_->GetCachedFuncGraph(manager_, weights, queue_name);
layout_map_ = compile_cache_manager_->layout_map();
}
//This function is used to initialize and fetch compiled cache resources
//It creating a CompileCacheManager object
//Initialize and save the parallel checkpoint file
//Check the hash consistency of dependent files
//Get cached function graphs and layout maps
void Resource::CacheFuncGraph() const {
FuncGraphPtr layout_fg = nullptr;
@ -381,7 +396,10 @@ void Resource::CacheFuncGraph() const {
}
compile_cache_manager_->CacheFuncGraph(func_graph_, layout_fg);
}
//This function is used to cache the compiled function graph
//It determines whether the current function graph has automatic parallelism enabled
//If so, further obtain a step-by-step parallel function graph
//Cache function diagrams and layout diagrams
void Resource::Clean() {
// AbstractTensor->elements() will be saved in AbstractBasePtrList
args_spec_.clear();

View File

@ -25,6 +25,8 @@ namespace mindspore {
namespace abstract {
thread_local std::string AnalysisSchedule::thread_id_ = "m";
// The role of this code in the MindSpore project is to control the scheduling and execution of threads,
// and realize the dynamic management and control of threads by constantly checking the conditions and executing the corresponding actions through the loop.
void AnalysisSchedule::Schedule() {
const auto checkPeriod = std::chrono::seconds(3);
while (run_ || infer_thread_count_.load() > 0) {
@ -38,6 +40,7 @@ void AnalysisSchedule::Schedule() {
MS_LOG(DEBUG) << "Success to exit.";
}
// The thread that performs an asynchronous task frees up CPU resources so that other threads can continue executing.
void AnalysisSchedule::Yield(const AsyncInferTask *async_infer_task) {
MS_EXCEPTION_IF_NULL(async_infer_task);
{
@ -51,6 +54,12 @@ void AnalysisSchedule::Yield(const AsyncInferTask *async_infer_task) {
activate_thread_cv_.notify_one();
}
// Analyze a member function of the scheduling class.
// Its role is to handle anomalies that occur during analysis.
// Specifically, it logs the first exception and, if the incoming exception is a Python exception, gets the exception stack and logs it;
// Then release all locks so that other threads can continue running;
// Clear the list of ongoing tasks. Finally, the global raw evaluation cache is cleared to avoid the cache containing invalid results.
void AnalysisSchedule::HandleException(const std::exception &ex) {
// Just record the first exception information.
if (!StaticAnalysisException::Instance().HasException()) {
@ -84,12 +93,16 @@ void AnalysisSchedule::HandleException(const std::exception &ex) {
}
}
// Stop the analysis task in progress. It stops a task by creating an asynchronous inference task in a stopped state and adding it to the scheduler.
void AnalysisSchedule::Stop() {
AsyncInferTaskPtr stop_task = AsyncInferTask::MakeShared(std::make_shared<AsyncAbstract>(), kStateStop);
Add2Schedule(stop_task);
MS_LOG(DEBUG) << "Set analysis schedule to stop";
}
// Wait for the analysis task to complete.
// It waits for the task by waiting for the condition variable and checking the number of threads,
// and outputs the relevant information and checks the exception after the task is completed.
void AnalysisSchedule::Wait() {
EnterWaiting();
if (infer_thread_count_.load() > 0) {
@ -104,6 +117,7 @@ void AnalysisSchedule::Wait() {
StaticAnalysisException::Instance().CheckException();
}
// Adds asynchronous inference tasks to the scheduling list and updates related statistics.
void AnalysisSchedule::Add2Schedule(const AsyncInferTaskPtr &async_infer_task_ptr) {
std::lock_guard<std::mutex> lock(activate_thread_lock_);
MS_EXCEPTION_IF_NULL(async_infer_task_ptr);
@ -115,6 +129,9 @@ void AnalysisSchedule::Add2Schedule(const AsyncInferTaskPtr &async_infer_task_pt
<< " schedule list size: " << schedule_list_.size();
}
// Set up the next executable analysis task.
// It determines whether to continue waiting or trigger an infinite loop exception by judging the status of the task and the number of threads in the thread pool,
// and marks the task as ready when it finds a result.
void AnalysisSchedule::SetNextReady() {
if (schedule_list_.empty()) {
return;
@ -154,6 +171,8 @@ void AnalysisSchedule::SetNextReady() {
<< " address: " << async_task.get();
}
// Gets the result of an asynchronous task.
// It determines whether to wait and schedule by judging whether the result is a null pointer, and outputs relevant information after obtaining the result.
AbstractBasePtr AsyncAbstract::GetResult() {
auto ret = TryGetResult();
if (ret != nullptr) {
@ -195,6 +214,8 @@ AbstractFunctionPtr GetAbstractFuncRecursively(const AbstractBasePtr &abs, const
}
} // namespace
// Gets a unique asynchronous abstract function pointer,
// returned directly if it has already been parsed, otherwise retrieved and parsed by a recursive call.
AbstractFunctionPtr AsyncAbstractFuncAtom::GetUnique() {
if (resolved_ != nullptr) {
return resolved_;
@ -208,6 +229,9 @@ AbstractFunctionPtr AsyncAbstractFuncAtom::GetUnique() {
return resolved_;
}
// Converts the AsyncAbstractFuncAtom object to a string representation.
// It determines the content of the returned string by determining whether the member variable resolved_ is a null pointer,
// and calls resolved_'s ToString() method to get more information if needed.
std::string AsyncAbstractFuncAtom::ToString() const {
if (resolved_ == nullptr) {
return "AsyncAbstractFuncAtom(Not Resolved)";
@ -221,6 +245,7 @@ std::string AsyncAbstractFuncAtom::ToString() const {
return buffer.str();
}
// Clear the cache of analysis results, including the original evaluation cache and three different types of cache objects.
void AnalysisResultCacheMgr::Clear() {
prim_eval_cache_->Clear();
std::lock_guard<std::mutex> lock(lock_);
@ -229,6 +254,7 @@ void AnalysisResultCacheMgr::Clear() {
switch_cache_for_check_.clear();
}
// Initializes the switch value by fetching or creating a new asynchronous abstract result object from the cache.
void AnalysisResultCacheMgr::InitSwitchValue(const AnfNodeConfigPtr &conf) {
std::lock_guard<std::mutex> lock(lock_);
AsyncAbstractPtr async_eval_result = switch_cache_.get(conf);
@ -238,6 +264,7 @@ void AnalysisResultCacheMgr::InitSwitchValue(const AnfNodeConfigPtr &conf) {
}
}
// According to the given configuration information, the corresponding switch value is obtained from the analysis result cache.
AbstractBasePtr AnalysisResultCacheMgr::GetSwitchValue(const AnfNodeConfigPtr &conf) {
// don't call lock_.lock(). switch_cache is protected. and it waits for result.
AsyncAbstractPtr async_eval_result = switch_cache_.get(conf);
@ -247,6 +274,7 @@ AbstractBasePtr AnalysisResultCacheMgr::GetSwitchValue(const AnfNodeConfigPtr &c
return async_eval_result->GetResult();
}
// Cache the analysis results and update the asynchronous abstract result objects in the cache by merging the current abstract result with the previous abstract result.
void AnalysisResultCacheMgr::SetCacheValue(const AnfNodeConfigPtr &conf, const AbstractBasePtr &current_abs,
AnalysisConfigAsyncResultCache *cache) {
MS_EXCEPTION_IF_NULL(conf);
@ -277,6 +305,7 @@ void AnalysisResultCacheMgr::SetCacheValue(const AnfNodeConfigPtr &conf, const A
}
}
// Set and check the cache of switch values in the analysis results cache manager.
void AnalysisResultCacheMgr::CheckSwitchValueJoinable(const AnfNodeConfigPtr &conf, const AbstractBasePtr &arg) {
SetCacheValue(conf, arg, &switch_cache_for_check_);
}

View File

@ -30,6 +30,7 @@
namespace mindspore {
namespace abstract {
namespace {
// Record the run logs of the evaluator, including the evaluator name, scope name, and information about the abstract base pointer.
string EvalEntryLogging(const EvaluatorPtr &evaluator, const AbstractBasePtrList &arg_spec_list,
const AnfNodeConfigPtr &out_conf) {
MS_EXCEPTION_IF_NULL(evaluator);
@ -44,6 +45,9 @@ string EvalEntryLogging(const EvaluatorPtr &evaluator, const AbstractBasePtrList
return ss.str();
}
// Check whether the evaluator and output configuration are empty,
// get the node and determine the node type, and then output the appropriate error log based on the node type,
// including the evaluator name, node full name, or debugging information.
void EvalFailLogging(const EvaluatorPtr &evaluator, const AbstractBasePtrList &, const AnfNodeConfigPtr &out_conf) {
MS_EXCEPTION_IF_NULL(evaluator);
if (out_conf != nullptr) {
@ -59,6 +63,7 @@ void EvalFailLogging(const EvaluatorPtr &evaluator, const AbstractBasePtrList &,
}
} // namespace
// Check whether a given parameter is always evaluated, based on the results of previous analysis and the value of the current parameter.
bool CheckIfAlwaysEval(const AnfNodeConfigPtr &conf, const AbstractBasePtr &arg) {
auto new_sequence = dyn_cast<AbstractSequence>(arg);
if (new_sequence != nullptr && new_sequence->sequence_nodes() != nullptr && new_sequence->size() != 0) {
@ -78,6 +83,12 @@ bool CheckIfAlwaysEval(const AnfNodeConfigPtr &conf, const AbstractBasePtr &arg)
return false;
}
// Checks if the argument passed in is empty and throws an exception if it is.
// Enter the new func graph. Gets the current node and the current context, and creates the call configuration.
// Create a new evaluator and get a new context.
// Log new context and call configuration entry events.Increase and check function call depth and stack frame depth.
// If the depth of a function call exceeds the maximum depth limit, output an exception log with methods for adjusting the maximum depth of calls and suggestions on how to avoid stack overflows.
// Output a debug log, showing the evaluator type, name, and depth of incoming function calls and stack frame depth information.
void BaseFuncGraphEvaluator::EnterStackFrame(const AnalysisEnginePtr &engine, const StackFramePtr &current_stack_frame,
const StackFramePtr &new_stack_frame) {
MS_EXCEPTION_IF_NULL(current_stack_frame);
@ -111,6 +122,7 @@ void BaseFuncGraphEvaluator::EnterStackFrame(const AnalysisEnginePtr &engine, co
<< "), enter, function call depth: " << FunctionCallDepth() << " - " << StackFrameDepth();
}
// Leave the current function call stack frame and perform the associated operations and records.
void BaseFuncGraphEvaluator::LeaveStackFrame(const AnalysisEnginePtr &, const StackFramePtr &current_stack_frame) {
MS_EXCEPTION_IF_NULL(current_stack_frame);
// Leave current func graph.
@ -174,6 +186,7 @@ AbstractBasePtr BaseFuncGraphEvaluator::LaunchStackFrame(const AnalysisEnginePtr
return res_base;
}
// Recursively executes the function graph and returns the result
AbstractBasePtr BaseFuncGraphEvaluator::LaunchRecursiveEval(const AnalysisEnginePtr &engine, const FuncGraphPtr &fg,
const AnalysisContextPtr &context) {
MS_EXCEPTION_IF_NULL(fg);
@ -207,6 +220,12 @@ AbstractBasePtr BaseFuncGraphEvaluator::LaunchRecursiveEval(const AnalysisEngine
return res_base;
}
// Checks if the argument passed in is empty and throws an exception if it is.
// Enter the new func graph. Gets the current node and the current context, and creates the call configuration.
// Create a new evaluator and get a new context.
// Log new context and call configuration entry events.Increase and check function call depth and stack frame depth.
// If the depth of a function call exceeds the maximum depth limit, output an exception log with methods for adjusting the maximum depth of calls and suggestions on how to avoid stack overflows.
// Output a debug log, showing the evaluator type, name, and depth of incoming function calls and stack frame depth information.
EvalResultPtr BaseFuncGraphEvaluator::Eval(AnalysisEnginePtr engine, const AbstractBasePtrList &args_abs_list,
const AnfNodeConfigPtr &out_conf) {
auto eval_result = evaluator_cache_mgr_->GetValue(args_abs_list);
@ -301,6 +320,7 @@ EvalResultPtr BaseFuncGraphEvaluator::Eval(AnalysisEnginePtr engine, const Abstr
return res;
}
// Each parameter in the input parameter list is extended and the extended parameter list is stored at the location pointed by broaded_args.
void BroadenArgs(const AbstractBasePtrList &args_spec_list, AbstractBasePtrList *broaded_args) {
MS_EXCEPTION_IF_NULL(broaded_args);
(void)std::transform(args_spec_list.begin(), args_spec_list.end(), std::back_inserter(*broaded_args),
@ -313,6 +333,7 @@ void BroadenArgs(const AbstractBasePtrList &args_spec_list, AbstractBasePtrList
});
}
// The input parameter list is extended or not extended depending on whether the function graph has a flag for ignoring values.
AbstractBasePtrList FuncGraphEvaluator::NormalizeArgs(const AbstractBasePtrList &args_spec_list) const {
MS_EXCEPTION_IF_NULL(func_graph_);
if (func_graph_->has_flag(FUNC_GRAPH_FLAG_IGNORE_VALUE)) {
@ -325,6 +346,11 @@ AbstractBasePtrList FuncGraphEvaluator::NormalizeArgs(const AbstractBasePtrList
return args_spec_list;
}
// Checks if the argument passed in is empty and throws an exception if it is.
// If the func graph has an ignore value flag, the parameter specification list (args_spec_list) is returned directly.
// If the function graph has an undetermined flag, set the ignore value flag to true, normalize the list of parameter specifications,
// and output a debug log. Finally, the normalized parameter specification list is returned.
// If the function graph has neither ignored value flags nor undefined flags, the list of parameter specifications is directly returned.
AbstractBasePtrList FuncGraphEvaluator::BroadenUndeterminedArgs(const AbstractBasePtrList &args_spec_list) {
MS_EXCEPTION_IF_NULL(func_graph_);
if (func_graph_->has_flag(FUNC_GRAPH_FLAG_IGNORE_VALUE)) {
@ -341,6 +367,8 @@ AbstractBasePtrList FuncGraphEvaluator::BroadenUndeterminedArgs(const AbstractBa
return args_spec_list;
}
// The corresponding function graph object is obtained from the input parameter list.
// If it does not exist in the cache, a new function graph object is generated and added to the cache.
FuncGraphPtr FuncGraphEvaluator::GetFuncGraph(AnalysisEnginePtr engine, const AbstractBasePtrList &args_spec_list) {
auto iter = func_graph_cache_.find(args_spec_list);
FuncGraphPtr res;
@ -369,6 +397,13 @@ FuncGraphPtr FuncGraphEvaluator::GetFuncGraph(AnalysisEnginePtr engine, const Ab
return res;
}
// First, check the cache (func_graph_cache_) to see if a function graph object corresponding to the parameter specification list already exists, and return it directly if it does.
// If no corresponding function graph object exists in the cache, a new function graph object is generated based on whether the bound_node() pointer of the current object is empty.
// If bound_node() is not empty, then meta_func_graph_ and bound_node()->debug_info() are used to generate a new function graph object;
// Otherwise, a new function graph object is also generated using meta_func_graph_ and bound_node()->debug_info().
// Create a new clone function graph object (cloned_func_graph) and add it to the cache (func_graph_cache_).
// Add the newly generated function graph object to the engine's function graph manager.
// Finally, the newly generated function graph object is returned.
FuncGraphPtr MetaFuncGraphEvaluator::GetFuncGraph(AnalysisEnginePtr engine, const AbstractBasePtrList &args_spec_list) {
auto iter = func_graph_cache_.find(args_spec_list);
if (iter != func_graph_cache_.end()) {
@ -396,6 +431,17 @@ FuncGraphPtr MetaFuncGraphEvaluator::GetFuncGraph(AnalysisEnginePtr engine, cons
return cloned_func_graph;
}
// The function takes three arguments:
// engine for the analysis engine object,
// args_conf_list for the function parameter configuration list to run,
// out_conf for the output node configuration object.
// converts each configuration object in args_conf_list into a corresponding evaluation result object
// and stores them in args_spec_list. It then normalizes args_spec_list and extends the undefined parameters.
// Next, the function attempts to retrieve the evaluation result object corresponding to args_spec_list from the cache.
// If it does not exist in the cache, the corresponding evaluation function is called to evaluate and the result is stored in the cache.
// If it exists in the cache, the evaluation result object in the cache is returned directly.
// The function also determines whether to update the information of the input sequence node based on the value of the environment variable MS_DEV_ENABLE_DDE before returning the result object.
// If this option is enabled, usage flags for the nodes of the old sequence and the new sequence are recursively synchronized.
EvalResultPtr Evaluator::Run(AnalysisEnginePtr engine, const ConfigPtrList &args_conf_list,
const AnfNodeConfigPtr &out_conf) {
AbstractBasePtrList args_spec_list;
@ -450,6 +496,10 @@ EvalResultPtr Evaluator::Run(AnalysisEnginePtr engine, const ConfigPtrList &args
return eval_result;
}
// determine whether the current Evaluator is a Python Prim Evaluator based on the identifier passed in (identifier_), and if so set is_py_eval to true
// Convert the parameter configuration list (args_conf_list) into an abstract base pointer list (args_spec_list) and process each element in it.
// If the current Evaluator is a Python Prim Evaluator and the parameter configuration object is an AbstractRef type, convert it to an AbstractRefPtr type and extend its ref_key.
// Call EvalPrim function, pass the engine, abstract base pointer list (args_spec_list) and other parameters, and return the evaluation result (EvalResultPtr).
EvalResultPtr TrivialPrimEvaluator::Run(AnalysisEnginePtr engine, const ConfigPtrList &args_conf_list,
const AnfNodeConfigPtr &) {
AbstractBasePtrList args_spec_list;
@ -469,6 +519,9 @@ EvalResultPtr TrivialPrimEvaluator::Run(AnalysisEnginePtr engine, const ConfigPt
return EvalPrim(engine, args_spec_list);
}
// Checks if args_conf_list is empty, and throws an exception if it is empty and the identifiers are not "MakeTupleEvaluator", "MakeListEvaluator", or" RaiseEvaluator".
// Convert each configuration object in args_conf_list to the corresponding evaluation result object and store them in args_spec_list.
// The EvalPrim() function is called for in-place conversion evaluation and the result is stored in res. Finally, it returns res as the result. Note that because caching is not required, the cache manager is not used.
EvalResultPtr TransitionPrimEvaluator::Run(AnalysisEnginePtr engine, const ConfigPtrList &args_conf_list,
const AnfNodeConfigPtr &out_conf) {
if (args_conf_list.empty() && identifier_ != "MakeTupleEvaluator" && identifier_ != "MakeListEvaluator" &&
@ -486,6 +539,8 @@ EvalResultPtr TransitionPrimEvaluator::Run(AnalysisEnginePtr engine, const Confi
return res;
}
// Their main function is to run a Prim algorithm by configuring the list (args_conf_list) and identifier_ (identifier_) based on the parameters passed in,
// and return the evaluation result (EvalResultPtr).
EvalResultPtr SymbolicPrimEvaluator::Run(AnalysisEnginePtr, const ConfigPtrList &args_conf_list,
const AnfNodeConfigPtr &) {
return EvalPrim(args_conf_list);
@ -506,6 +561,12 @@ EvalResultPtr TrackedEvaluator::Run(AnalysisEnginePtr engine, const ConfigPtrLis
return res;
}
// engine represents the analysis engine object, args_conf_list represents the function parameter configuration list to run, and out_conf represents the output node configuration object
// Convert each configuration object in args_conf_list to the corresponding evaluation result object and store them in args_spec_list.
// Checks if the cache manager contains the evaluation result in evaluator_cache_mgr_ and returns it directly if it does.
// Otherwise, it merges some of the application arguments and the remaining arguments into a new parameter configuration list, partial_args_conf_list,
// and calls the evaluator evaluator_ to evaluate.
// The result of the evaluation is stored in the cache manager and the result is returned.
EvalResultPtr PartialAppEvaluator::Run(AnalysisEnginePtr engine, const ConfigPtrList &args_conf_list,
const AnfNodeConfigPtr &out_conf) {
AbstractBasePtrList args_spec_list;
@ -532,6 +593,7 @@ EvalResultPtr PartialAppEvaluator::Run(AnalysisEnginePtr engine, const ConfigPtr
return res;
}
// Run a Prim algorithm and return an EvalResultPtr by configuring the args_conf_list and engine based on the parameters passed in.
EvalResultPtr JEvaluator::Run(AnalysisEnginePtr engine, const ConfigPtrList &args_conf_list, const AnfNodeConfigPtr &) {
AbstractBasePtrList args_spec_list;
(void)std::transform(args_conf_list.begin(), args_conf_list.end(), std::back_inserter(args_spec_list),
@ -577,6 +639,7 @@ EvalResultPtr JEvaluator::Run(AnalysisEnginePtr engine, const ConfigPtrList &arg
return res;
}
// Run the Taylor evaluator.
EvalResultPtr TaylorEvaluator::Run(AnalysisEnginePtr engine, const ConfigPtrList &args_conf_list,
const AnfNodeConfigPtr &) {
AbstractBasePtrList args_spec_list;
@ -598,6 +661,7 @@ EvalResultPtr TaylorEvaluator::Run(AnalysisEnginePtr engine, const ConfigPtrList
return result;
}
// Configure a list (args_conf_list) and an engine based on the parameters passed in to run a Prim algorithm and return an EvalResultPtr. To be specific:
EvalResultPtr ShardEvaluator::Run(AnalysisEnginePtr engine, const ConfigPtrList &args_conf_list,
const AnfNodeConfigPtr &) {
AbstractBasePtrList args_spec_list;
@ -621,6 +685,12 @@ EvalResultPtr ShardEvaluator::Run(AnalysisEnginePtr engine, const ConfigPtrList
}
namespace {
// Reduce the dimension of the tensor.
// axis represents the dimension index to be reduced, orig_abs represents the original tensor, and axis_size represents the size of each dimension.
// Checks if orig_abs is of type AbstractTensor, and throws an exception if it is not. It then takes the shape of the original tensor and calculates the length of the shape.
// Check that axis is in a valid range and throw an exception if it is not
// Check that axis is in a valid range and throw an exception if it is not
// Removes the dimensions specified in the original tensor and returns a new tensor object whose dimensions have been reduced by the specified dimensions.
AbstractBasePtr ReduceDim(int *axis, const AbstractBasePtr &orig_abs, int *axis_size) {
if (!orig_abs->isa<abstract::AbstractTensor>()) {
MS_LOG(EXCEPTION) << "ValueError: orig_abs should be AbstractTensor, but got a " << orig_abs->ToString() << ".";
@ -646,10 +716,13 @@ AbstractBasePtr ReduceDim(int *axis, const AbstractBasePtr &orig_abs, int *axis_
return abs_clone;
}
// Accept the physical view (physical_view_abs), the input axis (in_axes), and the axis size (axis_size) as parameters.
AbstractBasePtr GetLogicalViewAbs(const AbstractBasePtr &physical_view_abs, const ValuePtr &in_axes, int *axis_size) {
MS_EXCEPTION_IF_NULL(physical_view_abs);
MS_EXCEPTION_IF_NULL(in_axes);
auto physical_view_abs_sequence = dyn_cast<abstract::AbstractSequence>(physical_view_abs);
// Determines whether the physical view is of a sequence type, and if so,
// calls the GetLogicalViewAbs function recursively to combine the abstract base pointer list of the subviews into a new logical view abstract base pointer list.
if (physical_view_abs_sequence != nullptr) {
AbstractBasePtrList abs_list = physical_view_abs_sequence->elements();
AbstractBasePtrList logical_view_abs_list;
@ -670,7 +743,9 @@ AbstractBasePtr GetLogicalViewAbs(const AbstractBasePtr &physical_view_abs, cons
}
return std::make_shared<AbstractTuple>(logical_view_abs_list);
}
// If the physical view is not of a sequence type, it is processed according to the type of the input axis.
ValuePtr in_axis = in_axes;
// If the input axis is Int64Imm, the ReduceDim function is called to reduce the dimension of the physical view and the result is returned.
if (in_axis->isa<Int64Imm>()) {
int axis = dyn_cast<Int64Imm>(in_axis)->value();
auto logical_view_abs = ReduceDim(&axis, physical_view_abs, axis_size);
@ -684,6 +759,7 @@ AbstractBasePtr GetLogicalViewAbs(const AbstractBasePtr &physical_view_abs, cons
return physical_view_abs;
}
// Extend the dimensions of the tensor.
AbstractBasePtr ExtendDim(int *axis, const AbstractBasePtr &orig_abs, int axis_size) {
MS_EXCEPTION_IF_NULL(orig_abs);
AbstractBasePtr out_abs = nullptr;
@ -711,65 +787,91 @@ AbstractBasePtr ExtendDim(int *axis, const AbstractBasePtr &orig_abs, int axis_s
return out_abs;
}
// Process physical view
AbstractBasePtr GetPhysicalViewAbs(const AbstractBasePtr &logical_view_abs, const ValuePtr &out_axes, int axis_size) {
// Check whether the logical view abstraction is empty, if it is empty, raise the exception
MS_EXCEPTION_IF_NULL(logical_view_abs);
// Attempts to convert the abstraction of a logical view to an abstract sequence type
auto logical_view_abs_sequence = dyn_cast<abstract::AbstractSequence>(logical_view_abs);
// if the conversion is successful, the logical view is a sequence.
if (logical_view_abs_sequence != nullptr) {
// Gets the element list of a logical view sequence
AbstractBasePtrList logical_view_abs_list = logical_view_abs_sequence->elements();
AbstractBasePtrList physical_view_abs_list;
// Try to convert the value of the output axis to the value sequence type
auto out_axes_seq = dyn_cast<ValueSequeue>(out_axes);
// if the conversion is successful, the output axis is a sequence
if (out_axes_seq != nullptr) {
// Check whether the size of the output axis sequence is equal to the size of the logical view sequence. if not, throw an exception
if (logical_view_abs_list.size() != out_axes_seq->size()) {
MS_LOG(EXCEPTION) << "The size of vmap's 'out_axes' should be equal to the number of results of 'fn': "
<< logical_view_abs_list.size() << ", but got size: " << out_axes_seq->size() << ".";
}
}
// Defines an index variable that traverses the output axis sequence
int index = 0;
// For each element in the logical view sequence, convert according to the corresponding output axis value. And add the result to the physical view sequence
(void)std::transform(
logical_view_abs_list.begin(), logical_view_abs_list.end(), std::back_inserter(physical_view_abs_list),
[&axis_size, &index, &out_axes_seq, out_axes](const AbstractBasePtr &arg_spec) -> AbstractBasePtr {
// Defines a child output axis value that holds the output axis value corresponding to the current element
ValuePtr sub_out_axes = out_axes;
// if the output axis isa sequence, take the value corresponding to the current index from it and update the index
if (out_axes->isa<ValueSequeue>()) {
sub_out_axes = (*out_axes_seq)[index];
index++;
}
// If the current element is an abstract sequence type, this function is called recursively.
if (arg_spec->isa<AbstractSequence>()) {
return GetPhysicalViewAbs(arg_spec, sub_out_axes, axis_size);
}
// If the sub-output axis value is an integer type, then the ExtendDim function is called to extend the dimension of the current element based on the axis value and axis size.
if (sub_out_axes->isa<Int64Imm>()) {
int axis = dyn_cast<Int64Imm>(sub_out_axes)->value();
return ExtendDim(&axis, arg_spec, axis_size);
} else if (sub_out_axes->isa<None>()) {
// If the suboutput axis value is an empty type, return the current element without any conversion.
return arg_spec;
}
// If the suboutput axis value is neither an integer nor an empty type,
MS_LOG(EXCEPTION) << "The axis in vmap's 'out_axes' should be a None or a scalar of type Int64Imm, but got a "
<< sub_out_axes->ToString() << ".";
});
// If the logical view is an abstract list type, Returns an abstract list type
if (logical_view_abs->isa<AbstractList>()) {
// Otherwise an abstract tuple consisting of a sequence of physical views is returned.
return std::make_shared<AbstractList>(physical_view_abs_list);
}
return std::make_shared<AbstractTuple>(physical_view_abs_list);
}
// for the single output case, outputs: A, and out_axes: 1 or (1,).
// If the logical view is not a sequence but a single output, then the output axis should also be a single value
// Define a suboutput axis value to hold the value of the output axis
ValuePtr sub_out_axes = out_axes;
// Try to convert the value of the output axis to the value sequence type
ValueSequeuePtr out_axes_seq = dyn_cast<ValueSequeue>(out_axes);
// if the conversion is successful, the output axis is a sequence
if (out_axes_seq != nullptr) {
// Check whether the output axis sequence size is 1, if not, throw an exception
if (out_axes_seq->size() != 1) {
MS_LOG(EXCEPTION) << "The size of vmap's 'out_axes' should be equal to the result size: 1, but got size: "
<< out_axes_seq->size() << ".";
}
sub_out_axes = (*out_axes_seq)[0];
}
// Define an axis variable that holds the sub-output axis value
int axis = 0;
// Try to convert the sub-output axis value to an integer type
auto axis_int_ptr = dyn_cast<Int64Imm>(sub_out_axes);
// if the conversion succeeds, the integer value is assigned to the axis variable
if (axis_int_ptr != nullptr) {
axis = LongToInt(axis_int_ptr->value());
} else {
MS_LOG(EXCEPTION) << "The axis in vmap's 'out_axes' should be a None or a scalar of type Int64Imm, but got a "
<< sub_out_axes->ToString() << ".";
}
// Call ExtendDim function, extending the dimension of the logical view based on axis variable and axis size, and return the result
return ExtendDim(&axis, logical_view_abs, axis_size);
}
} // namespace
@ -829,15 +931,19 @@ EvalResultPtr VmapEvaluator::Run(AnalysisEnginePtr engine, const ConfigPtrList &
return res;
}
// VirtualEvaluator::Eval method to evaluate the output of VirtualEvaluator
EvalResultPtr VirtualEvaluator::Eval(AnalysisEnginePtr, const AbstractBasePtrList &args_spec_list,
const AnfNodeConfigPtr &out_conf) {
// Check whether the size of the parameter list is as expected, and throw an exception if it is not
if (args_spec_list.size() != args_spec_list_.size()) {
MS_LOG(EXCEPTION) << "Arguments mismatch, parameters no: " << args_spec_list_.size()
<< ", arguments no: " << args_spec_list.size();
}
// Gets the value of the environment variable MS_DEV_ENABLE_DDE. If it is not 0, the function to eliminate unused elements is enabled
static const auto enable_eliminate_unused_element = (common::GetEnv("MS_DEV_ENABLE_DDE") != "0");
// Check each parameter and argument match;
for (std::size_t i = 0; i < args_spec_list.size(); i++) {
// If the argument is null, an exception is thrown
MS_EXCEPTION_IF_NULL(args_spec_list[i]);
// For VirtualAbstractClosure, likely J's bprop, we just set its tuple arguments as used before really grad.
if (enable_eliminate_unused_element && args_spec_list[i]->isa<abstract::AbstractSequence>()) {
@ -845,14 +951,18 @@ EvalResultPtr VirtualEvaluator::Eval(AnalysisEnginePtr, const AbstractBasePtrLis
<< "]: " << args_spec_list[i]->ToString();
SetSequenceElementsUseFlagsRecursively(args_spec_list[i], true);
}
// Join the parameters with the expected ones, throwing an exception if they are incompatible
(void)args_spec_list[i]->Join(args_spec_list_[i]);
}
// Returns evaluation results, including output and attribute value mapping
return std::make_shared<EvalResult>(output_, std::make_shared<AttrValueMap>());
}
// Evaluator::SingleRun method for performing a single evaluation
EvalResultPtr Evaluator::SingleRun(AnalysisEnginePtr engine, const ConfigPtrList &args_conf_list,
const AnfNodeConfigPtr &out_conf) {
EvalResultPtr result;
try {
// Call the Run method, which implements different logic depending on the type of evaluator
result = this->Run(engine, args_conf_list, out_conf);
} catch (const std::exception &ex) {
MS_LOG(INFO) << "Eval " << ToString() << " throw exception.";

File diff suppressed because it is too large Load Diff

View File

@ -40,17 +40,25 @@ inline AbstractBasePtr GetEvaluatedValue(const AnfNodeConfigPtr &conf) {
}
AnfNodePtr BuildValueNode(const ValuePtr &v, const AbstractBasePtr &abs_base) {
// Ensure that the abstract base is not null
MS_EXCEPTION_IF_NULL(abs_base);
// Create a new value node with the given value
AnfNodePtr value_node = NewValueNode(v);
// Set the abstract base of the value node to the given abstract base
value_node->set_abstract(abs_base);
// Log a debug message indicating the creation of a new value node with its corresponding abstract base
MS_LOG(DEBUG) << "Create ValueNode: " << value_node->ToString() << ", with abstract: " << abs_base->ToString();
// Return the new value node
return value_node;
}
bool IsVisible(FuncGraphPtr fg, const FuncGraphPtr &parent) {
// Iterate until the current function graph is nullptr or matches the parent function graph
while (fg != nullptr && fg != parent) {
// Move up to the parent function graph
fg = fg->parent();
}
// Check if the current function graph matches the parent function graph
return fg == parent;
}
@ -77,12 +85,16 @@ bool CanSpecializeValueNode(const AnfNodePtr &node) {
void PurifyAbstractOfSequence(ProgramSpecializer *const specializer) {
constexpr int recursive_level = 2;
// Iterate over the sequence abstract list in the specializer
for (auto &abstract_and_node : specializer->sequence_abstract_list()) {
auto &sequence_abs = abstract_and_node.first;
// Purify the elements of the abstract value
if (!sequence_abs->PurifyElements()) {
// If purification fails, log an error message with the abstract value and corresponding node information
MS_LOG(ERROR) << "Purify elements failed, abstract: " << sequence_abs->ToString()
<< ", node: " << abstract_and_node.second->DebugString(recursive_level);
} else {
// If purification is successful, log a debug message with the abstract value and corresponding node information
MS_LOG(DEBUG) << "Purify elements, abstract: " << sequence_abs->ToString()
<< ", node: " << abstract_and_node.second->DebugString(recursive_level);
}
@ -169,60 +181,96 @@ void EliminateCollectedSequenceNodes(ProgramSpecializer *const specializer) {
} // namespace
FuncGraphPtr ProgramSpecializer::Run(const FuncGraphPtr &fg, const AnalysisContextPtr &context) {
// Check if the function graph and context are not null
MS_EXCEPTION_IF_NULL(fg);
MS_EXCEPTION_IF_NULL(context);
// Log a debug message indicating the specialization of the topmost function graph
MS_LOG(DEBUG) << "Specialize topmost function graph: "
<< (context->func_graph() ? context->func_graph()->ToString() : "FG(Null)");
// If top_context_ is null, set it to the given context and log an info message
if (top_context_ == nullptr) {
top_context_ = context;
MS_LOG(INFO) << "Specialize set top func graph context: " << context->ToString();
}
// Specialize the function graph using the given context and store the result in 'res'
auto res = SpecializeFuncGraph(fg, context);
// Eliminate collected sequence nodes
EliminateCollectedSequenceNodes(this);
// Return the specialized function graph 'res'
return res;
}
FuncGraphPtr ProgramSpecializer::SpecializeFuncGraph(const FuncGraphPtr &fg, const AnalysisContextPtr &context) {
// Check if the function graph and context are not null
MS_EXCEPTION_IF_NULL(fg);
MS_EXCEPTION_IF_NULL(context);
// Check if a specialization for the given context already exists
auto iter = specializations_.find(context->SpecializeKey());
if (iter != specializations_.end()) {
// If a specialization exists, return the corresponding specialized function graph
MS_EXCEPTION_IF_NULL(iter->second);
return iter->second->specialized_func_graph();
}
// Create a new FuncGraphSpecializer instance for the function graph and context
std::shared_ptr<FuncGraphSpecializer> fg_spec = std::make_shared<FuncGraphSpecializer>(this, fg, context);
// Get the specialized function graph from the FuncGraphSpecializer
FuncGraphPtr specialized_func_graph = fg_spec->specialized_func_graph();
// Store the FuncGraphSpecializer instance in the specializations map
specializations_[context->SpecializeKey()] = fg_spec;
// Run the specialization process
fg_spec->Run();
// Return the specialized function graph
return specialized_func_graph;
}
std::shared_ptr<FuncGraphSpecializer> ProgramSpecializer::GetFuncGraphSpecializer(const AnalysisContextPtr &context) {
// Check if the context is not null
MS_EXCEPTION_IF_NULL(context);
// Check if a specialization for the given context exists
auto iter = specializations_.find(context->SpecializeKey());
if (iter != specializations_.end()) {
// Return the corresponding FuncGraphSpecializer instance
return iter->second;
}
// If no specialization exists, return nullptr
return nullptr;
}
void ProgramSpecializer::PutSpecializedAbstract(const CNodePtr &cnode, const AnfNodePtr &func,
const AbstractFunctionPtr &old_abs_func,
const AbstractFunctionPtr &new_abs_func) {
// Check if a specialization for the old abstract function already exists in the specialized abstract map
auto iter = specialized_abs_map_.find(old_abs_func);
if (iter == specialized_abs_map_.end()) {
// If no specialization exists for the old abstract function, add a new entry to the map
MS_LOG(DEBUG) << "Emplace cnode: " << cnode->DebugString() << ", func: " << func->ToString()
<< ", old_abstract: " << old_abs_func->ToString() << ", new_abs_func: " << new_abs_func->ToString();
(void)specialized_abs_map_.emplace(old_abs_func, new_abs_func);
} else {
// If a specialization already exists, compare the new and existing specialized abstract functions
MS_LOG(DEBUG) << "Duplicate abstract from cnode: " << cnode->DebugString() << ", func: " << func->ToString()
<< ", old_abstract: " << old_abs_func->ToString() << ", new_abs_func: " << new_abs_func->ToString();
if (!(*iter->second == *new_abs_func)) {
// If the specialized abstract functions do not match, log an error and replace the existing specialization
MS_LOG(DEBUG) << "Duplicate abstract from cnode: " << cnode->DebugString() << ", func: " << func->ToString()
<< ", old_abstract: " << old_abs_func->ToString() << ", first: " << iter->second->ToString()
<< ", new_abs_func: " << new_abs_func->ToString();
// Cannot determined which one to use.
// Replace the existing specialization with an AbstractError indicating a poly node
const auto poly_abstract = std::make_shared<AbstractError>(kPolyNode, func);
iter->second = poly_abstract;
}
@ -230,19 +278,25 @@ void ProgramSpecializer::PutSpecializedAbstract(const CNodePtr &cnode, const Anf
}
AbstractBasePtr ProgramSpecializer::GetSpecializedAbstract(const AbstractFunctionPtr &old_abs_func) {
// Check if a specialization for the old abstract function exists in the specialized abstract map
auto iter = specialized_abs_map_.find(old_abs_func);
if (iter != specialized_abs_map_.end()) {
// If a specialization is found, log the details and return the specialized abstract function
MS_LOG(DEBUG) << "Find abstract for old_abstract: " << old_abs_func->ToString()
<< ", new_abs_func: " << iter->second->ToString();
// Check if the specialized abstract function is of type AbstractFunction
if (iter->second->isa<AbstractFunction>()) {
return iter->second;
}
// Return nullptr if the specialized abstract function is not of type AbstractFunction
return nullptr;
}
// If no specialization is found, log an error and return nullptr
MS_LOG(DEBUG) << "Cannot find abstract for old_abstract: " << old_abs_func->ToString();
return nullptr;
}
AbstractBasePtr ProgramSpecializer::SpecializeAbstractFuncRecursively(const AbstractFunctionPtr &old_abs_func) {
AbstractBasePtr new_abs = nullptr;
if (old_abs_func->isa<AbstractFuncUnion>()) {
@ -301,23 +355,32 @@ AbstractBasePtr ProgramSpecializer::SpecializeAbstractFuncRecursively(const Abst
}
void ProgramSpecializer::SpecializeCNodeInput0FuncGraph() {
// Retrieve all nodes in the manager.
const auto &all_nodes = mng_->all_nodes();
// Iterate over each node.
for (auto node : all_nodes) {
// Skip nodes that are not CNodes.
if (!node->isa<CNode>()) {
continue;
}
// Get the input 0 of the CNode.
auto &input0 = node->cast<CNodePtr>()->input(0);
MS_EXCEPTION_IF_NULL(input0);
// Skip if the input is a ValueNode of type FuncGraph.
if (IsValueNode<FuncGraph>(input0)) {
continue;
}
// Check the abstract value of input0 and skip if it does not match any specific types.
const auto &old_abs = input0->abstract();
if (!(old_abs->isa<FuncGraphAbstractClosure>() || old_abs->isa<MetaFuncGraphAbstractClosure>() ||
old_abs->isa<AbstractFuncUnion>() || old_abs->isa<PartialAbstractClosure>())) {
continue;
}
// Cast the abstract value to AbstractFunctionPtr.
auto old_abs_func = old_abs->cast<AbstractFunctionPtr>();
// Specialize the abstract function recursively.
auto new_abs_func = SpecializeAbstractFuncRecursively(old_abs_func);
// Update the abstract value of input0 if specialization is successful.
if (new_abs_func != nullptr) {
input0->set_abstract(new_abs_func);
MS_LOG(DEBUG) << "Find specialized abstract for node: " << input0->DebugString()
@ -330,6 +393,7 @@ void ProgramSpecializer::SpecializeCNodeInput0FuncGraph() {
}
}
static int64_t GetNextCounter() {
static int64_t g_CloneCounter = 1;
return g_CloneCounter++;
@ -338,38 +402,49 @@ static int64_t GetNextCounter() {
FuncGraphSpecializer::FuncGraphSpecializer(ProgramSpecializer *const s, const FuncGraphPtr &fg,
const AnalysisContextPtr &context)
: specializer_(s), func_graph_(fg), context_(context) {
// Retrieve the parent function graph specializer from the program specializer.
parent_ = s->GetFuncGraphSpecializer(context->parent());
if (parent_ == nullptr && context->parent()->func_graph() != nullptr) { // If context's not dummy context.
// If the parent is not null and the parent's context has a function graph (not a dummy context),
// then throw an exception.
if (parent_ == nullptr && context->parent()->func_graph() != nullptr) {
MS_LOG(EXCEPTION) << "Parent func graph should be handled in advance, fg: " << fg->ToString()
<< ", context: " << context->ToString() << ", parent context: " << context->parent()->ToString();
}
engine_ = s->engine();
// Retrieve the engine from the program specializer.
engine_ = s->engine()
// Clone the original function graph using the TraceSpecialize clone method.
cloner_ = SpecializerClone(fg, std::make_shared<TraceSpecialize>(GetNextCounter()));
// Get the specialized function graph from the cloned function graphs.
specialized_func_graph_ = cloner_->cloned_func_graphs().find(fg)->second;
// Add the return node and the parameter nodes of the function graph as todo items.
AddTodoItem(fg->get_return());
AddTodoItem(fg->parameters());
}
AnfNodePtr FuncGraphSpecializer::ReplicateDisconnectedNode(const AnfNodePtr &node) {
MS_EXCEPTION_IF_NULL(node);
// If the node is a ValueNode, simply return it as it doesn't need to be replicated.
if (node->isa<ValueNode>()) {
return node;
}
// Get the top specializer for the node.
std::shared_ptr<FuncGraphSpecializer> specializer = GetTopSpecializer(node);
// If had replicated, just return that.
// Check if the node has already been replicated, and if so, return the replicated node.
auto iter = specializer->cloned_nodes().find(node);
if (iter != specializer->cloned_nodes().end()) {
return iter->second;
}
// Clone the disconnected node using the specializer's cloner.
auto new_node = specializer->cloner_->CloneDisconnected(node);
// If the original node is a CNode, ensure that the cloned node is also a CNode and update its inputs.
if (node->isa<CNode>()) {
if (!new_node->isa<CNode>()) {
MS_LOG(EXCEPTION) << "new_node must be a CNode, but is " << new_node->DebugString() << ".";
}
UpdateNewCNodeInputs(node, new_node);
}
// Check if the node has been replicated and ensure it is not the same as the original node.
iter = specializer->cloned_nodes().find(node);
if (iter != specializer->cloned_nodes().end()) {
if (iter->second == node) {
@ -381,33 +456,42 @@ AnfNodePtr FuncGraphSpecializer::ReplicateDisconnectedNode(const AnfNodePtr &nod
return new_node;
}
void FuncGraphSpecializer::UpdateNewCNodeInputs(const AnfNodePtr &node, const AnfNodePtr &new_node) {
// Check if node and c_node are not null.
MS_EXCEPTION_IF_NULL(node);
auto c_node = node->cast<CNodePtr>();
MS_EXCEPTION_IF_NULL(c_node);
// Get the inputs of the c_node.
auto inputs = c_node->inputs();
// Create a vector to store the new inputs.
std::vector<AnfNodePtr> new_inputs;
// Iterate over each input and transform them.
(void)std::transform(
inputs.begin(), inputs.end(), std::back_inserter(new_inputs), [this](const AnfNodePtr &inp) -> AnfNodePtr {
// Replicate the disconnected node.
auto new_inp = ReplicateDisconnectedNode(inp);
// Refer the comments in BuildReplacedNode.
// Check if the input is a CNode.
if (inp->isa<CNode>()) {
auto c_inp = inp->cast<CNodePtr>();
MS_EXCEPTION_IF_NULL(c_inp);
auto c_new_inp = new_inp->cast<CNodePtr>();
MS_EXCEPTION_IF_NULL(c_new_inp);
MS_EXCEPTION_IF_NULL(c_new_inp->func_graph());
// Replace the original CNode with the replicated CNode in the function graph.
MS_LOG(DEBUG) << "Replace in order, inp node: " << inp->DebugString() << " -> " << new_inp->DebugString();
c_new_inp->func_graph()->ReplaceInOrder(c_inp, c_new_inp);
}
return new_inp;
});
// Set the new inputs for the new_node.
auto c_new_node = new_node->cast<CNodePtr>();
MS_EXCEPTION_IF_NULL(c_new_node);
c_new_node->set_inputs(new_inputs);
}
AnfNodePtr FuncGraphSpecializer::GetReplicatedNode(const AnfNodePtr &node) {
std::shared_ptr<FuncGraphSpecializer> specializer = GetTopSpecializer(node);
auto iter = specializer->cloned_nodes().find(node);
@ -464,13 +548,17 @@ std::shared_ptr<FuncGraphSpecializer> FuncGraphSpecializer::GetTopSpecializer(co
}
void FuncGraphSpecializer::Run() {
// Print debug information about the original and cloned function graphs.
MS_LOG(DEBUG) << "Before run, origin func graph name: " << (func_graph_ ? func_graph_->ToString() : "FG(Null)")
<< ", cloned func graph name: "
<< (specialized_func_graph_ ? specialized_func_graph_->ToString() : "FG(Null)") << ", func graph: "
<< (func_graph_ ? func_graph_->get_return() ? func_graph_->get_return()->DebugString() : "return null"
: "FG(null)");
// Perform the first pass of the specialization process.
FirstPass();
// Perform the second pass of the specialization process.
SecondPass();
// Print debug information after the specialization process is completed.
MS_LOG(DEBUG) << "After run, origin func graph name: " << (func_graph_ ? func_graph_->ToString() : "FG(Null)")
<< ", cloned func graph name: "
<< (specialized_func_graph_ ? specialized_func_graph_->ToString() : "FG(Null)") << ", new func graph: "
@ -480,6 +568,7 @@ void FuncGraphSpecializer::Run() {
: "FG(null)");
}
void FuncGraphSpecializer::FirstPass() {
while (todo_.size()) {
AnfNodePtr node = todo_.back();
@ -607,34 +696,49 @@ void UpdateSequenceNode(const AnfNodePtr &new_node, const AnfNodePtr &old_node,
// Purify specific input of a CNode.
template <typename T>
void PurifySequenceValueNode(const CNodePtr &cnode, size_t index, ProgramSpecializer *const specializer) {
// Get the original input value at the specified index.
const auto &old_input = cnode->input(index);
// Attempt to cast the input value to a shared pointer of type T.
auto sequence_value = GetValueNode<std::shared_ptr<T>>(old_input);
// If the cast fails or the sequence value is null, return without further processing.
if (sequence_value == nullptr) {
return;
}
// Retrieve the use flags for the elements in the sequence node.
auto flags = GetSequenceNodeElementsUseFlags(old_input);
// If the flags are null, return without further processing.
if (flags == nullptr) {
return;
}
// Initialize variables for collecting dead node positions and updated elements.
std::vector<size_t> dead_node_positions;
ValuePtrList elements;
// Iterate over each element in the sequence node.
for (size_t i = 0; i < (*flags).size(); ++i) {
// Get the old sequence value at position i.
ValuePtr old_sequence_value = sequence_value->value()[i];
auto old_sequence_str_value = old_sequence_value->cast<StringImmPtr>();
// Check if the flag for this element is false. If so, replace the element with zero and log the information.
if (!(*flags)[i]) {
auto zero = MakeValue(0);
(void)elements.emplace_back(zero);
MS_LOG(DEBUG) << "Erase elements[" << i << "] as zero for " << old_input->DebugString() << ", which is inputs["
<< index << "] of " << cnode->DebugString();
} else if (old_sequence_str_value != nullptr && old_sequence_str_value->value() == kDeadNodeName) {
}
// Check if the old sequence value is a StringImmPtr and its value is equal to kDeadNodeName.
// If so, collect the position for erasing later and add the old sequence value to the updated elements.
else if (old_sequence_str_value != nullptr && old_sequence_str_value->value() == kDeadNodeName) {
MS_LOG(DEBUG) << "Collect for erasing elements[" << i << "] DeadNode as zero for " << old_input->DebugString()
<< ", which is inputs[" << index << "] of " << cnode->DebugString();
(void)dead_node_positions.emplace_back(i);
(void)elements.emplace_back(old_sequence_value);
} else {
}
// Otherwise, add the old sequence value to the updated elements.
else {
(void)elements.emplace_back(old_sequence_value);
}
}
}
auto new_sequence_value = std::make_shared<T>(elements);
auto new_input = NewValueNode(new_sequence_value);
auto new_input_abs = new_sequence_value->ToAbstract();

View File

@ -81,17 +81,19 @@ size_t StackFrameDepth() { return stack_frame_depth; }
size_t StackFrameMaxDepth() { return stack_frame_max_depth; }
EvalResultPtr PrimitiveEvalCache::Get(const PrimitivePtr &prim, const AbstractBasePtrList &args) const {
std::lock_guard<std::mutex> guard(mutex_);
auto cache_iter = prim_cache_.find(prim->name());
if (cache_iter == prim_cache_.end()) {
return nullptr;
std::lock_guard<std::mutex> guard(mutex_); // Locks the mutex to ensure atomic execution
auto cache_iter = prim_cache_.find(prim->name()); // Looks up the prim_cache_ map using the name() method of the Primitive object prim
if (cache_iter == prim_cache_.end()) { // If the key is not found in the map
return nullptr; // Returns a null pointer
}
auto &cache = cache_iter->second;
auto iter = cache.find(PrimitiveEvalCacheKey{prim->attrs(), args});
if (iter == cache.end()) {
return nullptr;
auto &cache = cache_iter->second; // Obtains a reference to the value (a map) corresponding to the key in cache_iter
auto iter = cache.find(PrimitiveEvalCacheKey{prim->attrs(), args}); // Searches the cache map using a PrimitiveEvalCacheKey object created from prim's attributes and args
if (iter == cache.end()) { // If the key is not found in the map
return nullptr; // Returns a null pointer
}
return iter->second;
return iter->second; // Returns the value (a shared pointer to an EvalResult object) corresponding to the key in iter
}
void PrimitiveEvalCache::Put(const PrimitivePtr &prim, AttrValueMap &&attrs, const AbstractBasePtrList &args,
@ -106,41 +108,54 @@ void PrimitiveEvalCache::Clear() {
}
AnalysisResult AnalysisEngine::Run(const FuncGraphPtr &func_graph, const AbstractBasePtrList &args_spec_list) {
StaticAnalysisException::Instance().ClearException();
AnalysisResult result;
StaticAnalysisException::Instance().ClearException(); // Clears any previous exceptions in StaticAnalysisException
AnalysisResult result; // Creates an empty AnalysisResult object
try {
MS_EXCEPTION_IF_NULL(func_graph);
ConfigPtrList args_conf_list;
MS_EXCEPTION_IF_NULL(func_graph); // Checks if func_graph is null and throws an exception if it is
ConfigPtrList args_conf_list; // Creates an empty list of ConfigPtr objects
// Transforms each element in args_spec_list into a ConfigPtr object using a lambda function and appends it to args_conf_list
(void)std::transform(args_spec_list.begin(), args_spec_list.end(), std::back_inserter(args_conf_list),
[](const AbstractBasePtr &arg) -> ConfigPtr { return std::make_shared<VirtualConfig>(arg); });
MS_EXCEPTION_IF_NULL(func_graph_manager_);
func_graph_manager_->AddFuncGraph(func_graph);
root_func_graph_ = func_graph;
MS_EXCEPTION_IF_NULL(func_graph_manager_); // Checks if func_graph_manager_ is null and throws an exception if it is
func_graph_manager_->AddFuncGraph(func_graph); // Adds func_graph to func_graph_manager_
root_func_graph_ = func_graph; // Sets root_func_graph_ to func_graph
// Running the analyzer.
ResetFunctionCallDepth();
ResetStackFrameDepth();
AnalysisContextPtr dummy_context = AnalysisContext::DummyContext();
AnalysisContextPtr root_context = Run(func_graph, dummy_context, args_conf_list);
MS_EXCEPTION_IF_NULL(root_context);
auto root_context_fg = root_context->func_graph();
MS_EXCEPTION_IF_NULL(root_context_fg);
AnfNodeConfigPtr output_conf = MakeConfig(root_context_fg->get_return(), root_context, root_context_fg);
MS_EXCEPTION_IF_NULL(func_graph);
MS_LOG(INFO) << func_graph->ToString() << ": Run finished.";
ResetFunctionCallDepth(); // Resets the function call depth counter
ResetStackFrameDepth(); // Resets the stack frame depth counter
AnalysisContextPtr dummy_context = AnalysisContext::DummyContext(); // Creates a dummy AnalysisContext object
AnalysisContextPtr root_context = Run(func_graph, dummy_context, args_conf_list); // Runs the analysis with func_graph, dummy_context, and args_conf_list
MS_EXCEPTION_IF_NULL(root_context); // Checks if root_context is null and throws an exception if it is
auto root_context_fg = root_context->func_graph(); // Gets the function graph associated with root_context
MS_EXCEPTION_IF_NULL(root_context_fg); // Checks if root_context_fg is null and throws an exception if it is
AnfNodeConfigPtr output_conf = MakeConfig(root_context_fg->get_return(), root_context, root_context_fg); // Creates a config object for the return node of the function graph
MS_EXCEPTION_IF_NULL(func_graph); // Checks if func_graph is null and throws an exception if it is
MS_LOG(INFO) << func_graph->ToString() << ": Run finished."; // Logs an informational message
MS_EXCEPTION_IF_NULL(output_conf); // Checks if output_conf is null and throws an exception if it is
auto eval_result = output_conf->ObtainEvalResult(); // Obtains the evaluation result from output_conf
MS_EXCEPTION_IF_NULL(output_conf);
auto eval_result = output_conf->ObtainEvalResult();
// Set the sequence nodes' elements use flags all true.
SetSequenceElementsUseFlagsRecursively(eval_result->abstract(), true);
result.eval_result = eval_result;
result.context = root_context;
SetSequenceElementsUseFlagsRecursively(eval_result->abstract(), true); // Sets the use flags of sequence elements to true recursively
result.eval_result = eval_result; // Sets the eval_result field of the AnalysisResult object
result.context = root_context; // Sets the context field of the AnalysisResult object
} catch (const std::exception &ex) {
MS_LOG(INFO) << "Eval " << func_graph->ToString() << " threw exception.";
AnalysisSchedule::GetInstance().HandleException(ex);
MS_LOG(INFO) << "Eval " << func_graph->ToString() << " threw exception."; // Logs an informational message
AnalysisSchedule::GetInstance().HandleException(ex); // Handles the exception in AnalysisSchedule
}
AnalysisSchedule::GetInstance().Wait();
return result;
AnalysisSchedule::GetInstance().Wait(); // Waits for analysis tasks to complete
return result; // Returns the AnalysisResult object
}
AnalysisContextPtr AnalysisEngine::Run(const FuncGraphPtr &func_graph, const AnalysisContextPtr &context,
@ -151,14 +166,22 @@ AnalysisContextPtr AnalysisEngine::Run(const FuncGraphPtr &func_graph, const Ana
}
void AnalysisEngine::SaveEvalResultInCache(const AnfNodeConfigPtr &conf, const EvalResultPtr &result) {
// Check that the pointers to AnfNodeConfig and EvalResult objects are not null
MS_EXCEPTION_IF_NULL(conf);
MS_EXCEPTION_IF_NULL(result);
// Get an instance of AnalysisResultCacheMgr from the AnalysisResultCacheMgr singleton object
static AnalysisResultCacheMgr &cache_mgr = AnalysisResultCacheMgr::GetInstance();
// Search for the given AnfNodeConfigPtr object in the cache
auto iter = cache_mgr.GetCache().find(conf);
// If the object is found in the cache, update the use flags of sequence elements in the cached evaluation result with the use flags in the new evaluation result, if enabled by the MS_DEV_ENABLE_DDE environment variable.
if (iter != cache_mgr.GetCache().end()) {
MS_LOG(DEBUG) << "Found previous result for NodeConfig: " << conf->ToString()
<< ", result: " << iter->second->abstract().get() << "/" << iter->second->abstract()->ToString();
// Update sequence nodes info, if matched in cache.
// If MS_DEV_ENABLE_DDE environment variable is enabled, update sequence nodes info
static const auto enable_eliminate_unused_element = (common::GetEnv("MS_DEV_ENABLE_DDE") != "0");
if (enable_eliminate_unused_element) {
auto new_sequence = dyn_cast<AbstractSequence>(result->abstract());
@ -174,20 +197,34 @@ void AnalysisEngine::SaveEvalResultInCache(const AnfNodeConfigPtr &conf, const E
}
}
}
// Log debug message indicating that the new evaluation result is being saved in the cache
MS_LOG(DEBUG) << "Save result for NodeConfig: " << conf->ToString() << ", result: " << result->abstract().get() << "/"
<< result->abstract()->ToString();
// Save the new evaluation result in the cache using the SetValue() method of AnalysisResultCacheMgr
cache_mgr.SetValue(conf, result);
}
EvalResultPtr AnalysisEngine::ObtainEvalResultWithCache(const AnfNodeConfigPtr &conf) {
// Check that the pointer to AnfNodeConfig object is not null
MS_EXCEPTION_IF_NULL(conf);
// Get an instance of AnalysisResultCacheMgr from the AnalysisResultCacheMgr singleton object
static AnalysisResultCacheMgr &cache_mgr = AnalysisResultCacheMgr::GetInstance();
// Search for the given AnfNodeConfigPtr object in the cache
auto result = cache_mgr.GetValue(conf);
// If the object is found in the cache, return the cached evaluation result
if (result != nullptr) {
MS_LOG(DEBUG) << "Evaluate cache found for NodeConfig: " << conf->ToString()
<< ", result: " << result->abstract().get() << "/" << result->abstract()->ToString();
return result;
}
// If the object is not found in the cache, perform evaluation and save the result in the cache before returning it
MS_LOG(DEBUG) << "Evaluate cache miss for NodeConfig: " << conf->ToString();
result = Eval(conf);
if (result == nullptr) {
@ -199,6 +236,7 @@ EvalResultPtr AnalysisEngine::ObtainEvalResultWithCache(const AnfNodeConfigPtr &
return result;
}
EvalResultPtr AnalysisEngine::ObtainEvalResultWithoutCache(const AnfNodeConfigPtr &conf) {
MS_EXCEPTION_IF_NULL(conf);
EvalResultPtr result = nullptr;
@ -213,11 +251,20 @@ EvalResultPtr AnalysisEngine::ObtainEvalResultWithoutCache(const AnfNodeConfigPt
}
EvalResultPtr AnalysisEngine::Eval(const AnfNodeConfigPtr &conf) {
// Check that the pointer to AnfNodeConfig object is not null
MS_EXCEPTION_IF_NULL(conf);
// Get the AnfNodePtr from the AnfNodeConfigPtr object
AnfNodePtr node = conf->node();
// Initialize the EvalResultPtr object as nullptr
EvalResultPtr eval_result = nullptr;
#ifdef DEBUG
// Push the current node onto the compute_conf_stack_ vector for debugging purposes
compute_conf_stack_.push_back(node);
// Build a string representation of the compute_conf_stack_ for debugging purposes
std::ostringstream buffer;
buffer << "Compute Config Begin:";
for (auto iter : compute_conf_stack_) {
@ -225,21 +272,29 @@ EvalResultPtr AnalysisEngine::Eval(const AnfNodeConfigPtr &conf) {
}
MS_LOG(DEBUG) << buffer.str();
#endif
MS_LOG(DEBUG) << "Begin Eval NodeConfig " << conf->ToString();
MS_EXCEPTION_IF_NULL(node);
// If the node already has an abstract value, return it as the evaluation result
if (node->abstract() != nullptr) {
MS_LOG(DEBUG) << "Return old abstract: " << node->DebugString();
eval_result = std::make_shared<EvalResult>(node->abstract(), std::make_shared<AttrValueMap>());
} else if (node->isa<ValueNode>()) {
}
// If the node is a ValueNode, evaluate its abstract value
else if (node->isa<ValueNode>()) {
auto value_node = node->cast<ValueNodePtr>();
auto abstract = EvalValueNode(value_node, conf);
eval_result = std::make_shared<EvalResult>(abstract, std::make_shared<AttrValueMap>());
} else if (node->isa<CNode>()) {
}
// If the node is a CNode, evaluate its abstract value
else if (node->isa<CNode>()) {
auto cnode = node->cast<CNodePtr>();
trace::TraceEvalCNodeEnter(conf);
eval_result = EvalCNode(cnode, conf);
trace::TraceEvalCNodeLeave();
} else {
}
// If the node type is not supported for evaluation, throw an exception
else {
MS_LOG(EXCEPTION) << "Illegal AnfNode for evaluating, node: " << node->DebugString()
<< "(type:" << node->type_name()
<< "), fg: " << (node->func_graph() != nullptr ? node->func_graph()->ToString() : "nullgraph")
@ -247,13 +302,19 @@ EvalResultPtr AnalysisEngine::Eval(const AnfNodeConfigPtr &conf) {
}
#ifdef DEBUG
// Pop the current node from the compute_conf_stack_ vector for debugging purposes
compute_conf_stack_.pop_back();
// If the evaluation result is still nullptr, throw an exception
if (eval_result == nullptr) {
MS_LOG(EXCEPTION) << "Compute Config failed, node: " << node->DebugString()
<< " NodeInfo: " << trace::GetDebugInfo(node->debug_info());
}
#endif
MS_LOG(DEBUG) << "End Eval NodeConfig " << conf->ToString() << ", res: " << eval_result->abstract()->ToString();
// Return the evaluation result
return eval_result;
}
@ -269,25 +330,38 @@ AbstractBasePtr AnalysisEngine::EvalValueNode(const ValueNodePtr &value_node, co
AbstractBasePtr AnalysisEngine::GetCNodeOperatorAbstract(const CNodePtr &cnode, const AnalysisContextPtr &context,
const FuncGraphPtr &func_graph) {
// Check that the pointer to CNode object is not null
MS_EXCEPTION_IF_NULL(cnode);
// Get the inputs of the CNode
auto &inputs = cnode->inputs();
// Check that the inputs are not empty
if (inputs.empty()) {
MS_LOG(EXCEPTION) << "CNode->inputs() is empty, CNode: " << cnode->DebugString();
}
// Get the function node from the inputs
AnfNodePtr func_node = inputs[0];
// Check that the function node is not null
MS_EXCEPTION_IF_NULL(func_node);
MS_LOG(DEBUG) << "Current CNode function: " << func_node->DebugString();
// Create a AnfNodeConfigPtr object for the function node
AnfNodeConfigPtr func_conf = MakeConfig(func_node, context, func_graph);
// Check that the pointer to AnfNodeConfig object is not null
MS_EXCEPTION_IF_NULL(func_conf);
// Keep it in a local variable, otherwise smart pointer will free it.
// Obtain the evaluation result for the function node
auto possible_func_eval_result = func_conf->ObtainEvalResult();
// Get the abstract value from the evaluation result
AbstractBasePtr possible_func = possible_func_eval_result->abstract();
// Check that the abstract value is not null
if (possible_func == nullptr) {
MS_LOG(EXCEPTION) << "No abstract, func_conf: " << func_conf->ToString();
}
// Return the abstract value of the function node
return possible_func;
}
void CheckInterpretedObject(const AbstractBasePtr &abs) {
static const auto support_fallback = common::GetEnv("MS_DEV_ENABLE_FALLBACK");
static const auto use_fallback = (support_fallback != "0");
@ -303,33 +377,49 @@ void CheckInterpretedObject(const AbstractBasePtr &abs) {
}
EvalResultPtr AnalysisEngine::EvalCNode(const CNodePtr &cnode, const AnfNodeConfigPtr &conf) {
// Check that the pointers to CNode and AnfNodeConfig objects are not null
MS_EXCEPTION_IF_NULL(conf);
MS_EXCEPTION_IF_NULL(cnode);
// Get the abstract value of the CNode's operator
AbstractBasePtr possible_func = GetCNodeOperatorAbstract(cnode, conf->context(), conf->func_graph());
// Check if the abstract value has undetermined type
if (possible_func->BuildType()->type_id() == kObjectTypeUndeterminedType) {
MS_LOG(DEBUG) << "EvalCNode eval Undetermined";
return std::make_shared<EvalResult>(possible_func->Clone(), std::make_shared<AttrValueMap>());
}
// Check if the abstract value can be casted to AbstractFunction
AbstractFunctionPtr func = dyn_cast<AbstractFunction>(possible_func);
if (func == nullptr) {
// If not, log an error and throw an exception
CheckInterpretedObject(possible_func);
MS_LOG(ERROR) << "Can not cast to a AbstractFunction from " << possible_func->ToString() << ".";
MS_LOG(ERROR) << "It's called at: " << cnode->DebugString();
MS_EXCEPTION(ValueError) << "This may be not defined, or it can't be a operator. Please check code.";
}
// Create a ConfigPtrList to store the configurations of the arguments
ConfigPtrList args_conf_list;
// Ignore the first node which is function name
// Iterate through the inputs of the CNode, ignoring the first node (function name)
auto &inputs = cnode->inputs();
for (std::size_t i = 1; i < inputs.size(); i++) {
const AnfNodePtr &node = inputs[i];
// Create an AnfNodeConfigPtr object for the argument node
args_conf_list.push_back(MakeConfig(node, conf->context(), conf->func_graph()));
}
// Create a vector to store the evaluators
std::vector<EvaluatorPtr> evaluators;
// Define a lambda function to build evaluators for each resolved AtomicAbstractFunc
auto build_evaluator = [this, &evaluators, &cnode](const AbstractFuncAtomPtr &poss) {
auto resolved_atom = poss;
// If the resolved AtomicAbstractFunc is an AsyncAbstractFunc, resolve it to get the actual function
if (poss->isa<AsyncAbstractFuncAtom>()) {
const auto &async_abs_func = poss->cast<AsyncAbstractFuncAtomPtr>();
const auto &resolved_func = async_abs_func->GetUnique();
@ -337,51 +427,73 @@ EvalResultPtr AnalysisEngine::EvalCNode(const CNodePtr &cnode, const AnfNodeConf
MS_EXCEPTION_IF_NULL(resolved_atom);
MS_LOG(DEBUG) << "Resolved AsyncAbstractFuncAtom is: " << resolved_atom->ToString();
}
// Get an evaluator for the resolved AtomicAbstractFunc
auto evaluator = this->GetEvaluatorFor(resolved_atom);
// Set the bound node of the evaluator to the current CNode
evaluator->set_bound_node(cnode);
// Add the evaluator to the vector
evaluators.push_back(evaluator);
};
// Visit the AbstractFunction to build evaluators
func->Visit(build_evaluator);
// Execute the evaluators with the given configurations and return the evaluation result
auto eval_result = ExecuteEvaluators(evaluators, conf, args_conf_list);
return eval_result;
}
EvalResultPtr AnalysisEngine::Execute(const AbstractFunctionPtr &func, const AbstractBasePtrList &args_spec_list) {
// Check that the AbstractFunction pointer is not null
MS_EXCEPTION_IF_NULL(func);
// Create a ConfigPtrList to store the configurations of the arguments
ConfigPtrList args_conf_list;
// Transform the input argument list into a list of VirtualConfigs
(void)std::transform(args_spec_list.begin(), args_spec_list.end(), std::back_inserter(args_conf_list),
[](const AbstractBasePtr &arg) -> ConfigPtr { return std::make_shared<VirtualConfig>(arg); });
// Create a vector to store the evaluators
std::vector<EvaluatorPtr> infs;
MS_EXCEPTION_IF_NULL(func);
// Define a lambda function to build evaluators for each resolved AtomicAbstractFunc
auto build_evaluator = [this, &infs](const AbstractFuncAtomPtr &poss) {
auto evaluator = this->GetEvaluatorFor(poss);
infs.push_back(evaluator);
};
// Visit the AbstractFunction to build evaluators
func->Visit(build_evaluator);
// Execute the evaluators with the given configurations and return the evaluation result
return ExecuteEvaluators(infs, nullptr, args_conf_list);
}
void AnalysisEngine::ClearEvaluatorCache() {
// Clear cache for evaluators in evaluators_ map
for (auto &element : evaluators_) {
EvaluatorPtr evaluator = element.second;
MS_EXCEPTION_IF_NULL(evaluator);
MS_EXCEPTION_IF_NULL(evaluator->evaluator_cache_mgr());
evaluator->evaluator_cache_mgr()->Clear();
}
// Clear cache for evaluators in prim_constructors_ map
for (auto &element : prim_constructors_) {
EvaluatorPtr evaluator = element.second;
MS_EXCEPTION_IF_NULL(evaluator);
MS_EXCEPTION_IF_NULL(evaluator->evaluator_cache_mgr());
evaluator->evaluator_cache_mgr()->Clear();
}
// Clear cache for evaluators in prim_py_evaluators_ map
for (auto &element : prim_py_evaluators_) {
EvaluatorPtr evaluator = element.second;
MS_EXCEPTION_IF_NULL(evaluator);
MS_EXCEPTION_IF_NULL(evaluator->evaluator_cache_mgr());
evaluator->evaluator_cache_mgr()->Clear();
}
// Release Exception to avoid hup at exit.
// Clear exceptions in the StaticAnalysisException singleton
StaticAnalysisException::Instance().ClearException();
}
@ -399,38 +511,39 @@ void AnalysisEngine::Clear() {
EvaluatorPtr GetPrimEvaluator(const PrimitivePtr &prim, const AnalysisEnginePtr &engine) {
// Custom Primitive with python infer_shape, infer_type
MS_EXCEPTION_IF_NULL(prim);
if (prim->isa<prim::DoSignaturePrimitive>()) {
return std::make_shared<DoSignatureEvaluator>(prim);
if (prim->isa<prim::DoSignaturePrimitive>()) { // Check if it is a custom DoSignaturePrimitive
return std::make_shared<DoSignatureEvaluator>(prim); // Return an instance of DoSignatureEvaluator
}
if (prim->isa<prim::UnpackGraphPrimitive>()) {
return std::make_shared<UnpackGraphEvaluator>(prim);
if (prim->isa<prim::UnpackGraphPrimitive>()) { // Check if it is a custom UnpackGraphPrimitive
return std::make_shared<UnpackGraphEvaluator>(prim); // Return an instance of UnpackGraphEvaluator
}
if (prim->Hash() == prim::kPrimMixedPrecisionCast->Hash() && prim->name() == prim::kPrimMixedPrecisionCast->name()) {
return std::make_shared<MixedPrecisionCastEvaluator>(prim);
// Check if it is a mixed precision cast operation
return std::make_shared<MixedPrecisionCastEvaluator>(prim); // Return an instance of MixedPrecisionCastEvaluator
}
// Find prim infer function in the prim function map return a standard evaluator
auto eval_impl = GetPrimitiveInferImpl(prim);
// Find prim infer function in the prim function map and return a standard evaluator
auto eval_impl = GetPrimitiveInferImpl(prim); // Get the infer implementation of the prim from the prim function map
if (eval_impl.infer_shape_impl_ != nullptr && prim->name() != prim::kPrimMakeTuple->name() &&
prim->name() != prim::kPrimMakeList->name()) { // Refactoring infer routine soon.
return std::make_shared<StandardPrimEvaluator>(prim, eval_impl);
prim->name() != prim::kPrimMakeList->name()) { // Check if the infer implementation exists and it is not MakeTuple or MakeList
return std::make_shared<StandardPrimEvaluator>(prim, eval_impl); // Return an instance of StandardPrimEvaluator
}
// Use python infer function if the infer function not founded in the map return a python evaluator
// Use python infer function if the infer function is not found in the map, and return a python evaluator
EvaluatorPtr evaluator = nullptr;
if (prim->HasPyEvaluator()) {
if (prim->HasPyEvaluator()) { // Check if it has a Python infer function
auto prim_py = dyn_cast<PrimitivePy>(prim);
if (prim_py != nullptr) {
if (engine == nullptr) {
return std::make_shared<PythonPrimEvaluator>(prim_py);
if (engine == nullptr) { // Check if the analysis engine is provided
return std::make_shared<PythonPrimEvaluator>(prim_py); // Return an instance of PythonPrimEvaluator
}
const auto &iter = engine->prim_py_evaluators_.find(prim_py);
const auto &iter = engine->prim_py_evaluators_.find(prim_py); // Find the cached PythonPrimEvaluator in the engine
if (iter != engine->prim_py_evaluators_.end()) {
return iter->second;
return iter->second; // If already cached, return the cached PythonPrimEvaluator
}
evaluator = std::make_shared<PythonPrimEvaluator>(prim_py);
engine->prim_py_evaluators_[prim_py] = evaluator;
evaluator = std::make_shared<PythonPrimEvaluator>(prim_py); // Create a new PythonPrimEvaluator
engine->prim_py_evaluators_[prim_py] = evaluator; // Cache the new PythonPrimEvaluator in the engine
return evaluator;
}
MS_LOG(ERROR) << "The primitive with python evaluator should be a python primitive.";
@ -438,25 +551,25 @@ EvaluatorPtr GetPrimEvaluator(const PrimitivePtr &prim, const AnalysisEnginePtr
}
// Return a default evaluator
if (engine == nullptr) {
if (engine == nullptr) { // Check if the analysis engine is provided
// If engine is nullptr, get constructor from default.
const PrimEvaluatorMap &prim_evaluator_map = GetPrimEvaluatorConstructors();
auto iter = prim_evaluator_map.find(prim);
const PrimEvaluatorMap &prim_evaluator_map = GetPrimEvaluatorConstructors(); // Get the constructor from the default PrimEvaluatorMap
auto iter = prim_evaluator_map.find(prim); // Find the constructor that matches the prim
if (iter != prim_evaluator_map.end()) {
evaluator = iter->second;
evaluator = iter->second; // Get the evaluator instance from the constructor
}
} else {
// If engine is given, get constructor from engine resource.
const PrimEvaluatorMap &prim_evaluator_map = engine->PrimConstructors();
auto iter = prim_evaluator_map.find(prim);
const PrimEvaluatorMap &prim_evaluator_map = engine->PrimConstructors(); // Get the PrimEvaluatorMap from the engine
auto iter = prim_evaluator_map.find(prim); // Find the constructor that matches the prim
if (iter != prim_evaluator_map.end()) {
evaluator = iter->second;
evaluator = iter->second; // Get the evaluator instance from the constructor
}
}
if (evaluator == nullptr) {
MS_LOG(DEBUG) << "The evaluator of the primitive is not defined (" << prim->name() << ").";
}
return evaluator;
return evaluator; // Return the obtained evaluator instance, which can be nullptr
}
EvaluatorPtr AnalysisEngine::_GetEvaluatorFor(const std::shared_ptr<PrimitiveAbstractClosure> &func) {
@ -541,20 +654,32 @@ EvaluatorPtr AnalysisEngine::_GetEvaluatorFor(const std::shared_ptr<VirtualAbstr
}
EvaluatorPtr AnalysisEngine::_GetEvaluatorFor(const std::shared_ptr<PartialAbstractClosure> &func) {
// Check if the input argument is not null
MS_EXCEPTION_IF_NULL(func);
// Get the original function from the partial closure
AbstractFunctionPtr func_orig = func->fn();
// Get the evaluator for the original function
EvaluatorPtr evaluator_orig = GetEvaluatorFor(func_orig);
// Create a pair of the original function and its arguments
auto part_pair = std::make_pair(func_orig, func->args());
// Check if an evaluator for the partial closure already exists in the map
auto itr = constructors_app_.find(part_pair);
if (itr != constructors_app_.end()) {
// Return the existing evaluator
return itr->second;
}
// If an evaluator doesn't exist, create a new PartialAppEvaluator
// Pass the original evaluator and the arguments of the partial closure to the constructor
std::shared_ptr<PartialAppEvaluator> partial_evaluator =
std::make_shared<PartialAppEvaluator>(evaluator_orig, func->args());
// Cache the newly created PartialAppEvaluator in the map
constructors_app_[part_pair] = partial_evaluator;
// Return the created PartialAppEvaluator
return partial_evaluator;
}
EvaluatorPtr AnalysisEngine::_GetEvaluatorFor(const std::shared_ptr<TypedPrimitiveAbstractClosure> &) {
MS_LOG(EXCEPTION) << "Should not be called ";
}
@ -659,24 +784,29 @@ EvalResultPtr AnalysisEngine::ExecuteEvaluators(const std::vector<EvaluatorPtr>
void AnalysisEngine::SetUndeterminedFlag(const EvaluatorPtr &evaluator, const FuncGraphPtr &possible_parent_fg) {
MS_EXCEPTION_IF_NULL(evaluator);
static std::mutex fg_lock;
std::lock_guard<std::mutex> infer_lock(fg_lock);
if (possible_parent_fg != nullptr) {
possible_parent_fg->set_flag(kFuncGraphFlagUndetermined, true);
static std::mutex fg_lock; // A static mutex to ensure thread safety for modifying the func graph
std::lock_guard<std::mutex> infer_lock(fg_lock); // Acquire the lock
if (possible_parent_fg != nullptr) { // If a parent func graph is provided...
possible_parent_fg->set_flag(kFuncGraphFlagUndetermined, true); // Set the undetermined flag of the parent func graph
MS_LOG(DEBUG) << "Set graph undetermined: " << possible_parent_fg->ToString();
}
auto fg_eval = evaluator->cast<FuncGraphEvaluatorPtr>();
if (fg_eval == nullptr) {
auto fg_eval = evaluator->cast<FuncGraphEvaluatorPtr>(); // Cast the evaluator to FuncGraphEvaluatorPtr
if (fg_eval == nullptr) { // If it doesn't cast to FuncGraphEvaluatorPtr, simply return
return;
}
auto fg = fg_eval->func_graph();
auto fg = fg_eval->func_graph(); // Get the func graph from the FuncGraphEvaluatorPtr
MS_EXCEPTION_IF_NULL(fg);
auto fg_parent = fg->parent();
if (fg_parent != nullptr) {
fg_parent->set_flag(kFuncGraphFlagUndetermined, true);
auto fg_parent = fg->parent(); // Get the parent func graph of the current func graph
if (fg_parent != nullptr) { // If the parent func graph exists...
fg_parent->set_flag(kFuncGraphFlagUndetermined, true); // Set the undetermined flag of the parent func graph
MS_LOG(DEBUG) << "Set graph undetermined: " << fg_parent->ToString() << " for fg: " << fg->ToString();
return;
} else {
} else { // If the parent func graph doesn't exist...
MS_LOG(DEBUG) << "cannot find parent for fg: " << fg->ToString();
}
}
@ -738,6 +868,7 @@ EvaluatorPtr AnalysisEngine::HandleNestedRecursion(const std::vector<EvaluatorPt
std::string JoinBranchesFailedInfo(const AbstractBasePtr &spec, const AbstractBasePtr &last_spec,
const AnfNodePtr &node, const std::string &error_info) {
constexpr int recursive_level = 2;
// Use a stringstream to build the error message string.
std::ostringstream buffer;
buffer << "Cannot join the return values of different branches, perhaps you need to make them equal.\n"
<< error_info << "\nThe abstract type of the return value of the current branch is " << spec->ToString()
@ -745,6 +876,7 @@ std::string JoinBranchesFailedInfo(const AbstractBasePtr &spec, const AbstractBa
<< "The node is " << node->DebugString(recursive_level);
if (node->isa<CNode>()) {
auto cnode = node->cast<CNodePtr>()->input(0);
// If the current node is a Switch node, output the information of the True branch and False branch.
if (IsPrimitiveCNode(cnode, prim::kPrimSwitch)) {
// {prim::kPrimSwitch, cond, true_branch, false_branch}
constexpr int true_index = 2;
@ -752,7 +884,9 @@ std::string JoinBranchesFailedInfo(const AbstractBasePtr &spec, const AbstractBa
auto inputs = cnode->cast<CNodePtr>()->inputs();
buffer << ", true branch: " << inputs.at(true_index)->ToString()
<< ", false branch: " << inputs.at(false_index)->ToString();
} else if (IsPrimitiveCNode(cnode, prim::kPrimSwitchLayer)) {
}
// If the current node is a SwitchLayer node, output the information of each branch.
else if (IsPrimitiveCNode(cnode, prim::kPrimSwitchLayer)) {
// {prim::kPrimSwitchLayer, X, {prim::kPrimMakeTuple, branch1, branch2, ...}}
constexpr int branch_index = 2;
auto tuple_node = cnode->cast<CNodePtr>()->input(branch_index);
@ -764,10 +898,12 @@ std::string JoinBranchesFailedInfo(const AbstractBasePtr &spec, const AbstractBa
}
}
}
// Output the source code location of the current node.
buffer << trace::DumpSourceLines(node);
return buffer.str();
}
EvalResultPtr AnalysisEngine::ProcessEvalResults(const AbstractBasePtrList &out_specs, const AnfNodePtr &node) {
if (out_specs.empty()) {
MS_LOG(EXCEPTION) << "There is an endless loop for evaluator.";

View File

@ -75,6 +75,10 @@ void ValidateOperation(const AnfNodePtr &node) {
MS_LOG(EXCEPTION) << "Illegal primitive: " << prim->name();
}
//The function of this code is to verify whether a node operation is legal,
//mainly by judging whether the Primitive corresponding to the node is in the whitelist,
//whether there are specific attributes or methods to judge its legitimacy,
//if the node operation is illegal, an exception will be thrown.
bool CheckAbstractScalar(const AnfNodePtr &node) {
MS_EXCEPTION_IF_NULL(node);
@ -96,6 +100,12 @@ bool CheckAbstractScalar(const AnfNodePtr &node) {
}
return false;
}
//What this code does is check whether the abstract value of a node is a scalar type.
//Returns false if the abstract value is not of type AbstractScalar;
// If it is an AbstractScalar type,
//it further checks whether the type of the abstract value is legal,
//and if not, an exception is thrown;
//Returning true if legal indicates that the abstract value is a scalar type.
void ValidateAbstract(const AnfNodePtr &node) {
if (node == nullptr) {
@ -132,6 +142,15 @@ void ValidateAbstract(const AnfNodePtr &node) {
// Other types show exception
MS_LOG(EXCEPTION) << "Illegal type in the graph: " << abstract->ToString();
}
//The purpose of this code is to verify that the abstract value of a node is valid.
// First check whether the node and abstract value are empty,
//and then verify whether the type of the abstract value is AbstractClass type or AbstractJTagged type, respectively,
//if so, throw an exception;
//Then call the CheckAbstractScalar function to verify whether the abstract value is a scalar type,
//and if so, return;
//Then determine whether the abstract value is of type AbstractError,
//and if so, print the debug log;
//Finally, determine whether the abstract value type is legal, and if so, return.
void ValidateValueNode(const AnfNodePtr &node) {
if (node == nullptr) {
@ -147,6 +166,11 @@ void ValidateValueNode(const AnfNodePtr &node) {
<< "https://www.mindspore.cn/search?inputValue=JIT%20Fallback";
}
}
//The purpose of this code is to verify the validity of a value node.
//First check whether the node is empty,
//then determine whether the node is a value node of type parse,
//and if so, throw an exception.
//The purpose of this validation function is to ensure that Python objects are not used at runtime
void CheckValueTuple(const AnfNodePtr &node) {
const auto &value_node = node->cast<ValueNodePtr>();
@ -162,6 +186,11 @@ void CheckValueTuple(const AnfNodePtr &node) {
ValidateValueNode(input_node);
}
}
//The purpose of this code is to check
// whether a value node is a tuple type and validate each value node in the tuple.
//It first gets the value object of the value node and converts it to a tuple type.
//Each value node in the tuple is then looped through and operational and value node validation is performed
void Validate(const FuncGraphPtr &fg) {
FuncGraphManagerPtr mgr = Manage(fg, false);

View File

@ -84,6 +84,7 @@ std::map<std::string, std::shared_ptr<session::SessionBasic>> kSessionBackends;
std::map<std::string, std::shared_ptr<compile::MindRTBackend>> kMindRtBackends;
PyObjectIdCache g_pyobj_id_cache;
// General exception handling function that executes a method and handles exceptions.
template <typename T, typename... Args>
void PynativeExecutorTry(const std::function<void(T *ret, const Args &...)> &method, T *ret, const Args &... args) {
const auto inst = PynativeExecutor::GetInstance();
@ -128,6 +129,7 @@ void PynativeExecutorTry(const std::function<void(T *ret, const Args &...)> &met
}
}
// Convert a py::object to a pointer to Value.
inline ValuePtr PyObjToValue(const py::object &obj) {
ValuePtr converted_ret = parse::data_converter::PyDataToValue(obj);
if (!converted_ret) {
@ -144,6 +146,14 @@ std::string GetPyObjId(const py::handle &obj) {
return out.cast<std::string>();
}
// Get the identifier of the given Python object.
// If obj is Tensor and is not Parameter, then return id.
// If obj is Parameter, then return name.
// If obj is mindspore::Type, then return "type" + ToString(obj).
// If obj is str or int_ or float_, then return string(obj).
// If obj is None, then return "none".
// If obj is tuple or list, then return "tuple"/"list" + "empty"/str(obj[0]):str(obj[1]):...
// If obj is Cell of function, then return GetPyObjId(obj).
std::string GetId(const py::handle &obj) {
if (py::isinstance<tensor::Tensor>(obj)) {
auto tensor_ptr = py::cast<tensor::TensorPtr>(obj);
@ -197,6 +207,7 @@ bool IsFunctionType(const py::object &cell) {
return false;
}
// Find all indexs of types from type_indexes.
void GetTypeIndex(const std::vector<SignatureEnumDType> &dtypes,
mindspore::HashMap<SignatureEnumDType, std::vector<size_t>> *type_indexes) {
MS_EXCEPTION_IF_NULL(type_indexes);
@ -763,6 +774,7 @@ void RunReplace(const CNodePtr &added_make_tuple, const std::vector<tensor::Tens
}
}
// Replace the new output tensor in the gradient graph so that the tensors of the forward and backward nodes can correspond.
void ReplaceNewTensorsInGradGraph(const TopCellInfoPtr &top_cell, const OpExecInfoPtr &op_exec_info,
const ValuePtr &added_out, const FuncGraphPtr &ms_func_graph,
const FuncGraphPtr &grad_graph) {
@ -814,6 +826,7 @@ void SaveOpInfo(const TopCellInfoPtr &top_cell, const std::string &op_info,
});
}
// Update the new tensor information to the pre tensor and make inter device memory data copies as needed.
void UpdateTensorInfo(const tensor::TensorPtr &new_tensor, const std::vector<tensor::TensorPtr> &pre_tensors) {
MS_EXCEPTION_IF_NULL(new_tensor);
if (pre_tensors.empty() || new_tensor->device_address() == nullptr) {
@ -938,6 +951,7 @@ ValuePtr ShallowCopyValue(const OpExecInfoPtr &op_exec_info, const ValuePtr &val
}
} // namespace
// The true operation of the operator is in this function.
py::object RealRunOp(const py::args &args) {
CheckPyNativeContext();
const auto &executor = PynativeExecutor::GetInstance();
@ -1303,6 +1317,7 @@ void ForwardExecutor::DoNopOutput(const OpExecInfoPtr &op_exec_info, ValuePtr *o
MS_LOG(DEBUG) << "New copy value is " << (*out_real_value)->ToString();
}
// Get output of operator in forward graph.
void ForwardExecutor::GetOpOutput(const OpExecInfoPtr &op_exec_info,
const abstract::AbstractBasePtrList &args_spec_list, const CNodePtr &cnode,
bool prim_cache_hit, py::object *ret) {
@ -1893,6 +1908,7 @@ void GradExecutor::DoOpGrad(const OpExecInfoPtr &op_exec_info, const CNodePtr &c
}
}
// Update tensors in forward graph created by ms_function.
void GradExecutor::UpdateMsFunctionForwardTensors(const OpExecInfoPtr &op_exec_info,
const ValuePtr &new_forward_value) {
MS_LOG(DEBUG) << "Ms func graph has already ran before. The graph phase is: " << graph_phase();
@ -2000,6 +2016,10 @@ void GradExecutor::MakeAdjointForMsFunction(const FuncGraphPtr &ms_func_graph, c
top_cell()->set_ms_function_flag(true);
}
// Update forward tensor info in backprop graph.
// If you need to construct a graph, use the SaveOpInfo function to save all tensor information for the current operation.
// Its implementation is determined by the need construct graph function, which returns whether the graph has already been constructed before, and if it has,
// returns false and does not need to save the tensor information again.
void GradExecutor::UpdateForwardTensorInfoInBpropGraph(const OpExecInfoPtr &op_exec_info, const ValuePtr &op_out) {
if (!grad_flag_) {
MS_LOG(DEBUG) << "The grad flag is false, no need to update forward op info in bprop graph";
@ -2126,6 +2146,7 @@ MsBackendPolicy ForwardExecutor::GetBackendPolicy(const OpExecInfoPtr &op_exec_i
return backend_policy;
}
// Diffrent backend policy has dirrent handling func.
py::object ForwardExecutor::RunOpWithBackendPolicy(MsBackendPolicy backend_policy, const OpExecInfoPtr &op_exec_info) {
py::object result;
if (backend_policy == kMsBackendVmOnly) {
@ -2551,6 +2572,7 @@ void GradExecutor::NewGraphInner(py::object *ret, const py::object &cell, const
}
}
// Create new top-level maps and manage the number and resources of top-level maps.
void GradExecutor::MakeNewTopGraph(const string &cell_id, const py::object &cell, const py::args &args,
bool is_topest) {
pipeline::CheckArgsValid(cell, args);
@ -2631,6 +2653,8 @@ void GradExecutor::SetTupleItemArgsToGraphInfoMap(const FuncGraphPtr &g, const p
}
}
// Cleaning and processing logic at the end of the calculation graph execution, including updating the gradient flag,
// popping the stack, setting the output node, dumping the IR graph, and checking the compiled graph
void GradExecutor::EndGraphInner(py::object *ret, const py::object &cell, const py::object &out, const py::args &args) {
MS_EXCEPTION_IF_NULL(ret);
const auto &cell_id = GetCellId(cell, args);
@ -2800,6 +2824,8 @@ void GradExecutor::MarkMsFunctionNodes(const pipeline::ResourcePtr &resource) {
}
}
// Execute the backpropagation graph and manage related resources, including creating, configuring and preparing the graph,
// launching and executing the action, and finally performing the necessary cleaning and releasing operations
void GradExecutor::GradNetInner(py::object *ret, const prim::GradOperationPtr &grad, const py::object &cell,
const py::object &weights, const py::object &grad_position, const py::args &args) {
MS_EXCEPTION_IF_NULL(ret);
@ -2925,6 +2951,7 @@ std::vector<size_t> GradExecutor::GetGradPositionArgs(const py::object &grad_pos
MS_LOG(EXCEPTION) << "Grad position only support tuple.";
}
// Shallow copy of sens parameters. That is, create a new sens parameter and replace the original sens parameter to share and transfer data.
void GradExecutor::ShallowCopySensValue(const py::tuple &input_args, bool has_sens, VectorRef *run_args) {
if (!has_sens) {
return;
@ -3157,6 +3184,7 @@ void GradExecutor::CheckNeedCompileGraph() {
}
}
// The execution process of gradient graph calculation is realized, including the processing of input parameters, shallow copy of sensitive parameters, calculation execution, result conversion and so on.
void GradExecutor::RunGradGraph(py::object *ret, const py::object &cell, const py::tuple &args) {
MS_EXCEPTION_IF_NULL(ret);
const auto &cell_id = GetCellId(cell, args);
@ -3356,6 +3384,8 @@ void GradExecutor::EraseTopCellFromTopCellList(const TopCellInfoPtr &top_cell) {
}
}
// The process of gradient graph calculation for ms function type graph is realized, including creating operation execution information,
// updating tensor information, replacing new tensor, cloning calculation graph and generating backpropagation function.
void GradExecutor::GradMsFunctionInner(const std::string &phase, const py::object &out, const py::args &args,
const FuncGraphPtr &ms_func_graph, const FuncGraphPtr &grad_graph) {
// Get actual output value and added output value.
@ -3405,6 +3435,8 @@ void GradExecutor::GradMsFunctionInner(const std::string &phase, const py::objec
MakeAdjointForMsFunction(new_ms_func_graph, new_grad_graph, actual_out, args, actual_out_v);
}
// The process of gradient calculation for ms function diagram is realized, including obtaining the phase of the calculation diagram,
// obtaining the original calculation diagram and the gradient calculation diagram, modifying the output, performing the gradient calculation and so on.
py::object GradExecutor::GradMsFunction(const py::object &out, const py::args &args) {
// Get actual forward output object.
if (graph_phase().empty()) {

View File

@ -755,5 +755,5 @@ def constexpr(fn=None, get_instance=True, name=None, reuse_result=True):
@_wrap_func
def _run_op(obj, op_name, args):
"""Single op execution function supported by ge in PyNative mode."""
output = real_run_op(obj, op_name, args)
output = real_run_op(obj, op_name, args) # jump into C++ function: RealRunOp
return output