From 3bcbeb6bc3220b1dc8a1fe5dc5760fa3d964a7e9 Mon Sep 17 00:00:00 2001 From: WEI_4614 <2291172974@qq.com> Date: Sun, 1 Oct 2023 20:14:33 +0800 Subject: [PATCH 01/26] Update pynative_execute.cc --- mindspore/ccsrc/pipeline/pynative/pynative_execute.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mindspore/ccsrc/pipeline/pynative/pynative_execute.cc b/mindspore/ccsrc/pipeline/pynative/pynative_execute.cc index 0be7e3db32c..f1a168ab2e0 100644 --- a/mindspore/ccsrc/pipeline/pynative/pynative_execute.cc +++ b/mindspore/ccsrc/pipeline/pynative/pynative_execute.cc @@ -84,6 +84,7 @@ std::map> kSessionBackends; std::map> kMindRtBackends; PyObjectIdCache g_pyobj_id_cache; +// General exception handling function that executes a method and handles exceptions. template void PynativeExecutorTry(const std::function &method, T *ret, const Args &... args) { const auto inst = PynativeExecutor::GetInstance(); @@ -128,6 +129,7 @@ void PynativeExecutorTry(const std::function &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(); } +// 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(obj)) { auto tensor_ptr = py::cast(obj); -- 2.34.1 From 3839b9c723acee8d119fe77836757265ae667f67 Mon Sep 17 00:00:00 2001 From: gxl1 Date: Mon, 2 Oct 2023 19:04:35 +0800 Subject: [PATCH 02/26] Update primitive.py --- mindspore/python/mindspore/ops/primitive.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mindspore/python/mindspore/ops/primitive.py b/mindspore/python/mindspore/ops/primitive.py index ceb52ff0c9e..9ff6fa4294f 100644 --- a/mindspore/python/mindspore/ops/primitive.py +++ b/mindspore/python/mindspore/ops/primitive.py @@ -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 -- 2.34.1 From 3fbf99722f7e7e9a25efe9453d4d812fe512e9c3 Mon Sep 17 00:00:00 2001 From: gxl1 Date: Wed, 4 Oct 2023 11:05:43 +0800 Subject: [PATCH 03/26] Update resource.cc --- mindspore/ccsrc/pipeline/jit/resource.cc | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/mindspore/ccsrc/pipeline/jit/resource.cc b/mindspore/ccsrc/pipeline/jit/resource.cc index 7718611eeef..d67a77fc14a 100644 --- a/mindspore/ccsrc/pipeline/jit/resource.cc +++ b/mindspore/ccsrc/pipeline/jit/resource.cc @@ -285,7 +285,8 @@ Resource::Resource(const py::object &obj) : engine_(std::make_shared(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(); -- 2.34.1 From 4058cd2557e70c31bd97f62ff3e3181718416146 Mon Sep 17 00:00:00 2001 From: WEI_4614 <2291172974@qq.com> Date: Wed, 4 Oct 2023 11:11:28 +0800 Subject: [PATCH 04/26] Update pynative_execute.cc --- .../pipeline/pynative/pynative_execute.cc | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/mindspore/ccsrc/pipeline/pynative/pynative_execute.cc b/mindspore/ccsrc/pipeline/pynative/pynative_execute.cc index f1a168ab2e0..eb06b52f421 100644 --- a/mindspore/ccsrc/pipeline/pynative/pynative_execute.cc +++ b/mindspore/ccsrc/pipeline/pynative/pynative_execute.cc @@ -207,6 +207,7 @@ bool IsFunctionType(const py::object &cell) { return false; } +// Find all indexs of types from type_indexes. void GetTypeIndex(const std::vector &dtypes, mindspore::HashMap> *type_indexes) { MS_EXCEPTION_IF_NULL(type_indexes); @@ -773,6 +774,7 @@ void RunReplace(const CNodePtr &added_make_tuple, const std::vector &pre_tensors) { MS_EXCEPTION_IF_NULL(new_tensor); if (pre_tensors.empty() || new_tensor->device_address() == nullptr) { @@ -948,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(); @@ -1313,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) { @@ -1903,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(); @@ -2010,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"; @@ -2136,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) { @@ -2561,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); @@ -2641,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); @@ -2810,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); @@ -2935,6 +2951,7 @@ std::vector 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; @@ -3167,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); @@ -3366,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. @@ -3415,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()) { -- 2.34.1 From 7a25ec4b3fc5edd70cc55e14733696bd26862163 Mon Sep 17 00:00:00 2001 From: WEI_4614 <2291172974@qq.com> Date: Wed, 4 Oct 2023 11:44:45 +0800 Subject: [PATCH 05/26] Update action.cc --- mindspore/ccsrc/pipeline/jit/action.cc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/mindspore/ccsrc/pipeline/jit/action.cc b/mindspore/ccsrc/pipeline/jit/action.cc index 686fc840b1f..7dd9f87a375 100644 --- a/mindspore/ccsrc/pipeline/jit/action.cc +++ b/mindspore/ccsrc/pipeline/jit/action.cc @@ -141,6 +141,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(); @@ -322,6 +323,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 +376,8 @@ 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(); @@ -555,6 +559,9 @@ 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) { @@ -710,6 +717,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(MS_CTX_EXECUTION_MODE) == kGraphMode) { @@ -762,6 +771,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 &all_nodes) { for (const auto &node : all_nodes) { if (!node->isa()) { @@ -930,6 +940,7 @@ void SetRunMode(const FuncGraphPtr &func_graph, compile::Backend *backend_ptr) { 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 +975,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(); @@ -1097,6 +1109,7 @@ 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(); @@ -1266,6 +1279,7 @@ 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); @@ -1342,6 +1356,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,6 +1399,7 @@ bool OptActionGePyStub(const ResourcePtr &res) { return true; } +// Returns a vector containing multiple Actionitems static std::vector CommonPipeline() { std::vector actions; -- 2.34.1 From 281b34517e2dfeecd214bbacee6ad8f52bf761a1 Mon Sep 17 00:00:00 2001 From: gxl1 Date: Wed, 4 Oct 2023 11:51:31 +0800 Subject: [PATCH 06/26] Update pipeline.cc --- mindspore/ccsrc/pipeline/jit/pipeline.cc | 37 ++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/mindspore/ccsrc/pipeline/jit/pipeline.cc b/mindspore/ccsrc/pipeline/jit/pipeline.cc index 8e8ccf3ee61..5d060518fd2 100644 --- a/mindspore/ccsrc/pipeline/jit/pipeline.cc +++ b/mindspore/ccsrc/pipeline/jit/pipeline.cc @@ -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(arg) || py::isinstance(arg)) { auto vector_arg = py::cast(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(arg)) { auto dict_arg = py::cast(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(arg) || py::isinstance(arg) || py::isinstance(arg) || py::isinstance(arg) || ((py::isinstance(arg) || py::isinstance(arg) || py::isinstance(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 GenerateJitConfigMap(const py::dict &jit_config) { std::map ret{}; @@ -225,6 +247,8 @@ std::map 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(); -- 2.34.1 From 044e5a93cd813c399bd6a4bb2fd000affa426e30 Mon Sep 17 00:00:00 2001 From: WEI_4614 <2291172974@qq.com> Date: Thu, 5 Oct 2023 11:17:18 +0800 Subject: [PATCH 07/26] Update compile_cache_manager.cc --- mindspore/ccsrc/pipeline/jit/compile_cache_manager.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mindspore/ccsrc/pipeline/jit/compile_cache_manager.cc b/mindspore/ccsrc/pipeline/jit/compile_cache_manager.cc index 6b548ab294a..438712acb62 100644 --- a/mindspore/ccsrc/pipeline/jit/compile_cache_manager.cc +++ b/mindspore/ccsrc/pipeline/jit/compile_cache_manager.cc @@ -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(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 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. -- 2.34.1 From 4a3aac60996f65af11db1f5e0e9819dfeef0498a Mon Sep 17 00:00:00 2001 From: gxl1 Date: Thu, 5 Oct 2023 13:57:29 +0800 Subject: [PATCH 08/26] Update pipeline.cc --- mindspore/ccsrc/pipeline/jit/pipeline.cc | 140 ++++++++++++++++++++--- 1 file changed, 124 insertions(+), 16 deletions(-) diff --git a/mindspore/ccsrc/pipeline/jit/pipeline.cc b/mindspore/ccsrc/pipeline/jit/pipeline.cc index 5d060518fd2..424b418c860 100644 --- a/mindspore/ccsrc/pipeline/jit/pipeline.cc +++ b/mindspore/ccsrc/pipeline/jit/pipeline.cc @@ -308,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); @@ -318,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(arg_obj)) { @@ -335,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>(); ShapeVector sig_shape = sig->shape(); TypePtr sig_type = sig->Dtype(); @@ -356,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) { @@ -372,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()) { @@ -380,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()) { @@ -414,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) { @@ -457,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) { @@ -473,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); } @@ -486,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!"; @@ -497,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!"; @@ -512,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!"; @@ -561,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_phase)) { MS_LOG(ERROR) << "Expect string phase, but got " << py::str(py_phase); @@ -578,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!"; @@ -669,6 +717,10 @@ std::map> 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 nodes = DeepScopedGraphSearchWithFilter(func_graph->get_return(), AlwaysInclude, filter); auto is_quant_cnode = [](const AnfNodePtr &node) { return IsPrimitiveCNode(node, prim::kPrimFakeQuantPerLayer) || @@ -676,6 +728,12 @@ std::map> 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) { @@ -732,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!"; } @@ -743,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"; @@ -771,17 +834,22 @@ std::vector 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); @@ -802,6 +870,8 @@ std::vector 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. @@ -996,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(); @@ -1014,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, @@ -1038,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, @@ -1046,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) { @@ -1139,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(); @@ -1187,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()) { @@ -1197,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 @@ -1249,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; @@ -1264,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 { @@ -1274,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 ¶ms_value) { @@ -1292,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 @@ -1303,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_exe_path)) { @@ -1312,6 +1402,7 @@ void GraphExecutorPy::PyExePath(const py::object &py_exe_path) { auto ms_context = MsContext::GetInstance(); ms_context->set_param(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(kernel_build_server_dir)) { @@ -1321,6 +1412,7 @@ void GraphExecutorPy::KernelBuildServerDir(const py::object &kernel_build_server auto ms_context = MsContext::GetInstance(); ms_context->set_param(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 &types, const std::vector> &shapes, @@ -1463,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(MS_CTX_DEVICE_ID); @@ -1517,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; @@ -1526,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; @@ -1535,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 @@ -1543,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) { @@ -1566,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)) { @@ -1586,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 @@ -1599,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(); @@ -1611,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(); @@ -1751,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(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; @@ -1763,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(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 -- 2.34.1 From ee6905dc37e055ee8dbc010f10250875bc582a8a Mon Sep 17 00:00:00 2001 From: gxl1 Date: Thu, 5 Oct 2023 14:15:45 +0800 Subject: [PATCH 09/26] Update pipeline_ge.cc --- mindspore/ccsrc/pipeline/jit/pipeline_ge.cc | 51 +++++++++++++++------ 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/mindspore/ccsrc/pipeline/jit/pipeline_ge.cc b/mindspore/ccsrc/pipeline/jit/pipeline_ge.cc index 0c9dad73aa7..4c3f16161f2 100644 --- a/mindspore/ccsrc/pipeline/jit/pipeline_ge.cc +++ b/mindspore/ccsrc/pipeline/jit/pipeline_ge.cc @@ -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 &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 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 &types, const std::vector> &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 &info, const py::dict &init_params, const std::string &phase, const py::object &broadcast_params) { @@ -211,15 +223,15 @@ bool AddDFGraph(const std::map &info, const py::di } #ifdef ENABLE_DUMP_IR if (MsContext::GetInstance()->get_param(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 &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()) { @@ -337,7 +354,7 @@ py::object ExtractGeneralCnodeRet(const AbstractBasePtr &cnode_data, const py::t auto shape_me = shape->cast()->shape(); auto shape_ge = py::cast(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(); 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 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 DoExecGraph(const FuncGraphPtr &graph, const std::ve void ProcessGeArg(const std::map &info, const py::tuple &args, const std::string &phase, std::vector *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 &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 &info, const FuncGraphPtr anf_graph = info.at(phase)->func_graph; std::shared_ptr ret_val = std::make_shared(); - // 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 -- 2.34.1 From 4de0357f823f40f4cac7148c0171b24bdde61c9b Mon Sep 17 00:00:00 2001 From: gxl1 Date: Thu, 5 Oct 2023 14:38:04 +0800 Subject: [PATCH 10/26] Update pipeline_split.cc --- .../ccsrc/pipeline/jit/pipeline_split.cc | 57 ++++++++++++++++++- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/mindspore/ccsrc/pipeline/jit/pipeline_split.cc b/mindspore/ccsrc/pipeline/jit/pipeline_split.cc index 40e45c465f3..0fee1e914a1 100644 --- a/mindspore/ccsrc/pipeline/jit/pipeline_split.cc +++ b/mindspore/ccsrc/pipeline/jit/pipeline_split.cc @@ -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 &all_nodes) { for (auto &node : all_nodes) { @@ -86,7 +99,11 @@ static bool HasVirtualDataset(const std::vector &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_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 FindForwardGraph(const FuncGraphPtr &root, const std::vector &all_nodes) { std::set graph_sets; @@ -176,6 +202,7 @@ static std::set 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 &all_nodes) { MS_EXCEPTION_IF_NULL(root); @@ -219,6 +246,7 @@ static void InsertVirtualDataset(const FuncGraphPtr &root, const std::vector &nodes, const int64_t device_num, std::vector> *default_strategy) { @@ -240,6 +268,8 @@ void GenerateDefaultStrategy(const ValueNodePtr &axes, const std::vectorvalue()->cast()->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 *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>(strategy->at(0)); input_strategy.push_back(1); strategy->at(0) = MakeValue(input_strategy); } void HandleStrategyForMatMul(std::vector *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>(strategy->at(0)); auto right_matrix_strategy = GetValue>(strategy->at(1)); auto index_a = left_matrix_strategy.size() - 1; @@ -342,6 +375,8 @@ void HandleSpecialStrategy(std::vector *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 *input_nodes) { auto parameters = func_graph->parameters(); @@ -352,6 +387,8 @@ void GetInputNodes(const FuncGraphPtr &func_graph, std::vector *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 *output_nodes) { auto return_node = func_graph->get_return(); @@ -368,6 +405,9 @@ void GetOutputNodes(const FuncGraphPtr &func_graph, std::vector *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> &strategies, const int64_t &device_num) { for (size_t i = 0; i < strategies.size(); ++i) { @@ -387,6 +427,8 @@ bool CheckDeviceNum(const std::vector> &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(); @@ -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 elements; elements.push_back(MakeValue(output_strategy[i])); auto prim = GetCNodePrimitive(node); @@ -451,6 +496,8 @@ std::vector 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 &all_nodes, @@ -526,6 +574,9 @@ void SetStrategyForShard(const FuncGraphPtr &root, const std::vector } } } + //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) { -- 2.34.1 From bc0ebfe8ff9b12d2473276b4be2e0f6a7baa7e82 Mon Sep 17 00:00:00 2001 From: WEI_4614 <2291172974@qq.com> Date: Thu, 5 Oct 2023 14:58:10 +0800 Subject: [PATCH 11/26] Update function_block.cc --- mindspore/ccsrc/pipeline/jit/parse/function_block.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mindspore/ccsrc/pipeline/jit/parse/function_block.cc b/mindspore/ccsrc/pipeline/jit/parse/function_block.cc index 9f94b1c42c7..08eb361618e 100644 --- a/mindspore/ccsrc/pipeline/jit/parse/function_block.cc +++ b/mindspore/ccsrc/pipeline/jit/parse/function_block.cc @@ -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(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; -- 2.34.1 From 9f384e718fdee0a5be92ddb71b8ab02c389dfb09 Mon Sep 17 00:00:00 2001 From: gxl1 Date: Thu, 5 Oct 2023 15:01:30 +0800 Subject: [PATCH 12/26] Update pass.cc --- mindspore/ccsrc/pipeline/jit/pass.cc | 62 ++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/mindspore/ccsrc/pipeline/jit/pass.cc b/mindspore/ccsrc/pipeline/jit/pass.cc index 4eddd948393..2d59cee07d4 100644 --- a/mindspore/ccsrc/pipeline/jit/pass.cc +++ b/mindspore/ccsrc/pipeline/jit/pass.cc @@ -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); } -- 2.34.1 From a63f1fb2b99e9c427007129e2a6f4c31a7bea432 Mon Sep 17 00:00:00 2001 From: gxl1 Date: Thu, 5 Oct 2023 15:31:44 +0800 Subject: [PATCH 13/26] Update action.cc --- mindspore/ccsrc/pipeline/jit/action.cc | 89 +++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/mindspore/ccsrc/pipeline/jit/action.cc b/mindspore/ccsrc/pipeline/jit/action.cc index 7dd9f87a375..2ec65e56c2e 100644 --- a/mindspore/ccsrc/pipeline/jit/action.cc +++ b/mindspore/ccsrc/pipeline/jit/action.cc @@ -94,6 +94,11 @@ 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); @@ -103,6 +108,7 @@ bool IsDynamicShapeGraph(const FuncGraphPtr &func_graph) { } // Disable mindRT in the heterogeneous scenario + dynamic_shape scenario. + void DisableMindRT(const ResourcePtr &res) { MS_EXCEPTION_IF_NULL(res); auto context_ptr = MsContext::GetInstance(); @@ -132,25 +138,30 @@ void DisableMindRT(const ResourcePtr &res) { void TaskEmitActionForMindRT(const ResourcePtr &res) { MS_EXCEPTION_IF_NULL(res); // Get the mindRT backend. + auto bc_ptr = res->GetResult(kBackend).cast(); auto mindrt_bc_ptr = std::dynamic_pointer_cast(bc_ptr); MS_EXCEPTION_IF_NULL(mindrt_bc_ptr); // The output of graph compiler is actor. + auto actor_info = mindrt_bc_ptr->CompileGraphs(res->func_graph()); 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(); // Get the mindRT backend. + std::shared_ptr bc_ptr = res->GetResult(kBackend).cast>(); auto mindrt_bc_ptr = (std::dynamic_pointer_cast(bc_ptr)).get(); MS_EXCEPTION_IF_NULL(mindrt_bc_ptr); // Construct the graph run function ptr. + compile::VmEvalFuncPtr run = std::make_shared([mindrt_bc_ptr, actor_info](const VectorRef &args) -> BaseRef { MS_LOG(DEBUG) << "Execute args size " << args.size(); @@ -167,17 +178,20 @@ void ExecuteActionForMindRT(const ResourcePtr &res) { } // Modify the output node of func_graph to add forward nodes used in bprop graph. + void ModifyOutputNode(const FuncGraphPtr &func_graph) { MS_EXCEPTION_IF_NULL(func_graph); const auto &used_forward_nodes = func_graph->used_forward_nodes(); // Get original output node and abstract + auto original_output_node = func_graph->output(); MS_EXCEPTION_IF_NULL(original_output_node); auto original_output_abs = original_output_node->abstract(); MS_EXCEPTION_IF_NULL(original_output_abs); // Create a new make tuple node to hold all forward used nodes. + abstract::AbstractBasePtrList added_abs_list; std::vector added_node_list{NewValueNode(prim::kPrimMakeTuple)}; std::for_each(used_forward_nodes.begin(), used_forward_nodes.end(), @@ -199,6 +213,7 @@ void ModifyOutputNode(const FuncGraphPtr &func_graph) { MS_LOG(DEBUG) << "Added output node info: " << added_output_node->DebugString(); // Merge original output node and used forward nodes to return node. + std::vector new_output_nodes{NewValueNode(prim::kPrimMakeTuple), original_output_node, added_output_node}; auto merge_node = func_graph->NewCNode(std::move(new_output_nodes)); abstract::AbstractBasePtrList new_output_abs{original_output_abs, added_output_abs}; @@ -207,10 +222,12 @@ void ModifyOutputNode(const FuncGraphPtr &func_graph) { func_graph->set_output(merge_node); // Clear + func_graph->set_modify_output(true); func_graph->ClearUsedForwardNodes(); } } // namespace + using CompileGraphs = compile::CompileGraphs; using abstract::AnalysisResult; using mindspore::abstract::AnalysisContextPtr; @@ -229,8 +246,10 @@ 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); if (primitive != nullptr && abstract::GetPrimEvaluator(primitive, engine) == nullptr) { MS_LOG(INFO) << "The primitive is not defined in front end. Primitive: " << primitive->ToString(); @@ -243,8 +262,10 @@ 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. + if (!node->isa() || (prev_inferred != nullptr && prev_inferred->isa())) { // Reset tuple/list abstract use flags. + if (enable_eliminate_unused_element && prev_inferred != nullptr && prev_inferred->isa()) { SetSequenceNodeElementsUseFlags(node, nullptr); @@ -324,6 +345,7 @@ const FuncGraphPtr GetLoadedGraph(const ResourcePtr &res) { } // 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(); @@ -377,7 +399,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(); @@ -420,9 +444,13 @@ bool ParseAction(const ResourcePtr &res) { } // obj_map's graphs have the same construct, these graphs can be optimized to one graph. + // 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(); @@ -452,6 +480,7 @@ bool CombineLikeGraphs(const ResourcePtr &res) { auto &node_users = res->manager()->node_users()[fv]; for (auto &n : node_users) { // If the user is not in this graph, no need to change. + auto iter = cloned_nodes.find(n.first); if (iter == cloned_nodes.end()) { continue; @@ -493,6 +522,7 @@ bool SymbolResolveAction(const ResourcePtr &res) { } bool ret = parse::ResolveFuncGraph(func_graph, res); // Remove unused nodes in cnode order list. + if (func_graph) { func_graph->EraseUnusedNodeInOrder(); for (auto fg : func_graph->func_graphs_used_total()) { @@ -560,8 +590,11 @@ bool EliminateUnusedParameterAction(const ResourcePtr &res) { } // 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) { @@ -593,16 +626,20 @@ 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(); for (auto fg : new_fg->func_graphs_used_total()) { @@ -612,6 +649,7 @@ bool AbstractSpecializeAction(const ResourcePtr &res) { } } // Check input after abstract when there is a loaded graph + if (loaded_graph_ptr != nullptr) { CheckRootInputShapeAndType(res, loaded_graph_ptr); } @@ -718,9 +756,12 @@ bool CheckGraphOutputConstOrParameter(const FuncGraphPtr &func_graph) { } // 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(MS_CTX_EXECUTION_MODE) == kGraphMode) { return true; } @@ -730,6 +771,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."; @@ -739,6 +781,7 @@ bool EliminateForwardCNode(const ResourcePtr &res) { } // Run grad process for func_graph and replace forward nodes with its output tensors. + MS_LOG(INFO) << "Run eliminate forward nodes action."; MS_EXCEPTION_IF_NULL(res); auto ms_func_graph = res->func_graph(); @@ -752,6 +795,7 @@ bool EliminateForwardCNode(const ResourcePtr &res) { ModifyOutputNode(ms_func_graph); // Keep roots for only keeping forward func graph in resource. + auto manager = res->manager(); MS_EXCEPTION_IF_NULL(manager); manager->KeepRoots({ms_func_graph}); @@ -772,6 +816,7 @@ bool EliminateAdRelatedSpecialOpNode(const ResourcePtr &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 &all_nodes) { for (const auto &node : all_nodes) { if (!node->isa()) { @@ -836,9 +881,12 @@ bool ExistTarget(const std::vector &all_nodes, const std::string &ta } // If the return value of subgraph is Ref in control flow scenarios, should run graph mode with kernelbykernel. + bool ExistSwitchRef(const FuncGraphPtr &func_graph, const std::vector &all_nodes) { // %1 = switch(cond, func1, func2) + // %2 = %1() if the abstract of the node is AbstractRef, return true. + auto manager = func_graph->manager(); MS_EXCEPTION_IF_NULL(manager); auto &node_users = manager->node_users(); @@ -882,11 +930,13 @@ 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; } // GRAPH | Single Op : KernelByKernel path in MindRT. + if (common::GetEnv(kGraphOpRun) == "1") { MS_LOG(INFO) << "Run graph mode with kernelbykernel."; set_ctx(false, false, false); @@ -894,6 +944,7 @@ void SetRunMode(const FuncGraphPtr &func_graph, compile::Backend *backend_ptr) { } // GRAPH | Dynamic Shape : KernelByKernel path in MindRT. + if (IsDynamicShapeGraph(func_graph)) { MS_LOG(INFO) << "Run Graph mode with kernelbykernel(Dynamic Shape)."; set_ctx(false, false, false); @@ -901,6 +952,7 @@ void SetRunMode(const FuncGraphPtr &func_graph, compile::Backend *backend_ptr) { } // GRAPH | Closure\ENV\While scenario : KernelByKernel path in MindRT. + auto graphs = func_graph->func_graphs_used_total(); (void)graphs.insert(func_graph); bool exist_control_flow = ExistControlFlow(func_graph); @@ -921,26 +973,33 @@ void SetRunMode(const FuncGraphPtr &func_graph, compile::Backend *backend_ptr) { } // Multiple device targets scenario. + if (func_graph->exist_multi_target()) { // Heterogeneous scenario + ControlFlow : KernelByKernel path in MindRT. + if (exist_control_flow) { MS_LOG(INFO) << "Run graph mode with kernelbykernel."; set_ctx(false, false, false); return; } // GRAPH | Heterogeneous scenario : No control flow, subgraph sink path in MindRT. + MS_LOG(INFO) << "Run graph mode with subgraph sink."; set_ctx(true, false, false); return; } // GRAPH | normal network and if/for/switch scenario etc : MultiGraph path in MindRT. + 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 +// 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); @@ -1001,6 +1060,7 @@ bool TaskEmitAction(const ResourcePtr &res) { !is_parallel; if (context_ptr->get_param(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(MS_CTX_EXECUTION_MODE, kGraphMode); MS_LOG(INFO) << "PyNative graph Compile and Run in GRAPH_MODE"; @@ -1014,6 +1074,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(MS_CTX_ENABLE_MINDRT)) { TaskEmitActionForMindRT(res); if (pynative_switch_to_graph_mode) { @@ -1023,6 +1084,7 @@ bool TaskEmitAction(const ResourcePtr &res) { } // 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); @@ -1049,12 +1111,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(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(); std::shared_ptr bc_ptr = res->GetResult(kBackend).cast>(); @@ -1110,6 +1174,7 @@ bool StartPSServerAction(const ResourcePtr &res) { } // 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(); @@ -1119,7 +1184,9 @@ bool StartServerAction(const ResourcePtr &res) { uint16_t fl_server_port = ps::PSContext::instance()->fl_server_port(); // 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(std::ceil(start_fl_job_threshold * update_model_ratio)); @@ -1226,9 +1293,13 @@ bool DistributedSplitAction(const ResourcePtr &res) { #endif // The parallel primitive related valuenode might be partitioned so that its value changes by device, + // 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()); @@ -1261,6 +1332,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; @@ -1280,6 +1352,7 @@ 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); @@ -1336,6 +1409,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; @@ -1357,6 +1431,7 @@ bool PreAdActionPyStub(const ResourcePtr &res) { } // 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()) { @@ -1400,13 +1475,16 @@ bool OptActionGePyStub(const ResourcePtr &res) { } // Returns a vector containing multiple Actionitems + static std::vector CommonPipeline() { std::vector 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)); auto multi_graphs = parallel::CostModelContext::GetInstance()->is_multi_subgraphs(); @@ -1416,16 +1494,22 @@ static std::vector CommonPipeline() { (void)actions.emplace_back(std::make_pair("inference_opt_prepare", InferenceOptPrepareAction)); // Eliminate unused parameters before renormalize. + (void)actions.emplace_back(std::make_pair("elininate_unused_parameter", EliminateUnusedParameterAction)); // Evaluate type and shape, and specialize. + (void)actions.emplace_back(std::make_pair("abstract_specialize", AbstractSpecializeAction)); // Auto-monad for side-effects handling. + (void)actions.emplace_back(std::make_pair("auto_monad", AutoMonadAction)); // Do data structure simplifications and inline. + (void)actions.emplace_back(std::make_pair("inline", OptInlineAction)); // Add pre-ad, post-inline python pass stub. + (void)actions.emplace_back(std::make_pair("py_pre_ad", PreAdActionPyStub)); // Do PipelineSplit action. + (void)actions.emplace_back(std::make_pair("pipeline_split", PipelineSplitAction)); return actions; @@ -1434,8 +1518,10 @@ static std::vector CommonPipeline() { std::vector GePipeline() { auto actions = CommonPipeline(); // Optimize + (void)actions.emplace_back(std::make_pair("optimize", GeOptimizeAction)); // Add opt-stage python pass stub + (void)actions.emplace_back(std::make_pair("py_opt", OptActionGePyStub)); (void)actions.emplace_back(std::make_pair("remove_value_node_duplications", RemoveValueNodeDuplicationsAction)); (void)actions.emplace_back(std::make_pair("auto_monad_reorder", OrderEnforceAction)); @@ -1448,6 +1534,7 @@ std::vector GePipeline() { std::vector VmPipeline(const ResourcePtr &resource) { std::vector actions; // If enable compilation cache and the cache is read successfully, only do the backend actions. + if (!resource->EnableCompileCache() || resource->func_graph() == nullptr) { actions = CommonPipeline(); -- 2.34.1 From fcabe6b9a2ca7a27a8773ce5d536d96fff67ba73 Mon Sep 17 00:00:00 2001 From: gxl1 Date: Thu, 5 Oct 2023 15:41:28 +0800 Subject: [PATCH 14/26] Update validator.cc --- mindspore/ccsrc/pipeline/jit/validator.cc | 29 +++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/mindspore/ccsrc/pipeline/jit/validator.cc b/mindspore/ccsrc/pipeline/jit/validator.cc index 86268a09a12..855698d5d41 100644 --- a/mindspore/ccsrc/pipeline/jit/validator.cc +++ b/mindspore/ccsrc/pipeline/jit/validator.cc @@ -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(); @@ -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); -- 2.34.1 From 47564b10e50c77a4228c9caac7534e4768d12261 Mon Sep 17 00:00:00 2001 From: gxl1 Date: Thu, 5 Oct 2023 15:44:56 +0800 Subject: [PATCH 15/26] Update action.cc --- mindspore/ccsrc/pipeline/jit/action.cc | 91 +++----------------------- 1 file changed, 8 insertions(+), 83 deletions(-) diff --git a/mindspore/ccsrc/pipeline/jit/action.cc b/mindspore/ccsrc/pipeline/jit/action.cc index 2ec65e56c2e..ee03e162bc6 100644 --- a/mindspore/ccsrc/pipeline/jit/action.cc +++ b/mindspore/ccsrc/pipeline/jit/action.cc @@ -94,10 +94,8 @@ 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, - + //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) { @@ -108,7 +106,6 @@ bool IsDynamicShapeGraph(const FuncGraphPtr &func_graph) { } // Disable mindRT in the heterogeneous scenario + dynamic_shape scenario. - void DisableMindRT(const ResourcePtr &res) { MS_EXCEPTION_IF_NULL(res); auto context_ptr = MsContext::GetInstance(); @@ -138,30 +135,25 @@ void DisableMindRT(const ResourcePtr &res) { void TaskEmitActionForMindRT(const ResourcePtr &res) { MS_EXCEPTION_IF_NULL(res); // Get the mindRT backend. - auto bc_ptr = res->GetResult(kBackend).cast(); auto mindrt_bc_ptr = std::dynamic_pointer_cast(bc_ptr); MS_EXCEPTION_IF_NULL(mindrt_bc_ptr); // The output of graph compiler is actor. - auto actor_info = mindrt_bc_ptr->CompileGraphs(res->func_graph()); 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(); // Get the mindRT backend. - std::shared_ptr bc_ptr = res->GetResult(kBackend).cast>(); auto mindrt_bc_ptr = (std::dynamic_pointer_cast(bc_ptr)).get(); MS_EXCEPTION_IF_NULL(mindrt_bc_ptr); // Construct the graph run function ptr. - compile::VmEvalFuncPtr run = std::make_shared([mindrt_bc_ptr, actor_info](const VectorRef &args) -> BaseRef { MS_LOG(DEBUG) << "Execute args size " << args.size(); @@ -178,20 +170,17 @@ void ExecuteActionForMindRT(const ResourcePtr &res) { } // Modify the output node of func_graph to add forward nodes used in bprop graph. - void ModifyOutputNode(const FuncGraphPtr &func_graph) { MS_EXCEPTION_IF_NULL(func_graph); const auto &used_forward_nodes = func_graph->used_forward_nodes(); // Get original output node and abstract - auto original_output_node = func_graph->output(); MS_EXCEPTION_IF_NULL(original_output_node); auto original_output_abs = original_output_node->abstract(); MS_EXCEPTION_IF_NULL(original_output_abs); // Create a new make tuple node to hold all forward used nodes. - abstract::AbstractBasePtrList added_abs_list; std::vector added_node_list{NewValueNode(prim::kPrimMakeTuple)}; std::for_each(used_forward_nodes.begin(), used_forward_nodes.end(), @@ -213,7 +202,6 @@ void ModifyOutputNode(const FuncGraphPtr &func_graph) { MS_LOG(DEBUG) << "Added output node info: " << added_output_node->DebugString(); // Merge original output node and used forward nodes to return node. - std::vector new_output_nodes{NewValueNode(prim::kPrimMakeTuple), original_output_node, added_output_node}; auto merge_node = func_graph->NewCNode(std::move(new_output_nodes)); abstract::AbstractBasePtrList new_output_abs{original_output_abs, added_output_abs}; @@ -227,7 +215,6 @@ void ModifyOutputNode(const FuncGraphPtr &func_graph) { func_graph->ClearUsedForwardNodes(); } } // namespace - using CompileGraphs = compile::CompileGraphs; using abstract::AnalysisResult; using mindspore::abstract::AnalysisContextPtr; @@ -249,7 +236,6 @@ abstract::AnalysisResult AbstractAnalyze(const ResourcePtr &resource, const Func if (resource->is_load()) { // If the primitive is not defined in front end, keep the inferred value loaded from MindIR. - auto primitive = GetCNodePrimitive(node); if (primitive != nullptr && abstract::GetPrimEvaluator(primitive, engine) == nullptr) { MS_LOG(INFO) << "The primitive is not defined in front end. Primitive: " << primitive->ToString(); @@ -261,11 +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() || (prev_inferred != nullptr && prev_inferred->isa())) { - // Reset tuple/list abstract use flags. - + // Reset tuple/list abstract use flags if (enable_eliminate_unused_element && prev_inferred != nullptr && prev_inferred->isa()) { SetSequenceNodeElementsUseFlags(node, nullptr); @@ -344,8 +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. - +// 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(); @@ -398,8 +381,7 @@ 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, - +// 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) { @@ -444,11 +426,8 @@ bool ParseAction(const ResourcePtr &res) { } // obj_map's graphs have the same construct, these graphs can be optimized to one graph. - // 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) { @@ -480,7 +459,6 @@ bool CombineLikeGraphs(const ResourcePtr &res) { auto &node_users = res->manager()->node_users()[fv]; for (auto &n : node_users) { // If the user is not in this graph, no need to change. - auto iter = cloned_nodes.find(n.first); if (iter == cloned_nodes.end()) { continue; @@ -522,7 +500,6 @@ bool SymbolResolveAction(const ResourcePtr &res) { } bool ret = parse::ResolveFuncGraph(func_graph, res); // Remove unused nodes in cnode order list. - if (func_graph) { func_graph->EraseUnusedNodeInOrder(); for (auto fg : func_graph->func_graphs_used_total()) { @@ -590,9 +567,7 @@ bool EliminateUnusedParameterAction(const ResourcePtr &res) { } // 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) { @@ -626,20 +601,13 @@ 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(); for (auto fg : new_fg->func_graphs_used_total()) { @@ -649,7 +617,6 @@ bool AbstractSpecializeAction(const ResourcePtr &res) { } } // Check input after abstract when there is a loaded graph - if (loaded_graph_ptr != nullptr) { CheckRootInputShapeAndType(res, loaded_graph_ptr); } @@ -756,12 +723,9 @@ bool CheckGraphOutputConstOrParameter(const FuncGraphPtr &func_graph) { } // 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(MS_CTX_EXECUTION_MODE) == kGraphMode) { return true; } @@ -781,7 +745,6 @@ bool EliminateForwardCNode(const ResourcePtr &res) { } // Run grad process for func_graph and replace forward nodes with its output tensors. - MS_LOG(INFO) << "Run eliminate forward nodes action."; MS_EXCEPTION_IF_NULL(res); auto ms_func_graph = res->func_graph(); @@ -795,7 +758,6 @@ bool EliminateForwardCNode(const ResourcePtr &res) { ModifyOutputNode(ms_func_graph); // Keep roots for only keeping forward func graph in resource. - auto manager = res->manager(); MS_EXCEPTION_IF_NULL(manager); manager->KeepRoots({ms_func_graph}); @@ -816,7 +778,6 @@ bool EliminateAdRelatedSpecialOpNode(const ResourcePtr &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 &all_nodes) { for (const auto &node : all_nodes) { if (!node->isa()) { @@ -881,12 +842,9 @@ bool ExistTarget(const std::vector &all_nodes, const std::string &ta } // If the return value of subgraph is Ref in control flow scenarios, should run graph mode with kernelbykernel. - bool ExistSwitchRef(const FuncGraphPtr &func_graph, const std::vector &all_nodes) { // %1 = switch(cond, func1, func2) - // %2 = %1() if the abstract of the node is AbstractRef, return true. - auto manager = func_graph->manager(); MS_EXCEPTION_IF_NULL(manager); auto &node_users = manager->node_users(); @@ -936,7 +894,6 @@ void SetRunMode(const FuncGraphPtr &func_graph, compile::Backend *backend_ptr) { } // GRAPH | Single Op : KernelByKernel path in MindRT. - if (common::GetEnv(kGraphOpRun) == "1") { MS_LOG(INFO) << "Run graph mode with kernelbykernel."; set_ctx(false, false, false); @@ -944,7 +901,6 @@ void SetRunMode(const FuncGraphPtr &func_graph, compile::Backend *backend_ptr) { } // GRAPH | Dynamic Shape : KernelByKernel path in MindRT. - if (IsDynamicShapeGraph(func_graph)) { MS_LOG(INFO) << "Run Graph mode with kernelbykernel(Dynamic Shape)."; set_ctx(false, false, false); @@ -952,7 +908,6 @@ void SetRunMode(const FuncGraphPtr &func_graph, compile::Backend *backend_ptr) { } // GRAPH | Closure\ENV\While scenario : KernelByKernel path in MindRT. - auto graphs = func_graph->func_graphs_used_total(); (void)graphs.insert(func_graph); bool exist_control_flow = ExistControlFlow(func_graph); @@ -973,31 +928,26 @@ void SetRunMode(const FuncGraphPtr &func_graph, compile::Backend *backend_ptr) { } // Multiple device targets scenario. - if (func_graph->exist_multi_target()) { // Heterogeneous scenario + ControlFlow : KernelByKernel path in MindRT. - if (exist_control_flow) { MS_LOG(INFO) << "Run graph mode with kernelbykernel."; set_ctx(false, false, false); return; } // GRAPH | Heterogeneous scenario : No control flow, subgraph sink path in MindRT. - MS_LOG(INFO) << "Run graph mode with subgraph sink."; set_ctx(true, false, false); 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) { @@ -1083,8 +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); @@ -1184,7 +1133,6 @@ bool StartServerAction(const ResourcePtr &res) { uint16_t fl_server_port = ps::PSContext::instance()->fl_server_port(); // 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(); @@ -1293,11 +1241,8 @@ bool DistributedSplitAction(const ResourcePtr &res) { #endif // The parallel primitive related valuenode might be partitioned so that its value changes by device, - // 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) { @@ -1430,8 +1375,7 @@ bool PreAdActionPyStub(const ResourcePtr &res) { return true; } -// Run the Python optimization procedure on the computation graph associated with the resource pointer. - +// 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()) { @@ -1478,13 +1422,9 @@ bool OptActionGePyStub(const ResourcePtr &res) { static std::vector CommonPipeline() { std::vector 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)); auto multi_graphs = parallel::CostModelContext::GetInstance()->is_multi_subgraphs(); @@ -1494,22 +1434,16 @@ static std::vector CommonPipeline() { (void)actions.emplace_back(std::make_pair("inference_opt_prepare", InferenceOptPrepareAction)); // Eliminate unused parameters before renormalize. - (void)actions.emplace_back(std::make_pair("elininate_unused_parameter", EliminateUnusedParameterAction)); // Evaluate type and shape, and specialize. - (void)actions.emplace_back(std::make_pair("abstract_specialize", AbstractSpecializeAction)); // Auto-monad for side-effects handling. - (void)actions.emplace_back(std::make_pair("auto_monad", AutoMonadAction)); // Do data structure simplifications and inline. - (void)actions.emplace_back(std::make_pair("inline", OptInlineAction)); // Add pre-ad, post-inline python pass stub. - (void)actions.emplace_back(std::make_pair("py_pre_ad", PreAdActionPyStub)); // Do PipelineSplit action. - (void)actions.emplace_back(std::make_pair("pipeline_split", PipelineSplitAction)); return actions; @@ -1518,10 +1452,8 @@ static std::vector CommonPipeline() { std::vector GePipeline() { auto actions = CommonPipeline(); // Optimize - (void)actions.emplace_back(std::make_pair("optimize", GeOptimizeAction)); // Add opt-stage python pass stub - (void)actions.emplace_back(std::make_pair("py_opt", OptActionGePyStub)); (void)actions.emplace_back(std::make_pair("remove_value_node_duplications", RemoveValueNodeDuplicationsAction)); (void)actions.emplace_back(std::make_pair("auto_monad_reorder", OrderEnforceAction)); @@ -1534,21 +1466,15 @@ std::vector GePipeline() { std::vector VmPipeline(const ResourcePtr &resource) { std::vector actions; // 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)); @@ -1571,7 +1497,6 @@ std::vector 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)); -- 2.34.1 From b5486e73d109f3d54e4f69ab9a0d5e468d9c978d Mon Sep 17 00:00:00 2001 From: gxl1 Date: Thu, 5 Oct 2023 15:46:56 +0800 Subject: [PATCH 16/26] Update init.cc --- mindspore/ccsrc/pipeline/jit/init.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mindspore/ccsrc/pipeline/jit/init.cc b/mindspore/ccsrc/pipeline/jit/init.cc index a0c8474afbb..84d2b31291f 100644 --- a/mindspore/ccsrc/pipeline/jit/init.cc +++ b/mindspore/ccsrc/pipeline/jit/init.cc @@ -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. \ No newline at end of file -- 2.34.1 From 6440a6a554bf072a11ab2a7f2709e5a2723055d0 Mon Sep 17 00:00:00 2001 From: gxl1 Date: Thu, 5 Oct 2023 15:51:33 +0800 Subject: [PATCH 17/26] Update remove_value_node_dup.cc --- .../pipeline/jit/remove_value_node_dup.cc | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/mindspore/ccsrc/pipeline/jit/remove_value_node_dup.cc b/mindspore/ccsrc/pipeline/jit/remove_value_node_dup.cc index 93eafb2b743..b0a115bbd4a 100644 --- a/mindspore/ccsrc/pipeline/jit/remove_value_node_dup.cc +++ b/mindspore/ccsrc/pipeline/jit/remove_value_node_dup.cc @@ -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); -- 2.34.1 From 6b0422e762accc635fae94fe057492dc6bdcf8aa Mon Sep 17 00:00:00 2001 From: WEI_4614 <2291172974@qq.com> Date: Thu, 5 Oct 2023 15:52:31 +0800 Subject: [PATCH 18/26] Update parse.cc --- mindspore/ccsrc/pipeline/jit/parse/parse.cc | 74 +++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/mindspore/ccsrc/pipeline/jit/parse/parse.cc b/mindspore/ccsrc/pipeline/jit/parse/parse.cc index ec4202d6ae7..755f010ba30 100644 --- a/mindspore/ccsrc/pipeline/jit/parse/parse.cc +++ b/mindspore/ccsrc/pipeline/jit/parse/parse.cc @@ -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 &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_ptrfunc_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> GetFreeVariable(const FuncGraphPtr &func_graph) { // Considering the performance, we didn't use Manager here. std::vector> free_variables; @@ -183,6 +189,11 @@ std::vector> 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 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 Parser::ParseException(const FunctionBlockPtr &block, co return node_inputs; } +// Parse the function call in the raise statement and return the parsing result std::vector 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 *packed_arguments, std::vector *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 *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); -- 2.34.1 From 0e2d4054331d9964d393a08ab9434e6dfe399d74 Mon Sep 17 00:00:00 2001 From: qsdy Date: Thu, 5 Oct 2023 15:54:59 +0800 Subject: [PATCH 19/26] Update async_eval_result.cc --- .../jit/static_analysis/async_eval_result.cc | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/mindspore/ccsrc/pipeline/jit/static_analysis/async_eval_result.cc b/mindspore/ccsrc/pipeline/jit/static_analysis/async_eval_result.cc index 2a41aaef748..cd045c54169 100644 --- a/mindspore/ccsrc/pipeline/jit/static_analysis/async_eval_result.cc +++ b/mindspore/ccsrc/pipeline/jit/static_analysis/async_eval_result.cc @@ -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(), 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 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 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 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 ¤t_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_); } -- 2.34.1 From 540f6ec62511792d9025f4c8170dbccc6064ab4a Mon Sep 17 00:00:00 2001 From: WEI_4614 <2291172974@qq.com> Date: Thu, 5 Oct 2023 16:04:57 +0800 Subject: [PATCH 20/26] Update parse_dynamic.cc --- .../ccsrc/pipeline/jit/parse/parse_dynamic.cc | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/mindspore/ccsrc/pipeline/jit/parse/parse_dynamic.cc b/mindspore/ccsrc/pipeline/jit/parse/parse_dynamic.cc index a97cbb11daa..8939005e917 100644 --- a/mindspore/ccsrc/pipeline/jit/parse/parse_dynamic.cc +++ b/mindspore/ccsrc/pipeline/jit/parse/parse_dynamic.cc @@ -33,6 +33,9 @@ static const std::set 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 &ast, const py::object &node, parse::AstMainType type) { MS_EXCEPTION_IF_NULL(ast); @@ -63,6 +66,9 @@ void DynamicParser::ParseInputArgs(const std::shared_ptr &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 &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 &, const py::object &node, const std::vector &compare_prim) { MS_LOG(DEBUG) << "Parse augassign expr"; @@ -168,6 +180,9 @@ bool DynamicParser::ParseAugAssignExprNode(const std::shared_ptr &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 &ast, const py::object &fn_node, const std::vector &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()) { -- 2.34.1 From 7a787260f30ddfa753eb87f102e9e05bf2fe3355 Mon Sep 17 00:00:00 2001 From: gxl1 Date: Thu, 5 Oct 2023 16:08:53 +0800 Subject: [PATCH 21/26] Update resolve.cc --- mindspore/ccsrc/pipeline/jit/parse/resolve.cc | 52 ++++++++++++++----- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/mindspore/ccsrc/pipeline/jit/parse/resolve.cc b/mindspore/ccsrc/pipeline/jit/parse/resolve.cc index 8469ddf4bd4..328fe025d29 100644 --- a/mindspore/ccsrc/pipeline/jit/parse/resolve.cc +++ b/mindspore/ccsrc/pipeline/jit/parse/resolve.cc @@ -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(prim::kPrimMakeRecord); return std::make_shared(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(); } + //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()) { @@ -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 &value_vec) { if (value_vec.empty()) { @@ -285,6 +302,9 @@ bool IsAllFuncInValueSequence(const std::vector &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 &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(resolved_node) || IsValueNode(resolved_node)) { (void)TransformVectorFuncValueNode(manager, node->func_graph(), resolved_node->cast(), &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); -- 2.34.1 From 18a2b69706574d3abd6869eb797ce35f06aa03b0 Mon Sep 17 00:00:00 2001 From: qsdy Date: Thu, 5 Oct 2023 17:22:27 +0800 Subject: [PATCH 22/26] Update evaluator.cc --- .../pipeline/jit/static_analysis/evaluator.cc | 112 +++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/mindspore/ccsrc/pipeline/jit/static_analysis/evaluator.cc b/mindspore/ccsrc/pipeline/jit/static_analysis/evaluator.cc index 162bc340ee9..cd8595725c0 100644 --- a/mindspore/ccsrc/pipeline/jit/static_analysis/evaluator.cc +++ b/mindspore/ccsrc/pipeline/jit/static_analysis/evaluator.cc @@ -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(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 ¤t_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 ¤t_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()) { 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(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(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()) { int axis = dyn_cast(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(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(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()) { 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()) { 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()) { int axis = dyn_cast(sub_out_axes)->value(); return ExtendDim(&axis, arg_spec, axis_size); } else if (sub_out_axes->isa()) { + // 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()) { + // Otherwise an abstract tuple consisting of a sequence of physical views is returned. return std::make_shared(physical_view_abs_list); } return std::make_shared(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(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(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()) { @@ -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(output_, std::make_shared()); } +// 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."; -- 2.34.1 From a9c0651145ad30aa82d50367345fa3fa084a2f98 Mon Sep 17 00:00:00 2001 From: qsdy Date: Thu, 5 Oct 2023 18:46:14 +0800 Subject: [PATCH 23/26] Update prim.cc --- .../pipeline/jit/static_analysis/prim.cc | 472 +++++++++++++----- 1 file changed, 336 insertions(+), 136 deletions(-) diff --git a/mindspore/ccsrc/pipeline/jit/static_analysis/prim.cc b/mindspore/ccsrc/pipeline/jit/static_analysis/prim.cc index 325af8f5757..1ecfd3a1c9e 100644 --- a/mindspore/ccsrc/pipeline/jit/static_analysis/prim.cc +++ b/mindspore/ccsrc/pipeline/jit/static_analysis/prim.cc @@ -161,6 +161,7 @@ EvalResultPtr UnpackGraphEvaluator::Run(AnalysisEnginePtr engine, const ConfigPt MS_EXCEPTION_IF_NULL(engine); MS_EXCEPTION_IF_NULL(out_conf); MS_EXCEPTION_IF_NULL(out_conf->node()); + // Ensure that the output node is of CNode type. if (out_conf->node() == nullptr || !out_conf->node()->isa()) { MS_LOG(EXCEPTION) << "Node of out_conf should be CNode"; } @@ -170,11 +171,13 @@ EvalResultPtr UnpackGraphEvaluator::Run(AnalysisEnginePtr engine, const ConfigPt auto out_node = out_conf->node()->cast(); MS_EXCEPTION_IF_NULL(out_node); const auto &out_node_inputs = out_node->inputs(); + // Ensure that the size of the input arguments list matches the number of input parameters of the input node. if (out_node->inputs().empty() || (out_node_inputs.size() - 1) != args_conf_list.size()) { MS_LOG(EXCEPTION) << "UnpackGraphPrimitive" << " args size should equal to inputs size minus 1, but args size " << args_conf_list.size() << ", inputs size " << out_node_inputs.size(); } + // Get the abstract types of the argument configurations. AbstractBasePtrList args_spec_list; (void)std::transform(args_conf_list.begin(), args_conf_list.end(), std::back_inserter(args_spec_list), [](const ConfigPtr &ref) -> AbstractBasePtr { @@ -182,7 +185,7 @@ EvalResultPtr UnpackGraphEvaluator::Run(AnalysisEnginePtr engine, const ConfigPt MS_EXCEPTION_IF_NULL(ref->ObtainEvalResult()); return ref->ObtainEvalResult()->abstract(); }); - // get the forward graph + // Get the forward computation graph. if (args_spec_list.empty()) { MS_LOG(EXCEPTION) << "args_spec_list can't be empty."; } @@ -195,6 +198,7 @@ EvalResultPtr UnpackGraphEvaluator::Run(AnalysisEnginePtr engine, const ConfigPt MS_EXCEPTION_IF_NULL(real_fn); FuncGraphPtr forward_graph = real_fn->func_graph(); MS_EXCEPTION_IF_NULL(forward_graph); + // Generate specialized abstract types based on the argument configurations list. AbstractBasePtrList graph_specialize_args = GetUnpackGraphSpecArgsList(args_spec_list, unpack_graph->need_unpack_args()); AbstractBasePtrList graph_specialize_args_without_sens; @@ -204,16 +208,21 @@ EvalResultPtr UnpackGraphEvaluator::Run(AnalysisEnginePtr engine, const ConfigPt (void)std::transform(graph_specialize_args.begin(), graph_specialize_args.end() - (unpack_graph->with_sens_in_args() ? 1 : 0), std::back_inserter(graph_specialize_args_without_sens), [](AbstractBasePtr abs) { return abs; }); + // Generate a new computation graph. auto new_graph = forward_graph->GenerateGraph(graph_specialize_args_without_sens); engine->func_graph_manager()->AddFuncGraph(new_graph); + // Set the scope, if the output configuration exists, use the scope of the configuration node, + // otherwise, use the default scope. ScopePtr scope = kDefaultScope; if (out_conf != nullptr) { scope = out_conf->node()->scope(); } ScopeGuard scope_guard(scope); + // Create a new value node and generate its configuration. AnfNodePtr new_vnode = NewValueNode(new_graph); AnfNodeConfigPtr fn_conf = engine->MakeConfig(new_vnode, out_conf->context(), out_conf->func_graph()); - + + // Perform forward computation. return engine->ForwardConfig(out_conf, fn_conf); } @@ -222,20 +231,26 @@ AnfNodePtr MixedPrecisionCastHelper(const AnfNodePtr &source_node, const Abstrac MS_EXCEPTION_IF_NULL(node_type); MS_EXCEPTION_IF_NULL(func_graph); AnfNodePtr target_node = source_node; + + // Check if the node type is AbstractTensor if (node_type->isa()) { auto x = node_type->cast(); if (x->element()->BuildType()->isa()) { + // Perform the cast operation auto cast = prim::GetPythonOps("cast", "mindspore.ops.functional"); MS_EXCEPTION_IF_NULL(cast); target_node = func_graph->NewCNodeAfter(source_node, {NewValueNode(cast), source_node, target_type}); } - } else if (node_type->isa()) { + } + // Check if the node type is AbstractTuple + else if (node_type->isa()) { auto x = node_type->cast(); auto &items = x->elements(); std::vector nodes; nodes.emplace_back(NewValueNode(prim::kPrimMakeTuple)); int64_t idx = 0; for (const auto &item : items) { + // Recursively perform mixed precision casting on tuple elements AnfNodePtr tuple_node = func_graph->NewCNode({NewValueNode(prim::kPrimTupleGetItem), source_node, NewValueNode(idx)}); AnfNodePtr node = MixedPrecisionCastHelper(tuple_node, item, target_type, func_graph); @@ -243,7 +258,9 @@ AnfNodePtr MixedPrecisionCastHelper(const AnfNodePtr &source_node, const Abstrac ++idx; } target_node = func_graph->NewCNode(nodes); - } else if (node_type->isa()) { + } + // Check if the node type is AbstractDictionary + else if (node_type->isa()) { auto x = node_type->cast(); auto &items = x->elements(); std::vector dict_key_nodes; @@ -251,6 +268,7 @@ AnfNodePtr MixedPrecisionCastHelper(const AnfNodePtr &source_node, const Abstrac dict_key_nodes.emplace_back(NewValueNode(prim::kPrimMakeTuple)); dict_value_nodes.emplace_back(NewValueNode(prim::kPrimMakeTuple)); for (const auto &item : items) { + // Recursively perform mixed precision casting on dictionary values AnfNodePtr dict_value_node = func_graph->NewCNode({NewValueNode(prim::kPrimDictGetItem), source_node, NewValueNode(item.first)}); AnfNodePtr node = MixedPrecisionCastHelper(dict_value_node, item.second, target_type, func_graph); @@ -260,7 +278,9 @@ AnfNodePtr MixedPrecisionCastHelper(const AnfNodePtr &source_node, const Abstrac target_node = func_graph->NewCNode({NewValueNode(prim::kPrimMakeDict), func_graph->NewCNode(std::move(dict_key_nodes)), func_graph->NewCNode(std::move(dict_value_nodes))}); - } else if (node_type->isa()) { + } + // Check if the node type is AbstractKeywordArg + else if (node_type->isa()) { auto x = node_type->cast(); std::string kwarg_key = x->get_key(); AnfNodePtr kwarg_value_node = @@ -268,6 +288,7 @@ AnfNodePtr MixedPrecisionCastHelper(const AnfNodePtr &source_node, const Abstrac AnfNodePtr node = MixedPrecisionCastHelper(kwarg_value_node, x->get_arg(), target_type, func_graph); target_node = func_graph->NewCNode({NewValueNode(prim::kPrimMakeKeywordArg), NewValueNode(kwarg_key), node}); } + return target_node; } @@ -276,37 +297,50 @@ EvalResultPtr MixedPrecisionCastEvaluator::Run(AnalysisEnginePtr engine, const C MS_EXCEPTION_IF_NULL(engine); AbstractBasePtrList args_spec_list; MS_EXCEPTION_IF_NULL(out_conf); + + // Check if the node of out_conf is CNode if (out_conf->node() == nullptr || !out_conf->node()->isa()) { MS_LOG(EXCEPTION) << "Node of out_conf should be CNode"; } auto out_node = out_conf->node()->cast(); MS_EXCEPTION_IF_NULL(out_node); const auto &out_node_inputs = out_node->inputs(); + + // Check the sizes of inputs and args if (out_node->inputs().empty() || (out_node_inputs.size() - 1) != args_conf_list.size()) { MS_LOG(EXCEPTION) << "MixedPrecisionCast" << " args size should equal to inputs size minus 1, but args size " << args_conf_list.size() << ", inputs size " << out_node_inputs.size(); } + + // Extract abstract types from args_conf_list (void)std::transform(args_conf_list.begin(), args_conf_list.end(), std::back_inserter(args_spec_list), [](const ConfigPtr &ref) -> AbstractBasePtr { return ref->ObtainEvalResult()->abstract(); }); + // Apply scope and trace guards ScopeGuard scope_guard(out_conf->node()->scope()); TraceGuard trace_guard(std::make_shared(out_conf->node()->debug_info())); FuncGraphPtr func_graph = out_node->func_graph(); constexpr size_t source_node_index = 2; + + // Check the size of inputs if (out_node_inputs.size() <= source_node_index) { MS_LOG(EXCEPTION) << "Input size:" << out_node_inputs.size() << " should bigger than 2."; } + // Perform mixed precision casting using the helper function AnfNodePtr new_node = MixedPrecisionCastHelper(out_node_inputs[source_node_index], args_spec_list[1], out_node_inputs[1], func_graph); AnfNodeConfigPtr fn_conf = engine->MakeConfig(new_node, out_conf->context(), out_conf->func_graph()); + // Clone CNode information if the new_node is a CNode if (new_node->isa()) { auto new_cnode = new_node->cast(); new_cnode->CloneCNodeInfo(out_node); } + + // Forward the configuration return engine->ForwardConfig(out_conf, fn_conf); } @@ -320,29 +354,41 @@ py::object BuildValue(const ValuePtr &value_ptr) { } py::object AbstractTupleValueToPython(const AbstractTuplePtr &tuple_abs) { + // Check for null pointer MS_EXCEPTION_IF_NULL(tuple_abs); + // Build the value auto value = tuple_abs->BuildValue(); + // Return none if the value is of AnyValue type if (value->isa()) { return py::none(); } + // Get the elements from the tuple abstract const auto &elements = tuple_abs->elements(); size_t len = elements.size(); + // Create a new Python tuple object with the same length as the number of elements in the tuple py::tuple value_tuple(len); + // Convert each element in the tuple to a Python object and store it in the newly created tuple for (size_t i = 0; i < len; ++i) { value_tuple[i] = ConvertAbstractToPython(elements[i], true)[ATTR_VALUE]; } + // Return the newly created tuple return std::move(value_tuple); } py::dict AbstractTupleToPython(const AbstractBasePtr &abs_base, bool only_convert_value) { + // Cast the abstract base pointer to an abstract tuple pointer auto arg_tuple = dyn_cast(abs_base); MS_EXCEPTION_IF_NULL(arg_tuple); + // Create a new Python dictionary object auto dic = py::dict(); + // Only convert the value if the only_convert_value flag is set if (only_convert_value) { dic[ATTR_VALUE] = AbstractTupleValueToPython(arg_tuple); return dic; } + // Get the number of elements in the abstract tuple size_t len = arg_tuple->size(); + // Create new Python tuple objects for each attribute py::tuple shape_tuple(len); py::tuple dtype_tuple(len); py::tuple value_tuple(len); @@ -350,16 +396,22 @@ py::dict AbstractTupleToPython(const AbstractBasePtr &abs_base, bool only_conver py::tuple max_value_tuple(len); py::tuple min_shape_tuple(len); py::tuple max_shape_tuple(len); + + // Flags to indicate whether dynamic shape or value is found bool dyn_shape = false; bool dyn_value = false; + // Iterate over each element in the abstract tuple for (size_t i = 0; i < len; i++) { + // Convert the current element to a Python dictionary py::dict out = ConvertAbstractToPython(arg_tuple->elements()[i]); + + // Get the attributes from the dictionary shape_tuple[i] = out[ATTR_SHAPE]; dtype_tuple[i] = out[ATTR_DTYPE]; value_tuple[i] = out[ATTR_VALUE]; - // Elements in tuple is tensor shape value. + // Check if dynamic value is found if (out.contains(py::str(ATTR_MIN_VALUE)) && out.contains(py::str(ATTR_MAX_VALUE))) { min_value_tuple[i] = out[ATTR_MIN_VALUE]; max_value_tuple[i] = out[ATTR_MAX_VALUE]; @@ -369,7 +421,7 @@ py::dict AbstractTupleToPython(const AbstractBasePtr &abs_base, bool only_conver max_value_tuple[i] = out[ATTR_VALUE]; } - // Elements in tuple is tensor, which shape is dynamic. + // Check if dynamic shape is found if (out.contains(py::str(ATTR_MIN_SHAPE)) && out.contains(py::str(ATTR_MAX_SHAPE))) { min_shape_tuple[i] = out[ATTR_MIN_SHAPE]; max_shape_tuple[i] = out[ATTR_MAX_SHAPE]; @@ -379,6 +431,7 @@ py::dict AbstractTupleToPython(const AbstractBasePtr &abs_base, bool only_conver max_shape_tuple[i] = out[ATTR_SHAPE]; } } + // Add each attribute to the dictionary dic[ATTR_SHAPE] = shape_tuple; dic[ATTR_DTYPE] = dtype_tuple; MS_EXCEPTION_IF_NULL(arg_tuple->BuildValue()); @@ -397,33 +450,45 @@ py::dict AbstractTupleToPython(const AbstractBasePtr &abs_base, bool only_conver dic[ATTR_MAX_SHAPE] = max_shape_tuple; } + // Return the dictionary return dic; } - py::object AbstractListValueToPython(const AbstractListPtr &list_abs) { + // Check for null pointer MS_EXCEPTION_IF_NULL(list_abs); + // Build the value auto value = list_abs->BuildValue(); + // Return none if the value is of AnyValue type if (value->isa()) { return py::none(); } + // Get the elements from the list abstract const auto &elements = list_abs->elements(); size_t len = elements.size(); + // Create a new Python list object with the same length as the number of elements in the list py::list value_list(len); + // Convert each element in the list to a Python object and store it in the newly created list for (size_t i = 0; i < len; ++i) { value_list[i] = ConvertAbstractToPython(elements[i], true)[ATTR_VALUE]; } + // Return the newly created list return std::move(value_list); } py::dict AbstractListToPython(const AbstractBasePtr &abs_base, bool only_convert_value) { + // Cast the abstract base pointer to an abstract list pointer auto arg_list = dyn_cast(abs_base); MS_EXCEPTION_IF_NULL(arg_list); + // Create a new Python dictionary object auto dic = py::dict(); + // Only convert the value if the only_convert_value flag is set if (only_convert_value) { dic[ATTR_VALUE] = AbstractListValueToPython(arg_list); return dic; } + // Get the number of elements in the abstract list size_t len = arg_list->size(); + // Create new Python list objects for each attribute py::list shape_list(len); py::list dtype_list(len); py::list value_list(len); @@ -431,16 +496,21 @@ py::dict AbstractListToPython(const AbstractBasePtr &abs_base, bool only_convert py::list max_value_list(len); py::list min_shape_list(len); py::list max_shape_list(len); + + // Flags to indicate whether dynamic shape or value is found bool dyn_value = false; bool dyn_shape = false; + // Iterate over each element in the abstract list for (size_t i = 0; i < len; i++) { + // Convert the current element to a Python dictionary py::dict out = ConvertAbstractToPython(arg_list->elements()[i]); + // Get the attributes from the dictionary shape_list[i] = out[ATTR_SHAPE]; dtype_list[i] = out[ATTR_DTYPE]; value_list[i] = out[ATTR_VALUE]; - // Elements in list is tensor, which value is dynamic. + // Check if dynamic value is found if (out.contains(py::str(ATTR_MIN_VALUE)) && out.contains(py::str(ATTR_MAX_VALUE))) { min_value_list[i] = out[ATTR_MIN_VALUE]; max_value_list[i] = out[ATTR_MAX_VALUE]; @@ -450,7 +520,7 @@ py::dict AbstractListToPython(const AbstractBasePtr &abs_base, bool only_convert max_value_list[i] = out[ATTR_VALUE]; } - // Elements in list is tensor, which shape is dynamic. + // Check if dynamic shape is found if (out.contains(py::str(ATTR_MIN_SHAPE)) && out.contains(py::str(ATTR_MAX_SHAPE))) { min_shape_list[i] = out[ATTR_MIN_SHAPE]; max_shape_list[i] = out[ATTR_MAX_SHAPE]; @@ -460,7 +530,8 @@ py::dict AbstractListToPython(const AbstractBasePtr &abs_base, bool only_convert max_shape_list[i] = out[ATTR_SHAPE]; } } - + + // Add each attribute to the dictionary dic[ATTR_SHAPE] = shape_list; dic[ATTR_DTYPE] = dtype_list; MS_EXCEPTION_IF_NULL(arg_list->BuildValue()); @@ -479,19 +550,24 @@ py::dict AbstractListToPython(const AbstractBasePtr &abs_base, bool only_convert dic[ATTR_MAX_SHAPE] = max_shape_list; } + // Return the dictionary return dic; } - +// Convert an abstract tensor to a Python dictionary void ConvertAbstractTensorToPython(const AbstractBasePtr &abs_base, bool only_convert_value, py::dict *dic) { + // Cast the abstract base pointer to an abstract tensor pointer auto arg_tensor = dyn_cast(abs_base); MS_EXCEPTION_IF_NULL(dic); MS_EXCEPTION_IF_NULL(arg_tensor); + // If the only_convert_value flag is set, only convert the value of the abstract tensor if (only_convert_value) { (*dic)[ATTR_VALUE] = BuildValue(arg_tensor->BuildValue()); return; } + // Get the shape of the abstract tensor MS_EXCEPTION_IF_NULL(arg_tensor->shape()); (*dic)[ATTR_SHAPE] = arg_tensor->shape()->shape(); + // Get the minimum and maximum shapes of the abstract tensor const auto &min_shape = arg_tensor->shape()->min_shape(); const auto &max_shape = arg_tensor->shape()->max_shape(); if (!min_shape.empty() && !max_shape.empty()) { @@ -499,6 +575,7 @@ void ConvertAbstractTensorToPython(const AbstractBasePtr &abs_base, bool only_co (*dic)[ATTR_MAX_SHAPE] = max_shape; } + // Get the minimum and maximum values of the abstract tensor auto min_value = arg_tensor->get_min_value(); auto max_value = arg_tensor->get_max_value(); if (min_value != nullptr && max_value != nullptr) { @@ -506,21 +583,31 @@ void ConvertAbstractTensorToPython(const AbstractBasePtr &abs_base, bool only_co (*dic)[ATTR_MAX_VALUE] = BuildValue(max_value); } + // Get the data type of the abstract tensor (*dic)[ATTR_DTYPE] = arg_tensor->BuildType(); + // Get the value of the abstract tensor (*dic)[ATTR_VALUE] = BuildValue(arg_tensor->BuildValue()); } +// Convert an abstract function to a Python dictionary void ConvertAbstractFunctionToPython(const AbstractBasePtr &abs_base, py::dict *dic) { MS_EXCEPTION_IF_NULL(dic); MS_EXCEPTION_IF_NULL(abs_base); + // Set the shape and value attributes to None (*dic)[ATTR_SHAPE] = py::none(); - (*dic)[ATTR_DTYPE] = abs_base->BuildType(); (*dic)[ATTR_VALUE] = py::none(); + // Get the data type of the abstract function + (*dic)[ATTR_DTYPE] = abs_base->BuildType(); + // Check if the abstract function is a PartialAbstractClosure if (abs_base->isa()) { + // Get the arguments of the PartialAbstractClosure AbstractBasePtrList args = abs_base->cast()->args(); + // Check if the arguments list is not empty if (!args.empty()) { MS_EXCEPTION_IF_NULL(args[0]->BuildValue()); + // Get the value of the first argument as a ClassType auto value = args[0]->BuildValue()->cast(); + // If the value is not nullptr, update the data type and value attributes of the dictionary if (value != nullptr) { (*dic)[ATTR_DTYPE] = std::make_shared(); (*dic)[ATTR_VALUE] = value->obj(); @@ -528,104 +615,141 @@ void ConvertAbstractFunctionToPython(const AbstractBasePtr &abs_base, py::dict * } } } - bool CheckType(const TypePtr &expected_type, const TypePtr &x) { // As x and predicate both are mindspore type statically, here we only to judge whether // x is predicate or is a subclass of predicate. return IsIdentidityOrSubclass(x, expected_type); } -// Join all types in args_type_list; +// Join multiple types from a list of types TypePtr TypeJoin(const TypePtrList &args_type_list) { + // Check if the input list is empty if (args_type_list.empty()) { MS_LOG(EXCEPTION) << "args_type_list is empty"; } - + + // Initialize a temporary type with the first type in the list TypePtr type_tmp = args_type_list[0]; + // Iterate through the rest of the list and join the types for (std::size_t i = 1; i < args_type_list.size(); i++) { type_tmp = abstract::TypeJoin(type_tmp, args_type_list[i]); } return type_tmp; } +// Check if a list of types matches a given predicate type TypePtr CheckTypeList(const TypePtr &predicate, const TypePtrList &args_type_list) { + // Check for null pointers MS_EXCEPTION_IF_NULL(predicate); + // Iterate through the list of types for (const auto &arg_type : args_type_list) { MS_EXCEPTION_IF_NULL(arg_type); + // If a type does not match the predicate type, throw an exception if (!CheckType(predicate, arg_type)) { MS_LOG(EXCEPTION) << "The expected is " << predicate->ToString() << ", not " << arg_type->ToString(); } } + + // Join the list of types into a single type return TypeJoin(args_type_list); } } // namespace +// This function converts an AbstractBasePtr object to a Python dictionary. py::dict ConvertAbstractToPython(const AbstractBasePtr &abs_base, bool only_convert_value) { - MS_EXCEPTION_IF_NULL(abs_base); - auto dic = py::dict(); - if (abs_base->isa()) { - ConvertAbstractTensorToPython(abs_base, only_convert_value, &dic); - } else if (abs_base->isa() || abs_base->isa() || abs_base->isa()) { - ShapeVector shape; - dic[ATTR_SHAPE] = shape; - dic[ATTR_DTYPE] = abs_base->BuildType(); - dic[ATTR_VALUE] = BuildValue(abs_base->BuildValue()); - } else if (abs_base->isa()) { - return AbstractTupleToPython(abs_base, only_convert_value); - } else if (abs_base->isa()) { - return AbstractListToPython(abs_base, only_convert_value); - } else if (abs_base->isa()) { - auto arg_slice = dyn_cast(abs_base); - ShapeVector shape; - dic[ATTR_SHAPE] = shape; - dic[ATTR_DTYPE] = arg_slice->BuildType(); - dic[ATTR_VALUE] = BuildValue(arg_slice->BuildValue()); - } else if (abs_base->isa()) { - auto arg = dyn_cast(abs_base); - dic[ATTR_SHAPE] = arg->shape()->shape(); - dic[ATTR_DTYPE] = arg->BuildType(); - dic[ATTR_VALUE] = BuildValue(arg->BuildValue()); - } else if (abs_base->isa()) { - auto arg = dyn_cast(abs_base); - dic[ATTR_SHAPE] = arg->shape()->shape(); - dic[ATTR_DTYPE] = arg->BuildType(); - dic[ATTR_VALUE] = BuildValue(arg->BuildValue()); - } else if (abs_base->isa()) { - auto arg = dyn_cast(abs_base); - dic[ATTR_SHAPE] = arg->shape()->shape(); - dic[ATTR_DTYPE] = arg->BuildType(); - dic[ATTR_VALUE] = BuildValue(arg->BuildValue()); - } else if (abs_base->isa()) { - dic[ATTR_SHAPE] = py::none(); - dic[ATTR_DTYPE] = py::ellipsis(); - dic[ATTR_VALUE] = py::ellipsis(); - } else if (abs_base->isa()) { - dic[ATTR_SHAPE] = py::none(); - dic[ATTR_DTYPE] = py::none(); - dic[ATTR_VALUE] = py::none(); - } else if (abs_base->isa()) { - ConvertAbstractFunctionToPython(abs_base, &dic); - } else if (abs_base->isa()) { - auto arg = dyn_cast(abs_base); - dic[ATTR_SHAPE] = py::none(); - dic[ATTR_DTYPE] = arg->BuildType(); - dic[ATTR_VALUE] = py::none(); - } else if (abs_base->isa()) { - dic[ATTR_SHAPE] = py::none(); - dic[ATTR_DTYPE] = abs_base->BuildType(); - dic[ATTR_VALUE] = py::none(); - } else { - auto value = abs_base->BuildValue(); - MS_EXCEPTION_IF_NULL(value); - if ((*value == *kAnyValue)) { - auto value_desc = abs_base->value_desc(); - MS_EXCEPTION(TypeError) << "Unsupported parameter " << (value_desc.empty() ? "type" : value_desc) - << " for python primitive." << abs_base->ToString(); - } - MS_EXCEPTION(TypeError) << "Unsupported parameter type for python primitive, the parameter value is " - << value->ToString(); - } - return dic; +MS_EXCEPTION_IF_NULL(abs_base); +auto dic = py::dict(); +// If the abstract object is an AbstractTensor, call the ConvertAbstractTensorToPython function to convert it to a Python dictionary. +if (abs_base->isa()) { +ConvertAbstractTensorToPython(abs_base, only_convert_value, &dic); +} +// If the abstract object is an AbstractScalar, AbstractType, or AbstractRefKey, convert its type, shape, and value to the Python dictionary. +else if (abs_base->isa() || abs_base->isa() || abs_base->isa()) { +ShapeVector shape; +dic[ATTR_SHAPE] = shape; +dic[ATTR_DTYPE] = abs_base->BuildType(); +dic[ATTR_VALUE] = BuildValue(abs_base->BuildValue()); +} +// If the abstract object is an AbstractTuple, call the AbstractTupleToPython function to convert it to a Python dictionary. +else if (abs_base->isa()) { +return AbstractTupleToPython(abs_base, only_convert_value); +} +// If the abstract object is an AbstractList, call the AbstractListToPython function to convert it to a Python dictionary. +else if (abs_base->isa()) { +return AbstractListToPython(abs_base, only_convert_value); +} +// If the abstract object is an AbstractSlice, convert its type, shape, and value to the Python dictionary. +else if (abs_base->isa()) { +auto arg_slice = dyn_cast(abs_base); +ShapeVector shape; +dic[ATTR_SHAPE] = shape; +dic[ATTR_DTYPE] = arg_slice->BuildType(); +dic[ATTR_VALUE] = BuildValue(arg_slice->BuildValue()); +} +// If the abstract object is an AbstractRowTensor, convert its type, shape, and value to the Python dictionary. +else if (abs_base->isa()) { +auto arg = dyn_cast(abs_base); +dic[ATTR_SHAPE] = arg->shape()->shape(); +dic[ATTR_DTYPE] = arg->BuildType(); +dic[ATTR_VALUE] = BuildValue(arg->BuildValue()); +} +// If the abstract object is an AbstractCOOTensor, convert its type, shape, and value to the Python dictionary. +else if (abs_base->isa()) { +auto arg = dyn_cast(abs_base); +dic[ATTR_SHAPE] = arg->shape()->shape(); +dic[ATTR_DTYPE] = arg->BuildType(); +dic[ATTR_VALUE] = BuildValue(arg->BuildValue()); +} +// If the abstract object is an AbstractCSRTensor, convert its type, shape, and value to the Python dictionary. +else if (abs_base->isa()) { +auto arg = dyn_cast(abs_base); +dic[ATTR_SHAPE] = arg->shape()->shape(); +dic[ATTR_DTYPE] = arg->BuildType(); +dic[ATTR_VALUE] = BuildValue(arg->BuildValue()); +} +// If the abstract object is an AbstractEllipsis, set its shape and dtype to None in the Python dictionary. +else if (abs_base->isa()) { +dic[ATTR_SHAPE] = py::none(); +dic[ATTR_DTYPE] = py::ellipsis(); +dic[ATTR_VALUE] = py::ellipsis(); +} +// If the abstract object is an AbstractNone, set its shape, dtype, and value to None in the Python dictionary. +else if (abs_base->isa()) { +dic[ATTR_SHAPE] = py::none(); +dic[ATTR_DTYPE] = py::none(); +dic[ATTR_VALUE] = py::none(); +} +// If the abstract object is an AbstractFunction, call the ConvertAbstractFunctionToPython function to convert it to a Python dictionary. +else if (abs_base->isa()) { +ConvertAbstractFunctionToPython(abs_base, &dic); +} +// If the abstract object is an AbstractUndetermined, set its shape and value to None, and its dtype to the corresponding type in the Python dictionary. +else if (abs_base->isa()) { +auto arg = dyn_cast(abs_base); +dic[ATTR_SHAPE] = py::none(); +dic[ATTR_DTYPE] = arg->BuildType(); +dic[ATTR_VALUE] = py::none(); +} +// If the abstract object is an AbstractMonad, set its shape and value to None, and its dtype to the corresponding type in the Python dictionary. +else if (abs_base->isa()) { +dic[ATTR_SHAPE] = py::none(); +dic[ATTR_DTYPE] = abs_base->BuildType(); +dic[ATTR_VALUE] = py::none(); +} +// If the abstract object is not one of the supported types, raise an exception. +else { +auto value = abs_base->BuildValue(); +MS_EXCEPTION_IF_NULL(value); +if ((*value == *kAnyValue)) { +auto value_desc = abs_base->value_desc(); +MS_EXCEPTION(TypeError) << "Unsupported parameter " << (value_desc.empty() ? "type" : value_desc) +<< " for python primitive." << abs_base->ToString(); +} +MS_EXCEPTION(TypeError) << "Unsupported parameter type for python primitive, the parameter value is " +<< value->ToString(); +} + +return dic; } namespace { @@ -645,7 +769,7 @@ void CheckCustomPrimOutputInferResult(const PrimitivePtr &prim, const AbstractBa MS_EXCEPTION_IF_NULL(res_spec); const string kOutputNum = "output_num"; if (prim->IsCustomPrim()) { - // Raise error if output_num is not match the infer result. + // Raise an error if the output_num attribute does not match the infer result. auto output_num_value = prim->GetAttr(kOutputNum); if (output_num_value == nullptr) { MS_LOG(DEBUG) << "The output num may no need to check"; @@ -665,144 +789,220 @@ void CheckCustomPrimOutputInferResult(const PrimitivePtr &prim, const AbstractBa } } + +// Set the value range for the abstract tensor based on the given output object. void SetValueRange(const AbstractBasePtr &tensor, const py::object &output) { - if (output.is_none()) { + if (output.is_none()) { + // If the output is none, return directly. return; } - py::object obj_min = - output.contains(py::str(ATTR_MIN_VALUE)) ? (py::object)output[ATTR_MIN_VALUE] : (py::object)py::none(); - py::object obj_max = - output.contains(py::str(ATTR_MAX_VALUE)) ? (py::object)output[ATTR_MAX_VALUE] : (py::object)py::none(); - if (!obj_min.is_none() && !obj_max.is_none()) { + py::object obj_min = output.contains(py::str(ATTR_MIN_VALUE)) + ? (py::object)output[ATTR_MIN_VALUE] : (py::object)py::none(); + // Get the minimum value object. + py::object obj_max = output.contains(py::str(ATTR_MAX_VALUE)) + ? (py::object)output[ATTR_MAX_VALUE] : (py::object)py::none(); + // Get the maximum value object. + if (!obj_min.is_none() && !obj_max.is_none()) { + // If both minimum and maximum values exist. bool converted = true; ValuePtr min_value = nullptr; ValuePtr max_value = nullptr; + // Convert the minimum value object to the corresponding data type. converted = parse::ConvertData(obj_min, &min_value); if (!converted) { MS_LOG(EXCEPTION) << "Convert shape min value data failed"; - } + } + // Convert the maximum value object to the corresponding data type. converted = parse::ConvertData(obj_max, &max_value); + if (!converted) { MS_LOG(EXCEPTION) << "Convert shape max value data failed"; } + // Convert the tensor to an abstract tensor. auto abs_tensor = dyn_cast(tensor); + // Set the value range of the abstract tensor. abs_tensor->set_value_range(min_value, max_value); + } } +// Check if the given `type_obj` is a MonadType. static bool IsMonadType(const py::object &type_obj) { + // Check if `type_obj` is an instance of Type. if (py::isinstance(type_obj)) { + // Cast `type_obj` to Type pointer. auto type = type_obj.cast(); + // Check if the Type object is a MonadType. return type->isa(); } return false; } +// Convert the given `type_obj` to an AbstractMonad object. AbstractBasePtr ToMonadAbstract(const py::object &type_obj) { + // Check if `type_obj` is an instance of Type. if (py::isinstance(type_obj)) { - auto type = type_obj.cast(); - if (!type->isa()) { + // Cast `type_obj` to Type pointer. + auto type = type_obj.cast(); + // Check if the Type object is a MonadType. + if (!type->isa()) { MS_LOG(EXCEPTION) << "Not a monad type object: " << py::str(type_obj); } - return abstract::MakeMonadAbstract(type->cast()); + // Create an AbstractMonad object based on the MonadType. + return abstract::MakeMonadAbstract(type->cast()); } MS_LOG(EXCEPTION) << "Not a type object: " << py::str(type_obj); } +// Get the py::object corresponding to the specified `index` in the tuple output. py::object GetPyAbsItemOfTupleOut(const py::object &output, const size_t index) { + // Cast `output` to a dictionary object. auto out_dict = output.cast(); - auto type_obj = out_dict[ATTR_DTYPE]; - auto shape_obj = out_dict[ATTR_SHAPE]; - auto out_item = py::dict(); - auto shape_tuple = shape_obj.cast(); + // Get the `ATTR_DTYPE` key from the dictionary. + auto type_obj = out_dict[ATTR_DTYPE]; + // Get the `ATTR_SHAPE` key from the dictionary. + auto shape_obj = out_dict[ATTR_SHAPE]; + // Create a new dictionary object. + auto out_item = py::dict(); + // Cast `shape_obj` to a tuple object. + auto shape_tuple = shape_obj.cast(); + // Cast `type_obj` to a tuple object. auto typeid_tuple = type_obj.cast(); + // Get the element at the specified `index` from `typeid_tuple` and assign it to `ATTR_DTYPE` key in `out_item`. out_item[ATTR_DTYPE] = typeid_tuple[index]; - out_item[ATTR_SHAPE] = shape_tuple[index]; - if (output.contains(py::str(ATTR_MIN_SHAPE))) { + // Get the element at the specified `index` from `shape_tuple` and assign it to `ATTR_SHAPE` key in `out_item`. + out_item[ATTR_SHAPE] = shape_tuple[index]; + // Check if the output contains the `ATTR_MIN_SHAPE` key. + if (output.contains(py::str(ATTR_MIN_SHAPE))) { + // Get the element at the specified `index` from `ATTR_MIN_SHAPE` key and assign it to `ATTR_MIN_SHAPE` key in `out_item`. out_item[ATTR_MIN_SHAPE] = output[ATTR_MIN_SHAPE].cast()[index]; } - if (output.contains(py::str(ATTR_MAX_SHAPE))) { + // Check if the output contains the `ATTR_MAX_SHAPE` key. + if (output.contains(py::str(ATTR_MAX_SHAPE))) { + // Get the element at the specified `index` from `ATTR_MAX_SHAPE` key and assign it to `ATTR_MAX_SHAPE` key in `out_item`. out_item[ATTR_MAX_SHAPE] = output[ATTR_MAX_SHAPE].cast()[index]; } - out_item[ATTR_VALUE] = py::none(); - return out_item; + // Assign None to the `ATTR_VALUE` key in `out_item`. + out_item[ATTR_VALUE] = py::none(); + // Return the constructed py::object. + return out_item; } +// Convert the given `shape_obj` and `type_obj` to an AbstractTensor object based on the given `output`. AbstractBasePtr MakePyInferRes2AbstractTensor(const py::object &shape_obj, const py::object &type_obj, const py::object &output) { + // Cast `shape_obj` to a ShapeVector object. auto ret_vec = shape_obj.cast(); - auto ret_dtype = type_obj.cast(); + // Cast `type_obj` to a TypePtr object. + auto ret_dtype = type_obj.cast(); ShapeVector min_shape_vec; ShapeVector max_shape_vec; - - if (!output.is_none()) { - py::object min_shape = - output.contains(py::str(ATTR_MIN_SHAPE)) ? (py::object)output[ATTR_MIN_SHAPE] : (py::object)py::none(); - py::object max_shape = - output.contains(py::str(ATTR_MAX_SHAPE)) ? (py::object)output[ATTR_MAX_SHAPE] : (py::object)py::none(); - if (!min_shape.is_none()) { - min_shape_vec = min_shape.cast(); +// Check if `output` is not None. + if (!output.is_none()) { + // Check if `ATTR_MIN_SHAPE` key is contained in `output`, and get its value or assign None. + py::object min_shape = output.contains(py::str(ATTR_MIN_SHAPE)) ? (py::object)output[ATTR_MIN_SHAPE] : (py::object)py::none(); + // Check if `ATTR_MAX_SHAPE` key is contained in `output`, and get its value or assign None. + py::object max_shape = output.contains(py::str(ATTR_MAX_SHAPE)) ? (py::object)output[ATTR_MAX_SHAPE] : (py::object)py::none(); + // Check if `min_shape` is not None. + if (!min_shape.is_none()) { + // Cast `min_shape` to a ShapeVector object and assign it to `min_shape_vec`. + min_shape_vec = min_shape.cast(); } - if (!max_shape.is_none()) { - max_shape_vec = max_shape.cast(); + // Check if `max_shape` is not None. + if (!max_shape.is_none()) { + // Cast `max_shape` to a ShapeVector object and assign it to `max_shape_vec`. + max_shape_vec = max_shape.cast(); } } - - auto ret_shape = std::make_shared(ret_vec, min_shape_vec, max_shape_vec); + // Create an AbstractShape object based on `ret_vec`, `min_shape_vec`, and `max_shape_vec`. + auto ret_shape = std::make_shared(ret_vec, min_shape_vec, max_shape_vec); + // Create an AbstractTensor object based on `ret_shape` and `ret_dtype`. AbstractBasePtr tensor = MakeAbstractTensor(ret_shape, ret_dtype); - + // Set the value range of `tensor` based on `output`. SetValueRange(tensor, output); - return tensor; + + // Return the created AbstractTensor object. + return tensor; } +// Convert the Python inference result `output` to an AbstractBasePtr object. AbstractBasePtr MakePyInferRes2Abstract(const py::object &output) { - auto out_dict = output.cast(); - auto type_obj = out_dict[ATTR_DTYPE]; - auto shape_obj = out_dict[ATTR_SHAPE]; + // Cast `output` to a Python dictionary object. + auto out_dict = output.cast(); + // Get the value of key ATTR_DTYPE from `out_dict`. + auto type_obj = out_dict[ATTR_DTYPE]; + // Get the value of key ATTR_SHAPE from `out_dict`. + auto shape_obj = out_dict[ATTR_SHAPE]; + if ((py::isinstance(shape_obj) || py::isinstance(shape_obj)) && py::isinstance(type_obj)) { - auto ret_vec = shape_obj.cast(); - auto ret_dtype = type_obj.cast(); + // If `shape_obj` is a list or tuple and `type_obj` is a Type object. + // Cast `shape_obj` to a ShapeVector object. + auto ret_vec = shape_obj.cast(); + // Cast `type_obj` to a TypePtr object. + auto ret_dtype = type_obj.cast(); + MS_EXCEPTION_IF_NULL(ret_dtype); - // if the size of shape list is empty, return an scalar abstract + + // If the size of `ret_vec` is empty and `ret_dtype` is not a TensorType, return an AbstractScalar object. if (ret_vec.empty() && (!ret_dtype->isa())) { abstract::AbstractScalarPtr abs_scalar = std::make_shared(kAnyValue, ret_dtype); return abs_scalar; } + // Return the converted AbstractTensor object. return MakePyInferRes2AbstractTensor(shape_obj, type_obj, output); } else if (py::isinstance(shape_obj) && py::isinstance(type_obj)) { - auto typeid_tuple = type_obj.cast(); + // If both `shape_obj` and `type_obj` are tuples. + // Cast `type_obj` to a tuple. + auto typeid_tuple = type_obj.cast(); AbstractBasePtrList ptr_list; + for (size_t it = 0; it < typeid_tuple.size(); ++it) { - auto output_it = GetPyAbsItemOfTupleOut(output, it); - auto tensor_it = MakePyInferRes2Abstract(output_it); - ptr_list.push_back(tensor_it); + // Get the item at index `it` from `output`. + auto output_it = GetPyAbsItemOfTupleOut(output, it); + // Recursively convert the item to an AbstractBasePtr object. + auto tensor_it = MakePyInferRes2Abstract(output_it); + // Add the converted AbstractBasePtr object to the list. + ptr_list.push_back(tensor_it); } - auto tuple = std::make_shared(ptr_list); - return tuple; + + // Create an AbstractTuple object based on the list of AbstractBasePtr objects. + auto tuple = std::make_shared(ptr_list); + // Return the created AbstractTuple object + return tuple; } else if (py::isinstance(shape_obj) && py::isinstance(type_obj)) { + // If both `shape_obj` and `type_obj` are lists. + // Cast `type_obj` to a list. auto typeid_list = type_obj.cast(); AbstractBasePtrList ptr_list; + for (size_t it = 0; it < typeid_list.size(); ++it) { - auto output_it = GetPyAbsItemOfTupleOut(output, it); - auto tensor_it = MakePyInferRes2Abstract(output_it); + // Get the item at index `it` from `output`. + auto output_it = GetPyAbsItemOfTupleOut(output, it); + // Recursively convert the item to an AbstractBasePtr object. + auto tensor_it = MakePyInferRes2Abstract(output_it); + // Add the converted AbstractBasePtr object to the list. ptr_list.push_back(tensor_it); } - auto list = std::make_shared(ptr_list); - return list; + + // Create an AbstractList object based on the list of AbstractBasePtr objects. + auto list = std::make_shared(ptr_list); + // Return the created AbstractList object. + return list; } else if (shape_obj.is_none() && type_obj.is_none()) { - // AbstractNone indicates there is no output for this CNode node. + // If both `shape_obj` and `type_obj` are None, return an AbstractNone object indicating no output for this CNode node. auto abstract_none = std::make_shared(); return abstract_none; } else if (IsMonadType(type_obj)) { - // Return monad abstract if it is monad type. + // If `type_obj` is a monad type, return the corresponding monad abstract. return ToMonadAbstract(type_obj); } else { - // When sparse enabled, the undetermined might be raised and eliminated in opt passes + // When sparse enabled, the undetermined might be raised and eliminated in opt passes. auto context = MsContext::GetInstance(); MS_EXCEPTION_IF_NULL(context); bool enable_sparse = context->get_param(MS_CTX_ENABLE_SPARSE); if (enable_sparse) { - return std::make_shared(); + // Return an AbstractUndetermined object. + return std::make_shared(); } MS_LOG(EXCEPTION) << "Python evaluator return invalid shape or type. " << (std::string)py::str(type_obj); } -- 2.34.1 From 852c1abd22cf69e6f70d8135e2ec86d0da8858f2 Mon Sep 17 00:00:00 2001 From: WEI_4614 <2291172974@qq.com> Date: Thu, 5 Oct 2023 19:32:54 +0800 Subject: [PATCH 24/26] Update static_analysis.cc --- .../jit/static_analysis/static_analysis.cc | 306 +++++++++++++----- 1 file changed, 221 insertions(+), 85 deletions(-) diff --git a/mindspore/ccsrc/pipeline/jit/static_analysis/static_analysis.cc b/mindspore/ccsrc/pipeline/jit/static_analysis/static_analysis.cc index 575b3d58db6..bad6fd37847 100644 --- a/mindspore/ccsrc/pipeline/jit/static_analysis/static_analysis.cc +++ b/mindspore/ccsrc/pipeline/jit/static_analysis/static_analysis.cc @@ -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 guard(mutex_); - auto cache_iter = prim_cache_.find(prim->name()); - if (cache_iter == prim_cache_.end()) { - return nullptr; + std::lock_guard 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(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(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(node->abstract(), std::make_shared()); - } else if (node->isa()) { + } + // If the node is a ValueNode, evaluate its abstract value + else if (node->isa()) { auto value_node = node->cast(); auto abstract = EvalValueNode(value_node, conf); eval_result = std::make_shared(abstract, std::make_shared()); - } else if (node->isa()) { + } + // If the node is a CNode, evaluate its abstract value + else if (node->isa()) { auto cnode = node->cast(); 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(possible_func->Clone(), std::make_shared()); } - + + // Check if the abstract value can be casted to AbstractFunction AbstractFunctionPtr func = dyn_cast(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 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()) { const auto &async_abs_func = poss->cast(); 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(arg); }); + // Create a vector to store the evaluators std::vector 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()) { - return std::make_shared(prim); + if (prim->isa()) { // Check if it is a custom DoSignaturePrimitive + return std::make_shared(prim); // Return an instance of DoSignatureEvaluator } - if (prim->isa()) { - return std::make_shared(prim); + if (prim->isa()) { // Check if it is a custom UnpackGraphPrimitive + return std::make_shared(prim); // Return an instance of UnpackGraphEvaluator } if (prim->Hash() == prim::kPrimMixedPrecisionCast->Hash() && prim->name() == prim::kPrimMixedPrecisionCast->name()) { - return std::make_shared(prim); + // Check if it is a mixed precision cast operation + return std::make_shared(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(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(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(prim); if (prim_py != nullptr) { - if (engine == nullptr) { - return std::make_shared(prim_py); + if (engine == nullptr) { // Check if the analysis engine is provided + return std::make_shared(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(prim_py); - engine->prim_py_evaluators_[prim_py] = evaluator; + evaluator = std::make_shared(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 &func) { @@ -541,20 +654,32 @@ EvaluatorPtr AnalysisEngine::_GetEvaluatorFor(const std::shared_ptr &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 partial_evaluator = std::make_shared(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 &) { MS_LOG(EXCEPTION) << "Should not be called "; } @@ -659,24 +784,29 @@ EvalResultPtr AnalysisEngine::ExecuteEvaluators(const std::vector void AnalysisEngine::SetUndeterminedFlag(const EvaluatorPtr &evaluator, const FuncGraphPtr &possible_parent_fg) { MS_EXCEPTION_IF_NULL(evaluator); - static std::mutex fg_lock; - std::lock_guard 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 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(); - if (fg_eval == nullptr) { + + auto fg_eval = evaluator->cast(); // 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::vectorToString() @@ -745,6 +876,7 @@ std::string JoinBranchesFailedInfo(const AbstractBasePtr &spec, const AbstractBa << "The node is " << node->DebugString(recursive_level); if (node->isa()) { auto cnode = node->cast()->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()->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()->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."; -- 2.34.1 From 19be76e52d2c1570526ea2a5637df30323938e81 Mon Sep 17 00:00:00 2001 From: gxl1 Date: Thu, 5 Oct 2023 19:40:45 +0800 Subject: [PATCH 25/26] Update program_specialize.cc --- .../jit/static_analysis/program_specialize.cc | 126 ++++++++++++++++-- 1 file changed, 115 insertions(+), 11 deletions(-) diff --git a/mindspore/ccsrc/pipeline/jit/static_analysis/program_specialize.cc b/mindspore/ccsrc/pipeline/jit/static_analysis/program_specialize.cc index e4ce80b69c2..a735e0ed572 100644 --- a/mindspore/ccsrc/pipeline/jit/static_analysis/program_specialize.cc +++ b/mindspore/ccsrc/pipeline/jit/static_analysis/program_specialize.cc @@ -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 fg_spec = std::make_shared(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 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(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()) { 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()) { @@ -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()) { continue; } + // Get the input 0 of the CNode. auto &input0 = node->cast()->input(0); MS_EXCEPTION_IF_NULL(input0); + // Skip if the input is a ValueNode of type FuncGraph. if (IsValueNode(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() || old_abs->isa() || old_abs->isa() || old_abs->isa())) { continue; } + // Cast the abstract value to AbstractFunctionPtr. auto old_abs_func = old_abs->cast(); + // 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(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()) { return node; } + // Get the top specializer for the node. std::shared_ptr 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()) { if (!new_node->isa()) { 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(); 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 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()) { auto c_inp = inp->cast(); MS_EXCEPTION_IF_NULL(c_inp); auto c_new_inp = new_inp->cast(); 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(); MS_EXCEPTION_IF_NULL(c_new_node); c_new_node->set_inputs(new_inputs); } + AnfNodePtr FuncGraphSpecializer::GetReplicatedNode(const AnfNodePtr &node) { std::shared_ptr specializer = GetTopSpecializer(node); auto iter = specializer->cloned_nodes().find(node); @@ -464,13 +548,17 @@ std::shared_ptr 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 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>(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 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(); + // 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(elements); auto new_input = NewValueNode(new_sequence_value); auto new_input_abs = new_sequence_value->ToAbstract(); -- 2.34.1 From d7d6cdf8591cd482cea06eb15caad8075d841c5f Mon Sep 17 00:00:00 2001 From: qsdy Date: Thu, 5 Oct 2023 19:49:09 +0800 Subject: [PATCH 26/26] Update prim.cc --- .../pipeline/jit/static_analysis/prim.cc | 292 +++++++++++++++--- 1 file changed, 242 insertions(+), 50 deletions(-) diff --git a/mindspore/ccsrc/pipeline/jit/static_analysis/prim.cc b/mindspore/ccsrc/pipeline/jit/static_analysis/prim.cc index 1ecfd3a1c9e..170432cd025 100644 --- a/mindspore/ccsrc/pipeline/jit/static_analysis/prim.cc +++ b/mindspore/ccsrc/pipeline/jit/static_analysis/prim.cc @@ -1349,35 +1349,43 @@ EvaluatorPtr InitStandardPrimEvaluator(PrimitivePtr primitive, const StandardPri return prim_evaluator; } +// Create a new evaluator for a uniform primitive operation. EvaluatorPtr InitUniformPrimEvaluator(const PrimitivePtr &primitive, PrimitiveImpl prim_impl, bool eval_value, const TypePtr &specify_out_type) { FunctionPtr func = nullptr; - (void)prim::PrimToFunction::GetInstance().GetFunction(primitive, &func); + (void)prim::PrimToFunction::GetInstance().GetFunction(primitive, &func); // Get the corresponding function for the primitive. MS_EXCEPTION_IF_NULL(func); + // Create a UniformPrimEvaluator object using the function, primitive implementation, evaluation value flag, and specified output type. EvaluatorPtr uniform_primitive_evaluator = std::make_shared(func, prim_impl, eval_value, specify_out_type); - return uniform_primitive_evaluator; + return uniform_primitive_evaluator; // Return the created evaluator. } +// Convert a Python object to a graph in MindSpore. FuncGraphPtr PyObjToGraph(const AnalysisEnginePtr &engine, const ValuePtr &method) { MS_EXCEPTION_IF_NULL(engine); MS_EXCEPTION_IF_NULL(method); + // Check if the method is of type PyObjectWrapper. if (!method->isa()) { MS_LOG(EXCEPTION) << "Method type error: " << method->ToString(); } std::shared_ptr obj = method->cast>(); + // Convert the Python object to a FuncGraph object. FuncGraphPtr func_graph = mindspore::parse::ConvertToFuncGraph(obj->obj()); + // Check if the conversion was successful. if (func_graph == nullptr) { MS_LOG(EXCEPTION) << "Parse python object: " << method->ToString() << " failed"; } + // Add the FuncGraph object to the function graph manager. FuncGraphManagerPtr manager = engine->func_graph_manager(); manager->AddFuncGraph(func_graph); - return func_graph; + return func_graph; // Return the converted FuncGraph object. } +// Add a FuncGraph object to the function graph manager. inline void AddToManager(const AnalysisEnginePtr &engine, const FuncGraphPtr func_graph) { MS_EXCEPTION_IF_NULL(engine); FuncGraphManagerPtr manager = engine->func_graph_manager(); @@ -1389,63 +1397,83 @@ enum class REQUIRE_TYPE { ATTR, METHOD }; EvalResultPtr StaticGetterInferred(const ValuePtr &value, const ConfigPtr &data_conf, const AnfNodeConfigPtr &old_conf, REQUIRE_TYPE require_type = REQUIRE_TYPE::METHOD) { MS_EXCEPTION_IF_NULL(old_conf); + // Convert the given value to an AbstractBase object using the old configuration. AbstractBasePtr abstract = ToAbstract(value, AnalysisContext::DummyContext(), old_conf); - // Create new cnode + + // Create a new CNode for the static getter operation. std::vector input = {NewValueNode(prim::kPrimPartial)}; auto func_graph_func = dyn_cast(abstract); if (func_graph_func != nullptr) { + // If the abstract value is a FuncGraph, add the FuncGraph to the input of the CNode. FuncGraphPtr fg = func_graph_func->func_graph(); input.push_back(NewValueNode(fg)); } else { + // If the abstract value is a Primitive, add the Primitive to the input of the CNode. auto prim_func = dyn_cast(abstract); MS_EXCEPTION_IF_NULL(prim_func); PrimitivePtr prim = prim_func->prim(); input.push_back(NewValueNode(prim)); } + // Get the node configuration from the data configuration. AnfNodeConfigPtr conf = dyn_cast(data_conf); MS_EXCEPTION_IF_NULL(conf); + // Add the node configuration as input to the CNode. input.push_back(conf->node()); - MS_EXCEPTION_IF_NULL(old_conf); + + // Get the function graph from the old configuration. FuncGraphPtr func_graph = old_conf->node()->func_graph(); MS_EXCEPTION_IF_NULL(func_graph); + + // Create a new CNode using the input vector and the function graph. CNodePtr new_cnode = func_graph->NewCNode(input); if (require_type == REQUIRE_TYPE::ATTR) { + // If the requirement type is attribute, create a new CNode with the previous CNode as its input. new_cnode = func_graph->NewCNode({new_cnode}); } + + // Get the analysis engine from the old configuration. AnalysisEnginePtr eng = old_conf->engine(); + // Create a new node configuration using the new CNode and the old configuration's context and function graph. AnfNodeConfigPtr fn_conf = eng->MakeConfig(new_cnode, old_conf->context(), old_conf->func_graph()); + // Forward the old configuration to the new configuration and return the result. return eng->ForwardConfig(old_conf, fn_conf); } EvalResultPtr GetEvaluatedValueForNameSpaceString(const AnalysisEnginePtr &, const AbstractBasePtrList &args_spec_list, const AnfNodeConfigPtr &out_conf) { // args_spec_list: same as StaticGetter + + // Check if the number of arguments is valid. if (args_spec_list.size() < 2) { MS_LOG(EXCEPTION) << "Size of args_spec_list is less than 2"; } MS_EXCEPTION_IF_NULL(out_conf); - // An external type. + // Get the data value from the first argument in the list. MS_EXCEPTION_IF_NULL(args_spec_list[0]); MS_EXCEPTION_IF_NULL(args_spec_list[1]); auto data_value = args_spec_list[0]->BuildValue(); MS_EXCEPTION_IF_NULL(data_value); + // Check if the data value is a NameSpace object. if (!data_value->isa()) { MS_EXCEPTION(TypeError) << "Not supported to get attribute for " << data_value->ToString() << "\nThe first argument should be a NameSpace, but got " << args_spec_list[0]->ToString(); } + // Get the item value from the second argument in the list. auto item_value = args_spec_list[1]->BuildValue(); MS_EXCEPTION_IF_NULL(item_value); + // If the item value is a string, wrap it in a Symbol object. if (item_value->isa()) { item_value = std::make_shared(item_value->cast()->value()); } + // Check if the item value is a Symbol object. if (!item_value->isa()) { MS_LOG(EXCEPTION) << "The value of the attribute could not be inferred: " << item_value->ToString(); } - // item_name to func addr from obj_map + // Resolve the symbol in the data namespace. parse::SymbolPtr symbol = item_value->cast(); parse::NameSpacePtr name_space = data_value->cast(); MS_EXCEPTION_IF_NULL(out_conf); @@ -1453,19 +1481,23 @@ EvalResultPtr GetEvaluatedValueForNameSpaceString(const AnalysisEnginePtr &, con FuncGraphPtr func_graph = out_node->func_graph(); MS_EXCEPTION_IF_NULL(func_graph); auto new_node = parse::ResolveSymbol(func_graph->manager(), name_space, symbol, out_node); + // If the symbol is not resolved, throw an exception. if (new_node == nullptr) { MS_LOG(EXCEPTION) << "Resolve node failed"; } + // Update debug information if in JIT mode. if (pipeline::GetJitLevel() == "o0" && IsValueNode(new_node)) { UpdateDebugInfo(GetValueNode(new_node), out_node->scope(), out_node->debug_info()); } - // Replace old node with the resolved new node in order list. + // Replace the old node with the resolved new node in the function graph's order list. func_graph->ReplaceInOrder(out_node, new_node); + // Create a new node configuration using the new node and the output configuration's context and function graph. AnalysisEnginePtr eng = out_conf->engine(); MS_EXCEPTION_IF_NULL(eng); AnfNodeConfigPtr fn_conf = eng->MakeConfig(new_node, out_conf->context(), out_conf->func_graph()); + // Forward the output configuration to the new configuration and return the result. return eng->ForwardConfig(out_conf, fn_conf); } @@ -1473,25 +1505,30 @@ EvalResultPtr GetEvaluatedValueForClassAttrOrMethod(const AnalysisEnginePtr &eng const AbstractBasePtrList &args_spec_list, const ValuePtr &item_value, const ConfigPtr &data_conf, const AnfNodeConfigPtr &out_conf) { + // Check if args_spec_list is empty. if (args_spec_list.empty()) { MS_LOG(EXCEPTION) << "args_spec_list is empty"; } + // Get the AbstractClass from the first argument in the args_spec_list. AbstractClassPtr cls = CheckArg("__FUNC__", args_spec_list, 0); - - // If item_value is an attribute, get abstract value from AbstractClass MS_EXCEPTION_IF_NULL(item_value); + // Check if the item_value is a string representing an attribute. if (!item_value->isa()) { MS_LOG(EXCEPTION) << "Attribute type error"; } + // Get the name of the attribute. std::string item_name = item_value->cast()->value(); MS_LOG(DEBUG) << "Resolve name: " << cls->tag().name(); MS_LOG(DEBUG) << "Resolve item: " << item_name; + // Check if the attribute exists in the AbstractClass. AbstractBasePtr attr = cls->GetAttribute(item_name); if (attr != nullptr) { return std::make_shared(attr, nullptr); } + // Get the method with the given name from the AbstractClass. ValuePtr method = cls->GetMethod(item_name); + // If the method is not found, throw an AttributeError. if (method->isa()) { MS_EXCEPTION_IF_NULL(args_spec_list[0]); MS_EXCEPTION_IF_NULL(args_spec_list[0]->BuildType()); @@ -1499,8 +1536,9 @@ EvalResultPtr GetEvaluatedValueForClassAttrOrMethod(const AnalysisEnginePtr &eng << ", item value: " << item_value->ToString(); } - // Infer class method + // Convert the method to a graph representation. ValuePtr converted_value = PyObjToGraph(engine, method); + // Call StaticGetterInferred to evaluate the converted method and return the result. return StaticGetterInferred(converted_value, data_conf, out_conf); } @@ -1509,12 +1547,14 @@ EvalResultPtr GetEvaluatedValueForBuiltinTypeAttrOrMethod(const AnalysisEnginePt const AnfNodeConfigPtr &out_conf) { MS_EXCEPTION_IF_NULL(item_value); MS_EXCEPTION_IF_NULL(data_type); - // The method maybe a Primitive or Composite + // Check if item_value is a string. if (!item_value->isa()) { MS_LOG(EXCEPTION) << "Expect a string, but got: " << item_value->ToString(); } + // Get the name of the attribute or method. std::string item_name = item_value->cast()->value(); + // Check if the attribute or method exists in the built-in type. REQUIRE_TYPE require_type = REQUIRE_TYPE::METHOD; Any require = pipeline::Resource::GetMethodPtr(data_type->type_id(), item_name); if (require.empty()) { @@ -1527,21 +1567,28 @@ EvalResultPtr GetEvaluatedValueForBuiltinTypeAttrOrMethod(const AnalysisEnginePt } ValuePtr converted_value = nullptr; + // If the attribute or method is a Composite registered in standard_method_map. if (require.is()) { - // composite registered in standard_method_map go to this branch converted_value = prim::GetPythonOps(require.cast()); MS_EXCEPTION_IF_NULL(converted_value); + // If in non-JIT mode and the converted_value is a FuncGraph, update the debug info. if (pipeline::GetJitLevel() == "o0" && converted_value->isa()) { UpdateDebugInfo(converted_value->cast(), out_conf->node()->scope(), out_conf->node()->debug_info()); } + // Add the converted_value to the engine if it is not a Primitive. if (!converted_value->isa()) { AddToManager(engine, converted_value->cast()); } - } else if (require.is()) { + } + // If the attribute or method is a Primitive. + else if (require.is()) { converted_value = require.cast(); - } else { + } + // Error handling. + else { MS_LOG(EXCEPTION) << "Expect to get string or PrimitivePtr from attr or method map, but got " << require.ToString(); } + // Call StaticGetterInferred to evaluate the converted method or attribute and return the result. return StaticGetterInferred(converted_value, data_conf, out_conf, require_type); } @@ -1553,13 +1600,16 @@ enum ResolveType : int64_t { int64_t GetResolveType(const TypePtr &data_type) { MS_EXCEPTION_IF_NULL(data_type); + // If the data_type is a user-defined class object. if (data_type->type_id() == kObjectTypeClass) { return kResolveTypeUserDefineClass; } - // Try to search method map, if not found, the data_type should be External type. + // If the data_type is found in the built-in type map. + // Otherwise, it should be an external type. if (pipeline::Resource::IsTypeInBuiltInMap(data_type->type_id())) { return kResolveTypeBuiltInType; } + // If the data_type is a function type. return kResolveTypeFunction; } @@ -1572,6 +1622,7 @@ EvalResultPtr StaticGetter(const AnalysisEnginePtr &engine, const AbstractBasePt MS_EXCEPTION_IF_NULL(args_spec_list[1]); MS_LOG(DEBUG) << "Args[0]: " << args_spec_list[0]->ToString(); MS_LOG(DEBUG) << "Args[1]: " << args_spec_list[1]->ToString(); + // Get the data type and value of the attribute or method. TypePtr data_type = args_spec_list[0]->BuildType(); ValuePtr item_value = args_spec_list[1]->BuildValue(); ScopePtr scope = kDefaultScope; @@ -1580,16 +1631,23 @@ EvalResultPtr StaticGetter(const AnalysisEnginePtr &engine, const AbstractBasePt } ScopeGuard scope_guard(scope); MS_EXCEPTION_IF_NULL(item_value); + // Check if the value of the attribute could be inferred. if (item_value->isa()) { MS_LOG(EXCEPTION) << "The value of the attribute could not be inferred: " << item_value->ToString(); } + // Determine the resolve type based on the data type. int64_t resolve_type = GetResolveType(data_type); + // If the resolve type is user-defined class, evaluate the attribute or method for the class. if (resolve_type == kResolveTypeUserDefineClass) { return GetEvaluatedValueForClassAttrOrMethod(engine, args_spec_list, item_value, data_conf, out_conf); - } else if (resolve_type == kResolveTypeBuiltInType) { + } + // If the resolve type is built-in type, evaluate the attribute or method for the built-in type. + else if (resolve_type == kResolveTypeBuiltInType) { return GetEvaluatedValueForBuiltinTypeAttrOrMethod(engine, item_value, data_type, data_conf, out_conf); - } else { + } + // If the resolve type is function, evaluate the attribute or method in the namespace. + else { return GetEvaluatedValueForNameSpaceString(engine, args_spec_list, out_conf); } } @@ -1599,37 +1657,52 @@ namespace { class EmbedEvaluator : public SymbolicPrimEvaluator { public: EmbedEvaluator() : SymbolicPrimEvaluator("EmbedEvaluator") {} + // Destructor ~EmbedEvaluator() override = default; MS_DECLARE_PARENT(EmbedEvaluator, SymbolicPrimEvaluator); + // Evaluate the Embed primitive operation EvalResultPtr EvalPrim(const ConfigPtrList &args_conf_list) override { // arg: free variable to be embedded if (args_conf_list.size() != 1) { MS_LOG(EXCEPTION) << "EmbedEvaluator requires 1 parameter, but got " << args_conf_list.size(); } + // Get the argument configuration AnfNodeConfigPtr node_conf = dyn_cast(args_conf_list[0]); MS_EXCEPTION_IF_NULL(node_conf); MS_EXCEPTION_IF_NULL(node_conf->ObtainEvalResult()); AbstractBasePtr x = node_conf->ObtainEvalResult()->abstract(); + // Apply sensitivity transform to the abstract value x = SensitivityTransform(x); + // Create a symbolic key instance using the node and the transformed abstract value SymbolicKeyInstancePtr key = std::make_shared(node_conf->node(), x); + // Create an abstract scalar with the symbolic key and the symbolic key type AbstractScalarPtr abs_scalar = std::make_shared(key, std::make_shared()); + // Return the evaluation result with the abstract scalar and an empty attribute value map return std::make_shared(abs_scalar, std::make_shared()); } }; +// Find the parameter node with the given name in the FuncGraph static AnfNodePtr FindParameterNodeByString(const FuncGraphManagerPtr &manager, const std::string &name) { MS_EXCEPTION_IF_NULL(manager); + // Get the set of root graphs in the manager auto root_g_set = manager->roots(); + // If there is not exactly one root graph, return nullptr if (root_g_set.size() != 1) { return nullptr; } + // Get the root graph const FuncGraphPtr &root_g = root_g_set.back(); + // Iterate through the parameter nodes in the root graph for (auto ¶m_node : root_g->parameters()) { auto param = param_node->cast(); + // If the parameter node has a matching name, return the parameter node if (param && name == param->name()) { return param; } } + + // If no parameter node with the given name is found, return nullptr return nullptr; } @@ -1760,27 +1833,37 @@ class ResolveEvaluator : public TransitionPrimEvaluator { } }; +// Check if an AbstractBasePtr argument contains an AbstractUndetermined value that has been broadened bool IsContainUndetermined(const AbstractBasePtr &arg) { - if (arg->isa()) { - auto seq_arg = arg->cast(); + // If the argument is an AbstractSequence + if (arg->isa()) { + // Cast it to an AbstractSequence pointer + auto seq_arg = arg->cast(); + // Check if any element in the sequence contains an undetermined value by recursively calling IsContainUndetermined return std::any_of(seq_arg->elements().begin(), seq_arg->elements().end(), IsContainUndetermined); } - - if (arg->isa()) { - auto kw_arg = arg->cast(); + // If the argument is an AbstractKeywordArg + if (arg->isa()) { + // Cast it to an AbstractKeywordArg pointer + auto kw_arg = arg->cast(); + // Check if the argument inside the keyword argument contains an undetermined value by calling IsContainUndetermined recursively return IsContainUndetermined(kw_arg->get_arg()); } - - return arg->isa() && arg->IsBroaden(); + // If the argument is an AbstractUndetermined and has been broadened, return true + return arg->isa() && arg->IsBroaden(); } class CreateInstanceEvaluator : public TransitionPrimEvaluator { public: + // Constructor CreateInstanceEvaluator() : TransitionPrimEvaluator("CreateInstanceEvaluator") {} + // Destructor ~CreateInstanceEvaluator() override = default; + // Declare parent class MS_DECLARE_PARENT(CreateInstanceEvaluator, TransitionPrimEvaluator); - EvalResultPtr EvalPrim(const AnalysisEnginePtr &engine, const AbstractBasePtrList &args_spec_list, const ConfigPtr &, - const AnfNodeConfigPtr &out_conf) override { + // Evaluate the primitive operation + EvalResultPtr EvalPrim(const AnalysisEnginePtr &engine, const AbstractBasePtrList &args_spec_list, + const ConfigPtr &, const AnfNodeConfigPtr &out_conf) override { if (args_spec_list.empty()) { MS_LOG(EXCEPTION) << "'args_spec_list' should not be empty"; } @@ -1817,7 +1900,7 @@ class CreateInstanceEvaluator : public TransitionPrimEvaluator { auto obj = parse::data_converter::CreatePythonObject(class_type, params); if (py::isinstance(obj)) { MS_LOG(EXCEPTION) << "Create python object `" << py::str(class_type) - << "` failed, only support to create \'Cell\' or \'Primitive\' object."; + << "` failed, only support to create 'Cell' or 'Primitive' object."; } // Process the object. @@ -1839,6 +1922,7 @@ class CreateInstanceEvaluator : public TransitionPrimEvaluator { return infer_result; } + // Get the parameters for creating the class instance py::tuple GetParameters(const AbstractBasePtrList &args_spec_list) const { if (args_spec_list.empty()) { MS_LOG(EXCEPTION) << "Unexpected arguments num, the min arguments num must be 1, but got 0."; @@ -1868,9 +1952,13 @@ class PyInterpretEvaluator : public TransitionPrimEvaluator { public: PyInterpretEvaluator() : TransitionPrimEvaluator("PyInterpretEvaluator") {} ~PyInterpretEvaluator() override = default; + + // Declare parent class. MS_DECLARE_PARENT(PyInterpretEvaluator, TransitionPrimEvaluator); - EvalResultPtr EvalPrim(const AnalysisEnginePtr &engine, const AbstractBasePtrList &args_spec_list, const ConfigPtr &, - const AnfNodeConfigPtr &out_conf) override { + + // Evaluate python interpret operations. + EvalResultPtr EvalPrim(const AnalysisEnginePtr &engine, const AbstractBasePtrList &args_spec_list, + const ConfigPtr &, const AnfNodeConfigPtr &out_conf) override { if (args_spec_list.empty()) { MS_LOG(ERROR) << "'args_spec_list' should not be empty"; } @@ -1880,6 +1968,7 @@ class PyInterpretEvaluator : public TransitionPrimEvaluator { ValuePtr value_track = args_spec_list[0]->GetValueTrack(); MS_EXCEPTION_IF_NULL(value_track); + // Check if the type is Script type. std::shared_ptr script_obj = dyn_cast(value_track); if (script_obj == nullptr) { MS_LOG(EXCEPTION) << "Cast value failed, not PyObjectWrapper:" << value_track->ToString() << "."; @@ -1901,6 +1990,8 @@ class PyInterpretEvaluator : public TransitionPrimEvaluator { auto cur_node = out_conf->node(); MS_EXCEPTION_IF_NULL(cur_node); bool has_switch_cond_user = CheckSwitchCondUser(cur_node); + + // Convert object to ValuePtr. ValuePtr converted_val = nullptr; if (has_switch_cond_user) { // If the cond input in switch is InterpretedObject, need convert InterpretedObject to a ValuePtr object. @@ -1911,19 +2002,22 @@ class PyInterpretEvaluator : public TransitionPrimEvaluator { converted_val = MakeValue(false); } } else { - // converted_val could be a InterpretedObject. + // Converted_val could be an InterpretedObject. bool converted = parse::ConvertData(obj, &converted_val, true); if (!converted) { MS_LOG(EXCEPTION) << "Convert the python object failed"; } } MS_EXCEPTION_IF_NULL(converted_val); + + // Convert ValuePtr to AbstractBasePtr. AbstractBasePtr res = ToAbstract(converted_val, AnalysisContext::DummyContext(), out_conf); auto infer_result = std::make_shared(res, std::make_shared()); evaluator_cache_mgr_->SetValue(args_spec_list, infer_result); return infer_result; } + // Check if current node is used as condition input in switch. bool CheckSwitchCondUser(const AnfNodePtr &node) { auto fg = node->func_graph(); MS_EXCEPTION_IF_NULL(fg); @@ -1947,6 +2041,7 @@ class PyInterpretEvaluator : public TransitionPrimEvaluator { return false; } + // Make global and local parameters as py tuple. py::tuple MakeParameters(const AbstractBasePtrList &args_spec_list) const { constexpr int params_size = 3; if (params_size != args_spec_list.size()) { @@ -1983,6 +2078,7 @@ class PyInterpretEvaluator : public TransitionPrimEvaluator { return params; } + // Convert local dictionary to py dict. py::dict ReCheckLocalDict(const AbstractDictionaryPtr &filtered_local_dict) const { const auto &keys_values = filtered_local_dict->elements(); py::dict local_params_dict; @@ -1995,6 +2091,7 @@ class PyInterpretEvaluator : public TransitionPrimEvaluator { return local_params_dict; } + // Filter out function type elements in given AbstractDictionary. AbstractDictionaryPtr FilterParameters(const AbstractDictionaryPtr &abstract_dict) const { std::vector kv; const auto &keys_values = abstract_dict->elements(); @@ -2012,25 +2109,32 @@ class MakeTupleEvaluator : public TransitionPrimEvaluator { public: MakeTupleEvaluator() : TransitionPrimEvaluator("MakeTupleEvaluator") {} ~MakeTupleEvaluator() override = default; + // Declare parent class. MS_DECLARE_PARENT(MakeTupleEvaluator, TransitionPrimEvaluator); + // Evaluate MakeTuple operations. EvalResultPtr EvalPrim(const AnalysisEnginePtr &engine, const AbstractBasePtrList &args_spec_list, const ConfigPtr &, const AnfNodeConfigPtr &out_conf) override { if (args_spec_list.empty()) { MS_LOG(INFO) << "For MakeTuple, the inputs should not be empty. node: " << out_conf->node()->DebugString(); } + // Enable elimination of unused elements based on the environment variable. static const auto enable_eliminate_unused_element = (common::GetEnv("MS_DEV_ENABLE_DDE") != "0"); if (enable_eliminate_unused_element) { + // Get the flags that indicate whether each element in the sequence is used. auto flags = GetSequenceNodeElementsUseFlags(out_conf->node()); if (flags == nullptr) { SetSequenceNodeElementsUseFlags(out_conf->node(), std::make_shared>(args_spec_list.size())); } } + // Create a shared pointer to store weak references to the sequence nodes. std::shared_ptr sequence_nodes = std::make_shared(); if (enable_eliminate_unused_element) { (void)sequence_nodes->emplace_back(AnfNodeWeakPtr(out_conf->node())); } + // Create an AbstractTuple object based on the input AbstractBase objects. auto abs = std::make_shared(args_spec_list, sequence_nodes); + // Create an EvalResult object with the AbstractTuple and an empty AttrValueMap. auto res = std::make_shared(abs, std::make_shared()); evaluator_cache_mgr_->SetValue(args_spec_list, res); return res; @@ -2041,49 +2145,67 @@ class MakeListEvaluator : public TransitionPrimEvaluator { public: MakeListEvaluator() : TransitionPrimEvaluator("MakeListEvaluator") {} ~MakeListEvaluator() override = default; + // Declare parent class. MS_DECLARE_PARENT(MakeListEvaluator, TransitionPrimEvaluator); + // Evaluate MakeList operations. EvalResultPtr EvalPrim(const AnalysisEnginePtr &engine, const AbstractBasePtrList &args_spec_list, const ConfigPtr &, const AnfNodeConfigPtr &out_conf) override { if (args_spec_list.empty()) { MS_LOG(INFO) << "For MakeList, the inputs should not be empty. node: " << out_conf->node()->DebugString(); } - + // Enable elimination of unused elements based on the environment variable. static const auto enable_eliminate_unused_element = (common::GetEnv("MS_DEV_ENABLE_DDE") != "0"); if (enable_eliminate_unused_element) { + // Get the flags that indicate whether each element in the sequence is used. auto flags = GetSequenceNodeElementsUseFlags(out_conf->node()); if (flags == nullptr) { SetSequenceNodeElementsUseFlags(out_conf->node(), std::make_shared>(args_spec_list.size())); } } + // Create a shared pointer to store weak references to the sequence nodes. std::shared_ptr sequence_nodes = std::make_shared(); if (enable_eliminate_unused_element) { (void)sequence_nodes->emplace_back(AnfNodeWeakPtr(out_conf->node())); } + // Create an AbstractList object based on the input AbstractBase objects. auto abs = std::make_shared(args_spec_list, sequence_nodes); + // Create an EvalResult object with the AbstractList and an empty AttrValueMap. auto res = std::make_shared(abs, std::make_shared()); evaluator_cache_mgr_->SetValue(args_spec_list, res); return res; } }; +// This class is the implementation of partial evaluator in MindSpore. +// It inherits from the Evaluator class and overrides its Run() and Eval() methods to evaluate a Partial node. +// The class takes a Partial node as input and returns an EvalResultPtr which contains the result of evaluation. +// The class works by extracting the arguments of the Partial node, evaluating the first argument (which should be a function), +// and creating a new function that partially applies the remaining arguments to the function. +// The new function is returned as the result of evaluation. +// The HandleDoSignature() method is used when the function to be partially applied is a signature Primitive. class PartialEvaluator : public Evaluator { public: PartialEvaluator() : Evaluator("PartialEvaluator") {} ~PartialEvaluator() override = default; + + // Method to run the actual evaluation of the Partial node EvalResultPtr Run(AnalysisEnginePtr engine, const ConfigPtrList &args_conf_list, const AnfNodeConfigPtr &out_conf) override { + // Check if there are any arguments passed to the Partial node if (args_conf_list.size() == 0) { MS_LOG(EXCEPTION) << "Args size should be greater than 0"; } + // Check for null pointers MS_EXCEPTION_IF_NULL(out_conf); MS_EXCEPTION_IF_NULL(out_conf->node()); MS_EXCEPTION_IF_NULL(args_conf_list[0]); MS_EXCEPTION_IF_NULL(args_conf_list[0]->ObtainEvalResult()); + // Extract the argument value of the first argument and create a list of AbstractBasePtrs for the arguments auto arg0_value = args_conf_list[0]->ObtainEvalResult()->abstract(); MS_EXCEPTION_IF_NULL(arg0_value); AbstractBasePtrList args_spec_list{arg0_value}; - // Func in hypermap(partial(Func, arg0), arg1, arg2) may become Poly Node. + // If the argument is an AbstractError, return a new AbstractError object if (arg0_value->isa()) { MS_EXCEPTION_IF_NULL(arg0_value->GetValueTrack()); auto ret = std::make_shared(arg0_value->GetValueTrack()->cast(), out_conf->node()); @@ -2093,8 +2215,9 @@ class PartialEvaluator : public Evaluator { evaluator_cache_mgr_->SetValue(args_spec_list, eval_result); return eval_result; } + // Check that the first argument is a function and extract it auto func = CheckArg("partial", args_spec_list, 0); - // Sometimes, node[0] in out_conf becomes phi0; + // If the function to be partially applied is a PrimitiveAbstractClousre, check if it is a DoSignaturePrimitive if (func->isa()) { auto prim_func = dyn_cast(func); MS_EXCEPTION_IF_NULL(prim_func->prim()); @@ -2103,12 +2226,14 @@ class PartialEvaluator : public Evaluator { return HandleDoSignature(engine, do_signature_prim->function(), out_conf); } } - + + // Extract the other arguments and create a list of AbstractBasePtrs for all arguments (void)std::transform( args_conf_list.begin() + 1, args_conf_list.end(), std::back_inserter(args_spec_list), [](const ConfigPtr &config) -> AbstractBasePtr { return config->ObtainEvalResult()->abstract(); }); AbstractBasePtrList args(args_spec_list.begin() + 1, args_spec_list.end()); + // Create a new PartialAbstractClosure object with the function and arguments auto cnode = out_conf->node()->cast(); MS_EXCEPTION_IF_NULL(cnode); if (cnode->size() != (args_conf_list.size() + 1)) { @@ -2122,18 +2247,22 @@ class PartialEvaluator : public Evaluator { }; func->Visit(build_partial); + // Return the newly created function as an EvalResultPtr auto ret = AbstractFunction::MakeAbstractFunction(partial_funcs_list); auto eval_result = std::make_shared(ret, std::make_shared()); evaluator_cache_mgr_->SetValue(args_spec_list, eval_result); return eval_result; } + // Method to evaluate an input for the Partial node EvalResultPtr Eval(AnalysisEnginePtr, const AbstractBasePtrList &, const AnfNodeConfigPtr &) override { MS_LOG(EXCEPTION) << "Eval() should not be called, Run() method should be called"; } + // Method to handle partially applying a signature Primitive to a function EvalResultPtr HandleDoSignature(const AnalysisEnginePtr &engine, const ValuePtr &signature_value, const AnfNodeConfigPtr &out_conf) const { + // Check for null pointers MS_EXCEPTION_IF_NULL(engine); MS_EXCEPTION_IF_NULL(out_conf); MS_EXCEPTION_IF_NULL(out_conf->node()); @@ -2142,6 +2271,7 @@ class PartialEvaluator : public Evaluator { MS_LOG(EXCEPTION) << "Cnode is nullptr"; } + // Create a new DoSignatureMetaFuncGraph object with the signature and update the arguments of the CNode object ScopeGuard scope_guard(out_conf->node()->scope()); TraceGuard trace_guard(std::make_shared(out_conf->node()->debug_info())); std::vector new_nodes_inputs = cnode->inputs(); @@ -2151,32 +2281,44 @@ class PartialEvaluator : public Evaluator { MS_EXCEPTION_IF_NULL(func_graph); CNodePtr new_cnode = func_graph->NewCNode(std::move(new_nodes_inputs)); AnfNodeConfigPtr fn_conf = engine->MakeConfig(new_cnode, out_conf->context(), out_conf->func_graph()); + + // Return the result of calling the Run() method on the updated config object return engine->ForwardConfig(out_conf, fn_conf); } }; +// This class is the implementation of the evaluator for the "raise" operation in MindSpore. +// It inherits from the TransitionPrimEvaluator class and overrides its EvalPrim() method. +// The class is responsible for evaluating the "raise" operation by throwing an exception with the specified type and message. +// The EvalPrim() method checks if the current graph is a tensor condition branch, and if so, it throws an exception. +// Then it processes the arguments passed to the "raise" operation, which includes the exception type and optional exception message. +// Finally, it throws an exception with the specified type and message. class RaiseEvaluator : public TransitionPrimEvaluator { public: RaiseEvaluator() : TransitionPrimEvaluator("RaiseEvaluator") {} ~RaiseEvaluator() override = default; + // Define parent class MS_DECLARE_PARENT(RaiseEvaluator, TransitionPrimEvaluator); + // Method to evaluate the "raise" operation EvalResultPtr EvalPrim(const AnalysisEnginePtr &engine, const AbstractBasePtrList &args_spec_list, const ConfigPtr &in_conf0, const AnfNodeConfigPtr &out_conf) override { auto node = out_conf->node(); MS_EXCEPTION_IF_NULL(node); auto cur_graph = node->func_graph(); MS_EXCEPTION_IF_NULL(cur_graph); + // Check if the current graph is a tensor condition branch if (cur_graph->is_tensor_condition_branch()) { MS_LOG(EXCEPTION) << "Currently only supports raise in constant scenarios." << "Tensor type data cannot exist in the conditional statement." << "Please check your conditions which raise node is located at: " << trace::GetDebugInfo(node->debug_info()); } + // Check if there are any arguments passed to the "raise" operation if (args_spec_list.empty()) { - // process raise + // Process raise MS_LOG(EXCEPTION) << "No active exception to reraise."; } - + // Process the exception type and optional exception message std::string exception_type = GetScalarStringValue(args_spec_list[0]); auto iter = exception_types_map.find(exception_type); if (iter == exception_types_map.end()) { @@ -2191,11 +2333,13 @@ class RaiseEvaluator : public TransitionPrimEvaluator { for (size_t index = 1; index < args_spec_list.size(); ++index) { exception_string += GetExceptionString(args_spec_list[index]); } + // Throw an exception with the specified type and message MS_EXCEPTION(type) << exception_string; return nullptr; } private: + // Method to get the exception message as a string std::string GetExceptionString(const AbstractBasePtr &arg) { std::string exception_str = ""; if (arg->isa()) { @@ -2216,6 +2360,7 @@ class RaiseEvaluator : public TransitionPrimEvaluator { return exception_str; } + // Method to convert a scalar value to a string std::string GetScalarStringValue(const AbstractBasePtr &abs) { std::string str = ""; if (abs->isa()) { @@ -2270,16 +2415,20 @@ PrimEvaluatorMap PrimEvaluatorConstructors = PrimEvaluatorMap(); std::mutex PrimEvaluatorConstructorMutex; void InitPrimEvaluatorConstructors() { + // Initialize the constructor map PrimEvaluatorMap &constructor = PrimEvaluatorConstructors; + // Add standard evaluators to the map for (const auto &iter : GetPrimitiveToEvalImplMap()) { constructor[iter.first] = InitStandardPrimEvaluator(iter.first, iter.second); } + // Add uniform evaluators to the map for (const auto &iter : GetUniformPrimitiveToImplMap()) { constructor[iter.first] = InitUniformPrimEvaluator(iter.first, iter.second.impl_, iter.second.eval_value_, iter.second.specify_out_type_); } + // Add non-standard evaluators to the map constructor[prim::kPrimEmbed] = std::make_shared(); constructor[prim::kPrimRefToEmbed] = std::make_shared(); constructor[prim::kPrimGetAttr] = std::make_shared(); @@ -2294,33 +2443,40 @@ void InitPrimEvaluatorConstructors() { } // namespace void ClearPrimEvaluatorMap() { + // Clear the PrimEvaluatorConstructors map PrimEvaluatorConstructors.clear(); + // Clear the GetPrimitiveToEvalImplMap GetPrimitiveToEvalImplMap().clear(); + // Clear the GetUniformPrimitiveToImplMap GetUniformPrimitiveToImplMap().clear(); } bool IsInWhiteList(const PrimitivePtr &primitive) { MS_EXCEPTION_IF_NULL(primitive); - + + // Check if the primitive is in GetPrimitiveToEvalImplMap auto iter = GetPrimitiveToEvalImplMap().find(primitive); if (iter != GetPrimitiveToEvalImplMap().end()) { return iter->second.in_white_list_; - } - + + // Check if the primitive is in GetUniformPrimitiveToImplMap auto uni_iter = GetUniformPrimitiveToImplMap().find(primitive); if (uni_iter != GetUniformPrimitiveToImplMap().end()) { return uni_iter->second.in_white_list_; } - + // The primitive is not in the white list return false; } PrimEvaluatorMap &GetPrimEvaluatorConstructors() { PrimEvaluatorMap &constructor = PrimEvaluatorConstructors; + // If the map is not empty, return it if (!constructor.empty()) { return constructor; } + // Acquire a lock before initializing the map std::lock_guard initLock(PrimEvaluatorConstructorMutex); + // Double check if the map is still empty after acquiring the lock if (constructor.empty()) { InitPrimEvaluatorConstructors(); } @@ -2332,94 +2488,119 @@ namespace { bool IsSubtypeTuple(const AbstractBasePtr x, const TypePtr model) { MS_EXCEPTION_IF_NULL(x); MS_EXCEPTION_IF_NULL(model); + // Check if `x` is an instance of AbstractTuple and `model` is an instance of Tuple auto x_tuple = dyn_cast(x); auto model_tuple = dyn_cast(model); + // If either `x` or `model` is not of the expected type, return false if (x_tuple == nullptr || model_tuple == nullptr) { return false; } + // If `model` is a generic type, return true if (model->IsGeneric()) { return true; } + // Check if the number of elements in `x_tuple` matches the number of elements in `model_tuple` if (x_tuple->size() != model_tuple->size()) { return false; } + // Iterate through each element in `x_tuple` and `model_tuple` and check if they are subtypes for (size_t i = 0; i < x_tuple->size(); i++) { bool is_subtype = IsSubtype((*x_tuple)[i], (*model_tuple)[i]); + // If any pair of elements is not a subtype, return false if (!is_subtype) { return false; } } + + // All checks pass, `x` is a subtype of `model` return true; } bool IsSubtypeArray(const AbstractBasePtr x, const TypePtr model) { MS_EXCEPTION_IF_NULL(x); MS_EXCEPTION_IF_NULL(model); + // Check if `x` is an instance of AbstractTensor and `model` is an instance of TensorType auto x_tensor = dyn_cast(x); auto model_tensor = dyn_cast(model); + // If either `x` or `model` is not of the expected type, return false if (x_tensor == nullptr || model_tensor == nullptr) { return false; } + // If `model` is a generic type, return true if (model->IsGeneric()) { return true; } + // Check if the element type of `x_tensor` is a subtype of the element type of `model_tensor` return IsSubtype(x_tensor->element(), model_tensor->element()); } bool IsSubtypeList(const AbstractBasePtr x, const TypePtr model) { MS_EXCEPTION_IF_NULL(x); MS_EXCEPTION_IF_NULL(model); + // Check if `x` is an instance of AbstractList and `model` is an instance of List auto x_list = dyn_cast(x); auto model_list = dyn_cast(model); + // If either `x` or `model` is not of the expected type, return false if (x_list == nullptr || model_list == nullptr) { return false; } + // If `model` is a generic type, return true if (model->IsGeneric()) { return true; } + // Check if the size of `x_list` matches the size of `model_list` if (x_list->size() != model_list->size()) { return false; } bool is_subtype = true; + // Iterate through each element in `x_list` and `model_list` and check if they are subtypes for (size_t i = 0; i < x_list->size(); i++) { is_subtype = IsSubtype((*x_list)[i], (*model_list)[i]); + // If any pair of elements is not a subtype, return false if (!is_subtype) { return false; } } + return is_subtype; } bool IsSubtypeClass(const AbstractBasePtr x, const TypePtr model) { MS_EXCEPTION_IF_NULL(x); MS_EXCEPTION_IF_NULL(model); + // Check if `x` is an instance of AbstractClass and `model` is an instance of Class auto x_class = dyn_cast(x); auto model_class = dyn_cast(model); + // If `x` is not of the expected type, return false if (x_class == nullptr) { return false; } + // If `model` is a generic type, return true if (model->IsGeneric()) { return true; } MS_EXCEPTION_IF_NULL(model_class); + // Check if the tag of `x_class` matches the tag of `model_class` if (x_class->tag() == model_class->tag()) { auto m_attributes = model_class->GetAttributes(); auto x_attributes = x_class->attributes(); + // Check if the number of attributes in `x_class` matches the number of attributes in `model_class` if (m_attributes.size() != x_attributes.size()) { return false; } + // Iterate through each attribute in `x_class` and `model_class` and check if they are subtypes for (size_t i = 0; i < m_attributes.size(); i++) { if (!IsSubtype(x_attributes[i].second, m_attributes[i].second)) { return false; @@ -2434,32 +2615,43 @@ bool IsSubtypeClass(const AbstractBasePtr x, const TypePtr model) { inline bool IsSubtypeScalar(const AbstractBasePtr x, const TypePtr model) { MS_EXCEPTION_IF_NULL(x); MS_EXCEPTION_IF_NULL(model); - if (dyn_cast(x) == nullptr) { + // If `x` is not an instance of AbstractScalar, return false + if (dyn_cast(x) == nullptr) { return false; } - TypePtr x_type = x->GetTypeTrack(); - return IsSubType(x_type, model); + // Get the type of `x` + TypePtr x_type = x->GetTypeTrack(); + // Check if `x_type` is a subtype of `model` + return IsSubType(x_type, model); } } // namespace bool IsSubtype(const AbstractBasePtr x, const TypePtr model) { MS_EXCEPTION_IF_NULL(x); MS_EXCEPTION_IF_NULL(model); - TypeId model_typeid = model->type_id(); + // Get the type id of `model` + TypeId model_typeid = model->type_id(); switch (model_typeid) { - case kMetaTypeObject: + // If `model` is an instance of Object, return true + case kMetaTypeObject: return true; - case kObjectTypeTuple: + // If `model` is an instance of Tuple, check if `x` is a subtype of `model` + case kObjectTypeTuple: return IsSubtypeTuple(x, model); - case kObjectTypeTensorType: + // If `model` is an instance of TensorType, check if `x` is a subtype of `model` + case kObjectTypeTensorType: return IsSubtypeArray(x, model); + // If `model` is an instance of List, check if `x` is a subtype of `model` case kObjectTypeList: return IsSubtypeList(x, model); - case kObjectTypeClass: + // If `model` is an instance of Class, check if `x` is a subtype of `model` + case kObjectTypeClass: return IsSubtypeClass(x, model); default: + // If `model` is not one of the above types, check if it is a subtype of Number. if (IsSubType(model, std::make_shared())) { - return IsSubtypeScalar(x, model); + // If `model` is a subtype of Number, check if `x` is a subtype of `model` + return IsSubtypeScalar(x, model); } MS_LOG(EXCEPTION) << "Invalid model type: " << model->ToString() << "."; } -- 2.34.1