From df170fd4c30190de73d9c80bdcd7590f5500f576 Mon Sep 17 00:00:00 2001 From: ShiyuW Date: Wed, 4 Oct 2023 10:25:37 +0800 Subject: [PATCH] [chore] Add some comments --- mindspore/ccsrc/pipeline/jit/init.cc | 962 +++- mindspore/ccsrc/pipeline/jit/pass.cc | 1387 ++++- .../pipeline/jit/remove_value_node_dup.cc | 81 +- mindspore/ccsrc/pipeline/jit/resource.cc | 753 ++- .../pipeline/pynative/pynative_execute.cc | 4792 ++++++++++++++--- 5 files changed, 6303 insertions(+), 1672 deletions(-) diff --git a/mindspore/ccsrc/pipeline/jit/init.cc b/mindspore/ccsrc/pipeline/jit/init.cc index a0c8474afbb..123416b8637 100644 --- a/mindspore/ccsrc/pipeline/jit/init.cc +++ b/mindspore/ccsrc/pipeline/jit/init.cc @@ -14,232 +14,507 @@ * limitations under the License. */ +// Include the pybind11 operators header for defining custom operators in Python #include + +// Include the oplib header file #include "kernel/oplib/oplib.h" + +// Include the pipeline header file for just-in-time compilation #include "pipeline/jit/pipeline.h" + +// Include the composite header file for defining composite operators #include "frontend/operator/composite/composite.h" + +// Include the pynative_execute header file for executing Python-native operators #include "pipeline/pynative/pynative_execute.h" + +// Include the symbolic header file for symbolic operations #include "utils/symbolic.h" + +// Include the API register header file for registering APIs #include "include/common/pybind_api/api_register.h" + +// Include the Python adapter header file for Python-related utilities #include "include/common/utils/python_adapter.h" + +// Include the event writer header file for writing event summaries #ifndef ENABLE_SECURITY #include "include/common/utils/summary/event_writer.h" #endif + +// Include the config manager header file for managing configurations #include "include/common/utils/config_manager.h" + +// Include the MPI config header file for MPI-related configurations #include "include/common/utils/mpi/mpi_config.h" + +// Include the MS utils header file for MindSpore utilities #include "utils/ms_utils.h" + +// Include the parallel context header file for parallel execution context #include "include/common/utils/parallel_context.h" + +// Include the costmodel context header file for cost model context #include "frontend/parallel/costmodel_context.h" + +// Include the dfunctor header file for automatic differentiation #include "frontend/optimizer/ad/dfunctor.h" + +// Include the collective initialization header file for GPU collective operations #ifdef ENABLE_GPU_COLLECTIVE #include "plugin/device/gpu/hal/device/distribution/collective_init.h" #else #include "plugin/device/gpu/hal/device/distribution/collective_fake_init.h" #endif + +// Include the ps util header file for parameter server utilities #if ((defined ENABLE_CPU) && (!defined _WIN32)) #include "ps/util.h" #endif + +// Include the ps context header file for parameter server context #include "ps/ps_context.h" + +// Include the recovery context header file for distributed recovery context #include "distributed/recovery/recovery_context.h" +// Include the header file "gil_scoped_long_running.h" from the "pybind_api" directory #include "pybind_api/gil_scoped_long_running.h" +// Create an alias "py" for the namespace "pybind11" namespace py = pybind11; +// Define an alias GraphExecutorPy for the mindspore::pipeline::GraphExecutorPy class using GraphExecutorPy = mindspore::pipeline::GraphExecutorPy; + +// Define an alias Pipeline for the mindspore::pipeline::Pipeline class using Pipeline = mindspore::pipeline::Pipeline; + +// Define an alias PrimitivePy for the mindspore::PrimitivePy class using PrimitivePy = mindspore::PrimitivePy; + +// Define an alias MetaFuncGraph for the mindspore::MetaFuncGraph class using MetaFuncGraph = mindspore::MetaFuncGraph; + +// Conditionally define an alias EventWriter for the mindspore::summary::EventWriter class, only if ENABLE_SECURITY is not defined #ifndef ENABLE_SECURITY using EventWriter = mindspore::summary::EventWriter; #endif // ENABLE_SECURITY + +// Define an alias OpLib for the mindspore::kernel::OpLib class using OpLib = mindspore::kernel::OpLib; + +// Define an alias ParallelContext for the mindspore::parallel::ParallelContext class using ParallelContext = mindspore::parallel::ParallelContext; + +// Define an alias CostModelContext for the mindspore::parallel::CostModelContext class using CostModelContext = mindspore::parallel::CostModelContext; + +// Use the mindspore::MsCtxParam namespace using mindspore::MsCtxParam; + +// Define an alias PSContext for the mindspore::ps::PSContext class using PSContext = mindspore::ps::PSContext; + +// Define an alias RecoveryContext for the mindspore::distributed::recovery::RecoveryContext class using RecoveryContext = mindspore::distributed::recovery::RecoveryContext; -// Interface with python +// Interface with python using the Pybind11 library + +// Define a Pybind11 module named "_c_expression" and bind it to the variable "m" PYBIND11_MODULE(_c_expression, m) { - // The OMP_NUM_THREADS has no effect when set in backend, so set it here in advance. + + // Set the number of OpenMP threads to be used by the program + // This is done to ensure that the OMP_NUM_THREADS environment variable has an effect mindspore::common::SetOMPThreadNum(); - m.doc() = "MindSpore c plugin"; +// Set the documentation for the `m` object to "MindSpore c plugin" - auto fns = mindspore::PybindDefineRegister::AllFuncs(); - for (auto &item : fns) { +// Get all the registered functions from the `mindspore::PybindDefineRegister` class and store them in the `fns` variable +auto fns = mindspore::PybindDefineRegister::AllFuncs(); + +// Iterate over each item in the `fns` variable +for (auto &item : fns) { + + // Call the function pointer stored in the `second` member of the item, passing the `m` object as an argument item.second(&m); - } +} - mindspore::ScopedLongRunning::SetHook(std::make_unique()); +// Set the hook for long-running operations in the MindSpore framework +mindspore::ScopedLongRunning::SetHook(std::make_unique()); +// Create a unique pointer to an instance of the GilScopedLongRunningHook class and pass it to the SetHook function - // Class Pipeline interface - (void)py::class_>(m, "GraphExecutor_") +// Define the class interface for the Pipeline class +(void)py::class_>(m, "GraphExecutor_") + // Define a static method get_instance that returns an instance of the GraphExecutorPy class .def_static("get_instance", &GraphExecutorPy::GetInstance, "Executor get_instance.") + // Define the __call__ method that calls the Run method of the GraphExecutorPy class .def("__call__", &GraphExecutorPy::Run, py::arg("args"), py::arg("phase") = py::str(""), "Executor run function.") + // Define the del_net_res method that calls the DelNetRes method of the GraphExecutorPy class .def("del_net_res", &GraphExecutorPy::DelNetRes, py::arg("network_id") = py::set(), "Delete network resource.") + // Define the get_func_graph method that calls the GetFuncGraph method of the GraphExecutorPy class .def("get_func_graph", &GraphExecutorPy::GetFuncGraph, py::arg("phase") = py::str(""), "Get graph pointer.") + // Define the get_func_graph_proto method that calls the GetFuncGraphProto method of the GraphExecutorPy class .def("get_func_graph_proto", &GraphExecutorPy::GetFuncGraphProto, py::arg("phase") = py::str(""), py::arg("type") = py::str("onnx_ir"), "Get graph proto string by specifying ir type.") + // Define the compile method that calls the Compile method of the GraphExecutorPy class .def("compile", &GraphExecutorPy::Compile, py::arg("obj"), py::arg("args"), py::arg("phase") = py::str(""), py::arg("use_vm") = py::bool_(false), "Compile obj by executor.") + // Define the updata_param_node_default_input method that calls the UpdataParamNodeDefaultInput method of the GraphExecutorPy class .def("updata_param_node_default_input", &GraphExecutorPy::UpdataParamNodeDefaultInput, py::arg("phase"), py::arg("params"), "Fetch the inputs of Conv or Matmul for quant export.") + // Define the get_parameter_layout method that calls the GetParameterLayout method of the GraphExecutorPy class .def("get_parameter_layout", &GraphExecutorPy::GetParameterLayout, py::arg("phase") = py::str("train"), "Get Parameter Tensor Layout Dictionary.") + // Define the get_parallel_graph_info method that calls the GetParallelGraphInfo method of the GraphExecutorPy class .def("get_parallel_graph_info", &GraphExecutorPy::GetParallelGraphInfo, py::arg("phase") = py::str("train"), "Get graph info in step_parallel stage.") + // Define the get_parallel_parameter_name_list method that calls the GetParallelParameterNameList method of the GraphExecutorPy class .def("get_parallel_parameter_name_list", &GraphExecutorPy::GetParallelParameterNameList, py::arg("phase") = py::str("train"), "Get Parallel Parameter Name List.") + // Define the get_strategy method that calls the GetCNodeStrategy method of the GraphExecutorPy class .def("get_strategy", &GraphExecutorPy::GetCNodeStrategy, py::arg("phase") = py::str("train"), "Get CNode Strategy Dictionary.") + // Define the get_num_parallel_ops method that calls the GetNumOpsInfo method of the GraphExecutorPy class .def("get_num_parallel_ops", &GraphExecutorPy::GetNumOpsInfo, py::arg("phase") = py::str("train"), "Get the number of parallel operators.") + // Define the get_allreduce_fusion method that calls the GetAllreduceFusion method of the GraphExecutorPy class .def("get_allreduce_fusion", &GraphExecutorPy::GetAllreduceFusion, py::arg("phase") = py::str("train"), "Get Allreduce Fusion Dictionary.") + // Define the fetch_info_for_quant_export method that calls the FetchInfoForQuantExport method of the GraphExecutorPy class .def("fetch_info_for_quant_export", &GraphExecutorPy::FetchInfoForQuantExport, py::arg("phase") = py::str("train"), "Fetch the inputs of Conv or Matmul for quant export.") + // Define the build_data_graph method that calls the BuildGraph method of the GraphExecutorPy class .def("build_data_graph", &GraphExecutorPy::BuildGraph, py::arg("build_params"), py::arg("phase") = py::str("train"), py::arg("broadcast_params") = py::dict(), "Build data graph.") + // Define the has_compiled method that calls the HasCompiled method of the GraphExecutorPy class .def("has_compiled", &GraphExecutorPy::HasCompiled, py::arg("phase") = py::str(""), "Get if cell compiled.") + // Define the run_init_graph method that calls the RunInitGraph method of the GraphExecutorPy class .def("run_init_graph", &GraphExecutorPy::RunInitGraph, "Run init Graph.") + // Define the set_py_exe_path method that calls the PyExePath method of the GraphExecutorPy class .def("set_py_exe_path", &GraphExecutorPy::PyExePath, py::arg("py_exe_path") = py::str(""), "Set python executable path.") + // Define the set_kernel_build_server_dir method that calls the KernelBuildServerDir method of the GraphExecutorPy class .def("set_kernel_build_server_dir", &GraphExecutorPy::KernelBuildServerDir, py::arg("kernel_build_server_dir") = py::str(""), "Set kernel build server directory path.") + // Define the set_queue_name method that calls the set_queue_name method of the GraphExecutorPy class .def("set_queue_name", &GraphExecutorPy::set_queue_name, py::arg("queue_name") = py::str(""), "Set queue name for the graph loaded from compile cache.") + // Define the set_enable_tuple_broaden method that calls the set_enable_tuple_broaden method of the GraphExecutorPy class .def("set_enable_tuple_broaden", &GraphExecutorPy::set_enable_tuple_broaden, py::arg("enable_tuple_broaden") = py::bool_(false), "Set tuple broaden enable.") + // Define the set_compile_cache_dep_files method that calls the set_compile_cache_dep_files method of the GraphExecutorPy class .def("set_compile_cache_dep_files", &GraphExecutorPy::set_compile_cache_dep_files, py::arg("compile_cache_dep_files") = py::list(), "Set the compilation cache dependent files.") .def("set_weights_values", &GraphExecutorPy::set_weights_values, py::arg("weights") = py::dict(), - "Set values of weights.") + "Set values of weights.") // Define a Python binding for the "set_weights_values" method of the GraphExecutorPy class, which takes a dictionary as an argument and sets the values of weights. The default argument is an empty dictionary. This method is used to set the values of weights. + .def("get_optimize_graph_proto", &GraphExecutorPy::GetOptimizeGraphProto, py::arg("phase") = py::str(""), - "Get the optimize graph proto string.") - .def("set_jit_config", &GraphExecutorPy::SetJitConfig, py::arg("jit_config") = py::dict(), "Set the jit config.") - .def("generate_arguments_key", &GraphExecutorPy::GenerateArgumentsKey, "Generate unique key of argument."); + "Get the optimize graph proto string.") // Define a Python binding for the "get_optimize_graph_proto" method of the GraphExecutorPy class, which takes a string as an argument (default is an empty string) and returns the optimize graph proto string. This method is used to get the optimize graph proto string. - (void)m.def("real_run_op", &mindspore::pynative::RealRunOp, "Run op pynatively."); - (void)m.def("reset_op_id", &mindspore::pipeline::ResetOpId, "Reset Operator Id"); - (void)m.def("init_hccl", &mindspore::pipeline::InitHccl, "Init Hccl"); - (void)m.def("finalize_hccl", &mindspore::pipeline::FinalizeHccl, "Finalize Hccl"); - (void)m.def("get_hccl_rank_id", &mindspore::pipeline::GetHcclRankId, "Get Hccl Rank Id"); - (void)m.def("get_hccl_rank_size", &mindspore::pipeline::GetHcclRankSize, "Get Hccl Rank Size"); - (void)m.def("verify_inputs_signature", &mindspore::pipeline::VerifyInputSignature, "Verify input signature."); - (void)m.def("init_exec_dataset", &mindspore::pipeline::InitExecDataset, py::arg("queue_name"), py::arg("size"), - py::arg("batch_size"), py::arg("types"), py::arg("shapes"), py::arg("input_indexs"), - py::arg("phase") = py::str("dataset"), py::arg("need_run") = py::bool_(true), "Init and exec dataset."); - (void)m.def("_set_dataset_mode_config", &mindspore::ConfigManager::SetDatasetModeConfig, "API for set dataset mode."); - (void)m.def("init_pipeline", &mindspore::pipeline::InitPipeline, "Init Pipeline."); + .def("set_jit_config", &GraphExecutorPy::SetJitConfig, py::arg("jit_config") = py::dict(), "Set the jit config.") // Define a Python binding for the "set_jit_config" method of the GraphExecutorPy class, which takes a dictionary as an argument and sets the jit config. The default argument is an empty dictionary. This method is used to set the jit config. - (void)m.def("export_graph", &mindspore::pipeline::ExportGraph, "Export Graph."); - (void)m.def("load_mindir", &mindspore::pipeline::LoadMindIR, py::arg("file_name"), py::arg("dec_key") = nullptr, - py::arg("key_len") = py::int_(0), py::arg("dec_mode") = py::str("AES-GCM"), "Load model as Graph."); + .def("generate_arguments_key", &GraphExecutorPy::GenerateArgumentsKey, "Generate unique key of argument."); // Define a Python binding for the "generate_arguments_key" method of the GraphExecutorPy class, which takes no arguments and generates a unique key of argument. This method is used to generate a unique key of argument. - (void)py::class_>(m, "MpiConfig") +// Define a function "real_run_op" that takes a pointer to the "RealRunOp" function from the "mindspore::pynative" namespace +// and binds it to the name "real_run_op" in the Python module. The function is used to run operations in the Python native mode. +(void)m.def("real_run_op", &mindspore::pynative::RealRunOp, "Run op pynatively."); + +// Define a function "reset_op_id" that takes a pointer to the "ResetOpId" function from the "mindspore::pipeline" namespace +// and binds it to the name "reset_op_id" in the Python module. The function is used to reset the operator ID. +(void)m.def("reset_op_id", &mindspore::pipeline::ResetOpId, "Reset Operator Id"); + +// Define a function "init_hccl" that takes a pointer to the "InitHccl" function from the "mindspore::pipeline" namespace +// and binds it to the name "init_hccl" in the Python module. The function is used to initialize HCCL (Huawei Collective Communication Library). +(void)m.def("init_hccl", &mindspore::pipeline::InitHccl, "Init Hccl"); + +// Define a function "finalize_hccl" that takes a pointer to the "FinalizeHccl" function from the "mindspore::pipeline" namespace +// and binds it to the name "finalize_hccl" in the Python module. The function is used to finalize HCCL. +(void)m.def("finalize_hccl", &mindspore::pipeline::FinalizeHccl, "Finalize Hccl"); + +// Define a function "get_hccl_rank_id" that takes a pointer to the "GetHcclRankId" function from the "mindspore::pipeline" namespace +// and binds it to the name "get_hccl_rank_id" in the Python module. The function is used to get the HCCL rank ID. +(void)m.def("get_hccl_rank_id", &mindspore::pipeline::GetHcclRankId, "Get Hccl Rank Id"); + +// Define a function "get_hccl_rank_size" that takes a pointer to the "GetHcclRankSize" function from the "mindspore::pipeline" namespace +// and binds it to the name "get_hccl_rank_size" in the Python module. The function is used to get the HCCL rank size. +(void)m.def("get_hccl_rank_size", &mindspore::pipeline::GetHcclRankSize, "Get Hccl Rank Size"); + +// Define a function "verify_inputs_signature" that takes a pointer to the "VerifyInputSignature" function from the "mindspore::pipeline" namespace +// and binds it to the name "verify_inputs_signature" in the Python module. The function is used to verify the input signature. +(void)m.def("verify_inputs_signature", &mindspore::pipeline::VerifyInputSignature, "Verify input signature."); + +// Define a function "init_exec_dataset" that takes a pointer to the "InitExecDataset" function from the "mindspore::pipeline" namespace +// and binds it to the name "init_exec_dataset" in the Python module. The function is used to initialize and execute the dataset. +(void)m.def("init_exec_dataset", &mindspore::pipeline::InitExecDataset, py::arg("queue_name"), py::arg("size"), + py::arg("batch_size"), py::arg("types"), py::arg("shapes"), py::arg("input_indexs"), + py::arg("phase") = py::str("dataset"), py::arg("need_run") = py::bool_(true), "Init and exec dataset."); + +// Define a function "_set_dataset_mode_config" that takes a pointer to the "SetDatasetModeConfig" function from the "mindspore::ConfigManager" namespace +// and binds it to the name "_set_dataset_mode_config" in the Python module. The function is used to set the dataset mode configuration. +(void)m.def("_set_dataset_mode_config", &mindspore::ConfigManager::SetDatasetModeConfig, "API for set dataset mode."); + +// Define a function "init_pipeline" that takes a pointer to the "InitPipeline" function from the "mindspore::pipeline" namespace +// and binds it to the name "init_pipeline" in the Python module. The function is used to initialize the pipeline. +(void)m.def("init_pipeline", &mindspore::pipeline::InitPipeline, "Init Pipeline."); + +// Define a function binding for the "export_graph" function from the "mindspore::pipeline" namespace +(void)m.def("export_graph", &mindspore::pipeline::ExportGraph, "Export Graph."); + +// Define a function binding for the "load_mindir" function from the "mindspore::pipeline" namespace +// The function takes in multiple arguments, including "file_name", "dec_key", "key_len", and "dec_mode" +// The "dec_key" argument has a default value of nullptr +// The "key_len" argument has a default value of 0 +// The "dec_mode" argument has a default value of "AES-GCM" +// The function is used to load a model as a Graph +(void)m.def("load_mindir", &mindspore::pipeline::LoadMindIR, py::arg("file_name"), py::arg("dec_key") = nullptr, + py::arg("key_len") = py::int_(0), py::arg("dec_mode") = py::str("AES-GCM"), "Load model as Graph."); + +// Define a Python binding for the C++ class "MpiConfig" using pybind11 library +(void)py::class_>(m, "MpiConfig") + + // Define a static member function "get_instance" that calls the corresponding C++ function "GetInstance" .def_static("get_instance", &mindspore::MpiConfig::GetInstance, "Get mpi config instance.") + + // Define a member function "get_enable_mpi" that calls the corresponding C++ function "enable_mpi" .def("get_enable_mpi", &mindspore::MpiConfig::enable_mpi, "Get whether enable mpi.") + + // Define a member function "set_enable_mpi" that calls the corresponding C++ function "set_enable_mpi" .def("set_enable_mpi", &mindspore::MpiConfig::set_enable_mpi, "Set whether to enable mpi."); - (void)py::class_>(m, "AutoParallelContext") +// Define a Python module named "AutoParallelContext" using pybind11 +(void)py::class_>(m, "AutoParallelContext") + + // Define a static method "get_instance" that calls the "GetInstance" method of ParallelContext .def_static("get_instance", &ParallelContext::GetInstance, "Get auto parallel context instance.") + + // Define a method "get_device_num" that calls the "device_num" method of ParallelContext .def("get_device_num", &ParallelContext::device_num, "Get device num.") + + // Define a method "set_hccl_test_avaible" that calls the "set_hccl_test_available" method of ParallelContext .def("set_hccl_test_avaible", &ParallelContext::set_hccl_test_available, "Set hccl test available.") + + // Define a method "set_device_num" that calls the "set_device_num" method of ParallelContext .def("set_device_num", &ParallelContext::set_device_num, "Set device num.") + + // Define a method "get_device_num_is_set" that calls the "device_num_is_set" method of ParallelContext .def("get_device_num_is_set", &ParallelContext::device_num_is_set, "Get device num is set.") + + // Define a method "set_fusion_threshold_mb" that calls the "set_fusion_threshold_mb" method of ParallelContext .def("set_fusion_threshold_mb", &ParallelContext::set_fusion_threshold_mb, "Set fusion threshold.") - .def("set_allgather_fusion_threshold_mb", &ParallelContext::set_allgather_fusion_threshold_mb, - "Set allgather fusion threshold.") - .def("set_reducescatter_fusion_threshold_mb", &ParallelContext::set_reducescatter_fusion_threshold_mb, - "Set reducescatter fusion threshold.") + + // Define a method "set_allgather_fusion_threshold_mb" that calls the "set_allgather_fusion_threshold_mb" method of ParallelContext + .def("set_allgather_fusion_threshold_mb", &ParallelContext::set_allgather_fusion_threshold_mb, "Set allgather fusion threshold.") + + // Define a method "set_reducescatter_fusion_threshold_mb" that calls the "set_reducescatter_fusion_threshold_mb" method of ParallelContext + .def("set_reducescatter_fusion_threshold_mb", &ParallelContext::set_reducescatter_fusion_threshold_mb, "Set reducescatter fusion threshold.") + + // Define a method "fusion_threshold_mb" that calls the "fusion_threshold_mb" method of ParallelContext .def("fusion_threshold_mb", &ParallelContext::fusion_threshold_mb, "Get allreduce fusion threshold.") - .def("allgather_fusion_threshold_mb", &ParallelContext::allgather_fusion_threshold_mb, - "Get allgather fusion threshold.") - .def("reducescatter_fusion_threshold_mb", &ParallelContext::reducescatter_fusion_threshold_mb, - "Get reduce_scatter fusion threshold.") + + // Define a method "allgather_fusion_threshold_mb" that calls the "allgather_fusion_threshold_mb" method of ParallelContext + .def("allgather_fusion_threshold_mb", &ParallelContext::allgather_fusion_threshold_mb, "Get allgather fusion threshold.") + + // Define a method "reducescatter_fusion_threshold_mb" that calls the "reducescatter_fusion_threshold_mb" method of ParallelContext + .def("reducescatter_fusion_threshold_mb", &ParallelContext::reducescatter_fusion_threshold_mb, "Get reduce_scatter fusion threshold.") + + // Define a method "set_fusion_mode" that calls the "set_fusion_mode" method of ParallelContext .def("set_fusion_mode", &ParallelContext::set_fusion_mode, "Get fusion mode.") + + // Define a method "get_fusion_mode" that calls the "get_fusion_mode" method of ParallelContext .def("get_fusion_mode", &ParallelContext::get_fusion_mode, "Get fusion mode.") + + // Define a method "get_global_rank" that calls the "global_rank" method of ParallelContext .def("get_global_rank", &ParallelContext::global_rank, "Get global rank.") + + // Define a method "set_global_rank" that calls the "set_global_rank" method of ParallelContext .def("set_global_rank", &ParallelContext::set_global_rank, "Set global rank.") + + // Define a method "get_grad_accumulation_shard" that calls the "grad_accumulation_shard" method of ParallelContext .def("get_grad_accumulation_shard", &ParallelContext::grad_accumulation_shard, "Get grad_accumulation_shard.") + + // Define a method "set_grad_accumulation_shard" that calls the "set_grad_accumulation_shard" method of ParallelContext .def("set_grad_accumulation_shard", &ParallelContext::set_grad_accumulation_shard, "Set grad_accumulation_shard.") + + // Define a method "get_parallel_optimizer_threshold" that calls the "get_parallel_optimizer_threshold" method of ParallelContext .def("get_parallel_optimizer_threshold", &ParallelContext::get_parallel_optimizer_threshold, "Get opt threshold.") + + // Define a method "set_parallel_optimizer_threshold" that calls the "set_parallel_optimizer_threshold" method of ParallelContext .def("set_parallel_optimizer_threshold", &ParallelContext::set_parallel_optimizer_threshold, "Set opt threshold.") + + // Define a method "get_global_rank_is_set" that calls the "global_rank_is_set" method of ParallelContext .def("get_global_rank_is_set", &ParallelContext::global_rank_is_set, "Get global rank is set.") + + // Define a method "get_gradients_mean" that calls the "gradients_mean" method of ParallelContext .def("get_gradients_mean", &ParallelContext::gradients_mean, "Get mirror mean.") + + // Define a method "set_gradients_mean" that calls the "set_gradients_mean" method of ParallelContext .def("set_gradients_mean", &ParallelContext::set_gradients_mean, "Set mirror mean.") + + // Define a method "get_gradient_fp32_sync" that calls the "gradient_fp32_sync" method of ParallelContext .def("get_gradient_fp32_sync", &ParallelContext::gradient_fp32_sync, "Get cast before mirror.") + + // Define a method "set_gradient_fp32_sync" that calls the "set_gradient_fp32_sync" method of ParallelContext .def("set_gradient_fp32_sync", &ParallelContext::set_gradient_fp32_sync, "Set cast before mirror.") + + // Define a method "get_loss_repeated_mean" that calls the "loss_repeated_mean" method of ParallelContext .def("get_loss_repeated_mean", &ParallelContext::loss_repeated_mean, "Get loss repeated mean.") + + // Define a method "set_loss_repeated_mean" that calls the "set_loss_repeated_mean" method of ParallelContext .def("set_loss_repeated_mean", &ParallelContext::set_loss_repeated_mean, "Set loss repeated mean.") + + // Define a method "get_parallel_mode" that calls the "parallel_mode" method of ParallelContext .def("get_parallel_mode", &ParallelContext::parallel_mode, "Get parallel mode.") + + // Define a method "set_parallel_mode" that calls the "set_parallel_mode" method of ParallelContext .def("set_parallel_mode", &ParallelContext::set_parallel_mode, "Set parallel mode.") + + // Define a method "get_grad_accumulation_step" that calls the "grad_accumulation_step" method of ParallelContext .def("get_grad_accumulation_step", &ParallelContext::grad_accumulation_step, "Get grad accumulation step.") + + // Define a method "set_grad_accumulation_step" that calls the "set_grad_accumulation_step" method of ParallelContext .def("set_grad_accumulation_step", &ParallelContext::set_grad_accumulation_step, "Set grad accumulation step.") + + // Define a method "get_strategy_search_mode" that calls the "strategy_search_mode" method of ParallelContext .def("get_strategy_search_mode", &ParallelContext::strategy_search_mode, "Get strategy search mode.") + + // Define a method "set_strategy_search_mode" that calls the "set_strategy_search_mode" method of ParallelContext .def("set_strategy_search_mode", &ParallelContext::set_strategy_search_mode, "Set strategy search mode.") - .def("set_all_reduce_fusion_split_indices", &ParallelContext::SetAllReduceFusionSplitIndices, - "Set all reduce fusion split indices.") + + // Define a method "set_all_reduce_fusion_split_indices" that calls the "SetAllReduceFusionSplitIndices" method of ParallelContext + .def("set_all_reduce_fusion_split_indices", &ParallelContext::SetAllReduceFusionSplitIndices, "Set all reduce fusion split indices.") + + // Define a method "get_all_reduce_fusion_split_indices" that calls the "GetAllReduceFusionSplitIndices" method of ParallelContext + .def("get_all_reduce_fusion_split_indices", &ParallelContext::GetAllReduceFusionSplitIndices, +// Get all reduce fusion split indices. .def("get_all_reduce_fusion_split_indices", &ParallelContext::GetAllReduceFusionSplitIndices, "Get all reduce fusion split indices.") + + // Set all reduce fusion split sizes. .def("set_all_reduce_fusion_split_sizes", &ParallelContext::SetAllReduceFusionSplitSizes, "Set all reduce fusion split sizes.") + + // Get all reduce fusion split sizes. .def("get_all_reduce_fusion_split_sizes", &ParallelContext::GetAllReduceFusionSplitSizes, "Get all reduce fusion split sizes.") + + // Set enable/disable all reduce fusion. .def("set_enable_all_reduce_fusion", &ParallelContext::set_enable_all_reduce_fusion, "Set enable/disable all reduce fusion.") + + // Get enable/disable all reduce fusion. .def("get_enable_all_reduce_fusion", &ParallelContext::enable_all_reduce_fusion, "Get enable/disable all reduce fusion.") + + // Set enable/disable all gather fusion. .def("set_enable_all_gather_fusion", &ParallelContext::set_enable_all_gather_fusion, "Set enable/disable all gather fusion.") + + // Get enable/disable all gather fusion. .def("get_enable_all_gather_fusion", &ParallelContext::enable_all_gather_fusion, "Get enable/disable all gather fusion.") + + // Set enable/disable reduce scatter fusion. .def("set_enable_reduce_scatter_fusion", &ParallelContext::set_enable_reduce_scatter_fusion, "Set enable/disable reduce scatter fusion.") + + // Get enable/disable reduce scatter fusion. .def("get_enable_reduce_scatter_fusion", &ParallelContext::enable_reduce_scatter_fusion, "Get enable/disable reduce scatter fusion.") + + // Get parameter broadcast. .def("get_parameter_broadcast", &ParallelContext::parameter_broadcast, "Get parameter broadcast.") + + // Get parameter broadcast is set. .def("get_parameter_broadcast_is_set", &ParallelContext::parameter_broadcast_is_set, "Get parameter broadcast is set.") + + // Set parameter broadcast. .def("set_parameter_broadcast", &ParallelContext::set_parameter_broadcast, "Set parameter broadcast.") + + // Set strategy checkpoint load file. .def("set_strategy_ckpt_load_file", &ParallelContext::set_strategy_ckpt_load_file, "Set strategy checkpoint load file.") + + // Set strategy checkpoint save file. .def("set_strategy_ckpt_save_file", &ParallelContext::set_strategy_ckpt_save_file, "Set strategy checkpoint save file.") + + // Get strategy checkpoint load file. .def("get_strategy_ckpt_load_file", &ParallelContext::strategy_ckpt_load_file, "Get strategy checkpoint load file.") + + // Get strategy checkpoint save file. .def("get_strategy_ckpt_save_file", &ParallelContext::strategy_ckpt_save_file, "Get strategy checkpoint save file.") + + // Set group checkpoint save file. .def("set_group_ckpt_save_file", &ParallelContext::set_group_ckpt_save_file, "Set group checkpoint save file.") + + // Set pipeline stage split num. .def("set_pipeline_stage_split_num", &ParallelContext::set_pipeline_stage_split_num, "Set pipeline stage split num.") + + // Get pipeline stage split num. .def("get_pipeline_stage_split_num", &ParallelContext::pipeline_stage_split_num, "Get pipeline stage split num.") + + // Set whether load full batch on each device. .def("set_full_batch", &ParallelContext::set_full_batch, "Set whether load full batch on each device.") + + // Get whether load full batch on each device. .def("get_full_batch", &ParallelContext::full_batch, "Get whether load full batch on each device.") + + // Set dataset sharding strategy. .def("set_dataset_strategy", &ParallelContext::set_dataset_strategy, "Set dataset sharding strategy.") + + // Get dataset sharding strategy. .def("get_dataset_strategy", &ParallelContext::dataset_strategy, "Get dataset sharding strategy.") + + // Set enable/disable parallel optimizer. .def("set_enable_parallel_optimizer", &ParallelContext::set_enable_parallel_optimizer, "Set enable/disable parallel optimizer.") + + // Get enable/disable parallel optimizer. .def("get_enable_parallel_optimizer", &ParallelContext::enable_parallel_optimizer, "Get enable/disable parallel optimizer.") - .def("set_communi_parallel_mode", &ParallelContext::set_communi_parallel_mode, "Set communication parallel mode.") - .def("get_communi_parallel_mode", &ParallelContext::communi_parallel_mode, "Get communication parallel mode.") - .def("set_optimizer_weight_shard_size", &ParallelContext::set_optimizer_weight_shard_size, - "Set opt shard group size when not fully use parallel optimizer.") - .def("get_optimizer_weight_shard_size", &ParallelContext::optimizer_weight_shard_size, - "Get opt shard group size when not fully use parallel optimizer.") - .def("set_optimizer_weight_shard_aggregated_save", &ParallelContext::set_optimizer_weight_shard_aggregated_save, - "Set whether to integrated save weight shard when enable parallel optimizer.") - .def("get_optimizer_weight_shard_aggregated_save", &ParallelContext::optimizer_weight_shard_aggregated_save, - "Get whether to integrated save weight shard when enable parallel optimizer.") - .def("set_enable_alltoall", &ParallelContext::set_enable_all2all, "Set the enabling AllToAll value.") - .def("get_enable_alltoall", &ParallelContext::enable_all2all, "Get the enabling AllToAll value.") - .def("set_sharding_propagation", &ParallelContext::set_sharding_propagation, - "Set sharding strategy propagation value.") - .def("get_sharding_propagation", &ParallelContext::sharding_propagation, "Get sharding strategy propagation value.") - .def("reset", &ParallelContext::Reset, "Reset auto parallel context."); - (void)py::class_>(m, "CostModelContext") + // Set communication parallel mode. + .def("set_communi_parallel_mode", &ParallelContext::set_communi_parallel_mode, "Set communication parallel mode.") +// Define a function binding for the method "get_communi_parallel_mode" of the class ParallelContext +.def("get_communi_parallel_mode", &ParallelContext::communi_parallel_mode, "Get communication parallel mode.") + +// Define a function binding for the method "set_optimizer_weight_shard_size" of the class ParallelContext +.def("set_optimizer_weight_shard_size", &ParallelContext::set_optimizer_weight_shard_size, + "Set opt shard group size when not fully use parallel optimizer.") + +// Define a function binding for the method "get_optimizer_weight_shard_size" of the class ParallelContext +.def("get_optimizer_weight_shard_size", &ParallelContext::optimizer_weight_shard_size, + "Get opt shard group size when not fully use parallel optimizer.") + +// Define a function binding for the method "set_optimizer_weight_shard_aggregated_save" of the class ParallelContext +.def("set_optimizer_weight_shard_aggregated_save", &ParallelContext::set_optimizer_weight_shard_aggregated_save, + "Set whether to integrated save weight shard when enable parallel optimizer.") + +// Define a function binding for the method "get_optimizer_weight_shard_aggregated_save" of the class ParallelContext +.def("get_optimizer_weight_shard_aggregated_save", &ParallelContext::optimizer_weight_shard_aggregated_save, + "Get whether to integrated save weight shard when enable parallel optimizer.") + +// Define a function binding for the method "set_enable_alltoall" of the class ParallelContext +.def("set_enable_alltoall", &ParallelContext::set_enable_all2all, "Set the enabling AllToAll value.") + +// Define a function binding for the method "get_enable_alltoall" of the class ParallelContext +.def("get_enable_alltoall", &ParallelContext::enable_all2all, "Get the enabling AllToAll value.") + +// Define a function binding for the method "set_sharding_propagation" of the class ParallelContext +.def("set_sharding_propagation", &ParallelContext::set_sharding_propagation, + "Set sharding strategy propagation value.") + +// Define a function binding for the method "get_sharding_propagation" of the class ParallelContext +.def("get_sharding_propagation", &ParallelContext::sharding_propagation, "Get sharding strategy propagation value.") + +// Define a function binding for the method "reset" of the class ParallelContext +.def("reset", &ParallelContext::Reset, "Reset auto parallel context."); + +(void)py::class_>(m, "CostModelContext") .def_static("get_instance", &CostModelContext::GetInstance, "Get cost_model context instance.") .def("set_device_memory_capacity", &CostModelContext::set_device_memory_capacity, "Set the capacity of device memory.") @@ -279,265 +554,542 @@ PYBIND11_MODULE(_c_expression, m) { .def("set_costmodel_allreduce_fusion_times", &CostModelContext::set_costmodel_allreduce_fusion_times, "Set the parameter gradient AllReduce times.") .def("get_costmodel_allreduce_fusion_times", &CostModelContext::costmodel_allreduce_fusion_times, - "Get the parameter gradient AllReduce times.") +// Define the function "set_costmodel_allreduce_fusion_tail_percent" which sets the parameter for the tail percent of gradient AllReduce fusion .def("set_costmodel_allreduce_fusion_tail_percent", &CostModelContext::set_costmodel_allreduce_fusion_tail_percent, "Set the parameter gradient AllReduce fusion tail percent.") + +// Define the function "get_costmodel_allreduce_fusion_tail_percent" which retrieves the parameter for the tail percent of gradient AllReduce fusion .def("get_costmodel_allreduce_fusion_tail_percent", &CostModelContext::costmodel_allreduce_fusion_tail_percent, "Get the parameter gradient AllReduce fusion tail percent.") + +// Define the function "set_costmodel_allreduce_fusion_tail_time" which sets the parameter for the tail time of gradient AllReduce fusion .def("set_costmodel_allreduce_fusion_tail_time", &CostModelContext::set_costmodel_allreduce_fusion_tail_time, "Set the parameter gradient AllReduce fusion tail time.") + +// Define the function "get_costmodel_allreduce_fusion_tail_time" which retrieves the parameter for the tail time of gradient AllReduce fusion .def("get_costmodel_allreduce_fusion_tail_time", &CostModelContext::costmodel_allreduce_fusion_tail_time, "Get the parameter gradient AllReduce fusion tail time.") + +// Define the function "set_costmodel_allreduce_fusion_allreduce_inherent_time" which sets the parameter for the inherent time of gradient AllReduce fusion .def("set_costmodel_allreduce_fusion_allreduce_inherent_time", &CostModelContext::set_costmodel_allreduce_fusion_allreduce_inherent_time, "Set the parameter gradient AllReduce fusion allreduce inherent time.") + +// Define the function "get_costmodel_allreduce_fusion_allreduce_inherent_time" which retrieves the parameter for the inherent time of gradient AllReduce fusion .def("get_costmodel_allreduce_fusion_allreduce_inherent_time", &CostModelContext::costmodel_allreduce_fusion_allreduce_inherent_time, "Get the parameter gradient AllReduce fusion allreduce inherent time.") + +// Define the function "set_costmodel_allreduce_fusion_allreduce_bandwidth" which sets the parameter for the bandwidth of gradient AllReduce fusion .def("set_costmodel_allreduce_fusion_allreduce_bandwidth", &CostModelContext::set_costmodel_allreduce_fusion_allreduce_bandwidth, "Set the parameter gradient AllReduce fusion allreduce bandwidth.") + +// Define the function "get_costmodel_allreduce_fusion_allreduce_bandwidth" which retrieves the parameter for the bandwidth of gradient AllReduce fusion .def("get_costmodel_allreduce_fusion_allreduce_bandwidth", &CostModelContext::costmodel_allreduce_fusion_allreduce_bandwidth, "Get the parameter gradient AllReduce fusion allreduce bandwidth.") + +// Define the function "set_costmodel_allreduce_fusion_computation_time_parameter" which sets the parameter for the computation time of gradient AllReduce fusion .def("set_costmodel_allreduce_fusion_computation_time_parameter", &CostModelContext::set_costmodel_allreduce_fusion_computation_time_parameter, "Set the parameter gradient AllReduce fusion computation time parameter.") + +// Define the function "get_costmodel_allreduce_fusion_computation_time_parameter" which retrieves the parameter for the computation time of gradient AllReduce fusion .def("get_costmodel_allreduce_fusion_computation_time_parameter", &CostModelContext::costmodel_allreduce_fusion_computation_time_parameter, "Get the parameter gradient AllReduce fusion computation time parameter.") + +// Define the function "set_tensor_slice_align_enable" which sets the parameter for tensor slice alignment in strategy generation .def("set_tensor_slice_align_enable", &CostModelContext::set_tensor_slice_alignment_enable, "Set the parameter tensor_slice_align_enable in strategy generation.") + +// Define the function "get_tensor_slice_align_enable" which retrieves the parameter for tensor slice alignment in strategy generation .def("get_tensor_slice_align_enable", &CostModelContext::tensor_slice_alignment_enable, "Get the parameter tensor_slice_align_enable in strategy generation.") + +// Define the function "set_tensor_slice_align_size" which sets the parameter for tensor slice size in strategy generation .def("set_tensor_slice_align_size", &CostModelContext::set_tensor_slice_alignment_size, "Set the parameter tensor_slice_size in strategy generation.") + +// Define the function "get_tensor_slice_align_size" which retrieves the parameter for tensor slice size in strategy generation .def("get_tensor_slice_align_size", &CostModelContext::tensor_slice_alignment_size, "Get the parameter tensor_slice_size in strategy generation.") + +// Define the function "set_fully_use_devices" which sets the parameter for fully using devices in the DP algorithm .def("set_fully_use_devices", &CostModelContext::set_fully_use_device, "Set the parameter fully_use_devices in the DP algorithm.") + +// Define the function "get_fully_use_devices" which retrieves the parameter for fully using devices in the DP algorithm .def("get_fully_use_devices", &CostModelContext::fully_use_device, "Get the parameter fully_use_devices in the DP algorithm.") - .def("set_elementwise_op_strategy_follow", &CostModelContext::set_elementwise_stra_follow, - "Set the parameter elementwise_op_strategy_follow in the DP algorithm.") - .def("get_elementwise_op_strategy_follow", &CostModelContext::elementwise_stra_follow, - "Get the parameter elementwise_op_strategy_follow in the DP algorithm.") - .def("set_dp_algo_enable_approxi", &CostModelContext::set_dp_algo_enable_approxi, - "Set the flag whether enabling approximation in the DP algorithm.") - .def("get_dp_algo_enable_approxi", &CostModelContext::dp_algo_enable_approxi, - "Get the flag whether enabling approximation in the DP algorithm.") - .def("set_dp_algo_approxi_epsilon", &CostModelContext::set_dp_algo_approxi_epsilon, - "Set the epsilon which is used in the approximation of DP algorithm.") - .def("get_dp_algo_approxi_epsilon", &CostModelContext::dp_algo_approxi_epsilon, - "Get the epsilon which is used in the approximation of DP algorithm.") - .def("set_dp_algo_single_loop", &CostModelContext::set_dp_algo_single_loop, - "Set the flag of generating a single suite of OperatorInfos in for-loop.") - .def("get_dp_algo_single_loop", &CostModelContext::dp_algo_single_loop, - "Get the flag of whether or not generating a single suite of OperatorInfos in for-loop.") - .def("reset_cost_model", &CostModelContext::ResetCostModel, "Reset the CostModelContext.") - .def("reset_algo_parameters", &CostModelContext::ResetAlgoParameters, "Reset the AlgoParameters."); - (void)py::module::import("atexit").attr("register")(py::cpp_function{[&]() -> void { +// Define the function "set_elementwise_op_strategy_follow" which sets the parameter for elementwise operation strategy following + .def("set_elementwise_op_strategy_follow", &CostModelContext::set_elementwise_stra_follow, +// Set the parameter elementwise_op_strategy_follow in the DP algorithm. +.def("set_elementwise_op_strategy_follow", &CostModelContext::set_elementwise_stra_follow, + "Set the parameter elementwise_op_strategy_follow in the DP algorithm.") + +// Get the parameter elementwise_op_strategy_follow in the DP algorithm. +.def("get_elementwise_op_strategy_follow", &CostModelContext::elementwise_stra_follow, + "Get the parameter elementwise_op_strategy_follow in the DP algorithm.") + +// Set the flag whether enabling approximation in the DP algorithm. +.def("set_dp_algo_enable_approxi", &CostModelContext::set_dp_algo_enable_approxi, + "Set the flag whether enabling approximation in the DP algorithm.") + +// Get the flag whether enabling approximation in the DP algorithm. +.def("get_dp_algo_enable_approxi", &CostModelContext::dp_algo_enable_approxi, + "Get the flag whether enabling approximation in the DP algorithm.") + +// Set the epsilon which is used in the approximation of DP algorithm. +.def("set_dp_algo_approxi_epsilon", &CostModelContext::set_dp_algo_approxi_epsilon, + "Set the epsilon which is used in the approximation of DP algorithm.") + +// Get the epsilon which is used in the approximation of DP algorithm. +.def("get_dp_algo_approxi_epsilon", &CostModelContext::dp_algo_approxi_epsilon, + "Get the epsilon which is used in the approximation of DP algorithm.") + +// Set the flag of generating a single suite of OperatorInfos in for-loop. +.def("set_dp_algo_single_loop", &CostModelContext::set_dp_algo_single_loop, + "Set the flag of generating a single suite of OperatorInfos in for-loop.") + +// Get the flag of whether or not generating a single suite of OperatorInfos in for-loop. +.def("get_dp_algo_single_loop", &CostModelContext::dp_algo_single_loop, + "Get the flag of whether or not generating a single suite of OperatorInfos in for-loop.") + +// Reset the CostModelContext. +.def("reset_cost_model", &CostModelContext::ResetCostModel, "Reset the CostModelContext.") + +// Reset the AlgoParameters. +.def("reset_algo_parameters", &CostModelContext::ResetAlgoParameters, "Reset the AlgoParameters."); + +(void)py::module::import("atexit").attr("register")(py::cpp_function{[&]() -> void { + // Check if the ENABLE_MINDDATA macro is defined #ifdef ENABLE_MINDDATA + // Log an informational message indicating the start of releasing dataset handles MS_LOG(INFO) << "Start releasing dataset handles..."; + + // Import the mindspore.dataset.engine.iterators module py::module iterators = py::module::import("mindspore.dataset.engine.iterators"); + + // Call the _cleanup function from the iterators module to release dataset handles (void)iterators.attr("_cleanup")(); + + // Log an informational message indicating the end of releasing dataset handles MS_LOG(INFO) << "End release dataset handles."; #endif - // only in case that c++ calling python interface, ClearResAtexit should be called. + + // Check if the current environment is a Python environment if (mindspore::python_adapter::IsPythonEnv()) { + // Call the ClearResAtexit function from the mindspore.pipeline namespace to clear resources at exit mindspore::pipeline::ClearResAtexit(); } - }}); +}}); + +// This code is using preprocessor directives to conditionally compile a block of code based on the presence of the ENABLE_SECURITY macro. #ifndef ENABLE_SECURITY + // If ENABLE_SECURITY macro is not defined, define a Python binding for the EventWriter class + // using the pybind11 library. + // The class is named "EventWriter_" to avoid conflicts with any existing bindings for EventWriter. (void)py::class_>(m, "EventWriter_") - .def(py::init()) - .def("GetFileName", &EventWriter::GetFileName, "Get the file name.") - .def("Open", &EventWriter::Open, "Open the write file.") - .def("Write", &EventWriter::Write, "Write the serialize event.") - .def("EventCount", &EventWriter::GetWriteEventCount, "Write event count.") - .def("Flush", &EventWriter::Flush, "Flush the event.") - .def("Close", &EventWriter::Close, "Close the write.") - .def("Shut", &EventWriter::Shut, "Final close the write."); + .def(py::init()) // Define the constructor that takes a string argument + .def("GetFileName", &EventWriter::GetFileName, "Get the file name.") // Define the GetFileName method + .def("Open", &EventWriter::Open, "Open the write file.") // Define the Open method + .def("Write", &EventWriter::Write, "Write the serialize event.") // Define the Write method + .def("EventCount", &EventWriter::GetWriteEventCount, "Write event count.") // Define the EventCount method + .def("Flush", &EventWriter::Flush, "Flush the event.") // Define the Flush method + .def("Close", &EventWriter::Close, "Close the write.") // Define the Close method + .def("Shut", &EventWriter::Shut, "Final close the write."); // Define the Shut method #endif // ENABLE_SECURITY - (void)py::class_>(m, "Oplib") - .def(py::init()) - .def_static("reg_op", &OpLib::RegOp, "Register op info."); +// The #ifndef directive checks if the ENABLE_SECURITY macro is not defined. +// If it is not defined, the code block between #ifndef and #endif will be included in the compilation. +// The code block defines Python bindings for the EventWriter class using pybind11 library. +// Each method of the EventWriter class is defined using the .def() function of pybind11. +// The method names, function pointers, and docstrings are provided as arguments to the .def() function. +// The (void) before the py::class_ is used to suppress any unused variable warnings. +// The #endif directive marks the end of the conditional compilation block. + +// Define a Python module named "Oplib" using pybind11 library +(void)py::class_>(m, "Oplib") + .def(py::init()) // Define the constructor for the OpLib class + .def_static("reg_op", &OpLib::RegOp, "Register op info."); // Define a static method "reg_op" for registering op info + #ifdef ENABLE_GPU_COLLECTIVE - (void)m.def("init_gpu_collective", &mindspore::device::gpu::CollectiveInitializer::InitCollective, - "Init gpu collective communication mode."); - (void)m.def("finalize_gpu_collective", &mindspore::device::gpu::CollectiveInitializer::FinalizeCollective, - "Finalize gpu collective communication mode."); - (void)m.def("get_rank_id", &mindspore::device::gpu::CollectiveInitializer::GetRankID, - "Finalize gpu collective communication mode."); - (void)m.def("get_rank_size", &mindspore::device::gpu::CollectiveInitializer::GetRankSize, - "Finalize gpu collective communication mode."); +// If ENABLE_GPU_COLLECTIVE is defined, define the following functions using the gpu::CollectiveInitializer class +(void)m.def("init_gpu_collective", &mindspore::device::gpu::CollectiveInitializer::InitCollective, + "Init gpu collective communication mode."); +(void)m.def("finalize_gpu_collective", &mindspore::device::gpu::CollectiveInitializer::FinalizeCollective, + "Finalize gpu collective communication mode."); +(void)m.def("get_rank_id", &mindspore::device::gpu::CollectiveInitializer::GetRankID, + "Finalize gpu collective communication mode."); +(void)m.def("get_rank_size", &mindspore::device::gpu::CollectiveInitializer::GetRankSize, + "Finalize gpu collective communication mode."); #else - (void)m.def("init_gpu_collective", &mindspore::device::gpu::CollectiveFakeInitializer::InitCollective, - "Init gpu collective communication mode."); - (void)m.def("finalize_gpu_collective", &mindspore::device::gpu::CollectiveFakeInitializer::FinalizeCollective, - "Finalize gpu collective communication mode."); - (void)m.def("get_rank_id", &mindspore::device::gpu::CollectiveFakeInitializer::GetRankID, - "Finalize gpu collective communication mode."); - (void)m.def("get_rank_size", &mindspore::device::gpu::CollectiveFakeInitializer::GetRankSize, - "Finalize gpu collective communication mode."); +// If ENABLE_GPU_COLLECTIVE is not defined, define the following functions using the gpu::CollectiveFakeInitializer class +(void)m.def("init_gpu_collective", &mindspore::device::gpu::CollectiveFakeInitializer::InitCollective, + "Init gpu collective communication mode."); +(void)m.def("finalize_gpu_collective", &mindspore::device::gpu::CollectiveFakeInitializer::FinalizeCollective, + "Finalize gpu collective communication mode."); +(void)m.def("get_rank_id", &mindspore::device::gpu::CollectiveFakeInitializer::GetRankID, + "Finalize gpu collective communication mode."); +(void)m.def("get_rank_size", &mindspore::device::gpu::CollectiveFakeInitializer::GetRankSize, + "Finalize gpu collective communication mode."); #endif - (void)py::class_>(m, "PSContext") +// Define a Python binding for the C++ class PSContext using pybind11 library +(void)py::class_>(m, "PSContext") + + // Define a static member function get_instance that returns the PSContext instance .def_static("get_instance", &PSContext::instance, "Get PS context instance.") + + // Define a member function set_ps_enable that sets the PS mode enabled or disabled .def("set_ps_enable", &PSContext::SetPSEnable, "Set PS mode enabled or disabled.") + + // Define a member function is_ps_mode that returns the PS mode enable-disable status .def("is_ps_mode", &PSContext::is_ps_mode, "Get PS mode enable-disable status.") + + // Define a member function reset that resets the PS context attributes .def("reset", &PSContext::Reset, "Reset PS context attributes.") + + // Define a member function is_worker that returns whether the role of this process is Worker .def("is_worker", &PSContext::is_worker, "Get whether the role of this process is Worker.") + + // Define a member function is_server that returns whether the role of this process is PServer .def("is_server", &PSContext::is_server, "Get whether the role of this process is PServer.") + + // Define a member function is_scheduler that returns whether the role of this process is Scheduler .def("is_scheduler", &PSContext::is_scheduler, "Get whether the role of this process is Scheduler.") + + // Define a member function ps_rank_id that returns the Worker and PServer rank id .def("ps_rank_id", &PSContext::ps_rank_id, "Get Worker and PServer rank id.") + + // Define a member function insert_hash_table_size that inserts hash table size .def("insert_hash_table_size", &PSContext::InsertHashTableSize, "Insert hash table size.") + + // Define a member function reinsert_hash_table_size that inserts hash table size with new parameter name .def("reinsert_hash_table_size", &PSContext::ReInsertHashTableSize, "Insert hash table size with new parameter name.") + + // Define a member function insert_weight_init_info that inserts embedding table initialization seed .def("insert_weight_init_info", &PSContext::InsertWeightInitInfo, "Insert embedding table initialization seed.") + + // Define a member function insert_accumu_init_info that inserts accumulation initialization value .def("insert_accumu_init_info", &PSContext::InsertAccumuInitInfo, "Insert accumulation initialization value.") + + // Define a member function clone_hash_table that clones a hash table .def("clone_hash_table", &PSContext::CloneHashTable, "Clone a hash table.") + + // Define a member function set_cache_enable that sets ps mode cache enable or not .def("set_cache_enable", &PSContext::set_cache_enable, "Set ps mode cache enable or not.") + + // Define a member function cache_enable that returns ps mode cache enable or not .def("cache_enable", &PSContext::cache_enable, "Get ps mode cache enable or not.") + + // Define a member function set_rank_id that sets rank id for worker on ps mode .def("set_rank_id", &PSContext::set_rank_id, "Set rank id for worker on ps mode.") + + // Define a member function set_server_mode that sets server mode .def("set_server_mode", &PSContext::set_server_mode, "Set server mode.") + + // Define a member function server_mode that returns server mode .def("server_mode", &PSContext::server_mode, "Get server mode.") + + // Define a member function set_ms_role that sets role for this process .def("set_ms_role", &PSContext::set_ms_role, "Set role for this process.") + + // Define a member function ms_role that returns role for this process .def("ms_role", &PSContext::ms_role, "Get role for this process.") + + // Define a member function set_worker_num that sets worker number .def("set_worker_num", &PSContext::set_worker_num, "Set worker number.") + + // Define a member function worker_num that returns worker number .def("worker_num", &PSContext::worker_num, "Get worker number.") + + // Define a member function set_server_num that sets server number .def("set_server_num", &PSContext::set_server_num, "Set server number.") + + // Define a member function server_num that returns server number .def("server_num", &PSContext::server_num, "Get server number.") + + // Define a member function set_scheduler_ip that sets scheduler ip .def("set_scheduler_ip", &PSContext::set_scheduler_ip, "Set scheduler ip.") + + // Define a member function scheduler_ip that returns scheduler ip .def("scheduler_ip", &PSContext::scheduler_ip, "Get scheduler ip.") + + // Define a member function set_scheduler_port that sets scheduler port .def("set_scheduler_port", &PSContext::set_scheduler_port, "Set scheduler port.") + + // Define a member function scheduler_port that returns scheduler port .def("scheduler_port", &PSContext::scheduler_port, "Get scheduler port.") + + // Define a member function set_fl_server_port that sets federated learning server port .def("set_fl_server_port", &PSContext::set_fl_server_port, "Set federated learning server port.") + + // Define a member function fl_server_port that returns federated learning server port .def("fl_server_port", &PSContext::fl_server_port, "Get federated learning server port.") + + // Define a member function set_fl_client_enable that sets federated learning client .def("set_fl_client_enable", &PSContext::set_fl_client_enable, "Set federated learning client.") + + // Define a member function fl_client_enable that returns federated learning client .def("fl_client_enable", &PSContext::fl_client_enable, "Get federated learning client.") + + // Define a member function set_start_fl_job_threshold that sets threshold count for startFLJob round .def("set_start_fl_job_threshold", &PSContext::set_start_fl_job_threshold, "Set threshold count for startFLJob round.") + + // Define a member function start_fl_job_threshold that returns threshold count for startFLJob round .def("start_fl_job_threshold", &PSContext::start_fl_job_threshold, "Get threshold count for startFLJob round.") + + // Define a member function set_start_fl_job_time_window that sets time window for startFLJob round .def("set_start_fl_job_time_window", &PSContext::set_start_fl_job_time_window, "Set time window for startFLJob round.") - .def("start_fl_job_time_window", &PSContext::start_fl_job_time_window, "Get time window for startFLJob round.") - .def("set_update_model_ratio", &PSContext::set_update_model_ratio, - "Set threshold count ratio for updateModel round.") - .def("update_model_ratio", &PSContext::update_model_ratio, "Get threshold count ratio for updateModel round.") - .def("set_update_model_time_window", &PSContext::set_update_model_time_window, - "Set time window for updateModel round.") - .def("update_model_time_window", &PSContext::update_model_time_window, "Get time window for updateModel round.") - .def("set_share_secrets_ratio", &PSContext::set_share_secrets_ratio, - "Set threshold count ratio for share secrets round.") - .def("share_secrets_ratio", &PSContext::share_secrets_ratio, "Get threshold count ratio for share secrets round.") - .def("set_cipher_time_window", &PSContext::set_cipher_time_window, "Set time window for each cipher round.") - .def("cipher_time_window", &PSContext::cipher_time_window, "Get time window for cipher rounds.") - .def("set_reconstruct_secrets_threshold", &PSContext::set_reconstruct_secrets_threshold, - "Set threshold count for reconstruct secrets round.") - .def("reconstruct_secrets_threshold", &PSContext::reconstruct_secrets_threshold, - "Get threshold count for reconstruct secrets round.") - .def("set_fl_name", &PSContext::set_fl_name, "Set federated learning name.") - .def("fl_name", &PSContext::fl_name, "Get federated learning name.") - .def("set_fl_iteration_num", &PSContext::set_fl_iteration_num, "Set federated learning iteration number.") - .def("fl_iteration_num", &PSContext::fl_iteration_num, "Get federated learning iteration number.") - .def("set_client_epoch_num", &PSContext::set_client_epoch_num, "Set federated learning client epoch number.") - .def("client_epoch_num", &PSContext::client_epoch_num, "Get federated learning client epoch number.") - .def("set_client_batch_size", &PSContext::set_client_batch_size, "Set federated learning client batch size.") - .def("client_batch_size", &PSContext::client_batch_size, "Get federated learning client batch size.") - .def("set_client_learning_rate", &PSContext::set_client_learning_rate, - "Set federated learning client learning rate.") - .def("client_learning_rate", &PSContext::client_learning_rate, - "Get worker's standalone training step number before communicating with server.") - .def("set_worker_step_num_per_iteration", &PSContext::set_worker_step_num_per_iteration, - "Set worker's standalone training step number before communicating with server..") - .def("worker_step_num_per_iteration", &PSContext::worker_step_num_per_iteration, - "Get federated learning client learning rate.") - .def("set_secure_aggregation", &PSContext::set_secure_aggregation, - "Set federated learning client using secure aggregation.") - .def("set_dp_eps", &PSContext::set_dp_eps, "Set dp epsilon for federated learning secure aggregation.") - .def("dp_eps", &PSContext::dp_eps, "Get dp epsilon for federated learning secure aggregation.") - .def("set_dp_delta", &PSContext::set_dp_delta, "Set dp delta for federated learning secure aggregation.") - .def("dp_delta", &PSContext::dp_delta, "Get dp delta for federated learning secure aggregation.") - .def("set_dp_norm_clip", &PSContext::set_dp_norm_clip, - "Set dp norm clip for federated learning secure aggregation.") - .def("dp_norm_clip", &PSContext::dp_norm_clip, "Get dp norm clip for federated learning secure aggregation.") - .def("set_encrypt_type", &PSContext::set_encrypt_type, - "Set encrypt type for federated learning secure aggregation.") - .def("encrypt_type", &PSContext::encrypt_type, "Get encrypt type for federated learning secure aggregation.") - .def("set_root_first_ca_path", &PSContext::set_root_first_ca_path, "Set root first ca path.") - .def("root_first_ca_path", &PSContext::root_first_ca_path, "Get root first ca path.") - .def("set_root_second_ca_path", &PSContext::set_root_second_ca_path, "Set root second ca path.") - .def("root_second_ca_path", &PSContext::root_second_ca_path, "Get root second ca path.") - .def("set_pki_verify", &PSContext::set_pki_verify, "Set pki verify.") - .def("pki_verify", &PSContext::pki_verify, "Get pki verify.") - .def("set_scheduler_manage_port", &PSContext::set_scheduler_manage_port, - "Set scheduler manage port used to scale out/in.") - .def("scheduler_manage_port", &PSContext::scheduler_manage_port, "Get scheduler manage port used to scale out/in.") - .def("set_equip_crl_path", &PSContext::set_equip_crl_path, "Set root second crl path.") - .def("set_replay_attack_time_diff", &PSContext::set_replay_attack_time_diff, "Set replay attack time diff.") - .def("equip_crl_path", &PSContext::equip_crl_path, "Get root second crl path.") - .def("replay_attack_time_diff", &PSContext::replay_attack_time_diff, "Get replay attack time diff.") - .def("set_enable_ssl", &PSContext::set_enable_ssl, "Set PS SSL mode enabled or disabled.") - .def("enable_ssl", &PSContext::enable_ssl, "Get PS SSL mode enabled or disabled.") - .def("set_client_password", &PSContext::set_client_password, "Set the client password to decode the p12 file.") - .def("client_password", &PSContext::client_password, "Get the client password to decode the p12 file.") - .def("set_server_password", &PSContext::set_server_password, "Set the server password to decode the p12 file.") - .def("server_password", &PSContext::server_password, "Get the server password to decode the p12 file.") - .def("set_config_file_path", &PSContext::set_config_file_path, - "Set configuration files required by the communication layer.") - .def("config_file_path", &PSContext::config_file_path, - "Get configuration files required by the communication layer.") - .def("set_encrypt_type", &PSContext::set_encrypt_type, - "Set encrypt type for federated learning secure aggregation.") - .def("set_sign_k", &PSContext::set_sign_k, "Set sign k for federated learning SignDS.") - .def("sign_k", &PSContext::sign_k, "Get sign k for federated learning SignDS.") - .def("set_sign_eps", &PSContext::set_sign_eps, "Set sign eps for federated learning SignDS.") - .def("sign_eps", &PSContext::sign_eps, "Get sign eps for federated learning SignDS.") - .def("set_sign_thr_ratio", &PSContext::set_sign_thr_ratio, "Set sign thr ratio for federated learning SignDS.") - .def("sign_thr_ratio", &PSContext::sign_thr_ratio, "Get sign thr ratio for federated learning SignDS.") - .def("set_sign_global_lr", &PSContext::set_sign_global_lr, "Set sign global lr for federated learning SignDS.") - .def("sign_global_lr", &PSContext::sign_global_lr, "Get sign global lr for federated learning SignDS.") - .def("set_sign_dim_out", &PSContext::set_sign_dim_out, "Set sign dim out for federated learning SignDS.") - .def("sign_dim_out", &PSContext::sign_dim_out, "Get sign dim out for federated learning SignDS.") - .def("set_http_url_prefix", &PSContext::set_http_url_prefix, "Set http url prefix for http communication.") - .def("http_url_prefix", &PSContext::http_url_prefix, "http url prefix for http communication.") - .def("set_global_iteration_time_window", &PSContext::set_global_iteration_time_window, - "Set global iteration time window.") - .def("global_iteration_time_window", &PSContext::global_iteration_time_window, "Get global iteration time window.") - .def("set_upload_compress_type", &PSContext::set_upload_compress_type, "Set upload compress type.") - .def("upload_compress_type", &PSContext::upload_compress_type, "Get upload compress type.") - .def("set_upload_sparse_rate", &PSContext::set_upload_sparse_rate, "Set upload sparse rate.") - .def("upload_sparse_rate", &PSContext::upload_sparse_rate, "Get upload sparse rate.") - .def("set_download_compress_type", &PSContext::set_download_compress_type, "Set download compress type.") - .def("download_compress_type", &PSContext::download_compress_type, "Get download compress type.") - .def("set_checkpoint_dir", &PSContext::set_checkpoint_dir, "Set server checkpoint directory.") - .def("checkpoint_dir", &PSContext::checkpoint_dir, "Server checkpoint directory."); - (void)m.def("_encrypt", &mindspore::pipeline::PyEncrypt, "Encrypt the data."); - (void)m.def("_decrypt", &mindspore::pipeline::PyDecrypt, "Decrypt the data."); - (void)m.def("_is_cipher_file", &mindspore::pipeline::PyIsCipherFile, "Determine whether the file is encrypted"); - (void)py::class_>(m, "RecoveryContext") + // Define a member function start_fl_job_time_window that returns time window for startFLJob round + .def("start_fl_job_time_window", &PSContext::start_fl_job_time_window, "Get time window for startFLJob round.") +// Define a function "set_update_model_ratio" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_update_model_ratio". This function sets the threshold count ratio for the updateModel round. + +// Define a function "update_model_ratio" that takes a pointer to a member function of the class PSContext and binds it to the Python function "update_model_ratio". This function gets the threshold count ratio for the updateModel round. + +// Define a function "set_update_model_time_window" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_update_model_time_window". This function sets the time window for the updateModel round. + +// Define a function "update_model_time_window" that takes a pointer to a member function of the class PSContext and binds it to the Python function "update_model_time_window". This function gets the time window for the updateModel round. + +// Define a function "set_share_secrets_ratio" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_share_secrets_ratio". This function sets the threshold count ratio for the share secrets round. + +// Define a function "share_secrets_ratio" that takes a pointer to a member function of the class PSContext and binds it to the Python function "share_secrets_ratio". This function gets the threshold count ratio for the share secrets round. + +// Define a function "set_cipher_time_window" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_cipher_time_window". This function sets the time window for each cipher round. + +// Define a function "cipher_time_window" that takes a pointer to a member function of the class PSContext and binds it to the Python function "cipher_time_window". This function gets the time window for cipher rounds. + +// Define a function "set_reconstruct_secrets_threshold" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_reconstruct_secrets_threshold". This function sets the threshold count for the reconstruct secrets round. + +// Define a function "reconstruct_secrets_threshold" that takes a pointer to a member function of the class PSContext and binds it to the Python function "reconstruct_secrets_threshold". This function gets the threshold count for the reconstruct secrets round. + +// Define a function "set_fl_name" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_fl_name". This function sets the federated learning name. + +// Define a function "fl_name" that takes a pointer to a member function of the class PSContext and binds it to the Python function "fl_name". This function gets the federated learning name. + +// Define a function "set_fl_iteration_num" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_fl_iteration_num". This function sets the federated learning iteration number. + +// Define a function "fl_iteration_num" that takes a pointer to a member function of the class PSContext and binds it to the Python function "fl_iteration_num". This function gets the federated learning iteration number. + +// Define a function "set_client_epoch_num" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_client_epoch_num". This function sets the federated learning client epoch number. + +// Define a function "client_epoch_num" that takes a pointer to a member function of the class PSContext and binds it to the Python function "client_epoch_num". This function gets the federated learning client epoch number. + +// Define a function "set_client_batch_size" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_client_batch_size". This function sets the federated learning client batch size. + +// Define a function "client_batch_size" that takes a pointer to a member function of the class PSContext and binds it to the Python function "client_batch_size". This function gets the federated learning client batch size. + +// Define a function "set_client_learning_rate" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_client_learning_rate". This function sets the federated learning client learning rate. + +// Define a function "client_learning_rate" that takes a pointer to a member function of the class PSContext and binds it to the Python function "client_learning_rate". This function gets the federated learning client learning rate. + +// Define a function "set_worker_step_num_per_iteration" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_worker_step_num_per_iteration". This function sets the worker's standalone training step number before communicating with the server. + +// Define a function "worker_step_num_per_iteration" that takes a pointer to a member function of the class PSContext and binds it to the Python function "worker_step_num_per_iteration". This function gets the worker's standalone training step number before communicating with the server. + +// Define a function "set_secure_aggregation" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_secure_aggregation". This function sets whether the federated learning client uses secure aggregation. + +// Define a function "set_dp_eps" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_dp_eps". This function sets the differential privacy epsilon for federated learning secure aggregation. + +// Define a function "dp_eps" that takes a pointer to a member function of the class PSContext and binds it to the Python function "dp_eps". This function gets the differential privacy epsilon for federated learning secure aggregation. + +// Define a function "set_dp_delta" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_dp_delta". This function sets the differential privacy delta for federated learning secure aggregation. + +// Define a function "dp_delta" that takes a pointer to a member function of the class PSContext and binds it to the Python function "dp_delta". This function gets the differential privacy delta for federated learning secure aggregation. + +// Define a function "set_dp_norm_clip" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_dp_norm_clip". This function sets the differential privacy norm clip for federated learning secure aggregation. + +// Define a function "dp_norm_clip" that takes a pointer to a member function of the class PSContext and binds it to the Python function "dp_norm_clip". This function gets the differential privacy norm clip for federated learning secure aggregation. +// Define a function "set_encrypt_type" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_encrypt_type". This function sets the encrypt type for federated learning secure aggregation. +.def("set_encrypt_type", &PSContext::set_encrypt_type, "Set encrypt type for federated learning secure aggregation.") + +// Define a function "encrypt_type" that takes a pointer to a member function of the class PSContext and binds it to the Python function "encrypt_type". This function gets the encrypt type for federated learning secure aggregation. +.def("encrypt_type", &PSContext::encrypt_type, "Get encrypt type for federated learning secure aggregation.") + +// Define a function "set_root_first_ca_path" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_root_first_ca_path". This function sets the root first ca path. +.def("set_root_first_ca_path", &PSContext::set_root_first_ca_path, "Set root first ca path.") + +// Define a function "root_first_ca_path" that takes a pointer to a member function of the class PSContext and binds it to the Python function "root_first_ca_path". This function gets the root first ca path. +.def("root_first_ca_path", &PSContext::root_first_ca_path, "Get root first ca path.") + +// Define a function "set_root_second_ca_path" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_root_second_ca_path". This function sets the root second ca path. +.def("set_root_second_ca_path", &PSContext::set_root_second_ca_path, "Set root second ca path.") + +// Define a function "root_second_ca_path" that takes a pointer to a member function of the class PSContext and binds it to the Python function "root_second_ca_path". This function gets the root second ca path. +.def("root_second_ca_path", &PSContext::root_second_ca_path, "Get root second ca path.") + +// Define a function "set_pki_verify" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_pki_verify". This function sets pki verify. +.def("set_pki_verify", &PSContext::set_pki_verify, "Set pki verify.") + +// Define a function "pki_verify" that takes a pointer to a member function of the class PSContext and binds it to the Python function "pki_verify". This function gets pki verify. +.def("pki_verify", &PSContext::pki_verify, "Get pki verify.") + +// Define a function "set_scheduler_manage_port" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_scheduler_manage_port". This function sets the scheduler manage port used to scale out/in. +.def("set_scheduler_manage_port", &PSContext::set_scheduler_manage_port, "Set scheduler manage port used to scale out/in.") + +// Define a function "scheduler_manage_port" that takes a pointer to a member function of the class PSContext and binds it to the Python function "scheduler_manage_port". This function gets the scheduler manage port used to scale out/in. +.def("scheduler_manage_port", &PSContext::scheduler_manage_port, "Get scheduler manage port used to scale out/in.") + +// Define a function "set_equip_crl_path" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_equip_crl_path". This function sets the root second crl path. +.def("set_equip_crl_path", &PSContext::set_equip_crl_path, "Set root second crl path.") + +// Define a function "set_replay_attack_time_diff" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_replay_attack_time_diff". This function sets the replay attack time diff. +.def("set_replay_attack_time_diff", &PSContext::set_replay_attack_time_diff, "Set replay attack time diff.") + +// Define a function "equip_crl_path" that takes a pointer to a member function of the class PSContext and binds it to the Python function "equip_crl_path". This function gets the root second crl path. +.def("equip_crl_path", &PSContext::equip_crl_path, "Get root second crl path.") + +// Define a function "replay_attack_time_diff" that takes a pointer to a member function of the class PSContext and binds it to the Python function "replay_attack_time_diff". This function gets the replay attack time diff. +.def("replay_attack_time_diff", &PSContext::replay_attack_time_diff, "Get replay attack time diff.") + +// Define a function "set_enable_ssl" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_enable_ssl". This function sets PS SSL mode enabled or disabled. +.def("set_enable_ssl", &PSContext::set_enable_ssl, "Set PS SSL mode enabled or disabled.") + +// Define a function "enable_ssl" that takes a pointer to a member function of the class PSContext and binds it to the Python function "enable_ssl". This function gets PS SSL mode enabled or disabled. +.def("enable_ssl", &PSContext::enable_ssl, "Get PS SSL mode enabled or disabled.") + +// Define a function "set_client_password" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_client_password". This function sets the client password to decode the p12 file. +.def("set_client_password", &PSContext::set_client_password, "Set the client password to decode the p12 file.") + +// Define a function "client_password" that takes a pointer to a member function of the class PSContext and binds it to the Python function "client_password". This function gets the client password to decode the p12 file. +.def("client_password", &PSContext::client_password, "Get the client password to decode the p12 file.") + +// Define a function "set_server_password" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_server_password". This function sets the server password to decode the p12 file. +.def("set_server_password", &PSContext::set_server_password, "Set the server password to decode the p12 file.") + +// Define a function "server_password" that takes a pointer to a member function of the class PSContext and binds it to the Python function "server_password". This function gets the server password to decode the p12 file. +.def("server_password", &PSContext::server_password, "Get the server password to decode the p12 file.") + +// Define a function "set_config_file_path" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_config_file_path". This function sets configuration files required by the communication layer. +.def("set_config_file_path", &PSContext::set_config_file_path, "Set configuration files required by the communication layer.") + +// Define a function "config_file_path" that takes a pointer to a member function of the class PSContext and binds it to the Python function "config_file_path". This function gets configuration files required by the communication layer. +.def("config_file_path", &PSContext::config_file_path, "Get configuration files required by the communication layer.") + +// Define a function "set_encrypt_type" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_encrypt_type". This function sets the encrypt type for federated learning secure aggregation. +.def("set_encrypt_type", &PSContext::set_encrypt_type, "Set encrypt type for federated learning secure aggregation.") + +// Define a function "set_sign_k" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_sign_k". This function sets sign k for federated learning SignDS. +.def("set_sign_k", &PSContext::set_sign_k, "Set sign k for federated learning SignDS.") + +// Define a function "sign_k" that takes a pointer to a member function of the class PSContext and binds it to the Python function "sign_k". This function gets sign k for federated learning SignDS. +.def("sign_k", &PSContext::sign_k, "Get sign k for federated learning SignDS.") + +// Define a function "set_sign_eps" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_sign_eps". This function sets sign eps for federated learning SignDS. +.def("set_sign_eps", &PSContext::set_sign_eps, "Set sign eps for federated learning SignDS.") + +// Define a function "sign_eps" that takes a pointer to a member function of the class PSContext and binds it to the Python function "sign_eps". This function gets sign eps for federated learning SignDS. +.def("sign_eps", &PSContext::sign_eps, "Get sign eps for federated learning SignDS.") + +// Define a function "set_sign_thr_ratio" that takes a pointer to a member function of the class PSContext and binds it to the Python function "set_sign_thr_ratio". This function sets sign thr ratio for federated learning SignDS. +.def("set_sign_thr_ratio", &PSContext::set_sign_thr_ratio, "Set sign thr ratio for federated learning SignDS.") + +// Define a function "sign_thr_ratio" that takes a pointer to a member function of the class PSContext and binds it to the Python function +// Define a function "set_global_iteration_time_window" that takes a reference to a member function of the class PSContext and binds it to a Python function with the same name +// The function sets the global iteration time window +.def("set_global_iteration_time_window", &PSContext::set_global_iteration_time_window, "Set global iteration time window.") + +// Define a function "global_iteration_time_window" that takes a reference to a member function of the class PSContext and binds it to a Python function with the same name +// The function returns the global iteration time window +.def("global_iteration_time_window", &PSContext::global_iteration_time_window, "Get global iteration time window.") + +// Define a function "set_upload_compress_type" that takes a reference to a member function of the class PSContext and binds it to a Python function with the same name +// The function sets the upload compress type +.def("set_upload_compress_type", &PSContext::set_upload_compress_type, "Set upload compress type.") + +// Define a function "upload_compress_type" that takes a reference to a member function of the class PSContext and binds it to a Python function with the same name +// The function returns the upload compress type +.def("upload_compress_type", &PSContext::upload_compress_type, "Get upload compress type.") + +// Define a function "set_upload_sparse_rate" that takes a reference to a member function of the class PSContext and binds it to a Python function with the same name +// The function sets the upload sparse rate +.def("set_upload_sparse_rate", &PSContext::set_upload_sparse_rate, "Set upload sparse rate.") + +// Define a function "upload_sparse_rate" that takes a reference to a member function of the class PSContext and binds it to a Python function with the same name +// The function returns the upload sparse rate +.def("upload_sparse_rate", &PSContext::upload_sparse_rate, "Get upload sparse rate.") + +// Define a function "set_download_compress_type" that takes a reference to a member function of the class PSContext and binds it to a Python function with the same name +// The function sets the download compress type +.def("set_download_compress_type", &PSContext::set_download_compress_type, "Set download compress type.") + +// Define a function "download_compress_type" that takes a reference to a member function of the class PSContext and binds it to a Python function with the same name +// The function returns the download compress type +.def("download_compress_type", &PSContext::download_compress_type, "Get download compress type.") + +// Define a function "set_checkpoint_dir" that takes a reference to a member function of the class PSContext and binds it to a Python function with the same name +// The function sets the server checkpoint directory +.def("set_checkpoint_dir", &PSContext::set_checkpoint_dir, "Set server checkpoint directory.") + +// Define a function "checkpoint_dir" that takes a reference to a member function of the class PSContext and binds it to a Python function with the same name +// The function returns the server checkpoint directory +.def("checkpoint_dir", &PSContext::checkpoint_dir, "Server checkpoint directory."); + +// Bind the C++ function "_encrypt" to a Python function with the same name +// The function encrypts the data +(void)m.def("_encrypt", &mindspore::pipeline::PyEncrypt, "Encrypt the data."); + +// Bind the C++ function "_decrypt" to a Python function with the same name +// The function decrypts the data +(void)m.def("_decrypt", &mindspore::pipeline::PyDecrypt, "Decrypt the data."); + +// Bind the C++ function "_is_cipher_file" to a Python function with the same name +// The function determines whether the file is encrypted +(void)m.def("_is_cipher_file", &mindspore::pipeline::PyIsCipherFile, "Determine whether the file is encrypted"); + +// Define a Python binding for the C++ class RecoveryContext using pybind11 library +(void)py::class_>(m, "RecoveryContext") + + // Define a static method get_instance that calls the C++ method RecoveryContext::GetInstance .def_static("get_instance", &RecoveryContext::GetInstance, "Get recovery context instance.") + + // Define a member function enable_recovery that calls the C++ method RecoveryContext::enable_recovery .def("enable_recovery", &RecoveryContext::enable_recovery, "Get whether enable recovery.") + + // Define a member function latest_ckpt_file that calls the C++ method RecoveryContext::latest_ckpt_file .def("latest_ckpt_file", &RecoveryContext::latest_ckpt_file, "Get latest checkpoint file path.") + + // Define a member function latest_ckpt_epoch that calls the C++ method RecoveryContext::latest_ckpt_epoch .def("latest_ckpt_epoch", &RecoveryContext::latest_ckpt_epoch, "Get the epoch of latest checkpoint.") + + // Define a member function latest_ckpt_step that calls the C++ method RecoveryContext::latest_ckpt_step .def("latest_ckpt_step", &RecoveryContext::latest_ckpt_step, "Get the step of latest checkpoint.") + + // Define a member function set_need_reset that calls the C++ method RecoveryContext::set_need_reset .def("set_need_reset", &RecoveryContext::set_need_reset, "Set whether should call reset minddata and load ckpt for disaster recovery.") + + // Define a member function need_reset that calls the C++ method RecoveryContext::need_reset .def("need_reset", &RecoveryContext::need_reset, "Get whether should call reset minddata and load ckpt for disaster recovery.") + + // Define a member function recovery_path that calls the C++ method RecoveryContext::recovery_path .def("recovery_path", &RecoveryContext::recovery_path, "Get the recovery path used to save that need to be persisted.") + + // Define a member function ckpt_path that calls the C++ method RecoveryContext::GetCkptPath .def("ckpt_path", &RecoveryContext::GetCkptPath, "Get the recovery path used to save checkpoint.") + + // Define a member function set_ckpt_path that calls the C++ method RecoveryContext::SetCkptPath .def("set_ckpt_path", &RecoveryContext::SetCkptPath, "Set the recovery path used to save checkpoint."); #ifndef _WIN32 + // Define a function binding for the "_export_bprop_mindir" function from the "mindspore::ad::KPrim" class + // The function takes no arguments and returns void (void)m.def("_export_bprop_mindir", &mindspore::ad::KPrim::ExportBpropMindir, "Export the backpropagation function to mindir file."); #endif - (void)m.def("_ms_memory_recycle", &mindspore::pipeline::MemoryRecycle, "Recycle memory used by mindspore."); -} + +// Define a function binding for the "_ms_memory_recycle" function from the "mindspore::pipeline" namespace +// The function takes no arguments and returns void +(void)m.def("_ms_memory_recycle", &mindspore::pipeline::MemoryRecycle, "Recycle memory used by mindspore."); +} \ No newline at end of file diff --git a/mindspore/ccsrc/pipeline/jit/pass.cc b/mindspore/ccsrc/pipeline/jit/pass.cc index 4eddd948393..a6234d1a191 100644 --- a/mindspore/ccsrc/pipeline/jit/pass.cc +++ b/mindspore/ccsrc/pipeline/jit/pass.cc @@ -14,140 +14,275 @@ * limitations under the License. */ -#include "pipeline/jit/pass.h" +// Include the header file "pipeline/jit/pass.h" which contains declarations for pass-related functions and classes in the JIT pipeline. +// Include the memory header for smart pointers #include + +// Include the vector header for dynamic arrays #include + +// Include the string header for string manipulation #include + +// Include the algorithm header for various algorithms #include -#include "utils/hash_map.h" -#include "ir/func_graph_cloner.h" -#include "pipeline/jit/parse/parse_base.h" -#include "pipeline/jit/resource.h" -#include "pipeline/jit/validator.h" -#include "pipeline/jit/remove_value_node_dup.h" -#include "frontend/optimizer/opt.h" -#include "frontend/optimizer/optimizer.h" -#include "frontend/optimizer/cse_pass.h" -#include "frontend/optimizer/clean.h" -#include "frontend/optimizer/irpass.h" -#include "frontend/optimizer/graph_transform.h" -#include "frontend/optimizer/auto_monad_eliminate.h" -#include "include/common/utils/parallel_context.h" -#include "frontend/parallel/step_parallel.h" -#include "frontend/parallel/step_auto_parallel.h" -#include "frontend/parallel/cache_embedding/cache_embedding.h" -#include "frontend/parallel/allreduce_fusion/step_allreduce_fusion.h" -#include "frontend/optimizer/recompute.h" -#include "frontend/optimizer/slice_activation_in_recompute.h" -#include "frontend/optimizer/comm_op_attrs.h" -#include "frontend/optimizer/environ_conversion.h" -#include "utils/log_adapter.h" -#include "pipeline/jit/pipeline_split.h" -#include "pipeline/pynative/pynative_execute.h" -#include "pipeline/jit/static_analysis/auto_monad.h" -#include "frontend/optimizer/irpass/branch_culling.h" -#include "frontend/optimizer/irpass/meta_fg_eliminate.h" -#include "frontend/optimizer/irpass/ge_specialized_prepare.h" -#include "frontend/optimizer/irpass/gradient_eliminate.h" -#include "frontend/optimizer/irpass/shard_eliminate.h" -#include "frontend/optimizer/irpass/taylor_eliminate.h" -#include "frontend/optimizer/irpass/parameter_eliminate.h" -#include "frontend/optimizer/irpass/updatestate_eliminate.h" -#include "frontend/optimizer/irpass/expand_dump_flag.h" +// Include custom header files for various functionalities and modules + +#include "utils/hash_map.h" // Custom hash map implementation +#include "ir/func_graph_cloner.h" // Function graph cloner +#include "pipeline/jit/parse/parse_base.h" // Base parsing functionality +#include "pipeline/jit/resource.h" // Resource management for JIT pipeline +#include "pipeline/jit/validator.h" // Validation of JIT pipeline +#include "pipeline/jit/remove_value_node_dup.h" // Removal of duplicate value nodes +#include "frontend/optimizer/opt.h" // Optimization utilities +#include "frontend/optimizer/optimizer.h" // Optimizer for the frontend +#include "frontend/optimizer/cse_pass.h" // Common subexpression elimination pass +#include "frontend/optimizer/clean.h" // Cleaning up the graph +#include "frontend/optimizer/irpass.h" // IR pass utilities +#include "frontend/optimizer/graph_transform.h" // Graph transformation utilities +#include "frontend/optimizer/auto_monad_eliminate.h" // Elimination of automatic monad operations +#include "include/common/utils/parallel_context.h" // Parallel context utilities +#include "frontend/parallel/step_parallel.h" // Parallel execution step +#include "frontend/parallel/step_auto_parallel.h" // Automatic parallelization step +#include "frontend/parallel/cache_embedding/cache_embedding.h" // Cache embedding for parallel execution +#include "frontend/parallel/allreduce_fusion/step_allreduce_fusion.h" // Allreduce fusion step +#include "frontend/optimizer/recompute.h" // Recomputation optimization +#include "frontend/optimizer/slice_activation_in_recompute.h" // Slicing activation in recomputation +#include "frontend/optimizer/comm_op_attrs.h" // Communication operation attributes +#include "frontend/optimizer/environ_conversion.h" // Environment conversion optimization +#include "utils/log_adapter.h" // Logging adapter +#include "pipeline/jit/pipeline_split.h" // Splitting the pipeline +#include "pipeline/pynative/pynative_execute.h" // Execution in PyNative mode +#include "pipeline/jit/static_analysis/auto_monad.h" // Automatic monad analysis +#include "frontend/optimizer/irpass/branch_culling.h" // Branch culling IR pass +#include "frontend/optimizer/irpass/meta_fg_eliminate.h" // Meta function graph elimination IR pass +#include "frontend/optimizer/irpass/ge_specialized_prepare.h" // GE specialized preparation IR pass +#include "frontend/optimizer/irpass/gradient_eliminate.h" // Gradient elimination IR pass +#include "frontend/optimizer/irpass/shard_eliminate.h" // Shard elimination IR pass +#include "frontend/optimizer/irpass/taylor_eliminate.h" // Taylor elimination IR pass +#include "frontend/optimizer/irpass/parameter_eliminate.h" // Parameter elimination IR pass +#include "frontend/optimizer/irpass/updatestate_eliminate.h" // UpdateState elimination IR pass +#include "frontend/optimizer/irpass/expand_dump_flag.h" // Expansion of dump flag IR pass + +// Include additional header files for specific conditions #if ((defined ENABLE_CPU) && (!defined _WIN32)) -#include "ps/util.h" -#include "ps/ps_context.h" +#include "ps/util.h" // Utility functions for parameter server +#include "ps/ps_context.h" // Parameter server context #endif +// Define the namespace "mindspore" namespace mindspore { -namespace pipeline { -using OptPassGroupMap = opt::OptPassGroupMap; -using Optimizer = opt::Optimizer; -using CompileGraphs = compile::CompileGraphs; -using abstract::AnalysisResult; -using mindspore::abstract::AnalysisContextPtr; -using mindspore::validator::Validate; -namespace { -void UpdateArgsSpec(const FuncGraphPtr &func_graph, const ResourcePtr &res) { - MS_EXCEPTION_IF_NULL(func_graph); - MS_EXCEPTION_IF_NULL(res); - abstract::AbstractBasePtrList args_spec; - const auto ¶meters = func_graph->parameters(); - args_spec.reserve(parameters.size()); - (void)std::transform(parameters.begin(), parameters.end(), std::back_inserter(args_spec), - [](const AnfNodePtr &p) { return p->abstract(); }); - res->set_args_spec(args_spec); -} + + // Define the nested namespace "pipeline" + namespace pipeline { + + // Define an alias "OptPassGroupMap" for the type "opt::OptPassGroupMap" + using OptPassGroupMap = opt::OptPassGroupMap; + + // Define an alias "Optimizer" for the type "opt::Optimizer" + using Optimizer = opt::Optimizer; + + // Define an alias "CompileGraphs" for the type "compile::CompileGraphs" + using CompileGraphs = compile::CompileGraphs; + + // Define an alias "AnalysisResult" for the type "abstract::AnalysisResult" + using AnalysisResult = abstract::AnalysisResult; + + // Define an alias "AnalysisContextPtr" for the type "mindspore::abstract::AnalysisContextPtr" + using AnalysisContextPtr = mindspore::abstract::AnalysisContextPtr; + + // Define an alias "Validate" for the type "mindspore::validator::Validate" + using Validate = mindspore::validator::Validate; + + // Define an anonymous namespace for the following function + namespace { + + // Define a function "UpdateArgsSpec" that takes a "FuncGraphPtr" and a "ResourcePtr" as parameters + void UpdateArgsSpec(const FuncGraphPtr &func_graph, const ResourcePtr &res) { + // Check if the "func_graph" and "res" pointers are not null + MS_EXCEPTION_IF_NULL(func_graph); + MS_EXCEPTION_IF_NULL(res); + + // Create an empty list of "AbstractBasePtr" + abstract::AbstractBasePtrList args_spec; + + // Get the parameters of the "func_graph" + const auto ¶meters = func_graph->parameters(); + + // Reserve enough space in the "args_spec" list to avoid reallocations + args_spec.reserve(parameters.size()); + + // Transform each parameter in the "parameters" list to its corresponding abstract value and add it to the "args_spec" list + (void)std::transform(parameters.begin(), parameters.end(), std::back_inserter(args_spec), + [](const AnfNodePtr &p) { return p->abstract(); }); + + // Set the "args_spec" list as the arguments specification of the "res" resource + res->set_args_spec(args_spec); + } + + } // namespace + + } // namespace pipeline + } // namespace +// A function that simplifies data structures in a given resource bool SimplifyDataStructuresPass(const ResourcePtr &res) { + // Check if the resource is null, throw an exception if it is MS_EXCEPTION_IF_NULL(res); + + // Get the function graph from the resource FuncGraphPtr func_graph = res->func_graph(); + + // Check if the function graph is null, throw an exception if it is MS_EXCEPTION_IF_NULL(func_graph); + + // Call the SimplifyDataStructures function from the opt namespace, passing in the function graph and the resource manager + // The return value is ignored (void) (void)opt::SimplifyDataStructures(func_graph, res->manager()); + + // Call the UpdateArgsSpec function, passing in the function graph and the resource UpdateArgsSpec(func_graph, res); + + // Return true to indicate successful execution of the function return true; } +// A function that transforms the top graph of a resource bool TransformTopGraphPass(const ResourcePtr &res) { MS_EXCEPTION_IF_NULL(res); + + // Check if the function graph of the resource is null if (res->func_graph() == nullptr) { MS_LOG(EXCEPTION) << "Transform top graph error."; } + + // Get the function graph from the resource FuncGraphPtr func_graph = res->func_graph(); + + // Check if the function graph has tuple input if (opt::FuncGraphHasTupleInput(func_graph)) { + + // Create an instance of GraphTupleParamTransform opt::GraphTupleParamTransform graph_trans; + + // Transform the function graph using GraphTupleParamTransform func_graph = graph_trans(func_graph, res->manager()); + + // Set the transformed function graph back to the resource res->set_func_graph(func_graph); + + // Create a list to store the abstract base pointers AbstractBasePtrList abs_spec_list; + + // Get the parameters of the function graph auto ¶ms = func_graph->parameters(); + + // Transform each parameter into its abstract base pointer and store it in the list std::transform(params.begin(), params.end(), std::back_inserter(abs_spec_list), [](const AnfNodePtr &node) { return node->abstract(); }); + + // Set the list of abstract base pointers as the arguments specification of the resource res->set_args_spec(abs_spec_list); } + + // Return true to indicate successful transformation return true; } +// A function to clean up after running an optimization pass called "OptA" bool CleanAfterOptAPass(const ResourcePtr &res) { + // Check if the resource pointer is null, throw an exception if it is MS_EXCEPTION_IF_NULL(res); + + // Get the function graph from the resource pointer FuncGraphPtr func_graph = res->func_graph(); + + // Check if the function graph is null, throw an exception if it is MS_EXCEPTION_IF_NULL(func_graph); + + // Call the CleanAfterOptA function from the opt namespace, passing in the function graph and the resource manager + // The (void) is used to suppress any unused variable warnings (void)opt::CleanAfterOptA(func_graph, res->manager()); + + // Call the UpdateArgsSpec function, passing in the function graph and the resource UpdateArgsSpec(func_graph, res); + + // Return true to indicate that the clean-up was successful return true; } +// Define a function named "PrimBpOptPassStep1" that takes two parameters: "irpass" of type "const opt::irpass::OptimizeIRPassLib&" and "res" of type "const ResourcePtr&" FuncGraphPtr PrimBpOptPassStep1(const opt::irpass::OptimizeIRPassLib &irpass, const ResourcePtr &res) { + + // Check if the "res" pointer is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(res); + + // Check if the "func_graph" member of the "res" object is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(res->func_graph()); + + // Create an instance of "opt::OptPassConfig" named "pynative_eliminate" and initialize it with a single element, "irpass.pynative_eliminate_" opt::OptPassConfig pynative_eliminate = opt::OptPassConfig({ irpass.pynative_eliminate_, }); - opt::OptPassConfig switch_simplify = opt::OptPassConfig({ + // Return the "pynative_eliminate" object + return pynative_eliminate; +} + +// Create an instance of the OptPassConfig class named "switch_simplify" and initialize it with a list of passes +// The list contains a single pass, which is irpass.switch_simplify_ + +opt::OptPassConfig switch_simplify = opt::OptPassConfig({ irpass.switch_simplify_, - }); +}); - opt::OptPassConfig inline_opt = opt::OptPassConfig({ +// Create an instance of the OptPassConfig class named "inline_opt" and initialize it with a list of optimization passes +// The list contains a single pass named "irpass.inline_" + +opt::OptPassConfig inline_opt = opt::OptPassConfig({ irpass.inline_, - }); +}); - OptPassGroupMap map( - {{"ad_eliminate", pynative_eliminate}, {"ad_inline", inline_opt}, {"ad_switch_simplify", switch_simplify}}); +// Create an instance of the OptPassGroupMap class and initialize it with a list of pairs +// Each pair consists of a string key and a function pointer +OptPassGroupMap map( + { + {"ad_eliminate", pynative_eliminate}, // Key: "ad_eliminate", Value: pynative_eliminate function pointer + {"ad_inline", inline_opt}, // Key: "ad_inline", Value: inline_opt function pointer + {"ad_switch_simplify", switch_simplify} // Key: "ad_switch_simplify", Value: switch_simplify function pointer + } +); + // Create an instance of the optimizer "prim_bprop_opt_step_1" with the given parameters "res" and "map" auto prim_bprop_opt_step_1 = opt::Optimizer::MakeOptimizer("prim_bprop_opt_step_1", res, map); + + // Get the function graph from the result FuncGraphPtr func_graph = res->func_graph(); + + // Start a profiling step named "prim_bprop_opt_step_1" using the MsProfile::GetProfile() function WITH(MsProfile::GetProfile()->Step("prim_bprop_opt_step_1"))[&prim_bprop_opt_step_1, &func_graph]() { + + // Apply the "step" function of the optimizer to the function graph, with the "true" parameter indicating that it is a backward pass func_graph = prim_bprop_opt_step_1->step(func_graph, true); }; + + // Return the modified function graph return func_graph; } +// Define a function named "PrimBpOptPassStep2" that takes two parameters: "irpass" of type "const opt::irpass::OptimizeIRPassLib&" and "res" of type "const ResourcePtr&" FuncGraphPtr PrimBpOptPassStep2(const opt::irpass::OptimizeIRPassLib &irpass, const ResourcePtr &res) { + + // Check if the "res" pointer is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(res); + + // Check if the "func_graph" pointer inside "res" is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(res->func_graph()); + + // Create an instance of "OptPassConfig" named "special_op_simplify" and initialize it with a list of optimization passes opt::OptPassConfig special_op_simplify = opt::OptPassConfig({ irpass.switch_simplify_, irpass.reduce_eliminate_, @@ -155,33 +290,75 @@ FuncGraphPtr PrimBpOptPassStep2(const opt::irpass::OptimizeIRPassLib &irpass, co irpass.arithmetic_simplify_, }); - opt::OptPassConfig inline_opt = opt::OptPassConfig({ + // Return the function graph pointer + return special_op_simplify; +} + +// Create an instance of the OptPassConfig class named "inline_opt" and initialize it with a list of optimization passes +// The list contains a single pass named "irpass.inline_" + +opt::OptPassConfig inline_opt = opt::OptPassConfig({ irpass.inline_, - }); +}); - auto re_auto_monadwrapper = [](const FuncGraphPtr &root, const opt::OptimizerPtr &) -> bool { +// Define a lambda function named `re_auto_monadwrapper` that takes a `FuncGraphPtr` and an `opt::OptimizerPtr` as parameters and returns a boolean value +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)}}); +}; +// Create an `OptPassGroupMap` named `map` using initializer list syntax +OptPassGroupMap map({ + // Add an entry with key "ad_renormalize" and value `opt::OptPassConfig::Renormalize()` + {"ad_renormalize", opt::OptPassConfig::Renormalize()}, + // Add an entry with key "ad_inline" and value `inline_opt` + {"ad_inline", inline_opt}, + // Add an entry with key "ad_special_op_simplify" and value `special_op_simplify` + {"ad_special_op_simplify", special_op_simplify}, + // Add an entry with key "auto_monad_grad" and value `opt::OptPassConfig(re_auto_monadwrapper)` + {"auto_monad_grad", opt::OptPassConfig(re_auto_monadwrapper)} +}); + + // Create an instance of the optimizer "prim_bprop_opt_step_2" using the MakeOptimizer function from the opt namespace auto prim_bprop_opt_step_2 = opt::Optimizer::MakeOptimizer("prim_bprop_opt_step_2", res, map); + + // Get the function graph from the result object FuncGraphPtr func_graph = res->func_graph(); + + // Start a profiling step named "prim_bprop_opt_step_2" using the MsProfile::GetProfile() function WITH(MsProfile::GetProfile()->Step("prim_bprop_opt_step_2"))[&prim_bprop_opt_step_2, &func_graph]() { + + // Call the step function of the optimizer with the function graph and the flag "true" func_graph = prim_bprop_opt_step_2->step(func_graph, true); }; + + // Return the modified function graph return func_graph; } +// Define a function named BpropGraphFinalOptPass that takes a reference to a ResourcePtr object as a parameter FuncGraphPtr BpropGraphFinalOptPass(const ResourcePtr &res) { + + // Check if the resource pointer is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(res); + + // Get the function graph from the resource pointer, and throw an exception if it is null MS_EXCEPTION_IF_NULL(res->func_graph()); + + // Call the TransformTopGraphPass function with the resource pointer as a parameter + // The return value is ignored, so we use (void) to suppress any unused variable warnings (void)TransformTopGraphPass(res); - opt::irpass::OptimizeIRPassLib irpass; - opt::OptPassConfig bg_final_opt = opt::OptPassConfig({ + // Return the function graph + // Note: The return type of this function is FuncGraphPtr + // If the TransformTopGraphPass function modifies the function graph, the modified graph will be returned + return res->func_graph(); +} + +// Create an instance of the OptimizeIRPassLib class from the opt namespace +opt::irpass::OptimizeIRPassLib irpass; + +// Create an instance of the OptPassConfig class from the opt namespace, and initialize it with a list of optimization passes +opt::OptPassConfig bg_final_opt = opt::OptPassConfig({ irpass.inline_, irpass.tuple_list_get_set_item_eliminator_, irpass.tuple_list_get_item_eliminator_, @@ -191,210 +368,376 @@ FuncGraphPtr BpropGraphFinalOptPass(const ResourcePtr &res) { irpass.switch_simplify_, irpass.addn_zero_filter_, irpass.ad_related_special_op_eliminate_, - }); - opt::OptPassConfig fill_zeros_like = opt::OptPassConfig{irpass.zero_like_fill_zero_}; - OptPassGroupMap map({ +}); + +// Create another instance of the OptPassConfig class, and initialize it with a single optimization pass +opt::OptPassConfig fill_zeros_like = opt::OptPassConfig{irpass.zero_like_fill_zero_}; + +// Create an instance of the OptPassGroupMap class from the opt namespace, and initialize it with a map of pass groups +OptPassGroupMap map({ {"ad_final_opt", bg_final_opt}, {"zeros_like", fill_zeros_like}, - }); +}); + // Check if the grad_executor in the PynativeExecutor instance needs to be renormalized if (pynative::PynativeExecutor::GetInstance()->grad_executor()->need_renormalize()) { + + // Create a pair with the key "renormalize" and the value of an OptPassConfig object created using the Renormalize constructor (void)map.emplace_back(std::make_pair("renormalize", opt::OptPassConfig::Renormalize())); + + // Create an OptPassConfig object named real_op_eliminate with the value of irpass.real_op_eliminate_ opt::OptPassConfig real_op_eliminate = opt::OptPassConfig{irpass.real_op_eliminate_}; + + // Create a pair with the key "real_op_eliminate" and the value of the real_op_eliminate OptPassConfig object (void)map.emplace_back(std::make_pair("real_op_eliminate", real_op_eliminate)); + + // Create an OptPassConfig object named environ_eliminate with a list of irpass functions as its value opt::OptPassConfig environ_eliminate = opt::OptPassConfig({ irpass.incorporate_call_, irpass.incorporate_call_switch_, irpass.incorporate_getitem_set_, }); + + // Create a pair with the key "environ_eliminate" and the value of the environ_eliminate OptPassConfig object (void)map.emplace_back(std::make_pair("environ_eliminate", environ_eliminate)); } + // Create an instance of the optimizer "bprop_graph_final_opt" with the given inputs "res" and "map" auto bprop_graph_final_opt = opt::Optimizer::MakeOptimizer("bprop_graph_final_opt", res, map); + + // Get the function graph from the input "res" FuncGraphPtr func_graph = res->func_graph(); + + // Start a profiling step named "bprop_graph_final_opt" using the MsProfile::GetProfile() function WITH(MsProfile::GetProfile()->Step("bprop_graph_final_opt"))[&bprop_graph_final_opt, &func_graph]() { + + // Apply the "step" function of the optimizer to the function graph, with the "true" flag indicating it is the final optimization step func_graph = bprop_graph_final_opt->step(func_graph, true); }; + + // Create a lifted clone of the function graph using the LiftingClone function func_graph = LiftingClone(func_graph); + + // Validate the function graph to ensure its correctness Validate(func_graph); + + // Return the optimized function graph return func_graph; } -namespace { -bool ReAutoMonadWrapper(const FuncGraphPtr &root, const opt::OptimizerPtr &) { return ReAutoMonad(root); } +// Define an anonymous namespace to limit the visibility of the function to the current translation unit + +// Define a function named ReAutoMonadWrapper that takes a reference to a FuncGraphPtr object and an OptimizerPtr object as parameters +bool ReAutoMonadWrapper(const FuncGraphPtr &root, const opt::OptimizerPtr &) { + + // Call the ReAutoMonad function with the given root FuncGraphPtr object + // and return the result of the function call + return ReAutoMonad(root); +} + +// A function to determine if the program is running in parallel mode bool parallel_mode() { + + // Check if ENABLE_CPU is defined and _WIN32 is not defined #if ((defined ENABLE_CPU) && (!defined _WIN32)) + + // Check if the program is running on a server or scheduler if (ps::PSContext::instance()->is_server() || ps::PSContext::instance()->is_scheduler()) { - return false; + return false; // If running on a server or scheduler, return false } + #endif + + // Get the parallel mode from the ParallelContext singleton instance std::string parallel_mode = parallel::ParallelContext::GetInstance()->parallel_mode(); + + // Check if the parallel mode is either kAutoParallel or kSemiAutoParallel return (parallel_mode == parallel::kAutoParallel) || (parallel_mode == parallel::kSemiAutoParallel); } +// A function to add a parallel renormalize optimization pass to an OptPassGroupMap + void AddParallelRenormalize(OptPassGroupMap *map_a) { + + // Check if the program is running in parallel mode if (parallel_mode()) { - auto parallel_end_opt = - find_if(map_a->begin(), map_a->end(), [](auto opt_pair) { return opt_pair.first == "meta_fg_expand"; }); + + // Find the position of the "meta_fg_expand" optimization pass in the map + auto parallel_end_opt = find_if(map_a->begin(), map_a->end(), [](auto opt_pair) { return opt_pair.first == "meta_fg_expand"; }); + + // If the "meta_fg_expand" optimization pass is found if (parallel_end_opt != map_a->end()) { + + // Insert the "parallel_renormalize" optimization pass before the "meta_fg_expand" pass (void)map_a->insert(parallel_end_opt, {"parallel_renormalize", opt::OptPassConfig::Renormalize()}); } } } +// Define a function named "GetOptPassA1" that returns an object of type "opt::OptPassConfig" opt::OptPassConfig GetOptPassA1(const opt::irpass::OptimizeIRPassLib &irpass) { + + // Create an instance of "opt::OptPassConfig" by passing a list of optimization passes to its constructor return opt::OptPassConfig({ irpass.switch_defer_inline_, irpass.switch_layer_defer_inline_, irpass.switch_simplify_, irpass.exchange_switch_depend_value_, irpass.float_depend_g_call_, + // ... + }); +} - // Safe inlining +// Perform safe inlining of functions +irpass.inline_, + +// Eliminate useless nodes in the update state +irpass.updatestate_useless_node_eliminater_, + +// Eliminate pure nodes in the update state +irpass.updatestate_pure_node_eliminater_, + +// Eliminate load operations +irpass.load_eliminater_, + +// Eliminate stop gradient operations +irpass.stopgrad_eliminater_, + +// Perform partial elimination of nodes +irpass.partial_eliminate_, + +// Apply node replacement +irpass.replace_applicator_, + +// Miscellaneous transformations for the IR pass: + +irpass.tuple_list_get_item_eliminator_, + +irpass.make_slice_get_slice_eliminator_, + +irpass.tuple_list_get_item_const_eliminator_, + +irpass.tuple_list_set_item_eliminator_, + +irpass.tuple_list_get_set_item_eliminator_, + +irpass.tuple_list_get_item_depend_reorder_, + +irpass.tuple_list_convert_item_index_to_positive_, + +// Call the function `environ_get_eliminate_` from the `irpass` module +irpass.environ_get_eliminate_, + +// Call the function `environ_get_add_eliminate_` from the `irpass` module +irpass.environ_get_add_eliminate_, + +// Call the function `environ_get_set_eliminate_` from the `irpass` module +irpass.environ_get_set_eliminate_, + +// Call the function `environ_get_depend_swap_` from the `irpass` module +irpass.environ_get_depend_swap_, + +// Call the function `environ_add_const_eliminate_` from the `irpass` module +irpass.environ_add_const_eliminate_, + +// Apply the cast elimination optimization pass to simplify the code +irpass.cast_eliminate_, + +// Apply the reshape elimination optimization pass to simplify the code +irpass.reshape_eliminate_, + +// Apply the reduce elimination optimization pass to simplify the code +irpass.reduce_eliminate_, + +// Apply the tile elimination optimization pass to simplify the code +irpass.tile_eliminate_, + +// Apply the transpose elimination optimization pass to simplify the code +irpass.transpose_eliminate_, + +// Apply the minmaximum gradient optimization pass to simplify the code +irpass.minmaximum_grad_, + +// Apply the get make reference elimination optimization pass to simplify the code +irpass.get_make_ref_eliminate_, + +// Perform arithmetic simplifications on the intermediate representation (IR) +irpass.arithmetic_simplify_, + +// Filter out any additions with zero operands +irpass.addn_zero_filter_, + +// Adjust all reduce operations by combining multiplication and addition +irpass.adjust_all_reduce_mul_add_, + +// Eliminate redundant accumulation operations +irpass.accumulaten_eliminater_, + + // Perform safe inlining irpass.inline_, + + // Eliminate useless nodes in the update state irpass.updatestate_useless_node_eliminater_, + + // Eliminate pure nodes in the update state irpass.updatestate_pure_node_eliminater_, + + // Eliminate load operations irpass.load_eliminater_, - irpass.stopgrad_eliminater_, - irpass.partial_eliminate_, - irpass.replace_applicator_, - // Miscellaneous - irpass.tuple_list_get_item_eliminator_, - irpass.make_slice_get_slice_eliminator_, - irpass.tuple_list_get_item_const_eliminator_, - irpass.tuple_list_set_item_eliminator_, - irpass.tuple_list_get_set_item_eliminator_, - irpass.tuple_list_get_item_depend_reorder_, - irpass.tuple_list_convert_item_index_to_positive_, - - irpass.environ_get_eliminate_, - irpass.environ_get_add_eliminate_, - irpass.environ_get_set_eliminate_, - irpass.environ_get_depend_swap_, - irpass.environ_add_const_eliminate_, - - irpass.cast_eliminate_, - irpass.reshape_eliminate_, - irpass.reduce_eliminate_, - irpass.tile_eliminate_, - irpass.transpose_eliminate_, - irpass.minmaximum_grad_, - irpass.get_make_ref_eliminate_, - - // Arithmetic simplifications - irpass.arithmetic_simplify_, - irpass.addn_zero_filter_, - irpass.adjust_all_reduce_mul_add_, - irpass.accumulaten_eliminater_, - - // Safe inlining - irpass.inline_, - irpass.updatestate_useless_node_eliminater_, - irpass.updatestate_pure_node_eliminater_, - irpass.load_eliminater_, + // Eliminate stop gradient operations irpass.stopgrad_eliminater_, }); } +// Define a function named "GetGeTensorArrayPass" that takes an object of type "OptimizeIRPassLib" as a parameter and returns an object of type "OptPassConfig" opt::OptPassConfig GetGeTensorArrayPass(const opt::irpass::OptimizeIRPassLib &irpass) { + + // Create and return an object of type "OptPassConfig" initialized with a list of two elements return opt::OptPassConfig({ - irpass.ge_tensor_array_add_flow_, - irpass.ge_tensor_array_cast_index_, + irpass.ge_tensor_array_add_flow_, // Add the "ge_tensor_array_add_flow_" pass from the "irpass" object to the list + irpass.ge_tensor_array_cast_index_, // Add the "ge_tensor_array_cast_index_" pass from the "irpass" object to the list }); } -OptPassGroupMap GetOptPassesA(const opt::irpass::OptimizeIRPassLib &irpass) { - opt::OptPassConfig a_1 = GetOptPassA1(irpass); - opt::OptPassConfig a_2 = opt::OptPassConfig( +// Function to get a map of optimization passes for a given OptimizeIRPassLib + +// Get the first optimization pass configuration for OptPassA1 +opt::OptPassConfig a_1 = GetOptPassA1(irpass); + +// Create a new optimization pass configuration (a_2) with a list of passes +opt::OptPassConfig a_2 = opt::OptPassConfig( { - irpass.switch_simplify_, - irpass.specialize_transform_, - irpass.merge_addn_, - irpass.addn_check_dump_, - irpass.float_tuple_getitem_switch_, - irpass.float_environ_get_switch_, - irpass.inline_, - irpass.updatestate_useless_node_eliminater_, - irpass.tuple_list_get_item_eliminator_, - irpass.incorporate_getitem_set_, - irpass.incorporate_call_, - irpass.incorporate_call_switch_, - irpass.incorporate_environ_get_bypass_recursive_, - irpass.incorporate_environ_get_switch_, - irpass.environ_get_eliminate_, - irpass.depend_value_elim_, - irpass.all_reduce_const_elim_, + irpass.switch_simplify_, + irpass.specialize_transform_, + irpass.merge_addn_, + irpass.addn_check_dump_, + irpass.float_tuple_getitem_switch_, + irpass.float_environ_get_switch_, + irpass.inline_, + irpass.updatestate_useless_node_eliminater_, + irpass.tuple_list_get_item_eliminator_, + irpass.incorporate_getitem_set_, + irpass.incorporate_call_, + irpass.incorporate_call_switch_, + irpass.incorporate_environ_get_bypass_recursive_, + irpass.incorporate_environ_get_switch_, + irpass.environ_get_eliminate_, + irpass.depend_value_elim_, + irpass.all_reduce_const_elim_, }, false, true); - opt::OptPassConfig a_after_grad = opt::OptPassConfig({irpass.inline_without_move_, irpass.stack_unstack_eliminate_}); +// Return a map of optimization passes, where a_1 is mapped to "a_1" and a_2 is mapped to "a_2" +OptPassGroupMap optPassesA = { + {"a_1", a_1}, + {"a_2", a_2} +}; - opt::OptPassConfig a_3 = opt::OptPassConfig( +// Return the map of optimization passes +return optPassesA; + +// Create an instance of the OptPassConfig class named "a_after_grad" and initialize it with a list of optimization passes. +// The optimization passes included in the list are "irpass.inline_without_move_" and "irpass.stack_unstack_eliminate_". +opt::OptPassConfig a_after_grad = opt::OptPassConfig({irpass.inline_without_move_, irpass.stack_unstack_eliminate_}); + +// Create an instance of the OptPassConfig class named "a_3" with a list of optimization passes as arguments +opt::OptPassConfig a_3 = opt::OptPassConfig( { - irpass.arithmetic_simplify2_, - irpass.same_eliminate_, - irpass.check_bprop_eliminate_, - irpass.switch_layer_defer_inline_, - irpass.replace_applicator_, - irpass.mirror_mini_step_elim_, - irpass.virtual_add_elim_, - irpass.row_tensor_add_zeros_like_, - irpass.mini_step_allgather_replace_, - irpass.micro_step_allgather_replace_, - irpass.split_environ_get_set_with_tuple_value_, + irpass.arithmetic_simplify2_, + irpass.same_eliminate_, + irpass.check_bprop_eliminate_, + irpass.switch_layer_defer_inline_, + irpass.replace_applicator_, + irpass.mirror_mini_step_elim_, + irpass.virtual_add_elim_, + irpass.row_tensor_add_zeros_like_, + irpass.mini_step_allgather_replace_, + irpass.micro_step_allgather_replace_, + irpass.split_environ_get_set_with_tuple_value_, }, false, true); - opt::OptPassConfig accelerated_algorithm = opt::OptPassConfig({irpass.less_batch_normalization_}); - opt::OptPassConfig virtual_dataset = opt::OptPassConfig({irpass.virtual_dataset_eliminate_}); - opt::OptPassConfig after_resolve_pass = + +// Create an instance of the OptPassConfig class named "accelerated_algorithm" with a single optimization pass as argument +opt::OptPassConfig accelerated_algorithm = opt::OptPassConfig({irpass.less_batch_normalization_}); + +// Create an instance of the OptPassConfig class named "virtual_dataset" with a single optimization pass as argument +opt::OptPassConfig virtual_dataset = opt::OptPassConfig({irpass.virtual_dataset_eliminate_}); + +// Create an instance of the OptPassConfig class named "after_resolve_pass" with a list of optimization passes as arguments +opt::OptPassConfig after_resolve_pass = opt::OptPassConfig({irpass.get_make_ref_eliminate_, irpass.replace_old_param_}); - opt::OptPassConfig updatestate_depend_eliminate = opt::OptPassConfig(opt::irpass::UpdatestateDependEliminater()); - opt::OptPassConfig updatestate_assign_eliminate = opt::OptPassConfig(opt::irpass::UpdatestateAssignEliminater()); - opt::OptPassConfig updatestate_loads_eliminate = opt::OptPassConfig(opt::irpass::UpdatestateLoadsEliminater()); - opt::OptPassConfig recompute_prepare = opt::OptPassConfig({irpass.set_cell_output_no_recompute_}); - // Before adjusting map_a, check GetA1A2() and GetOptPynativeGradEpiloguePhases(). - OptPassGroupMap map_a({{"expand_dump_flag", opt::OptPassConfig(opt::irpass::ExpandDumpFlag())}, - {"switch_simplify", opt::OptPassConfig({irpass.switch_simplify_})}, - {"a_1", a_1}, - {"recompute_prepare", recompute_prepare}, - {"updatestate_depend_eliminate", updatestate_depend_eliminate}, - {"updatestate_assign_eliminate", updatestate_assign_eliminate}, - {"updatestate_loads_eliminate", updatestate_loads_eliminate}, - {"parameter_eliminate", opt::OptPassConfig(opt::irpass::ParameterEliminator())}, - {"a_2", a_2}, - {"accelerated_algorithm", accelerated_algorithm}, - {"auto_parallel", opt::OptPassConfig(parallel::StepAutoParallel)}, - {"parallel", opt::OptPassConfig(parallel::StepParallel)}, - {"allreduce_fusion", opt::OptPassConfig(parallel::StepAllreduceFusion)}, - {"virtual_dataset", virtual_dataset}, - {"virtual_output", opt::OptPassConfig({irpass.virtual_output_eliminate_})}, - {"shard", opt::OptPassConfig(opt::irpass::ExpandShardPrim())}, - {"meta_fg_expand", opt::OptPassConfig(opt::irpass::ExpandMetaFg())}, - {"after_resolve", after_resolve_pass}, - {"a_after_grad", a_after_grad}, - {"renormalize", opt::OptPassConfig::Renormalize()}, - {"real_op_eliminate", opt::OptPassConfig({irpass.real_op_eliminate_})}, - {"auto_monad_grad", opt::OptPassConfig(ReAutoMonadWrapper)}, - {"auto_monad_eliminator", opt::OptPassConfig(opt::AutoMonadEliminator())}, - {"cse", opt::OptPassConfig(opt::CSEPass(false))}, - {"a_3", a_3}}); - AddParallelRenormalize(&map_a); - return map_a; -} +// Create an instance of the OptPassConfig class named "updatestate_depend_eliminate" with a custom optimization pass as argument +opt::OptPassConfig updatestate_depend_eliminate = opt::OptPassConfig(opt::irpass::UpdatestateDependEliminater()); +// Create an instance of the OptPassConfig class named "updatestate_assign_eliminate" with a custom optimization pass as argument +opt::OptPassConfig updatestate_assign_eliminate = opt::OptPassConfig(opt::irpass::UpdatestateAssignEliminater()); + +// Create an instance of the OptPassConfig class named "updatestate_loads_eliminate" with a custom optimization pass as argument +opt::OptPassConfig updatestate_loads_eliminate = opt::OptPassConfig(opt::irpass::UpdatestateLoadsEliminater()); + +// Create an instance of the OptPassConfig class named "recompute_prepare" with a single optimization pass as argument +opt::OptPassConfig recompute_prepare = opt::OptPassConfig({irpass.set_cell_output_no_recompute_}); + +// Before adjusting map_a, it is important to understand the functions GetA1A2() and GetOptPynativeGradEpiloguePhases() that are used to populate the OptPassGroupMap. + +OptPassGroupMap map_a({ + {"expand_dump_flag", opt::OptPassConfig(opt::irpass::ExpandDumpFlag())}, // Apply the ExpandDumpFlag optimization pass + {"switch_simplify", opt::OptPassConfig({irpass.switch_simplify_})}, // Apply the switch_simplify optimization pass + {"a_1", a_1}, // Apply the a_1 optimization pass + {"recompute_prepare", recompute_prepare}, // Apply the recompute_prepare optimization pass + {"updatestate_depend_eliminate", updatestate_depend_eliminate}, // Apply the updatestate_depend_eliminate optimization pass + {"updatestate_assign_eliminate", updatestate_assign_eliminate}, // Apply the updatestate_assign_eliminate optimization pass + {"updatestate_loads_eliminate", updatestate_loads_eliminate}, // Apply the updatestate_loads_eliminate optimization pass + {"parameter_eliminate", opt::OptPassConfig(opt::irpass::ParameterEliminator())}, // Apply the ParameterEliminator optimization pass + {"a_2", a_2}, // Apply the a_2 optimization pass + {"accelerated_algorithm", accelerated_algorithm}, // Apply the accelerated_algorithm optimization pass + {"auto_parallel", opt::OptPassConfig(parallel::StepAutoParallel)}, // Apply the StepAutoParallel optimization pass + {"parallel", opt::OptPassConfig(parallel::StepParallel)}, // Apply the StepParallel optimization pass + {"allreduce_fusion", opt::OptPassConfig(parallel::StepAllreduceFusion)}, // Apply the StepAllreduceFusion optimization pass + {"virtual_dataset", virtual_dataset}, // Apply the virtual_dataset optimization pass + {"virtual_output", opt::OptPassConfig({irpass.virtual_output_eliminate_})}, // Apply the virtual_output_eliminate optimization pass + {"shard", opt::OptPassConfig(opt::irpass::ExpandShardPrim())}, // Apply the ExpandShardPrim optimization pass + {"meta_fg_expand", opt::OptPassConfig(opt::irpass::ExpandMetaFg())}, // Apply the ExpandMetaFg optimization pass + {"after_resolve", after_resolve_pass}, // Apply the after_resolve_pass optimization pass + {"a_after_grad", a_after_grad}, // Apply the a_after_grad optimization pass + {"renormalize", opt::OptPassConfig::Renormalize()}, // Apply the Renormalize optimization pass + {"real_op_eliminate", opt::OptPassConfig({irpass.real_op_eliminate_})}, // Apply the real_op_eliminate optimization pass + {"auto_monad_grad", opt::OptPassConfig(ReAutoMonadWrapper)}, // Apply the ReAutoMonadWrapper optimization pass + {"auto_monad_eliminator", opt::OptPassConfig(opt::AutoMonadEliminator())}, // Apply the AutoMonadEliminator optimization pass + {"cse", opt::OptPassConfig(opt::CSEPass(false))}, // Apply the CSEPass optimization pass + {"a_3", a_3} // Apply the a_3 optimization pass +}); + +AddParallelRenormalize(&map_a); // Add the ParallelRenormalize optimization pass to the OptPassGroupMap + +return map_a; // Return the OptPassGroupMap + +// Define a function named "GetA1A2" that takes an object of type "OptimizeIRPassLib" as a parameter and returns an object of type "OptPassGroupMap" OptPassGroupMap GetA1A2(const opt::irpass::OptimizeIRPassLib &irpass) { + + // Call the function "GetOptPassesA" and store the result in a variable named "opt_a" auto opt_a = GetOptPassesA(irpass); + + // Define a constant variable named "a1_a2_len" with a value of 9 constexpr auto a1_a2_len = 9; + + // Create a new object of type "OptPassGroupMap" named "a1_a2" by copying the elements from "opt_a" starting from the beginning and up to "a1_a2_len" OptPassGroupMap a1_a2(opt_a.begin(), opt_a.begin() + a1_a2_len); + + // Return the "a1_a2" object return a1_a2; } +// Define a function named "GetOptPassesAfterCconv" that takes an object of type "OptimizeIRPassLib" as a parameter and returns an object of type "OptPassGroupMap" + OptPassGroupMap GetOptPassesAfterCconv(const opt::irpass::OptimizeIRPassLib &irpass) { + + // Create an object of type "OptPassConfig" named "c_1" and initialize it with a list of optimization passes opt::OptPassConfig c_1 = opt::OptPassConfig({ - // Safe inlining, + // Safe inlining irpass.inline_, irpass.updatestate_useless_node_eliminater_, irpass.updatestate_pure_node_eliminater_, @@ -403,22 +746,47 @@ OptPassGroupMap GetOptPassesAfterCconv(const opt::irpass::OptimizeIRPassLib &irp irpass.stopgrad_eliminater_, irpass.partial_eliminate_, }); + + // Create an object of type "OptPassConfig" named "updatestate_depend_eliminate" and initialize it with an instance of the "UpdatestateDependEliminater" optimization pass opt::OptPassConfig updatestate_depend_eliminate = opt::OptPassConfig(opt::irpass::UpdatestateDependEliminater()); + + // Create an object of type "OptPassConfig" named "updatestate_assign_eliminate" and initialize it with an instance of the "UpdatestateAssignEliminater" optimization pass opt::OptPassConfig updatestate_assign_eliminate = opt::OptPassConfig(opt::irpass::UpdatestateAssignEliminater()); + + // Create an object of type "OptPassConfig" named "updatestate_loads_eliminate" and initialize it with an instance of the "UpdatestateLoadsEliminater" optimization pass opt::OptPassConfig updatestate_loads_eliminate = opt::OptPassConfig(opt::irpass::UpdatestateLoadsEliminater()); - OptPassGroupMap map_a({{"c_1", c_1}, - {"updatestate_depend_eliminate", updatestate_depend_eliminate}, - {"updatestate_assign_eliminate", updatestate_assign_eliminate}, - {"updatestate_loads_eliminate", updatestate_loads_eliminate}, - {"cse", opt::OptPassConfig(opt::CSEPass(false))}, - {"renormalize", opt::OptPassConfig::Renormalize()}}); + // ... (the code continues) - return map_a; -} +// Create an instance of the OptPassGroupMap class and initialize it with a list of key-value pairs +OptPassGroupMap map_a({ + {"c_1", c_1}, + {"updatestate_depend_eliminate", updatestate_depend_eliminate}, + {"updatestate_assign_eliminate", updatestate_assign_eliminate}, + {"updatestate_loads_eliminate", updatestate_loads_eliminate}, + {"cse", opt::OptPassConfig(opt::CSEPass(false))}, + {"renormalize", opt::OptPassConfig::Renormalize()} +}); -OptPassGroupMap GetOptPassesTransformGraph(const opt::irpass::OptimizeIRPassLib &irpass) { - opt::OptPassConfig d_1 = opt::OptPassConfig({ +// Return the value of the variable `map_a` from the function +return map_a; + +// This function takes an instance of the OptimizeIRPassLib class as input and returns an OptPassGroupMap object. +// The OptimizeIRPassLib class contains a set of optimization passes that can be applied to an intermediate representation (IR) graph. + +// Create an instance of the OptPassConfig class named d_1 and initialize it with a list of optimization passes. +// Each optimization pass is a member of the OptimizeIRPassLib class and is accessed using the irpass object. + +// The optimization passes included in d_1 are as follows: +// 1. call_graph_tuple_transform_ +// 2. tuple_list_get_item_eliminator_ +// 3. tuple_list_get_item_const_eliminator_ +// 4. tuple_list_set_item_eliminator_ +// 5. tuple_list_get_set_item_eliminator_ +// 6. tuple_list_get_item_depend_reorder_ +// 7. tuple_list_convert_item_index_to_positive_ + +opt::OptPassConfig d_1 = opt::OptPassConfig({ irpass.call_graph_tuple_transform_, irpass.tuple_list_get_item_eliminator_, irpass.tuple_list_get_item_const_eliminator_, @@ -426,14 +794,26 @@ OptPassGroupMap GetOptPassesTransformGraph(const opt::irpass::OptimizeIRPassLib irpass.tuple_list_get_set_item_eliminator_, irpass.tuple_list_get_item_depend_reorder_, irpass.tuple_list_convert_item_index_to_positive_, - }); +}); - OptPassGroupMap map_a({{"d_1", d_1}, {"renormalize", opt::OptPassConfig::Renormalize()}}); +// Create an instance of the OptPassGroupMap class and initialize it with a list of key-value pairs +// The keys are strings and the values are objects of different types +// The first key-value pair has the key "d_1" and the value d_1 +// The second key-value pair has the key "renormalize" and the value opt::OptPassConfig::Renormalize() +// The OptPassConfig::Renormalize() function returns an object of type opt::OptPassConfig +OptPassGroupMap map_a({{"d_1", d_1}, {"renormalize", opt::OptPassConfig::Renormalize()}}); - return map_a; -} +// Return the value of the variable `map_a` from the function +return map_a; +// Define a function named GetOptPassesB that returns an object of type OptPassGroupMap OptPassGroupMap GetOptPassesB(const opt::irpass::OptimizeIRPassLib &irpass) { + + // Create an OptPassConfig object named b_1 and initialize it with a list of optimization passes + // The passes are specified using the irpass object passed as an argument + // The list of passes is enclosed in curly braces {} + // The third argument to the OptPassConfig constructor is set to false, indicating that the passes are not enabled by default + // The fourth argument is set to true, indicating that the passes are required to be run in order opt::OptPassConfig b_1 = opt::OptPassConfig({irpass.zero_like_fill_zero_, irpass.tuple_list_get_item_eliminator_, irpass.tuple_list_get_item_const_eliminator_, @@ -464,206 +844,433 @@ OptPassGroupMap GetOptPassesB(const opt::irpass::OptimizeIRPassLib &irpass) { irpass.virtual_assign_add_, irpass.mirror_micro_step_}, false, true); + + // Create an OptPassConfig object named b_2 and initialize it with a list of optimization passes + // The passes are specified using the irpass object passed as an argument + // The list of passes is enclosed in curly braces {} opt::OptPassConfig b_2 = opt::OptPassConfig({ irpass.replace_refkey_by_param_, irpass.make_ref_eliminate_, irpass.get_ref_param_eliminate_, irpass.row_tensor_eliminate_, }); + + // Create an OptPassConfig object named updatestate_depend_eliminate and initialize it with an instance of the UpdatestateDependEliminater class opt::OptPassConfig updatestate_depend_eliminate = opt::OptPassConfig(opt::irpass::UpdatestateDependEliminater()); + + // Create an OptPassConfig object named updatestate_assign_eliminate and initialize it with an instance of the UpdatestateAssignEliminater class opt::OptPassConfig updatestate_assign_eliminate = opt::OptPassConfig(opt::irpass::UpdatestateAssignEliminater()); + + // Create an OptPassConfig object named updatestate_loads_eliminate and initialize it with an instance of the UpdatestateLoadsEliminater class opt::OptPassConfig updatestate_loads_eliminate = opt::OptPassConfig(opt::irpass::UpdatestateLoadsEliminater()); - OptPassGroupMap map({ - {"b_1", b_1}, - {"b_2", b_2}, - {"updatestate_depend_eliminate", updatestate_depend_eliminate}, - {"updatestate_assign_eliminate", updatestate_assign_eliminate}, - {"updatestate_loads_eliminate", updatestate_loads_eliminate}, - {"renormalize", opt::OptPassConfig::Renormalize()}, - {"cse", opt::OptPassConfig(opt::CSEPass(false))}, - }); - return map; + + // ... + // The code continues with more OptPassConfig objects being created and initialized + // ... + + // Return the OptPassGroupMap object + return ???; } +// Create an instance of the OptPassGroupMap, which is a map that associates pass names with pass objects +OptPassGroupMap map({ + + // Add entries to the map using initializer list syntax + {"b_1", b_1}, // Associate the pass object b_1 with the name "b_1" + {"b_2", b_2}, // Associate the pass object b_2 with the name "b_2" + {"updatestate_depend_eliminate", updatestate_depend_eliminate}, // Associate the pass object updatestate_depend_eliminate with the name "updatestate_depend_eliminate" + {"updatestate_assign_eliminate", updatestate_assign_eliminate}, // Associate the pass object updatestate_assign_eliminate with the name "updatestate_assign_eliminate" + {"updatestate_loads_eliminate", updatestate_loads_eliminate}, // Associate the pass object updatestate_loads_eliminate with the name "updatestate_loads_eliminate" + {"renormalize", opt::OptPassConfig::Renormalize()}, // Associate the Renormalize pass object with the name "renormalize" + {"cse", opt::OptPassConfig(opt::CSEPass(false))}, // Associate a CSEPass object with the name "cse", passing false as a parameter to the constructor +}); + +// Return the created OptPassGroupMap +return map; + +// Define a function named "GetOptPassesPynativeElim" that takes an object of type "OptimizeIRPassLib" as a parameter and returns an object of type "OptPassGroupMap" OptPassGroupMap GetOptPassesPynativeElim(const opt::irpass::OptimizeIRPassLib &irpass) { + + // Create an object of type "OptPassConfig" named "pynative_eliminate" and initialize it with a list containing a single element, which is the "pynative_eliminate_" member of the "irpass" object opt::OptPassConfig pynative_eliminate = opt::OptPassConfig({ irpass.pynative_eliminate_, }); - OptPassGroupMap map({ - {"pynative_eliminate", pynative_eliminate}, - }); - return map; + // Return the "pynative_eliminate" object + return pynative_eliminate; } +// Create an instance of the OptPassGroupMap, which is a map that associates pass group names with pass group functions +OptPassGroupMap map({ + + // Add an entry to the map with the key "pynative_eliminate" and the value pynative_eliminate + {"pynative_eliminate", pynative_eliminate}, +}); + +// Return the map +return map; +} + +// Define a function named "GetOptPassesC" that takes a reference to an object of type "opt::irpass::OptimizeIRPassLib" as a parameter OptPassGroupMap GetOptPassesC(const opt::irpass::OptimizeIRPassLib &) { + + // Create an instance of "OptPassGroupMap" using the initializer list syntax + // The initializer list contains a single pair of key-value, where the key is "renormalize" and the value is the result of calling the "Renormalize" function of "opt::OptPassConfig" class return OptPassGroupMap({{"renormalize", opt::OptPassConfig::Renormalize()}}); } +// Define a function named "GetControlPhases" that takes an argument of type "opt::irpass::OptimizeIRPassLib" and returns an object of type "OptPassGroupMap" OptPassGroupMap GetControlPhases(const opt::irpass::OptimizeIRPassLib &) { + + // Create an object named "control_group" of type "opt::OptPassConfig" and initialize it with an instance of "opt::irpass::ConvertSwitchReplacement" opt::OptPassConfig control_group = opt::OptPassConfig(opt::irpass::ConvertSwitchReplacement()); + + // Create an object named "map" of type "OptPassGroupMap" and initialize it with a list of pairs OptPassGroupMap map({ - {"control_group", control_group}, - {"renormalize", opt::OptPassConfig::Renormalize()}, + {"control_group", control_group}, // The first pair has a key "control_group" and a value of "control_group" + {"renormalize", opt::OptPassConfig::Renormalize()}, // The second pair has a key "renormalize" and a value of "opt::OptPassConfig::Renormalize()" }); + + // Return the "map" object return map; } +// Define a function named GetGeSpecializedPhases that returns an object of type OptPassGroupMap OptPassGroupMap GetGeSpecializedPhases() { + + // Create an instance of OptPassConfig named ge_ta_size_group and initialize it with the result of calling the constructor of OptPassConfig with an argument of type opt::irpass::GeTensorArrayPrepare() opt::OptPassConfig ge_ta_size_group = opt::OptPassConfig(opt::irpass::GeTensorArrayPrepare()); + + // Create an instance of OptimizeIRPassLib named irpass opt::irpass::OptimizeIRPassLib irpass; + + // Call the function GetGeTensorArrayPass with irpass as an argument and assign the result to an instance of OptPassConfig named ge_tensor_array_passes opt::OptPassConfig ge_tensor_array_passes = GetGeTensorArrayPass(irpass); + + // Create an instance of OptPassGroupMap named map and initialize it with a list of pairs OptPassGroupMap map({ - {"ge_ta_size_group", ge_ta_size_group}, - {"ge_ta_passes", ge_tensor_array_passes}, + {"ge_ta_size_group", ge_ta_size_group}, // The first pair has a key "ge_ta_size_group" and a value of ge_ta_size_group + {"ge_ta_passes", ge_tensor_array_passes}, // The second pair has a key "ge_ta_passes" and a value of ge_tensor_array_passes }); + + // Return the map object return map; } +// Function to get the optimization phases for the Pynative gradient epilogue OptPassGroupMap GetOptPynativeGradEpiloguePhases(const opt::irpass::OptimizeIRPassLib &irpass) { + + // Get the optimization passes from GetOptPassesA function auto opt_a = GetOptPassesA(irpass); + + // Get the last pass from opt_a auto a3 = opt_a[opt_a.size() - 1]; + + // Create an OptPassGroupMap object and initialize it with the optimization passes OptPassGroupMap map({ - {"renormalize", opt::OptPassConfig::Renormalize()}, - {"cse", opt::OptPassConfig(opt::CSEPass(false))}, - {a3}, + {"renormalize", opt::OptPassConfig::Renormalize()}, // Add a pass named "renormalize" using the Renormalize optimization pass + {"cse", opt::OptPassConfig(opt::CSEPass(false))}, // Add a pass named "cse" using the CSEPass optimization pass with the argument false + {a3}, // Add the last pass from opt_a }); + + // Return the OptPassGroupMap object return map; } +// Define a function named "GetInferenceOptPreparePhases" that returns an object of type "OptPassGroupMap" OptPassGroupMap GetInferenceOptPreparePhases() { + + // Create an instance of the "InferenceOptPrepareLib" class and assign it to the variable "irpass" opt::irpass::InferenceOptPrepareLib irpass; + + // Create an "OptPassConfig" object named "grad_var_prepare" and initialize it with a list containing the "grad_var_prepare_" member function of "irpass" auto grad_var_prepare = opt::OptPassConfig({irpass.grad_var_prepare_}); + + // Create an "OptPassGroupMap" object named "prepare_map" and initialize it with a map containing a single key-value pair + // The key is "inference_opt_prep" and the value is the "grad_var_prepare" object opt::OptPassGroupMap prepare_map({{"inference_opt_prep", grad_var_prepare}}); + + // Return the "prepare_map" object return prepare_map; } +// Function to get the prepare phases for optimizing the IR passes OptPassGroupMap GetPreparePhases(const opt::irpass::OptimizeIRPassLib &irpass) { + + // Create an instance of OptPassConfig with the given IR pass opt::OptPassConfig prepare_group = opt::OptPassConfig({irpass.print_tuple_wrapper_}); + + // Create a map with the prepare group name and the corresponding OptPassConfig instance OptPassGroupMap map({{"prepare_group", prepare_group}}); + + // Return the map containing the prepare group return map; } +// Define a function named "GetAfterRecomputePass" that takes an argument of type "opt::irpass::OptimizeIRPassLib" and returns an object of type "OptPassGroupMap" + OptPassGroupMap GetAfterRecomputePass(const opt::irpass::OptimizeIRPassLib &) { + + // Create an object of type "OptPassGroupMap" named "map" and initialize it with a single key-value pair + // The key is "cse" and the value is an object of type "opt::OptPassConfig" initialized with a "CSEPass" object + // The "CSEPass" object is constructed with a boolean argument set to "false" OptPassGroupMap map({{"cse", opt::OptPassConfig(opt::CSEPass(false))}}); + + // Return the "map" object return map; } +// Declare a static variable named g_pass_opts of type mindspore::HashMap> static mindspore::HashMap> g_pass_opts = {}; +// Function to initialize optimization options with a given resource void InitOpt(const ResourcePtr &res) { + + // Check if the global pass options map is empty if (g_pass_opts.size() == 0) { + + // Create an instance of the OptimizeIRPassLib class opt::irpass::OptimizeIRPassLib irpass; + + // Add optimization options to the global pass options map g_pass_opts["a1a2"] = Optimizer::MakeOptimizer("a1a2", res, GetA1A2(irpass)); g_pass_opts["opt_a"] = Optimizer::MakeOptimizer("opt_a", res, GetOptPassesA(irpass)); g_pass_opts["opt_b"] = Optimizer::MakeOptimizer("opt_b", res, GetOptPassesB(irpass), false, true); - g_pass_opts["opt_after_cconv"] = - Optimizer::MakeOptimizer("opt_after_cconv", res, GetOptPassesAfterCconv(irpass), false, true); - g_pass_opts["opt_trans_graph"] = - Optimizer::MakeOptimizer("opt_trans_graph", res, GetOptPassesTransformGraph(irpass), true, true); + g_pass_opts["opt_after_cconv"] = Optimizer::MakeOptimizer("opt_after_cconv", res, GetOptPassesAfterCconv(irpass), false, true); + g_pass_opts["opt_trans_graph"] = Optimizer::MakeOptimizer("opt_trans_graph", res, GetOptPassesTransformGraph(irpass), true, true); g_pass_opts["renormal"] = Optimizer::MakeOptimizer("renormal", res, GetOptPassesC(irpass)); g_pass_opts["opt_control"] = Optimizer::MakeOptimizer("opt_control", res, GetControlPhases(irpass), true, false); - g_pass_opts["opt_grad_epilogue"] = - Optimizer::MakeOptimizer("opt_grad_epilogue", res, GetOptPynativeGradEpiloguePhases(irpass), true, false); + g_pass_opts["opt_grad_epilogue"] = Optimizer::MakeOptimizer("opt_grad_epilogue", res, GetOptPynativeGradEpiloguePhases(irpass), true, false); g_pass_opts["opt_prepare"] = Optimizer::MakeOptimizer("opt_prepare", res, GetPreparePhases(irpass)); - g_pass_opts["opt_after_recompute"] = - Optimizer::MakeOptimizer("opt_after_recompute", res, GetAfterRecomputePass(irpass)); + g_pass_opts["opt_after_recompute"] = Optimizer::MakeOptimizer("opt_after_recompute", res, GetAfterRecomputePass(irpass)); } } -} // namespace + +// End of the namespace + +// A function to reclaim optimizer resources void ReclaimOptimizer() { + + // Iterate over each element in the g_pass_opts map for (auto &opt : g_pass_opts) { + + // Set the value of the current element to nullptr opt.second = nullptr; } + + // Clear the g_pass_opts map g_pass_opts.clear(); } +// A function named OptPassGroup that takes a ResourcePtr object and a string as parameters bool OptPassGroup(const ResourcePtr &res, const std::string &name) { + + // Check if the ResourcePtr object is null, if it is, throw an exception MS_EXCEPTION_IF_NULL(res); + + // Check if the func_graph of the ResourcePtr object is nullptr if (res->func_graph() == nullptr) { + + // Log an error message using MS_LOG(ERROR) and return false MS_LOG(ERROR) << "Opt passes int64_t error"; return false; } + // Get the function graph from the result object FuncGraphPtr func_graph = res->func_graph(); + + // Print debug information about the function graph and its return value MS_LOG(DEBUG) << "Start " << name << " func graph:" << func_graph->ToString() << ", " << func_graph->get_return()->DebugString(true); + + // Initialize optimizations for the result object InitOpt(res); + + // Check if there are any pass options available for the given name if (g_pass_opts.find(name) != g_pass_opts.end()) { + // Apply the step function of the pass option to the function graph and update the result object res->set_func_graph(g_pass_opts[name]->step(func_graph)); } + // Note: StepParallel may modify the AbstractValue of the parameters of func_graph, but they are not updated to // res->args_spec_ yet. So if any later pass or action want to use that variable, it should be set here. + + // Return true to indicate successful execution of the function return true; } -bool OptPassA1A2(const ResourcePtr &res) { return OptPassGroup(res, "a1a2"); } -bool OptPassAGroup(const ResourcePtr &res) { return OptPassGroup(res, "opt_a"); } -bool OptPassBGroup(const ResourcePtr &res) { return OptPassGroup(res, "opt_b"); } -bool OptPassAfterCconvGroup(const ResourcePtr &res) { return OptPassGroup(res, "opt_after_cconv"); } -bool OptPassTransformGraphGroup(const ResourcePtr &res) { return OptPassGroup(res, "opt_trans_graph"); } -bool ControlGroup(const ResourcePtr &res) { return OptPassGroup(res, "opt_control"); } -bool PrepareGroup(const ResourcePtr &res) { return OptPassGroup(res, "opt_prepare"); } -bool OptAfterRecomputeGroup(const ResourcePtr &res) { return OptPassGroup(res, "opt_after_recompute"); } +// Function to apply optimization passes A1A2 +bool OptPassA1A2(const ResourcePtr &res) { + // Call the OptPassGroup function with the specified group name "a1a2" + return OptPassGroup(res, "a1a2"); +} -bool OptPassRNGroup(const ResourcePtr &res) { return OptPassGroup(res, "renormal"); } +// Function to apply optimization passes in group A +bool OptPassAGroup(const ResourcePtr &res) { + // Call the OptPassGroup function with the specified group name "opt_a" + return OptPassGroup(res, "opt_a"); +} -bool OptPassGradEpilogueGroup(const ResourcePtr &res) { return OptPassGroup(res, "opt_grad_epilogue"); } +// Function to apply optimization passes in group B +bool OptPassBGroup(const ResourcePtr &res) { + // Call the OptPassGroup function with the specified group name "opt_b" + return OptPassGroup(res, "opt_b"); +} +// Function to apply optimization passes after Cconv +bool OptPassAfterCconvGroup(const ResourcePtr &res) { + // Call the OptPassGroup function with the specified group name "opt_after_cconv" + return OptPassGroup(res, "opt_after_cconv"); +} + +// Function to apply transformation passes on the graph +bool OptPassTransformGraphGroup(const ResourcePtr &res) { + // Call the OptPassGroup function with the specified group name "opt_trans_graph" + return OptPassGroup(res, "opt_trans_graph"); +} + +// Function to apply control optimization passes +bool ControlGroup(const ResourcePtr &res) { + // Call the OptPassGroup function with the specified group name "opt_control" + return OptPassGroup(res, "opt_control"); +} + +// Function to apply preparation optimization passes +bool PrepareGroup(const ResourcePtr &res) { + // Call the OptPassGroup function with the specified group name "opt_prepare" + return OptPassGroup(res, "opt_prepare"); +} + +// Function to apply optimization passes after recomputation +bool OptAfterRecomputeGroup(const ResourcePtr &res) { + // Call the OptPassGroup function with the specified group name "opt_after_recompute" + return OptPassGroup(res, "opt_after_recompute"); +} + +// A function named OptPassRNGroup that takes a reference to a ResourcePtr object as a parameter +bool OptPassRNGroup(const ResourcePtr &res) { + + // Call the OptPassGroup function with the given ResourcePtr object and the string "renormal" as parameters, + // and return the result of the function call + return OptPassGroup(res, "renormal"); +} + +// A function named OptPassGradEpilogueGroup that takes a reference to a ResourcePtr object as a parameter +bool OptPassGradEpilogueGroup(const ResourcePtr &res) { + + // Call the OptPassGroup function with the given ResourcePtr object and the string "opt_grad_epilogue" as parameters + // Return the result of the OptPassGroup function + return OptPassGroup(res, "opt_grad_epilogue"); +} + +// Function to add a recomputation pass for a given resource bool AddRecomputationPass(const ResourcePtr &res) { + // Check if the resource is null, throw an exception if it is MS_EXCEPTION_IF_NULL(res); + + // Call the InsertRecomputedNodes function from the opt namespace, passing the function graph of the resource opt::InsertRecomputedNodes(res->func_graph()); + + // Return true to indicate successful addition of the recomputation pass return true; } +// A function that recomputes activation nodes for a given resource bool SliceRecomputeActivationPass(const ResourcePtr &res) { + // Check if the resource is null, throw an exception if it is MS_EXCEPTION_IF_NULL(res); + + // Call the SliceRecomputedActivationNodes function from the opt namespace, + // passing in the function graph of the resource opt::SliceRecomputedActivationNodes(res->func_graph()); + + // Return true to indicate successful execution of the function return true; } +// A function that adds attributes to a communication operation bool CommOpAddAttrs(const ResourcePtr &res) { + // Check if the resource pointer is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(res); + + // Call the CommOpAttrs function from the opt namespace, passing in the function graph of the resource opt::CommOpAttrs(res->func_graph()); + + // Return true to indicate successful addition of attributes return true; } +// Define a function named "AddCacheEmbeddingPass" that takes a reference to a "ResourcePtr" object as a parameter and returns a boolean value + bool AddCacheEmbeddingPass(const ResourcePtr &res) { + // Check if the "res" pointer is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(res); -#if ((defined ENABLE_CPU) && (!defined _WIN32)) - if (ps::PSContext::instance()->is_ps_mode()) { - return true; - } -#endif + + // Check if the ENABLE_CPU macro is defined and the _WIN32 macro is not defined + #if ((defined ENABLE_CPU) && (!defined _WIN32)) + // Check if the PS mode is enabled in the PSContext singleton instance + if (ps::PSContext::instance()->is_ps_mode()) { + // If PS mode is enabled, return true + return true; + } + #endif + + // Get the function graph from the "res" object and assign it to the "func_graph" variable FuncGraphPtr func_graph = res->func_graph(); + + // Check if the "func_graph" pointer is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(func_graph); + // Continue with the rest of the function... + + // Call the function AddCacheEmbedding from the parallel namespace, passing in the func_graph as an argument parallel::AddCacheEmbedding(func_graph); + + // Check if the func_graph has the GRAPH_FLAG_CACHE_ENABLE flag set if (func_graph->has_flag(GRAPH_FLAG_CACHE_ENABLE)) { + + // Get the parameters of the func_graph auto params = func_graph->parameters(); + + // Create an empty list to store the abstract base pointers of the parameters AbstractBasePtrList args_spec_list; + + // Iterate over the parameters using std::for_each and lambda function std::for_each(params.begin(), params.end(), [&args_spec_list](const AnfNodePtr &node) { args_spec_list.push_back(node->abstract()); }); + + // Call the Renormalize function from the pipeline namespace, passing in res, func_graph, and args_spec_list as arguments func_graph = pipeline::Renormalize(res, func_graph, args_spec_list); } + + // Return true to indicate successful execution of the code return true; } +// A function to remove duplicated value nodes from a given resource bool RemoveValueNodeDuplicationsPass(const ResourcePtr &res) { MS_EXCEPTION_IF_NULL(res); + + // Check if the function graph of the resource is null if (res->func_graph() == nullptr) { MS_LOG(EXCEPTION) << "Remove value node duplications error."; } + + // Get the manager of the resource auto manager = res->manager(); + + // Create a hash cache and a hash value container HashCache hash_cache; HashValue hashes; - // Remove duplicated value nodes across all graphs in manager + + // Remove duplicated value nodes across all graphs in the manager auto node_user_map = manager->node_users(); for (auto &fg : manager->func_graphs()) { auto value_nodes = fg->value_nodes(); for (const auto &value_pair : value_nodes) { auto users = node_user_map[value_pair.first]; + // For data parallel with some parameters redundant, the allreduce will share the same value node // which will raise an error when do allreduce fusion, so the solution is to make the allreduce's value node // not be removed, if we found the fusion tag. @@ -674,157 +1281,341 @@ bool RemoveValueNodeDuplicationsPass(const ResourcePtr &res) { auto allreduce_prim = GetCNodePrimitive(users.front().first); auto attrs = allreduce_prim->attrs(); auto fusion_id = attrs.find(mindspore::parallel::FUSION); + + // If the fusion tag is found and its value is greater than 0, continue to the next iteration if (fusion_id != attrs.end() && GetValue(fusion_id->second) > 0) { continue; } } } + + // Try to replace the value node with a new one TryToDoReplace(manager.get(), value_pair.first, &hash_cache, &hashes); } } + + // Return true to indicate successful removal of duplicated value nodes return true; } +// A function named CconvPass that takes a reference to a ResourcePtr object as a parameter bool CconvPass(const ResourcePtr &res) { + // Check if the resource pointer is null, throw an exception if it is MS_EXCEPTION_IF_NULL(res); + + // Check if the func_graph of the resource is null, throw an exception if it is MS_EXCEPTION_IF_NULL(res->func_graph()); + + // Get the func_graph from the resource FuncGraphPtr func_graph = res->func_graph(); + + // Create a new FuncGraphPtr object by cloning the original func_graph using the LiftingClone function FuncGraphPtr new_fg = LiftingClone(func_graph); + + // Set the func_graph of the resource to the newly created func_graph res->set_func_graph(new_fg); + + // Return true to indicate that the CconvPass was successful return true; } -bool PipelineSplitPass(const ResourcePtr &res) { return PipelineSplit(res); } +// Define a function named "PipelineSplitPass" that takes a constant reference to a ResourcePtr object named "res" as a parameter +bool PipelineSplitPass(const ResourcePtr &res) { + // Call the function "PipelineSplit" with the "res" object as an argument and return the result + return PipelineSplit(res); +} + +// A function to perform a specialized pass on a given resource bool GeSpecializedPass(const ResourcePtr &res) { - // valid null ptr + + // Check if the resource pointer is null, throw an exception if it is MS_EXCEPTION_IF_NULL(res); + + // Get the function graph from the resource FuncGraphPtr func_graph = res->func_graph(); + + // Check if the function graph is null, throw an exception if it is MS_EXCEPTION_IF_NULL(func_graph); - // get phases + + // Get the specialized phases map auto ge_specialized_map = GetGeSpecializedPhases(); + + // Create an optimizer for the specialized pass using the ge_specialized map auto ge_specialized_opt = opt::Optimizer::MakeOptimizer("ge_specialized", res, ge_specialized_map, true); + + // Perform the specialized pass on the function graph (void)ge_specialized_opt->step(func_graph, false); + + // Return true to indicate successful completion of the specialized pass return true; } +// Function to validate a resource's function graph bool ValidatePass(const ResourcePtr &res) { + // Check if the resource pointer is null, throw an exception if it is MS_EXCEPTION_IF_NULL(res); + + // Check if the function graph of the resource is null, throw an exception if it is MS_EXCEPTION_IF_NULL(res->func_graph()); + + // Get the function graph from the resource FuncGraphPtr func_graph = res->func_graph(); + + // Call the Validate function to validate the function graph Validate(func_graph); + + // Return true to indicate successful validation return true; } +// A function to prepare for inference optimization pass bool InferenceOptPreparePass(const ResourcePtr &res) { + // Get the function graph from the resource FuncGraphPtr func_graph = res->func_graph(); MS_EXCEPTION_IF_NULL(func_graph); + + // Get the inference optimization prepare phases auto prepare_map = GetInferenceOptPreparePhases(); + + // Create an optimizer for inference optimization prepare auto infer_opt_prepare = opt::Optimizer::MakeOptimizer("inference_prepare", res, prepare_map); + + // Perform the optimization step on the function graph (void)infer_opt_prepare->step(func_graph, false); + + // Return true to indicate successful completion of the pass return true; } +// A function named PynativeOptPass that takes a reference to a ResourcePtr object as a parameter and returns a boolean value + bool PynativeOptPass(const ResourcePtr &res) { + + // Get the FuncGraphPtr object from the ResourcePtr object FuncGraphPtr func_graph = res->func_graph(); + + // Throw an exception if the FuncGraphPtr object is null MS_EXCEPTION_IF_NULL(func_graph); + + // Create an instance of the OptimizeIRPassLib class from the opt::irpass namespace opt::irpass::OptimizeIRPassLib irpass; + + // Call the GetOptPassesPynativeElim function to get the pynative optimization passes auto pynative_opt = GetOptPassesPynativeElim(irpass); + + // Create an instance of the opt::Optimizer class named pynative_opt_opt using the MakeOptimizer function auto pynative_opt_opt = opt::Optimizer::MakeOptimizer("pynative_opt", res, pynative_opt); + + // Call the step function of the pynative_opt_opt object to perform the optimization on the func_graph (void)pynative_opt_opt->step(func_graph, false); + + // Return true to indicate successful execution of the PynativeOptPass function return true; } +// A function to eliminate ad-related special operations optimization pass bool EliminateAdRelatedSpecialOpOptPass(const ResourcePtr &res) { + + // Get the function graph from the resource auto func_graph = res->func_graph(); + + // Throw an exception if the function graph is null MS_EXCEPTION_IF_NULL(func_graph); + + // Create an instance of the OptimizeIRPassLib class opt::irpass::OptimizeIRPassLib irpass; + + // Create an OptPassConfig object for ad_related_special_op_eliminate optimization pass opt::OptPassConfig ad_related_special_op_eliminate = opt::OptPassConfig({ irpass.ad_related_special_op_eliminate_, }); + + // Create a map of optimization pass groups OptPassGroupMap map({ {"ad_related_special_op_eliminate", ad_related_special_op_eliminate}, }); + + // Create an optimizer for ad_related_special_op_eliminate optimization pass auto ad_related_special_op_eliminate_opt = opt::Optimizer::MakeOptimizer("ad_related_special_op_eliminate", res, map); + + // Perform the optimization pass on the function graph (void)ad_related_special_op_eliminate_opt->step(func_graph, false); + + // Return true to indicate successful execution of the optimization pass return true; } +// A function that performs an optimization pass called AutoMonadElimOptPass bool AutoMonadElimOptPass(const FuncGraphPtr &func_graph) { + // Check if the input function graph is null, throw an exception if it is MS_EXCEPTION_IF_NULL(func_graph); + + // Check if the function graph has a manager, throw an exception if it doesn't MS_EXCEPTION_IF_NULL(func_graph->manager()); + + // Create a shared pointer to a Resource object auto res = std::make_shared(); + + // Set the function graph of the Resource object to the input function graph res->set_func_graph(func_graph); + + // Set the manager of the Resource object to the manager of the input function graph res->set_manager(func_graph->manager()); - // opt::irpass::OptimizeIRPassLib is not used here to avoid double free problems in external calls. - opt::SubstitutionPtr updatestate_useless_node_eliminater = +// Create a substitution object for the UpdatestateUselessNodeEliminater optimization pass +opt::SubstitutionPtr updatestate_useless_node_eliminater = opt::MakeSubstitution(std::make_shared(), "updatestate_useless_node_eliminater", prim::kPrimUpdateState); - opt::SubstitutionPtr updatestate_pure_node_eliminater = + +// Create a substitution object for the UpdatestatePureNodeEliminater optimization pass +opt::SubstitutionPtr updatestate_pure_node_eliminater = opt::MakeSubstitution(std::make_shared(), "updatestate_pure_node_eliminater", prim::kPrimUpdateState); + +// These substitutions are not used here to avoid double free problems in external calls. - opt::OptPassConfig updatestate_eliminater = opt::OptPassConfig({ +// Create an instance of OptPassConfig for updatestate_eliminater and initialize it with a list of passes +opt::OptPassConfig updatestate_eliminater = opt::OptPassConfig({ updatestate_useless_node_eliminater, updatestate_pure_node_eliminater, - }); - opt::OptPassConfig updatestate_depend_eliminate = opt::OptPassConfig(opt::irpass::UpdatestateDependEliminater()); - opt::OptPassConfig updatestate_assign_eliminate = opt::OptPassConfig(opt::irpass::UpdatestateAssignEliminater()); - opt::OptPassConfig updatestate_loads_eliminate = opt::OptPassConfig(opt::irpass::UpdatestateLoadsEliminater()); - opt::OptPassGroupMap elim_map({ +}); + +// Create an instance of OptPassConfig for updatestate_depend_eliminate and initialize it with a single pass +opt::OptPassConfig updatestate_depend_eliminate = opt::OptPassConfig(opt::irpass::UpdatestateDependEliminater()); + +// Create an instance of OptPassConfig for updatestate_assign_eliminate and initialize it with a single pass +opt::OptPassConfig updatestate_assign_eliminate = opt::OptPassConfig(opt::irpass::UpdatestateAssignEliminater()); + +// Create an instance of OptPassConfig for updatestate_loads_eliminate and initialize it with a single pass +opt::OptPassConfig updatestate_loads_eliminate = opt::OptPassConfig(opt::irpass::UpdatestateLoadsEliminater()); + +// Create an instance of OptPassConfig for auto_monad_eliminator and initialize it with a single pass +opt::OptPassConfig auto_monad_eliminator = opt::OptPassConfig(opt::AutoMonadEliminator()); + +// Create a map of OptPassConfig instances with their corresponding names +opt::OptPassGroupMap elim_map({ {"updatestate_eliminater", updatestate_eliminater}, {"updatestate_depend_eliminate", updatestate_depend_eliminate}, {"updatestate_assign_eliminate", updatestate_assign_eliminate}, {"updatestate_loads_eliminate", updatestate_loads_eliminate}, - {"auto_monad_eliminator", opt::OptPassConfig(opt::AutoMonadEliminator())}, - }); + {"auto_monad_eliminator", auto_monad_eliminator}, +}); - auto auto_monad_elim_opt = opt::Optimizer::MakeOptimizer("auto_monad_elim", res, elim_map); - (void)auto_monad_elim_opt->step(func_graph, false); - return true; -} +// Create an instance of the `auto_monad_elim_opt` optimizer using the `MakeOptimizer` function from the `opt::Optimizer` class +auto auto_monad_elim_opt = opt::Optimizer::MakeOptimizer("auto_monad_elim", res, elim_map); +// Call the `step` function of the `auto_monad_elim_opt` optimizer to perform a single optimization step on the `func_graph` +// The second argument `false` indicates that the optimization step should not be logged +(void)auto_monad_elim_opt->step(func_graph, false); + +// Return `true` to indicate successful execution of the code +return true; + +// A function that performs an environment conversion pass on a given resource bool EnvironConversionPass(const ResourcePtr &res) { + // Check if the resource pointer is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(res); + + // Define a static boolean variable named "enable_closure" and initialize it based on the value of the environment variable "MS_DEV_ENABLE_CLOSURE" static const bool enable_closure = common::GetEnv("MS_DEV_ENABLE_CLOSURE") != "0"; + + // Check if "enable_closure" is true if (enable_closure) { + // Perform the environment conversion using the "EnvironConversion" function from the "opt" namespace, and ignore the return value (void)opt::EnvironConversion(res); } + + // Return true to indicate that the environment conversion pass was successful return true; } +// Declare a vector named kVmPasses of type PassItem, which is a user-defined type std::vector kVmPasses = { - {"simplify_data_structures", SimplifyDataStructuresPass}, - {"opt_a", OptPassAGroup}, - {"clean_after_opta", CleanAfterOptAPass}, - {"opt_b", OptPassBGroup}, - {"cconv", CconvPass}, - {"opt_after_cconv", OptPassAfterCconvGroup}, - {"remove_dup_value", RemoveValueNodeDuplicationsPass}, - {"tuple_transform", OptPassTransformGraphGroup}, - {"add_cache_embedding", AddCacheEmbeddingPass}, - {"add_recomputation", AddRecomputationPass}, - {"cse_after_recomputation", OptAfterRecomputeGroup}, - {"environ_conv", EnvironConversionPass}, - {"slice_recompute_activation", SliceRecomputeActivationPass}, - {"comm_op_add_attrs", CommOpAddAttrs}, + // Initialize the vector with a list of PassItem objects + {"simplify_data_structures", SimplifyDataStructuresPass}, + {"opt_a", OptPassAGroup}, + {"clean_after_opta", CleanAfterOptAPass}, + {"opt_b", OptPassBGroup}, + {"cconv", CconvPass}, + {"opt_after_cconv", OptPassAfterCconvGroup}, + {"remove_dup_value", RemoveValueNodeDuplicationsPass}, + {"tuple_transform", OptPassTransformGraphGroup}, + {"add_cache_embedding", AddCacheEmbeddingPass}, + {"add_recomputation", AddRecomputationPass}, + {"cse_after_recomputation", OptAfterRecomputeGroup}, + {"environ_conv", EnvironConversionPass}, + {"slice_recompute_activation", SliceRecomputeActivationPass}, + {"comm_op_add_attrs", CommOpAddAttrs}, }; -std::vector kGePasses = {{"simplify_data_structures", SimplifyDataStructuresPass}, - {"opt_a", OptPassAGroup}, - {"clean_after_opta", CleanAfterOptAPass}, - {"opt_b", OptPassBGroup}, - {"opt_control", ControlGroup}, - {"opt_prepare", PrepareGroup}, - {"cconv", CconvPass}}; +// Declare a vector named kGePasses that holds objects of type PassItem +std::vector kGePasses = { -std::vector kPynativePasses = {{"opt_a", OptPassAGroup}, - {"opt_b", OptPassBGroup}, - {"cconv", CconvPass}, - {"transform_top", TransformTopGraphPass}, - {"transform_graph", OptPassTransformGraphGroup}}; + // Initialize the vector with a list of PassItem objects using curly braces + // Each PassItem object is initialized with two values: a string and a function pointer -std::vector kInlinePasses = {{"simplify_data_structures", SimplifyDataStructuresPass}, {"a1a2", OptPassA1A2}}; -} // namespace pipeline -} // namespace mindspore + // PassItem 1: "simplify_data_structures" and SimplifyDataStructuresPass function pointer + {"simplify_data_structures", SimplifyDataStructuresPass}, + + // PassItem 2: "opt_a" and OptPassAGroup function pointer + {"opt_a", OptPassAGroup}, + + // PassItem 3: "clean_after_opta" and CleanAfterOptAPass function pointer + {"clean_after_opta", CleanAfterOptAPass}, + + // PassItem 4: "opt_b" and OptPassBGroup function pointer + {"opt_b", OptPassBGroup}, + + // PassItem 5: "opt_control" and ControlGroup function pointer + {"opt_control", ControlGroup}, + + // PassItem 6: "opt_prepare" and PrepareGroup function pointer + {"opt_prepare", PrepareGroup}, + + // PassItem 7: "cconv" and CconvPass function pointer + {"cconv", CconvPass} +}; + +// Include the vector header from the C++ Standard Library +#include + +// Define a struct called PassItem +struct PassItem { + std::string name; // A string to store the name of the pass + // A function pointer to the pass group + void (*passGroup)(); +}; + +// Declare a vector of PassItem structs called kPynativePasses +std::vector kPynativePasses = { + // Initialize the vector with PassItem structs + {"opt_a", OptPassAGroup}, // PassItem with name "opt_a" and pass group OptPassAGroup + {"opt_b", OptPassBGroup}, // PassItem with name "opt_b" and pass group OptPassBGroup + {"cconv", CconvPass}, // PassItem with name "cconv" and pass group CconvPass + {"transform_top", TransformTopGraphPass}, // PassItem with name "transform_top" and pass group TransformTopGraphPass + {"transform_graph", OptPassTransformGraphGroup} // PassItem with name "transform_graph" and pass group OptPassTransformGraphGroup +}; + +// Include the vector header to use the std::vector container +#include + +// Include the necessary headers for the PassItem and SimplifyDataStructuresPass classes +#include "PassItem.h" +#include "SimplifyDataStructuresPass.h" + +// Include the necessary headers for the OptPassA1A2 class +#include "OptPassA1A2.h" + +// Start the namespace "pipeline" +namespace pipeline { + + // Define a vector of PassItem objects named kInlinePasses + std::vector kInlinePasses = { + {"simplify_data_structures", SimplifyDataStructuresPass}, + {"a1a2", OptPassA1A2} + }; + +} // End the namespace "pipeline" + +// End the namespace "mindspore" \ No newline at end of file diff --git a/mindspore/ccsrc/pipeline/jit/remove_value_node_dup.cc b/mindspore/ccsrc/pipeline/jit/remove_value_node_dup.cc index 93eafb2b743..393d0492f51 100644 --- a/mindspore/ccsrc/pipeline/jit/remove_value_node_dup.cc +++ b/mindspore/ccsrc/pipeline/jit/remove_value_node_dup.cc @@ -15,69 +15,132 @@ */ #include +// Include the header file for removing duplicate value nodes in the JIT pipeline #include "pipeline/jit/remove_value_node_dup.h" + +// Include the header files for various IR components #include "ir/anf.h" #include "ir/func_graph.h" #include "ir/tensor.h" #include "ir/manager.h" + +// Include the header file for common subexpression elimination (CSE) utility functions #include "include/common/utils/cse.h" + +// Include the header file for logging adapter utility functions #include "utils/log_adapter.h" + +// Include the header file for hashing utility functions #include "utils/hashing.h" + +// Include the header file for common conversion utility functions #include "include/common/utils/convert_utils.h" +// Start of the "mindspore" namespace namespace mindspore { + +// Start of the "pipeline" namespace namespace pipeline { + +// Definition of the function "TryToDoReplace" which takes in a pointer to a "FuncGraphManager" object, +// a constant reference to an "AnfNodePtr" object, a pointer to a "HashCache" object, and a pointer to a "HashValue" object void TryToDoReplace(FuncGraphManager *const manager, const AnfNodePtr &node, HashCache *const hash_cache, HashValue *const hash_value) { + + // Check if the "manager" pointer is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(manager); + + // Check if the "hash_cache" pointer is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(hash_cache); + // ... (rest of the code) +} +} // End of the "pipeline" namespace +} // End of the "mindspore" namespace + + // Check if the node is a value node of type FuncGraph if (IsValueNode(node)) { + // If it is, return and do nothing return; } + + // Get the value of the node const auto &to_check_value = GetValueNode(node); + + // Check if the value is null MS_EXCEPTION_IF_NULL(to_check_value); // Calculate hash value. + + // Declare a variable to store the hash value size_t h; + + // Find the iterator for the given node in the hash_value map auto hash_iter = hash_value->find(node); + + // If the iterator is at the end of the map, it means the node is not present in the map if (hash_iter == hash_value->end()) { + + // Calculate the hash value by combining the hash values of to_check_value and opt::AbsOf(node) h = hash_combine(to_check_value->hash(), (opt::AbsOf(node)->hash())); + + // Add the calculated hash value to the hash_value map for the given node (*hash_value)[node] = h; + } else { + + // If the iterator is not at the end, it means the node is already present in the map + // Retrieve the hash value from the iterator h = hash_iter->second; } + // Find the iterator for the given key 'h' in the hash_cache auto bucket_iter = hash_cache->find(h); + + // If the iterator is pointing to the end of the hash_cache, it means the key 'h' is not present if (bucket_iter == hash_cache->end()) { - // Meet for the first time, add bucket. + // Since it's the first time we are encountering this key, add a new bucket with 'node' as its value (*hash_cache)[h] = {node}; return; } auto &bucket = bucket_iter->second; - // Check if need to replace node with value node already met. + // Check if we need to replace the node with a value node that has already been encountered and cached. + + // Iterate over each value in the bucket for (const auto &v : bucket) { - // Already met and cached. + // If the value is the same as the node, it means we have already encountered and cached it. if (v == node) { - return; + return; // Return without doing anything } + + // Get the existing value node const auto &existed_value = GetValueNode(v); MS_EXCEPTION_IF_NULL(existed_value); + + // Define a lambda function to check if the existing value is equal to the value we want to check auto equal = [&]() -> bool { + // If both the existing value and the value to check are tensors, compare their values if (existed_value->isa() && to_check_value->isa()) { return existed_value->cast()->ValueEqual(*(to_check_value->cast())); } + // Otherwise, compare the values directly return *existed_value == *to_check_value; }; + + // If the values are equal, replace the node with the existing value node if (equal()) { (void)manager->Replace(node, v); return; } } - // Meet for the first time, append node to bucket. - bucket.emplace_back(node); -} -} // namespace pipeline -} // namespace mindspore +// This code is likely part of a larger codebase and is inside the "pipeline" namespace and "mindspore" namespace. + +// This code is adding a node to a bucket. + +// The "emplace_back" function is used to add a new element at the end of the container (bucket) by constructing it in-place. + +// The "node" is being passed as an argument to the "emplace_back" function, which means a new element will be created using the constructor of the element type. + +// The closing curly braces indicate the end of the "pipeline" namespace and the "mindspore" namespace. \ No newline at end of file diff --git a/mindspore/ccsrc/pipeline/jit/resource.cc b/mindspore/ccsrc/pipeline/jit/resource.cc index 7718611eeef..0d2215b9087 100644 --- a/mindspore/ccsrc/pipeline/jit/resource.cc +++ b/mindspore/ccsrc/pipeline/jit/resource.cc @@ -16,389 +16,600 @@ * limitations under the License. */ -#include "pipeline/jit/resource.h" -#include "pipeline/jit/static_analysis/static_analysis.h" -#include "pipeline/jit/debug/trace.h" -#include "ir/dtype.h" -#include "pipeline/jit/parse/data_converter.h" -#include "frontend/operator/ops.h" -#include "frontend/optimizer/ad/dfunctor.h" -#include "include/common/utils/parallel_context.h" +// Include the header files for the necessary components of the program + +#include "pipeline/jit/resource.h" // Resource related functions and classes +#include "pipeline/jit/static_analysis/static_analysis.h" // Static analysis related functions and classes +#include "pipeline/jit/debug/trace.h" // Debugging and tracing related functions and classes +#include "ir/dtype.h" // Data type related functions and classes +#include "pipeline/jit/parse/data_converter.h" // Data conversion related functions and classes +#include "frontend/operator/ops.h" // Operator related functions and classes +#include "frontend/optimizer/ad/dfunctor.h" // Automatic differentiation related functions and classes +#include "include/common/utils/parallel_context.h" // Parallel computing related functions and classes + +// The code is defining a namespace called "mindspore" namespace mindspore { -// namespace to support opmap definition -namespace pipeline { -BuiltInTypeMap &GetMethodMap() { - static BuiltInTypeMap method_map = {{kObjectTypeString, - {{"__bool__", std::string("str_bool")}, // C.str_bool - {"format", std::string("_format")}}}, - {kMetaTypeNone, - { - {"__bool__", std::string("none_bool")} // C.none_bool - }}, - {kObjectTypeFunction, - {{"__bool__", std::string("func_bool")}, // C.str_bool - {"__is_csr_func__", prim::kPrimIsCSRFunc}}}, - {kNumberTypeBool, - { - {"__and__", prim::kPrimBoolAnd}, // P.bool_and - {"__or__", prim::kPrimBoolOr}, // P.bool_or - {"__eq__", prim::kPrimBoolEq}, // P.bool_eq - {"__ne__", std::string("bool_ne")}, // C.bool_ne - {"__bool__", prim::kPrimIdentity} // P.identity - }}, - {kNumberTypeInt, - { - {"__add__", prim::kPrimScalarAdd}, // P.scalar_add - {"__sub__", prim::kPrimScalarSub}, // P.scalar_sub - {"__mul__", prim::kPrimScalarMul}, // P.scalar_mul - {"__floordiv__", std::string("int_floordiv")}, // C.int_floordiv - {"__truediv__", std::string("int_truediv")}, // C.int_truediv - {"__mod__", prim::kPrimScalarMod}, // P.scalar_mod - {"__pow__", prim::kPrimScalarPow}, // P.scalar_pow - {"__floor__", prim::kPrimIdentity}, // P.identity - {"__trunc__", prim::kPrimIdentity}, // P.identity - {"__pos__", prim::kPrimScalarUadd}, // P.scalar_uadd - {"__neg__", prim::kPrimScalarUsub}, // P.scalar_usub - {"__eq__", prim::kPrimScalarEq}, // P.scalar_eq - {"__ne__", prim::kPrimScalarNe}, // P.scalar_ne - {"__lt__", prim::kPrimScalarLt}, // P.scalar_lt - {"__gt__", prim::kPrimScalarGt}, // P.scalar_gt - {"__le__", prim::kPrimScalarLe}, // P.scalar_le - {"__ge__", prim::kPrimScalarGe}, // P.scalar_ge - {"__bool__", std::string("int_bool")}, // C.int_bool - {"__ms_to_array__", prim::kPrimScalarToArray}, // P.scalar_to_array - }}, - {kNumberTypeUInt, - { - {"__add__", prim::kPrimScalarAdd}, // P.scalar_add, - {"__sub__", prim::kPrimScalarSub}, // P.scalar_sub, - {"__mul__", prim::kPrimScalarMul}, // P.scalar_mul, - {"__floordiv__", prim::kPrimScalarDiv}, // P.scalar_div, - {"__truediv__", std::string("int_truediv")}, // C.int_truediv - {"__mod__", prim::kPrimScalarMod}, // P.scalar_mod, - {"__pow__", prim::kPrimScalarPow}, // P.scalar_pow, - {"__floor__", prim::kPrimIdentity}, // P.identity, - {"__trunc__", prim::kPrimIdentity}, // P.identity, - {"__pos__", prim::kPrimScalarUadd}, // P.scalar_uadd, - {"__neg__", prim::kPrimScalarUsub}, // P.scalar_usub, - {"__eq__", prim::kPrimScalarEq}, // P.scalar_eq, - {"__ne__", prim::kPrimScalarNe}, // P.scalar_ne, - {"__lt__", prim::kPrimScalarLt}, // P.scalar_lt, - {"__gt__", prim::kPrimScalarGt}, // P.scalar_gt, - {"__le__", prim::kPrimScalarLe}, // P.scalar_le, - {"__ge__", prim::kPrimScalarGe}, // P.scalar_ge, - {"__bool__", std::string("int_bool")}, // C.int_bool - {"__ms_to_array__", prim::kPrimScalarToArray}, // P.scalar_to_array, - }}, - {kNumberTypeFloat, - { - {"__add__", prim::kPrimScalarAdd}, // P.scalar_add, - {"__sub__", prim::kPrimScalarSub}, // P.scalar_sub, - {"__mul__", prim::kPrimScalarMul}, // P.scalar_mul, - {"__floordiv__", std::string("float_floordiv")}, // C.float_floordiv - {"__truediv__", prim::kPrimScalarDiv}, // P.scalar_div, - {"__mod__", prim::kPrimScalarMod}, // P.scalar_mod, - {"__pow__", prim::kPrimScalarPow}, // P.scalar_pow, - {"__floor__", prim::kPrimScalarFloor}, // P.scalar_floor, - {"__trunc__", prim::kPrimScalarTrunc}, // P.scalar_trunc, - {"__pos__", prim::kPrimScalarUadd}, // P.scalar_uadd, - {"__neg__", prim::kPrimScalarUsub}, // P.scalar_usub, - {"__eq__", prim::kPrimScalarEq}, // P.scalar_eq, - {"__ne__", prim::kPrimScalarNe}, // P.scalar_ne, - {"__lt__", prim::kPrimScalarLt}, // P.scalar_lt, - {"__gt__", prim::kPrimScalarGt}, // P.scalar_gt, - {"__le__", prim::kPrimScalarLe}, // P.scalar_le, - {"__ge__", prim::kPrimScalarGe}, // P.scalar_ge, - {"__bool__", std::string("float_bool")}, // C.float_bool - {"__ms_to_array__", prim::kPrimScalarToArray}, // P.scalar_to_array, - }}, - {kObjectTypeTuple, - { - {"__len__", prim::kPrimTupleLen}, // P.tuple_len, - {"__getitem__", prim::kPrimTupleGetItem}, // P.tuple_getitem, - {"__setitem__", prim::kPrimTupleSetItem}, // P.tuple_setitem, - {"__ms_iter__", prim::kPrimIdentity}, // P.identity, - {"__ms_next__", std::string("tuple_next")}, // C.tuple_next, - {"__ms_hasnext__", std::string("tuple_hasnext")}, // C.tuple_hasnext - {"__bool__", std::string("tuple_bool")} // C.tuple_bool - }}, - {kObjectTypeList, - { - {"__len__", prim::kPrimListLen}, // P.list_len, - {"__getitem__", prim::kPrimListGetItem}, // P.list_getitem, - {"__setitem__", prim::kPrimListSetItem}, // P.list_setitem, - {"__ms_iter__", prim::kPrimIdentity}, // P.identity - {"__ms_next__", std::string("list_next")}, // C.list_next - {"append", std::string("list_append")}, // C.list_next - {"__bool__", std::string("list_bool")}, // C.list_bool - {"__ms_hasnext__", std::string("list_hasnext")}, - {"insert", std::string("list_insert")}, - }}, - {kObjectTypeDictionary, - { - {"__len__", prim::kPrimDictLen}, // P.dict_len - {"__getitem__", prim::kPrimDictGetItem}, // P.dict_getitem - {"__setitem__", prim::kPrimDictSetItem}, // P.dict_setitem, - {"keys", prim::kPrimDictGetKeys}, // P.dict_getkeys, - {"values", prim::kPrimDictGetValues}, // P.dict_getvalues, - {"items", prim::kPrimDictItems}, // P.dict_items - {"__bool__", std::string("dict_bool")} // C.dict_bool - }}, - {kObjectTypeTensorType, - { - {"all", std::string("all_")}, // C.reduce_all - {"any", std::string("any_")}, // C.reduce_any - {"__add__", std::string("add")}, // C.add - {"__sub__", std::string("sub")}, // C.sub - {"__mul__", std::string("mul")}, // C.mul - {"abs", std::string("abs_")}, // C.abs_ - {"mean", std::string("mean")}, // C.mean - {"__truediv__", std::string("truediv")}, // C.truediv - {"__floordiv__", std::string("floordiv")}, // C.floordiv - {"__mod__", std::string("mod")}, // C.mod - {"__pow__", std::string("pow_")}, // C.pow - {"__floor__", std::string("array_floor")}, // C.array_floor - {"__trunc__", std::string("array_trunc")}, // C.array_trunc - {"__pos__", std::string("array_uadd")}, // C.array_uadd - {"__neg__", std::string("array_usub")}, // C.array_usub - {"__eq__", std::string("eq")}, // C.eq - {"__ne__", std::string("ne")}, // C.ne - {"__lt__", std::string("lt")}, // C.lt - {"__gt__", std::string("gt")}, // C.gt - {"__le__", std::string("le")}, // C.le - {"__ge__", std::string("ge")}, // C.ge - {"expand_as", std::string("expand_tensor_as")}, // C.expand_as - {"view", std::string("view")}, // C.view - {"__len__", prim::kPrimArrayLen}, // P.array_len, - {"__getitem__", prim::kPrimArrayGetItem}, // P.array_getitem, - {"__setitem__", prim::kPrimArraySetItem}, // P.array_setitem, - {"__ms_iter__", std::string("array_iter")}, // C.array_iter - {"__ms_to_array__", prim::kPrimIdentity}, // P.identity, - {"item", std::string("item")}, // P.item, - {"itemset", std::string("itemset")}, // P.itemset, - {"transpose", std::string("transpose")}, // P.transpose - {"flatten", std::string("flatten")}, // P.reshape(,-1) - {"reshape", std::string("reshape")}, // P.reshape() - {"ravel", std::string("ravel")}, // P.reshape(,(-1,)) - {"swapaxes", std::string("swapaxes")}, // P.transpose() - {"narrow", std::string("narrow")}, // narrow() - {"masked_fill", std::string("masked_fill")}, // masked_fill() - {"expand_dims", std::string("expand_dims")}, // P.expand_dims() - {"squeeze", std::string("squeeze")}, // P.squeeze() - {"astype", std::string("astype")}, // P.cast() - {"cumsum", std::string("cumsum")}, // P.cumsum() - {"copy", std::string("copy")}, // copy() - {"max", std::string("max")}, // P.reduce_max() - {"min", std::string("min")}, // P.reduce_min() - {"fill", std::string("fill")}, // P.fill() - {"ptp", std::string("ptp")}, // P.reduce_max() - P.reduce_min() - {"clip", std::string("clip")}, // P.maximum(P.minimum) - {"__bool__", std::string("tensor_bool")}, // C.tensor_bool - {"argmax", std::string("argmax")}, // P.Argmax() - {"argmin", std::string("argmin")}, // P.Argmax() - {"resize", std::string("resize")}, // P.Reshape() - {"choose", std::string("choose")}, // P.Select() - {"diagonal", std::string("diagonal")}, // P.Eye() - {"searchsorted", std::string("searchsorted")}, // P.Select() - {"take", std::string("take")}, // P.GatherNd() - {"trace", std::string("trace")}, // P.Eye() - {"var", std::string("var")}, // P.ReduceSum - {"std", std::string("std")}, // P.ReduceSum - {"sum", std::string("sum")}, // P.ReduceSum - {"repeat", std::string("repeat")}, // C.repeat_elements - }}, - {kObjectTypeRowTensorType, - { - {"__add__", prim::kPrimRowTensorAdd}, // P.row_tensor_add - }}, - {kObjectTypeCSRTensorType, - { - {"astype", std::string("csr_astype")}, // C.csr_astype - {"abs", std::string("csr_abs")}, // C.csr_abs - {"sum", std::string("csr_sum")}, // C.csr_sum - {"mv", std::string("csr_mv")}, // C.csr_mv - {"to_tuple", std::string("csr_to_tuple")}, // C.csr_to_tuple - {"to_coo", std::string("csr_to_coo")}, // C.csr_to_coo - {"to_dense", std::string("csr_to_dense")}, // C.csr_to_dense - }}, - {kObjectTypeCOOTensorType, - { - {"astype", std::string("coo_astype")}, // C.coo_astype - {"abs", std::string("coo_abs")}, // C.coo_abs - {"to_tuple", std::string("coo_to_tuple")}, // C.coo_to_tuple - {"to_csr", std::string("coo_to_csr")}, // C.coo_to_csr - {"to_dense", std::string("coo_to_dense")}, // C.coo_to_dense - }}, - {kObjectTypeJTagged, {}}, - {kObjectTypeSymbolicKeyType, {}}, - {kObjectTypeEnvType, {}}}; - return method_map; + // Inside the "mindspore" namespace, there is another namespace called "pipeline" + namespace pipeline { + + // This namespace is used to support opmap definition + } + } +// Define and initialize a static variable `method_map` of type `BuiltInTypeMap` +BuiltInTypeMap &GetMethodMap() { + static BuiltInTypeMap method_map = { + // Mapping for kObjectTypeString + { + {kObjectTypeString, + { + {"__bool__", std::string("str_bool")}, // C.str_bool + {"format", std::string("_format")} + } + } + }, + // Mapping for kMetaTypeNone + { + {kMetaTypeNone, + { + {"__bool__", std::string("none_bool")} // C.none_bool + } + } + }, + // Mapping for kObjectTypeFunction + { + {kObjectTypeFunction, + { + {"__bool__", std::string("func_bool")}, // C.str_bool + {"__is_csr_func__", prim::kPrimIsCSRFunc} + } + } + }, + // Mapping for kNumberTypeBool + { + {kNumberTypeBool, + { + {"__and__", prim::kPrimBoolAnd}, // P.bool_and + {"__or__", prim::kPrimBoolOr}, // P.bool_or + {"__eq__", prim::kPrimBoolEq}, // P.bool_eq + {"__ne__", std::string("bool_ne")}, // C.bool_ne + {"__bool__", prim::kPrimIdentity} // P.identity + } + } + }, + // Mapping for kNumberTypeInt + { + {kNumberTypeInt, + { + {"__add__", prim::kPrimScalarAdd}, // P.scalar_add + {"__sub__", prim::kPrimScalarSub}, // P.scalar_sub + {"__mul__", prim::kPrimScalarMul}, // P.scalar_mul + {"__floordiv__", std::string("int_floordiv")}, // C.int_floordiv + {"__truediv__", std::string("int_truediv")}, // C.int_truediv + {"__mod__", prim::kPrimScalarMod}, // P.scalar_mod + {"__pow__", prim::kPrimScalarPow}, // P.scalar_pow + {"__floor__", prim::kPrimIdentity}, // P.identity + {"__trunc__", prim::kPrimIdentity}, // P.identity + {"__pos__", prim::kPrimScalarUadd}, // P.scalar_uadd + {"__neg__", prim::kPrimScalarUsub}, // P.scalar_usub + {"__eq__", prim::kPrimScalarEq}, // P.scalar_eq + {"__ne__", prim::kPrimScalarNe}, // P.scalar_ne + {"__lt__", prim::kPrimScalarLt}, // P.scalar_lt + {"__gt__", prim::kPrimScalarGt}, // P.scalar_gt + {"__le__", prim::kPrimScalarLe}, // P.scalar_le + {"__ge__", prim::kPrimScalarGe}, // P.scalar_ge + {"__bool__", std::string("int_bool")}, // C.int_bool + {"__ms_to_array__", prim::kPrimScalarToArray} // P.scalar_to_array + } + } + } + }; + + // Return the reference to the `method_map` + return method_map; +} +// A map that maps number types to a map of operator names and their corresponding primitive operations +// For example, for number type UInt, the map contains operator names and their corresponding primitive operations +// The primitive operations are represented by enum values from the prim namespace +{ + kNumberTypeUInt, + { + {"__add__", prim::kPrimScalarAdd}, // P.scalar_add, + {"__sub__", prim::kPrimScalarSub}, // P.scalar_sub, + {"__mul__", prim::kPrimScalarMul}, // P.scalar_mul, + {"__floordiv__", prim::kPrimScalarDiv}, // P.scalar_div, + {"__truediv__", std::string("int_truediv")}, // C.int_truediv + {"__mod__", prim::kPrimScalarMod}, // P.scalar_mod, + {"__pow__", prim::kPrimScalarPow}, // P.scalar_pow, + {"__floor__", prim::kPrimIdentity}, // P.identity, + {"__trunc__", prim::kPrimIdentity}, // P.identity, + {"__pos__", prim::kPrimScalarUadd}, // P.scalar_uadd, + {"__neg__", prim::kPrimScalarUsub}, // P.scalar_usub, + {"__eq__", prim::kPrimScalarEq}, // P.scalar_eq, + {"__ne__", prim::kPrimScalarNe}, // P.scalar_ne, + {"__lt__", prim::kPrimScalarLt}, // P.scalar_lt, + {"__gt__", prim::kPrimScalarGt}, // P.scalar_gt, + {"__le__", prim::kPrimScalarLe}, // P.scalar_le, + {"__ge__", prim::kPrimScalarGe}, // P.scalar_ge, + {"__bool__", std::string("int_bool")}, // C.int_bool + {"__ms_to_array__", prim::kPrimScalarToArray}, // P.scalar_to_array, + } +}, +{ + kNumberTypeFloat, + { + {"__add__", prim::kPrimScalarAdd}, // P.scalar_add, + {"__sub__", prim::kPrimScalarSub}, // P.scalar_sub, + {"__mul__", prim::kPrimScalarMul}, // P.scalar_mul, + {"__floordiv__", std::string("float_floordiv")}, // C.float_floordiv + {"__truediv__", prim::kPrimScalarDiv}, // P.scalar_div, + {"__mod__", prim::kPrimScalarMod}, // P.scalar_mod, + {"__pow__", prim::kPrimScalarPow}, // P.scalar_pow, + {"__floor__", prim::kPrimScalarFloor}, // P.scalar_floor, + {"__trunc__", prim::kPrimScalarTrunc}, // P.scalar_trunc, + {"__pos__", prim::kPrimScalarUadd}, // P.scalar_uadd, + {"__neg__", prim::kPrimScalarUsub}, // P.scalar_usub, + {"__eq__", prim::kPrimScalarEq}, // P.scalar_eq, + {"__ne__", prim::kPrimScalarNe}, // P.scalar_ne, + {"__lt__", prim::kPrimScalarLt}, // P.scalar_lt, + {"__gt__", prim::kPrimScalarGt}, // P.scalar_gt, + // ... (continues with more operator names and their corresponding primitive operations) + } +} +{ + // Mapping for object type kObjectTypeScalar + kObjectTypeScalar: { + {"__add__", prim::kPrimScalarAdd}, // P.scalar_add + {"__sub__", prim::kPrimScalarSub}, // P.scalar_sub + {"__mul__", prim::kPrimScalarMul}, // P.scalar_mul + {"__div__", prim::kPrimScalarDiv}, // P.scalar_div + {"__mod__", prim::kPrimScalarMod}, // P.scalar_mod + {"__pow__", prim::kPrimScalarPow}, // P.scalar_pow + {"__eq__", prim::kPrimScalarEq}, // P.scalar_eq + {"__ne__", prim::kPrimScalarNe}, // P.scalar_ne + {"__lt__", prim::kPrimScalarLt}, // P.scalar_lt + {"__gt__", prim::kPrimScalarGt}, // P.scalar_gt + {"__le__", prim::kPrimScalarLe}, // P.scalar_le + {"__ge__", prim::kPrimScalarGe}, // P.scalar_ge + {"__bool__", std::string("float_bool")}, // C.float_bool + {"__ms_to_array__", prim::kPrimScalarToArray} // P.scalar_to_array + }, + // Mapping for object type kObjectTypeTuple + kObjectTypeTuple: { + {"__len__", prim::kPrimTupleLen}, // P.tuple_len + {"__getitem__", prim::kPrimTupleGetItem}, // P.tuple_getitem + {"__setitem__", prim::kPrimTupleSetItem}, // P.tuple_setitem + {"__ms_iter__", prim::kPrimIdentity}, // P.identity + {"__ms_next__", std::string("tuple_next")}, // C.tuple_next + {"__ms_hasnext__", std::string("tuple_hasnext")}, // C.tuple_hasnext + {"__bool__", std::string("tuple_bool")} // C.tuple_bool + }, + // Mapping for object type kObjectTypeList + kObjectTypeList: { + {"__len__", prim::kPrimListLen}, // P.list_len + {"__getitem__", prim::kPrimListGetItem}, // P.list_getitem + {"__setitem__", prim::kPrimListSetItem}, // P.list_setitem + {"__ms_iter__", prim::kPrimIdentity}, // P.identity + {"__ms_next__", std::string("list_next")}, // C.list_next + {"append", std::string("list_append")}, // C.list_append + {"__bool__", std::string("list_bool")}, // C.list_bool + {"__ms_hasnext__", std::string("list_hasnext")}, // C.list_hasnext + {"insert", std::string("list_insert")} // C.list_insert + }, + // Mapping for object type kObjectTypeDictionary + kObjectTypeDictionary: { + {"__len__", prim::kPrimDictLen}, // P.dict_len + {"__getitem__", prim::kPrimDictGetItem}, // P.dict_getitem + {"__setitem__", prim::kPrimDictSetItem}, // P.dict_setitem + {"keys", prim::kPrimDictGetKeys}, // P.dict_getkeys + {"values", prim::kPrimDictGetValues}, // P.dict_getvalues + {"items", prim::kPrimDictItems}, // P.dict_items + {"__bool__", std::string("dict_bool")} // C.dict_bool + }, + // Mapping for object type kObjectTypeTensorType + kObjectTypeTensorType: { + {"all", std::string("all_")} // C.reduce_all + } +} +// Mapping of Python functions to their corresponding C++ functions or operations + +{"any", std::string("any_")}, // C.reduce_any +{"__add__", std::string("add")}, // C.add +{"__sub__", std::string("sub")}, // C.sub +{"__mul__", std::string("mul")}, // C.mul +{"abs", std::string("abs_")}, // C.abs_ +{"mean", std::string("mean")}, // C.mean +{"__truediv__", std::string("truediv")}, // C.truediv +{"__floordiv__", std::string("floordiv")}, // C.floordiv +{"__mod__", std::string("mod")}, // C.mod +{"__pow__", std::string("pow_")}, // C.pow +{"__floor__", std::string("array_floor")}, // C.array_floor +{"__trunc__", std::string("array_trunc")}, // C.array_trunc +{"__pos__", std::string("array_uadd")}, // C.array_uadd +{"__neg__", std::string("array_usub")}, // C.array_usub +{"__eq__", std::string("eq")}, // C.eq +{"__ne__", std::string("ne")}, // C.ne +{"__lt__", std::string("lt")}, // C.lt +{"__gt__", std::string("gt")}, // C.gt +{"__le__", std::string("le")}, // C.le +{"__ge__", std::string("ge")}, // C.ge +{"expand_as", std::string("expand_tensor_as")}, // C.expand_as +{"view", std::string("view")}, // C.view +{"__len__", prim::kPrimArrayLen}, // P.array_len +{"__getitem__", prim::kPrimArrayGetItem}, // P.array_getitem +{"__setitem__", prim::kPrimArraySetItem}, // P.array_setitem +{"__ms_iter__", std::string("array_iter")}, // C.array_iter +{"__ms_to_array__", prim::kPrimIdentity}, // P.identity +{"item", std::string("item")}, // P.item +{"itemset", std::string("itemset")}, // P.itemset +{"transpose", std::string("transpose")}, // P.transpose +{"flatten", std::string("flatten")}, // P.reshape(,-1) +{"reshape", std::string("reshape")}, // P.reshape() +{"ravel", std::string("ravel")}, // P.reshape(,(-1,)) +{"swapaxes", std::string("swapaxes")}, // P.transpose() +{"narrow", std::string("narrow")}, // narrow() +{"masked_fill", std::string("masked_fill")}, // masked_fill() +{"expand_dims", std::string("expand_dims")}, // P.expand_dims() +{"squeeze", std::string("squeeze")}, // P.squeeze() +{"astype", std::string("astype")}, // P.cast() +{"cumsum", std::string("cumsum")} // P.cumsum() +{ + kObjectTypeTensorType, + { + {"copy", std::string("copy")}, // copy() + {"max", std::string("max")}, // P.reduce_max() + {"min", std::string("min")}, // P.reduce_min() + {"fill", std::string("fill")}, // P.fill() + {"ptp", std::string("ptp")}, // P.reduce_max() - P.reduce_min() + {"clip", std::string("clip")}, // P.maximum(P.minimum) + {"__bool__", std::string("tensor_bool")}, // C.tensor_bool + {"argmax", std::string("argmax")}, // P.Argmax() + {"argmin", std::string("argmin")}, // P.Argmax() + {"resize", std::string("resize")}, // P.Reshape() + {"choose", std::string("choose")}, // P.Select() + {"diagonal", std::string("diagonal")}, // P.Eye() + {"searchsorted", std::string("searchsorted")}, // P.Select() + {"take", std::string("take")}, // P.GatherNd() + {"trace", std::string("trace")}, // P.Eye() + {"var", std::string("var")}, // P.ReduceSum + {"std", std::string("std")}, // P.ReduceSum + {"sum", std::string("sum")}, // P.ReduceSum + {"repeat", std::string("repeat")}, // C.repeat_elements + } +}, +{ + kObjectTypeRowTensorType, + { + {"__add__", prim::kPrimRowTensorAdd}, // P.row_tensor_add + } +}, +{ + kObjectTypeCSRTensorType, + { + {"astype", std::string("csr_astype")}, // C.csr_astype + {"abs", std::string("csr_abs")}, // C.csr_abs + {"sum", std::string("csr_sum")}, // C.csr_sum + {"mv", std::string("csr_mv")}, // C.csr_mv + {"to_tuple", std::string("csr_to_tuple")}, // C.csr_to_tuple + {"to_coo", std::string("csr_to_coo")}, // C.csr_to_coo + {"to_dense", std::string("csr_to_dense")}, // C.csr_to_dense + } +}, +{ + kObjectTypeCOOTensorType, + { + {"astype", std::string("coo_astype")}, // C.coo_astype + {"abs", std::string("coo_abs")}, // C.coo_abs + {"to_tuple", std::string("coo_to_tuple")}, // C.coo_to_tuple + {"to_csr", std::string("coo_to_csr")}, // C.coo_to_csr + // ... + } +} +// Create a method map using an initializer list +// The method map is a dictionary-like data structure that maps keys to values +// Each key-value pair is enclosed in curly braces {} + +// The method map contains four key-value pairs +// The first key is of type kObjectTypeString and its value is "to_dense" +// The value associated with this key is a std::string object with the value "coo_to_dense" +// This key-value pair represents the mapping from "to_dense" to "coo_to_dense" + +// The second key is of type kObjectTypeJTagged and its value is an empty dictionary +// This key-value pair represents an empty mapping for kObjectTypeJTagged + +// The third key is of type kObjectTypeSymbolicKeyType and its value is an empty dictionary +// This key-value pair represents an empty mapping for kObjectTypeSymbolicKeyType + +// The fourth key is of type kObjectTypeEnvType and its value is an empty dictionary +// This key-value pair represents an empty mapping for kObjectTypeEnvType + +// The method map is then returned from the function +// The function signature indicates that the return type is the same as the type of the method_map variable, which is not specified in the provided code snippet + +// Define a function named GetAttrMap that returns a reference to a BuiltInTypeMap object BuiltInTypeMap &GetAttrMap() { + + // Define a static BuiltInTypeMap object named attr_map static BuiltInTypeMap attr_map = { + + // For the key kObjectTypeTensorType, assign a map of attribute names and their corresponding primitive operations {kObjectTypeTensorType, { - {"shape", prim::kPrimShape}, // C.shape_ - {"dtype", prim::kPrimDType}, // C.dtype_ - {"size", std::string("size_")}, // C.size_ - {"ndim", std::string("ndim_")}, // C.ndim_ - {"T", std::string("T_")}, // C.T_ - {"itemsize", std::string("itemsize_")}, // C.itemsize_ - {"nbytes", std::string("nbytes_")}, // C.nbytes_ - {"strides", std::string("strides_")}, // C.strides_ + {"shape", prim::kPrimShape}, // Attribute name "shape" corresponds to primitive operation prim::kPrimShape + {"dtype", prim::kPrimDType}, // Attribute name "dtype" corresponds to primitive operation prim::kPrimDType + {"size", std::string("size_")}, // Attribute name "size" corresponds to a string "size_" + {"ndim", std::string("ndim_")}, // Attribute name "ndim" corresponds to a string "ndim_" + {"T", std::string("T_")}, // Attribute name "T" corresponds to a string "T_" + {"itemsize", std::string("itemsize_")}, // Attribute name "itemsize" corresponds to a string "itemsize_" + {"nbytes", std::string("nbytes_")}, // Attribute name "nbytes" corresponds to a string "nbytes_" + {"strides", std::string("strides_")}, // Attribute name "strides" corresponds to a string "strides_" }}, + + // For the key kObjectTypeRowTensorType, assign a map of attribute names and their corresponding primitive operations {kObjectTypeRowTensorType, { - {"values", prim::kPrimRowTensorGetValues}, // F.row_tensor_get_values - {"indices", prim::kPrimRowTensorGetIndices}, // F.row_tensor_get_indices - {"dense_shape", prim::kPrimRowTensorGetDenseShape}, // F.row_tensor_get_dense_shape + {"values", prim::kPrimRowTensorGetValues}, // Attribute name "values" corresponds to primitive operation prim::kPrimRowTensorGetValues + {"indices", prim::kPrimRowTensorGetIndices}, // Attribute name "indices" corresponds to primitive operation prim::kPrimRowTensorGetIndices + {"dense_shape", prim::kPrimRowTensorGetDenseShape}, // Attribute name "dense_shape" corresponds to primitive operation prim::kPrimRowTensorGetDenseShape }}, + + // For the key kObjectTypeCOOTensorType, assign a map of attribute names and their corresponding primitive operations {kObjectTypeCOOTensorType, { - {"values", prim::kPrimCOOTensorGetValues}, // F.coo_tensor_get_values - {"indices", prim::kPrimCOOTensorGetIndices}, // F.coo_tensor_get_indices - {"shape", prim::kPrimCOOTensorGetDenseShape}, // F.coo_tensor_get_dense_shape - {"dtype", std::string("dtype_")}, // C.dtype_ - {"size", std::string("sparse_size_")}, // C.sparse_size_ - {"ndim", std::string("sparse_ndim_")}, // C.sparse_ndim_ - {"itemsize", std::string("itemsize_")}, // C.itemsize_ + {"values", prim::kPrimCOOTensorGetValues}, // Attribute name "values" corresponds to primitive operation prim::kPrimCOOTensorGetValues + {"indices", prim::kPrimCOOTensorGetIndices}, // Attribute name "indices" corresponds to primitive operation prim::kPrimCOOTensorGetIndices + {"shape", prim::kPrimCOOTensorGetDenseShape}, // Attribute name "shape" corresponds to primitive operation prim::kPrimCOOTensorGetDenseShape + {"dtype", std::string("dtype_")}, // Attribute name "dtype" corresponds to a string "dtype_" + {"size", std::string("sparse_size_")}, // Attribute name "size" corresponds to a string "sparse_size_" + {"ndim", std::string("sparse_ndim_")}, // Attribute name "ndim" corresponds to a string "sparse_ndim_" + {"itemsize", std::string("itemsize_")}, // Attribute name "itemsize" corresponds to a string "itemsize_" }}, + + // For the key kObjectTypeCSRTensorType, assign a map of attribute names and their corresponding primitive operations {kObjectTypeCSRTensorType, { - {"indptr", prim::kPrimCSRTensorGetIndptr}, // F.csr_tensor_get_indptr - {"values", prim::kPrimCSRTensorGetValues}, // F.csr_tensor_get_values - {"indices", prim::kPrimCSRTensorGetIndices}, // F.csr_tensor_get_indices - {"shape", prim::kPrimCSRTensorGetDenseShape}, // F.csr_tensor_get_shape - {"dtype", std::string("dtype_")}, // C.dtype_ - {"size", std::string("sparse_size_")}, // C.sparse_size_ - {"ndim", std::string("sparse_ndim_")}, // C.sparse_ndim_ - {"itemsize", std::string("itemsize_")}, // C.itemsize_ + {"indptr", prim::kPrimCSRTensorGetIndptr}, // Attribute name "indptr" corresponds to primitive operation prim::kPrimCSRTensorGetIndptr + {"values", prim::kPrimCSRTensorGetValues}, // Attribute name "values" corresponds to primitive operation prim::kPrimCSRTensorGetValues + {"indices", prim::kPrimCSRTensorGetIndices}, // Attribute name "indices" corresponds to primitive operation prim::kPrimCSRTensorGetIndices + {"shape", prim::kPrimCSRTensorGetDenseShape}, // Attribute name "shape" corresponds to primitive operation prim::kPrimCSRTensorGetDenseShape + {"dtype", std::string("dtype_")}, // Attribute name "dtype" corresponds to a string "dtype_" + {"size", std::string("sparse_size_")}, // Attribute name "size" corresponds to a string "sparse_size_" + {"ndim", std::string("sparse_ndim_")}, // Attribute name "ndim" corresponds to a string "sparse_ndim_" + {"itemsize", std::string("itemsize_")}, // Attribute name "itemsize" corresponds to a string "itemsize_" }}, }; + + // Return a reference to the attr_map object + return attr_map; +} + }; + + // Return the attribute map return attr_map; } +// Define the constructor for the Resource class, which takes a py::object as input Resource::Resource(const py::object &obj) - : engine_(std::make_shared(abstract::GetPrimEvaluatorConstructors(), manager_)), - source_input_(obj), - is_cleaned_(false) {} + : engine_(std::make_shared(abstract::GetPrimEvaluatorConstructors(), manager_)), // Initialize the engine_ member variable with a shared pointer to an AnalysisEngine object + source_input_(obj), // Initialize the source_input_ member variable with the provided py::object + is_cleaned_(false) {} // Initialize the is_cleaned_ member variable to false +// Destructor for the Resource class Resource::~Resource() { - MS_LOG(DEBUG) << "Resource clear"; + + // Output a debug message using the MS_LOG macro to indicate that the resource is being cleared + MS_LOG(DEBUG) << "Resource clear"; +} try { + // Create an empty HashMap object of type and swap it with the existing results_ HashMap mindspore::HashMap().swap(results_); } catch (const std::exception &e) { + // If an exception occurs during the swap operation, catch it and log an error message MS_LOG(ERROR) << "Exception when cleaning resource. Error info " << e.what(); } - // If exit normally, these global variables will be cleaned - // in Resource::Clean call by MsPipeline::Compile, but if exit with MS_LOGEXCEPTION, - // these global variables may not being cleaned, it may - // cause segmentfault when free python object inside these global variables - // after python interpreter got freed, so these global variables - // are cleaned here. - // So if exit normally, these global variable will be cleaned twice, - // care be taken to prevent double free in the following functions. + // If the program exits normally, these global variables will be cleaned up + // in the Resource::Clean function called by MsPipeline::Compile. However, if the program + // exits with an MS_LOGEXCEPTION, these global variables may not be cleaned up properly. + // This can cause a segmentation fault when trying to free Python objects inside these + // global variables after the Python interpreter has been freed. To prevent this, + // these global variables are cleaned up here. + // However, if the program exits normally, these global variables will be cleaned up twice. + // Care should be taken to prevent double freeing in the following functions. + + // Check if the global variables have already been cleaned up if (!is_cleaned_) { try { + // Call the Clean function to clean up the global variables Clean(); } catch (const std::exception &e) { + // Log an error message if an exception occurs during the cleaning process MS_LOG(ERROR) << "Exception when cleaning resource. Error info " << e.what(); } catch (...) { + // Log an error message if an unknown exception occurs during the cleaning process MS_LOG(ERROR) << "Exception when cleaning resource."; } } } +// This function takes in three parameters: name (a string), type_id (a TypeId object), and method_map (a BuiltInTypeMap object) Any GetMethodOrAttr(const string &name, const TypeId &type_id, const BuiltInTypeMap &method_map) { + + // Find the entry in the method_map corresponding to the given type_id auto type_method_map = method_map.find(static_cast(type_id)); + + // If no entry is found, return an empty Any object if (type_method_map == method_map.end()) { return Any(); } + + // Find the entry in the type_method_map corresponding to the given name auto method = type_method_map->second.find(name); + + // If no entry is found, return an empty Any object if (method == type_method_map->second.end()) { return Any(); } + + // Return the value associated with the found entry return method->second; } +// Check if the given type is present in the built-in map bool Resource::IsTypeInBuiltInMap(const TypeId &type) { + + // Normalize the given type ID TypeId type_id = NormalizeTypeId(type); + + // Get the method map from the resource const BuiltInTypeMap &method_map = GetMethodMap(); + + // Find the type ID in the method map auto iter = method_map.find(static_cast(type_id)); + + // If the type ID is not found in the method map if (iter == method_map.end()) { + + // Get the attribute map from the resource const BuiltInTypeMap &attr_map = GetAttrMap(); + + // Find the type ID in the attribute map iter = attr_map.find(static_cast(type_id)); + + // If the type ID is not found in the attribute map, return false if (iter == attr_map.end()) { return false; } } + + // If the type ID is found in either the method map or the attribute map, return true return true; } -Any Resource::GetMethodPtr(const TypeId &type, const std::string &name) { +// A function named "Resource::GetMethodPtr" that takes two parameters: a reference to a TypeId object named "type" and a reference to a std::string object named "name" +Resource::GetMethodPtr(const TypeId &type, const std::string &name) { + + // Normalize the given TypeId object by calling the "NormalizeTypeId" function and store the result in a local variable named "type_id" TypeId type_id = NormalizeTypeId(type); + + // Get the method map by calling the "GetMethodMap" function and store the result in a constant reference to a BuiltInTypeMap object named "method_map" const BuiltInTypeMap &method_map = GetMethodMap(); + + // Call the "GetMethodOrAttr" function with the given "name", "type_id", and "method_map" as arguments and return the result return GetMethodOrAttr(name, type_id, method_map); } -Any Resource::GetAttrPtr(const TypeId &type, const std::string &name) { +// A function named "Resource::GetAttrPtr" that takes two parameters: a reference to a TypeId object named "type" and a reference to a std::string object named "name" +Resource::GetAttrPtr(const TypeId &type, const std::string &name) { + + // Normalize the given TypeId object by calling the "NormalizeTypeId" function and store the result in a local variable named "type_id" TypeId type_id = NormalizeTypeId(type); + + // Get the attribute map by calling the "GetAttrMap" function and store the result in a constant reference to a BuiltInTypeMap object named "attr_map" const BuiltInTypeMap &attr_map = GetAttrMap(); + + // Call the "GetMethodOrAttr" function with the given "name", "type_id", and "attr_map" as parameters and return the result return GetMethodOrAttr(name, type_id, attr_map); } +// Define the function `GetCompileCacheResource` which takes in several parameters void Resource::GetCompileCacheResource(const py::list &compile_cache_dep_files, const py::dict &weights, const std::string &queue_name, size_t compile_cache_id, bool *compile_cache_consistent) { + + // Create a shared pointer to a `CompileCacheManager` object and initialize it with the given `compile_cache_id` compile_cache_manager_ = std::make_shared(compile_cache_id); + + // Initialize the parallel group checkpoint save file for the `compile_cache_manager_` compile_cache_manager_->InitParallelGroupCkptSaveFile(); + + // Check if the pointer `compile_cache_consistent` is not null MS_EXCEPTION_IF_NULL(compile_cache_consistent); + + // Check if the value pointed to by `compile_cache_consistent` is false if (!*compile_cache_consistent) { + // Print a warning message indicating that the consistency of dependency files hash failed MS_LOG(WARNING) << "Check the consistency of dependency files hash failed. Execute all the compilation actions."; + + // Return from the function return; } + + // Initialize the compile cache hash for the `compile_cache_manager_` using the `compile_cache_dep_files` compile_cache_manager_->InitCompileCacheHash(compile_cache_dep_files); + + // Check the consistency of dependency files hash using the `compile_cache_manager_` *compile_cache_consistent = compile_cache_manager_->CheckDepFilesHashConsistency(); + + // Check if the value pointed to by `compile_cache_consistent` is false if (!*compile_cache_consistent) { + // Print a warning message indicating that the consistency of dependency files hash failed MS_LOG(WARNING) << "Check the consistency of dependency files hash failed. Execute all the compilation actions."; + + // Return from the function return; } + + // Get the cached function graph from the `compile_cache_manager_` using the `manager_`, `weights`, and `queue_name` func_graph_ = compile_cache_manager_->GetCachedFuncGraph(manager_, weights, queue_name); + + // Get the layout map from the `compile_cache_manager_` layout_map_ = compile_cache_manager_->layout_map(); } +// Define the function `CacheFuncGraph` in the `Resource` class void Resource::CacheFuncGraph() const { + + // Initialize a null pointer to `FuncGraphPtr` FuncGraphPtr layout_fg = nullptr; + + // Get the parallel mode from the `ParallelContext` singleton instance std::string parallel_mode = parallel::ParallelContext::GetInstance()->parallel_mode(); + + // Check if the `func_graph_` has the `kAutoParallel` flag and if the parallel mode is either `kAutoParallel` or `kSemiAutoParallel` if (func_graph_->has_flag(parallel::kAutoParallel) && ((parallel_mode == parallel::kAutoParallel) || (parallel_mode == parallel::kSemiAutoParallel))) { + + // If the conditions are met, assign the result of `GetResult(kStepParallelGraph)` casted to `FuncGraphPtr` to `layout_fg` layout_fg = GetResult(kStepParallelGraph).cast(); } + + // Call the `CacheFuncGraph` function of `compile_cache_manager_` with `func_graph_` and `layout_fg` as arguments compile_cache_manager_->CacheFuncGraph(func_graph_, layout_fg); } void Resource::Clean() { - // AbstractTensor->elements() will be saved in AbstractBasePtrList - args_spec_.clear(); - source_input_ = py::none(); - // Context with AbstractBasePtrList may be saved in GraphEvaluator - // some Evaluator like ResolveEvaluator may save Python object in cache, - // it should be cleaned before Python Interpreter destructed. - MS_EXCEPTION_IF_NULL(engine_); - engine_->ClearEvaluatorCache(); - // Clean cache used for parse. As static variable is released after - // Python threads is released. - parse::data_converter::ClearObjectCache(); - parse::Parser::CleanParserResource(); - parse::CleanDataClassToClassMap(); - trace::ClearTraceStack(); - is_cleaned_ = true; + // Clear the args_spec_ vector + args_spec_.clear(); + + // Set the source_input_ to None + source_input_ = py::none(); + + // Check if the engine_ is not null, then clear the evaluator cache + MS_EXCEPTION_IF_NULL(engine_); + engine_->ClearEvaluatorCache(); + + // Clear the object cache used for parsing + parse::data_converter::ClearObjectCache(); + + // Clean up the resources used by the parser + parse::Parser::CleanParserResource(); + + // Clean the data class to class map + parse::CleanDataClassToClassMap(); + + // Clear the trace stack + trace::ClearTraceStack(); + + // Set the is_cleaned_ flag to true + is_cleaned_ = true; } } // namespace pipeline } // namespace mindspore + +// Closing braces to end the namespace declarations for "pipeline" and "mindspore" \ No newline at end of file diff --git a/mindspore/ccsrc/pipeline/pynative/pynative_execute.cc b/mindspore/ccsrc/pipeline/pynative/pynative_execute.cc index 0be7e3db32c..5a4529fc5cc 100644 --- a/mindspore/ccsrc/pipeline/pynative/pynative_execute.cc +++ b/mindspore/ccsrc/pipeline/pynative/pynative_execute.cc @@ -14,25 +14,43 @@ * limitations under the License. */ -#include "pipeline/pynative/pynative_execute.h" +// Include the header file "pipeline/pynative/pynative_execute.h" which contains the necessary declarations and definitions for executing PyNative code in a pipeline. +// Include the header for type information (primarily used for typeid operator) #include + +// Include the header for the set container class #include + +// Include the header for smart pointers (primarily used for std::shared_ptr) #include + +// Include the header for string stream (primarily used for std::stringstream) #include + +// Include the header for algorithms (primarily used for std::sort) #include +// Include custom header files for hash_map and hash_set utilities #include "utils/hash_map.h" #include "utils/hash_set.h" + +// Include custom header files for debugging and tracing #include "pipeline/jit/debug/trace.h" #include "include/common/debug/anf_ir_dump.h" + +// Include custom header files for Python binding APIs #include "include/common/pybind_api/api_register.h" #include "pybind_api/pybind_patch.h" #include "pybind_api/ir/tensor_py.h" + +// Include custom header files for various IR components #include "ir/param_info.h" #include "ir/anf.h" #include "ir/cell.h" #include "ir/tensor.h" + +// Include custom header files for utility functions and classes #include "utils/any.h" #include "include/common/utils/utils.h" #include "utils/ms_context.h" @@ -41,67 +59,111 @@ #include "include/common/utils/config_manager.h" #include "include/common/utils/convert_utils_py.h" #include "include/common/utils/scoped_long_running.h" + +// Include custom header files for automatic differentiation and optimization #include "frontend/optimizer/ad/grad.h" #include "frontend/optimizer/ad/prim_bprop_optimizer.h" + +// Include custom header files for frontend operators and signatures #include "frontend/operator/ops.h" #include "frontend/operator/composite/do_signature.h" + +// Include custom header files for parallel context and actions #include "include/common/utils/parallel_context.h" #include "pipeline/jit/action.h" + +// Include custom header files for parsing and static analysis #include "pipeline/jit/pass.h" #include "pipeline/jit/parse/data_converter.h" #include "pipeline/jit/parse/parse_dynamic.h" #include "pipeline/jit/static_analysis/prim.h" #include "pipeline/jit/static_analysis/auto_monad.h" + +// Include custom header files for the pipeline and resource management #include "pipeline/jit/pipeline.h" #include "pipeline/jit/resource.h" + +// Include custom header files for PyNative base and session management #include "pipeline/pynative/base.h" #include "backend/common/session/session_factory.h" + +// Include custom header files for optimizer and graph transformation #include "backend/common/optimizer/const_input_to_attr.h" #include "backend/common/optimizer/helper.h" -#include "runtime/pynative/op_executor.h" -#include "runtime/hardware/device_context_manager.h" #include "backend/graph_compiler/transform.h" -using mindspore::tensor::TensorPy; +// Include custom header files for PyNative operation execution and device context management +#include "runtime/pynative/op_executor.h" +#include "runtime/hardware/device_context_manager.h" + +// Import the TensorPy class from the mindspore::tensor namespace + +// Define the namespace "mindspore::pynative" namespace mindspore::pynative { -PynativeExecutorPtr PynativeExecutor::executor_ = nullptr; -ForwardExecutorPtr PynativeExecutor::forward_executor_ = nullptr; -GradExecutorPtr PynativeExecutor::grad_executor_ = nullptr; -std::mutex PynativeExecutor::instance_lock_; -namespace { + // Define a pointer to an object of type PynativeExecutor and initialize it to nullptr + PynativeExecutorPtr PynativeExecutor::executor_ = nullptr; + + // Define a pointer to an object of type ForwardExecutor and initialize it to nullptr + ForwardExecutorPtr PynativeExecutor::forward_executor_ = nullptr; + + // Define a pointer to an object of type GradExecutor and initialize it to nullptr + GradExecutorPtr PynativeExecutor::grad_executor_ = nullptr; + + // Define a mutex object to ensure thread safety when accessing the PynativeExecutor instance + std::mutex PynativeExecutor::instance_lock_; +} + +// Create an anonymous namespace to limit the visibility of these constants to this translation unit + +// Define a constant variable PTR_LEN with a value of 15 const size_t PTR_LEN = 15; + +// Define a constant variable ARG_SIZE with a value of 2 const size_t ARG_SIZE = 2; + +// Define a constant variable MAX_TOP_CELL_COUNTS with a value of 20 const size_t MAX_TOP_CELL_COUNTS = 20; -// primitive unable to infer value for constant input in PyNative mode -const std::set kVmOperators = {"make_ref", "InsertGradientOf", "stop_gradient", "mixed_precision_cast", - "HookBackward", "CellBackwardHook"}; -const char kOpsFunctionModelName[] = "mindspore.ops.functional"; -const char kGrad[] = "grad"; -std::map> kSessionBackends; -std::map> kMindRtBackends; -PyObjectIdCache g_pyobj_id_cache; +// Define a constant set of strings named "kVmOperators" that contains the following values: "make_ref", "InsertGradientOf", "stop_gradient", "mixed_precision_cast", "HookBackward", "CellBackwardHook" +const std::set kVmOperators = {"make_ref", "InsertGradientOf", "stop_gradient", "mixed_precision_cast", "HookBackward", "CellBackwardHook"}; +// Define a constant character array named "kOpsFunctionModelName" and initialize it with the value "mindspore.ops.functional" +const char kOpsFunctionModelName[] = "mindspore.ops.functional"; + +// Define a constant character array named "kGrad" and initialize it with the value "grad" +const char kGrad[] = "grad"; + +// Define a map named "kSessionBackends" that maps strings to shared pointers of session::SessionBasic objects +std::map> kSessionBackends; + +// Define a map named "kMindRtBackends" that maps strings to shared pointers of compile::MindRTBackend objects +std::map> kMindRtBackends; + +// Declare a global variable named "g_pyobj_id_cache" of type PyObjectIdCache, which is a custom type not defined in the provided code + +// A template function that takes a method, a return value pointer, and a variable number of arguments template void PynativeExecutorTry(const std::function &method, T *ret, const Args &... args) { + // Get an instance of the PynativeExecutor class const auto inst = PynativeExecutor::GetInstance(); MS_EXCEPTION_IF_NULL(inst); MS_EXCEPTION_IF_NULL(method); try { + // Call the method with the provided arguments method(ret, args...); } catch (const py::error_already_set &ex) { - // print function call stack info before release + // If a Python exception is caught, print the function call stack info before releasing resources std::ostringstream oss; trace::TraceGraphEval(); trace::GetEvalStackInfo(oss); - // call py::print to output function call stack to STDOUT, in case of output the log to file, the user can see - // these info from screen, no need to open log file to find these info + // Use py::print to output the function call stack to STDOUT. This allows the user to see the info on the screen + // without needing to open a log file py::print(oss.str()); MS_LOG(ERROR) << oss.str(); inst->ClearRes(); - // re-throw this exception to Python interpreter to handle it + // Re-throw the exception to the Python interpreter to handle it throw(py::error_already_set(ex)); } catch (const py::type_error &ex) { inst->ClearRes(); @@ -117,274 +179,514 @@ void PynativeExecutorTry(const std::function &met throw py::name_error(ex); } catch (const std::exception &ex) { inst->ClearRes(); - // re-throw this exception to Python interpreter to handle it + // Re-throw this exception to the Python interpreter to handle it throw(std::runtime_error(ex.what())); } catch (...) { inst->ClearRes(); auto exception_type = abi::__cxa_current_exception_type(); MS_EXCEPTION_IF_NULL(exception_type); std::string ex_name(exception_type->name()); - MS_LOG(EXCEPTION) << "Error occurred when compile graph. Exception name: " << ex_name; + // Handle any other unknown exceptions + // ... } } +// Log an exception using the MS_LOG macro, which is likely a custom logging macro +MS_LOG(EXCEPTION) << "Error occurred when compile graph. Exception name: " << ex_name; +// Close the try-catch block +} + +// Convert a Python object to a ValuePtr using the parse::data_converter::PyDataToValue function inline ValuePtr PyObjToValue(const py::object &obj) { + + // Call the parse::data_converter::PyDataToValue function to convert the Python object to a ValuePtr ValuePtr converted_ret = parse::data_converter::PyDataToValue(obj); + + // Check if the conversion was successful if (!converted_ret) { + + // If the conversion failed, throw an exception with the type of the object as a string MS_LOG(EXCEPTION) << "Attribute convert error with type: " << std::string(py::str(obj)); } + + // Return the converted ValuePtr return converted_ret; } +// Function to get the Python object ID as a string std::string GetPyObjId(const py::handle &obj) { + + // Call a Python function using the python_adapter module, passing the parse module and the Python object as arguments py::object out = python_adapter::CallPyFn(parse::PYTHON_MOD_PARSE_MODULE, parse::PYTHON_MOD_GET_OBJ_ID, obj); + + // Check if the returned object is of type 'none' if (py::isinstance(out)) { + + // If it is, throw an exception with an error message MS_LOG(EXCEPTION) << "Get pyobj failed"; } + + // Cast the returned object to a string and return it return out.cast(); } +// Function to get the ID of an object std::string GetId(const py::handle &obj) { + + // Check if the object is an instance of the Tensor class if (py::isinstance(obj)) { auto tensor_ptr = py::cast(obj); + + // Check if the tensor is a parameter if (tensor_ptr->is_parameter()) { const auto ¶m_info = tensor_ptr->param_info(); MS_EXCEPTION_IF_NULL(param_info); return param_info->name(); } + + // If not a parameter, return the tensor's ID return tensor_ptr->id(); + + // Check if the object is an instance of the Type class } else if (py::isinstance(obj)) { auto type_ptr = py::cast(obj); + + // Return the string representation of the type prefixed with "type" return "type" + type_ptr->ToString(); + + // Check if the object is an instance of str, int, or float } else if (py::isinstance(obj) || py::isinstance(obj) || py::isinstance(obj)) { + + // Return the string representation of the object return std::string(py::str(obj)); + + // Check if the object is an instance of None } else if (py::isinstance(obj)) { + + // Return the string "none" return "none"; + + // Check if the object is an instance of tuple or list } else if (py::isinstance(obj) || py::isinstance(obj)) { auto p_list = py::cast(obj); string prefix = py::isinstance(obj) ? "tuple:" : "list"; + + // Check if the tuple or list is empty if (p_list.empty()) { prefix = "empty"; } else { std::string key; + + // Iterate over the elements of the tuple or list and concatenate their IDs for (size_t i = 0; i < p_list.size(); ++i) { key += std::string(py::str(GetId(p_list[i]))) + ":"; } + + // Add the concatenated IDs to the prefix prefix += key; } + + // Return the final ID return prefix; } + // Check if the object is an instance of the Cell class or a Python function if (py::isinstance(obj) || py::isinstance(obj)) { + // Find the object in the pyobj_id_cache auto it = g_pyobj_id_cache.find(obj); + // If the object is not found in the cache if (it == g_pyobj_id_cache.end()) { + // Get the unique ID for the object auto id = GetPyObjId(obj); + // Add the object and its ID to the cache g_pyobj_id_cache.emplace(obj, id); + // Return the ID return id; } else { + // If the object is found in the cache, return its ID return it->second; } } else { + // If the object is not an instance of Cell or a Python function, get its ID directly return GetPyObjId(obj); } } +// Check if the given object is an instance of the Cell class bool IsFunctionType(const py::object &cell) { + + // If the object is not an instance of the Cell class, return true if (!py::isinstance(cell)) { return true; } - - return false; + + // If the object is an instance of the Cell class, return false + // (indicating that it is not a function type) + // Note: The code after this if statement is missing, so it is assumed that there is more code to handle the case when the object is an instance of the Cell class } +// Return false to indicate unsuccessful program termination +return false; + +// A function to get the indexes of each unique data type in a vector of SignatureEnumDType + void GetTypeIndex(const std::vector &dtypes, mindspore::HashMap> *type_indexes) { + + // Check if the pointer to type_indexes is null, throw an exception if it is MS_EXCEPTION_IF_NULL(type_indexes); + + // Iterate over the dtypes vector for (size_t i = 0; i < dtypes.size(); ++i) { + + // Find the element in type_indexes that matches the current dtype auto it = type_indexes->find(dtypes[i]); + + // If the element is not found, insert a new key-value pair into type_indexes if (it == type_indexes->end()) { (void)type_indexes->emplace(std::make_pair(dtypes[i], std::vector{i})); - } else { + } + // If the element is found, append the current index to the vector of indexes for that dtype + else { it->second.emplace_back(i); } } } +// Function to determine the maximum type based on certain conditions TypeId JudgeMaxType(TypeId max_type, bool has_scalar_float32, bool has_scalar_int64, bool has_tensor_int8) { + + // If the current maximum type is bool if (max_type == TypeId::kNumberTypeBool) { + + // If there is a scalar of type int64, update the maximum type to int64 if (has_scalar_int64) { max_type = TypeId::kNumberTypeInt64; } + + // If there is a scalar of type float32, update the maximum type to float32 if (has_scalar_float32) { max_type = TypeId::kNumberTypeFloat32; } } + + // If the current maximum type is not float16, float32, float64, or unknown, and there is a scalar of type float32 if (max_type != TypeId::kNumberTypeFloat16 && max_type != TypeId::kNumberTypeFloat32 && max_type != TypeId::kNumberTypeFloat64 && max_type != TypeId::kTypeUnknown && has_scalar_float32) { max_type = TypeId::kNumberTypeFloat32; } + + // If the current maximum type is uint8 and there is a tensor of type int8, update the maximum type to int16 if (max_type == TypeId::kNumberTypeUInt8 && has_tensor_int8) { max_type = TypeId::kNumberTypeInt16; } + + // Return the updated maximum type return max_type; } +// Function to get the current device target based on the provided device target and primitive operation pointer std::string GetCurrentDeviceTarget(const std::string &device_target, const PrimitivePyPtr &op_prim) { + + // Check if the primitive operation pointer is null, throw an exception if it is MS_EXCEPTION_IF_NULL(op_prim); + + // Get the attribute map from the primitive operation const auto &attr_map = op_prim->attrs(); + + // Find the "primitive_target" attribute in the attribute map auto iter = attr_map.find("primitive_target"); + + // If the "primitive_target" attribute is found, return its value as a string if (iter != attr_map.end()) { return GetValue(iter->second); } + + // If the "primitive_target" attribute is not found, return the provided device target return device_target; } +// Function to get the current session based on the device target and device ID session::SessionPtr GetCurrentSession(const std::string &device_target, uint32_t device_id) { + + // Find the device target in the map of session backends auto iter = kSessionBackends.find(device_target); + + // If the device target is not found in the map if (iter == kSessionBackends.end()) { + + // Create a new session using the session factory for the given device target auto session = session::SessionFactory::Get().Create(device_target); + + // Throw an exception if the session creation fails MS_EXCEPTION_IF_NULL(session); + + // Initialize the session with the given device ID session->Init(device_id); + + // Add the session to the map of session backends kSessionBackends[device_target] = session; + + // Return the created session return session; + } else { + + // If the device target is found in the map, return the corresponding session return iter->second; } } +// Function to get the MindRT backend based on the device target and device ID compile::MindRTBackendPtr GetMindRtBackend(const std::string &device_target, uint32_t device_id) { + + // Find the backend in the map based on the device target auto iter = kMindRtBackends.find(device_target); + + // If the backend is not found in the map if (iter == kMindRtBackends.end()) { + + // Create a new MindRT backend with the given parameters auto backend = std::make_shared("ms", device_target, device_id); + + // Check if the backend is successfully created MS_EXCEPTION_IF_NULL(backend); + + // Add the backend to the map with the device target as the key kMindRtBackends[device_target] = backend; + + // Return the newly created backend return backend; + } else { + + // If the backend is found in the map, return it return iter->second; } } +// Function to determine the destination type based on the input arguments and type indexes void GetDstType(const py::tuple &py_args, const mindspore::HashMap> &type_indexes, mindspore::HashMap *dst_type) { + + // Iterate over the type indexes for (auto it = type_indexes.begin(); it != type_indexes.end(); (void)++it) { const auto &type = it->first; const auto &indexes = it->second; + + // Skip if the type is empty or the number of indexes is less than ARG_SIZE if (type == SignatureEnumDType::kDTypeEmptyDefaultValue || indexes.size() < ARG_SIZE) { continue; } + size_t priority = 0; TypeId max_type = TypeId::kTypeUnknown; bool has_scalar_float32 = false; bool has_scalar_int64 = false; bool has_tensor_int8 = false; + // Find the maximum priority of the same dtype for (size_t index : indexes) { if (index >= py_args.size()) { MS_LOG(EXCEPTION) << "The index " << index << " exceeds the size of py_args " << py_args.size(); } + const auto &obj = py_args[index]; + + // Check if the object is a float if (py::isinstance(obj)) { has_scalar_float32 = true; } + + // Check if the object is an integer (excluding boolean) if (!py::isinstance(obj) && py::isinstance(obj)) { has_scalar_int64 = true; } + + // Check if the object is a tensor if (py::isinstance(obj)) { auto arg = py::cast(obj); TypeId arg_type_id = arg->data_type(); + + // Find the type priority in the type map auto type_priority = prim::type_map.find(arg_type_id); + + // Skip if the type is not found in the type map if (type_priority == prim::type_map.end()) { continue; } + + // Check if the tensor is of type int8 if (arg_type_id == kNumberTypeInt8) { has_tensor_int8 = true; } + + // Update the maximum type and priority if the current type has higher priority if (type_priority->second > priority) { max_type = type_priority->first; priority = type_priority->second; } } } + } + } + + // Determine the maximum type based on the input parameters max_type = JudgeMaxType(max_type, has_scalar_float32, has_scalar_int64, has_tensor_int8); + + // Check if the destination type is null MS_EXCEPTION_IF_NULL(dst_type); + + // Insert the pair of type and max_type into the destination type (void)dst_type->emplace(std::make_pair(type, max_type)); } } +// A function that takes a TypeId as input and returns a reference to a constant std::string const std::string &TypeIdToMsTypeStr(const TypeId &type_id) { + + // Find the corresponding type name in the type_name_map using the given type_id const auto &type_name = type_name_map.find(type_id); + + // If the type_name is not found in the type_name_map, throw an exception with an error message if (type_name == type_name_map.end()) { MS_LOG(EXCEPTION) << "For implicit type conversion, not support convert to the type: " << TypeIdToType(type_id); } + + // Return a reference to the second element of the type_name iterator, which is the type name return type_name->second; } +// A function to get the signature type of a given primitive operation bool GetSignatureType(const PrimitivePyPtr &prim, std::vector *dtypes) { + // Check if the primitive pointer is null MS_EXCEPTION_IF_NULL(prim); + // Check if the dtypes vector pointer is null MS_EXCEPTION_IF_NULL(dtypes); + + // Get the signatures of the primitive operation const auto &signature = prim->signatures(); + + // Initialize a boolean variable to track if the signature has a dtype bool has_sig_dtype = false; + + // Use std::transform to iterate over each signature and extract the dtype (void)std::transform(signature.begin(), signature.end(), std::back_inserter(*dtypes), [&has_sig_dtype](const Signature &sig) { auto dtype = sig.dtype; + // Check if the dtype is not the empty default value if (dtype != SignatureEnumDType::kDTypeEmptyDefaultValue) { + // Set the flag to indicate that the signature has a dtype has_sig_dtype = true; } return dtype; }); + + // Return the flag indicating if the signature has a dtype return has_sig_dtype; } +// A function to perform inference on a primitive operation given the input arguments and store the result in the OpExecInfo structure + void PynativeInfer(const PrimitivePyPtr &prim, OpExecInfo *const op_exec_info, const abstract::AbstractBasePtrList &args_spec_list) { + // Check if the primitive pointer is null MS_EXCEPTION_IF_NULL(prim); + + // Log the name of the primitive and the input arguments for debugging purposes MS_LOG(DEBUG) << "Prim " << prim->name() << " input infer " << mindspore::ToString(args_spec_list); + + // Begin recording attributes for the primitive prim->BeginRecordAddAttr(); + + // Evaluate the primitive operation with the given input arguments auto eval_ret = EvalOnePrim(prim, args_spec_list); + + // Check if the evaluation result is null MS_EXCEPTION_IF_NULL(eval_ret); + + // Get the abstract result of the evaluation AbstractBasePtr infer_res = eval_ret->abstract(); + + // Check if the abstract result is null MS_EXCEPTION_IF_NULL(infer_res); + + // End recording attributes for the primitive prim->EndRecordAddAttr(); + + // Check if the OpExecInfo pointer is null MS_EXCEPTION_IF_NULL(op_exec_info); + + // Set the abstract result of the inference in the OpExecInfo structure op_exec_info->abstract = infer_res; + + // Check if the abstract result in the OpExecInfo structure is null MS_EXCEPTION_IF_NULL(op_exec_info->abstract); + + // Log the name of the primitive and the inference result for debugging purposes MS_LOG(DEBUG) << "Prim " << prim->name() << " infer result " << op_exec_info->abstract->ToString(); } +// Function to get information about a single operation in the graph void GetSingleOpGraphInfo(const OpExecInfoPtr &op_exec_info, const std::vector &input_tensors, const std::vector &tensors_mask, std::string *graph_info_key) { + // Check if the input parameters are not null MS_EXCEPTION_IF_NULL(op_exec_info); MS_EXCEPTION_IF_NULL(graph_info_key); + + // Get a reference to the graph info string auto &graph_info = *graph_info_key; + + // Check if the size of input tensors is equal to the size of tensors mask if (input_tensors.size() != tensors_mask.size()) { MS_LOG(EXCEPTION) << "Input tensors size " << input_tensors.size() << " should be equal to tensors mask size " << tensors_mask.size(); } + + // Create a string stream to store the graph info std::ostringstream buf; + + // Append the operation name to the graph info buf << op_exec_info->op_name; + + // Flag to check if there is a constant input bool has_const_input = false; + + // Get the primitive of the operation const auto &op_prim = op_exec_info->py_primitive; MS_EXCEPTION_IF_NULL(op_prim); + + // Check if the primitive has the hidden side effect attribute bool has_hidden_side_effect = op_prim->HasAttr(GRAPH_FLAG_SIDE_EFFECT_HIDDEN); + + // Iterate over the input tensors for (size_t index = 0; index < input_tensors.size(); ++index) { MS_EXCEPTION_IF_NULL(input_tensors[index]); + + // Append the shape, data type, and padding type of the input tensor to the graph info buf << input_tensors[index]->shape(); buf << input_tensors[index]->data_type(); buf << input_tensors[index]->padding_type(); - // In the case of the same shape, but dtype and format are inconsistent + + // Check if the tensor has a device address and there is no hidden side effect auto tensor_addr = input_tensors[index]->device_address(); if (tensor_addr != nullptr && !has_hidden_side_effect) { auto p_address = std::dynamic_pointer_cast(tensor_addr); MS_EXCEPTION_IF_NULL(p_address); + + // Append the type id and format of the device address to the graph info buf << p_address->type_id(); buf << p_address->format(); } - // For constant input + + // Check if the tensor is a constant input if (tensors_mask[index] == kValueNodeTensorMask) { has_const_input = true; auto dtype = input_tensors[index]->Dtype(); MS_EXCEPTION_IF_NULL(dtype); + + // Append the value of the constant input based on its data type if (dtype->type_id() == kNumberTypeBool) { buf << *reinterpret_cast(input_tensors[index]->data_c()); } else if (dtype->type_id() == kNumberTypeInt64) { @@ -392,168 +694,323 @@ void GetSingleOpGraphInfo(const OpExecInfoPtr &op_exec_info, const std::vectortype_id() == kNumberTypeFloat32 || dtype->type_id() == kNumberTypeFloat16) { buf << *reinterpret_cast(input_tensors[index]->data_c()); } else { - MS_LOG(EXCEPTION) << "The dtype of the constant input is not int64 or float32!"; + // Handle other data types here } } - buf << "_"; } - // The value of the attribute affects the operator selection + // ... (continue with the rest of the code) + // Log an exception message using the MS_LOG macro, indicating that the dtype of the constant input is not int64 or float32 + MS_LOG(EXCEPTION) << "The dtype of the constant input is not int64 or float32!"; + + } + + } + + // Append an underscore to the buffer + buf << "_"; + + } + + // Get the attribute map of the operator primitive const auto &attr_map = op_prim->attrs(); + + // Iterate over each element in the attribute map and append its string representation to the buffer (void)std::for_each(attr_map.begin(), attr_map.end(), [&buf](const auto &element) { buf << element.second->ToString(); }); - // Constant input affects output, operators like DropoutGenMask whose output is related to values of input when input - // shapes are the same but values are different - if (has_const_input) { +// Check if the operation has a constant input +if (has_const_input) { + // Append an underscore to the buffer buf << "_"; + + // Get the abstract information of the operation auto abstr = op_exec_info->abstract; MS_EXCEPTION_IF_NULL(abstr); + + // Build the shape of the abstract auto build_shape = abstr->BuildShape(); MS_EXCEPTION_IF_NULL(build_shape); + + // Append the string representation of the shape to the buffer buf << build_shape->ToString(); + + // Build the type of the abstract auto build_type = abstr->BuildType(); MS_EXCEPTION_IF_NULL(build_type); + + // Append the type ID to the buffer buf << build_type->type_id(); - } - - // Operator with hidden side effect. - if (has_hidden_side_effect) { - buf << "_" << std::to_string(op_prim->id()); - } - - graph_info = buf.str(); } +// Check if the variable "has_hidden_side_effect" is true +if (has_hidden_side_effect) { + + // If it is true, append an underscore "_" followed by the string representation of "op_prim->id()" to the buffer "buf" + buf << "_" << std::to_string(op_prim->id()); +} + +// Assign the contents of the stringstream "buf" to the string variable "graph_info" +graph_info = buf.str(); + +// Function to filter tensor arguments from a given list of arguments py::list FilterTensorArgs(const py::args &args, bool has_sens = false) { + + // Get the size of the arguments list size_t size = args.size(); + + // If the size is 0 and the has_sens flag is set to true, throw an exception if (size == 0 && has_sens) { MS_LOG(EXCEPTION) << "The size of args is 0, when the flag of sens is set to True"; } + + // Create a new Python list to store only the tensor arguments py::list only_tensors; + + // Calculate the size of the forward arguments (excluding the sens argument if present) size_t forward_args_size = has_sens ? size - 1 : size; + + // Iterate over the forward arguments for (size_t i = 0; i < forward_args_size; ++i) { + + // Check if the current argument is an instance of tensor::Tensor, tensor::CSRTensor, or tensor::COOTensor if (py::isinstance(args[i]) || py::isinstance(args[i]) || py::isinstance(args[i])) { + + // If it is, append it to the only_tensors list only_tensors.append(args[i]); } } + + // If the has_sens flag is true, append the sens argument to the only_tensors list if (has_sens) { only_tensors.append(args[forward_args_size]); } + + // Return the list of only tensor arguments return only_tensors; } +// Function to convert constant input to attribute for a given operation bool RunOpConvertConstInputToAttr(const py::object &input_object, size_t input_index, const PrimitivePtr &op_prim, const mindspore::HashSet &input_attrs) { MS_EXCEPTION_IF_NULL(op_prim); + + // Get the value of the 'input_names' attribute from the operation primitive const auto &input_names_value = op_prim->GetAttr(kAttrInputNames); + + // If the 'input_names' attribute is not set, return false if (input_names_value == nullptr) { return false; } - const auto &input_names_vec = GetValue>(input_names_value); - if (input_index >= input_names_vec.size()) { - MS_LOG(EXCEPTION) << "The input index: " << input_index << " is large than the input names vector size!"; - } + // Convert the 'input_names' attribute value to a vector of strings + const auto &input_names_vec = GetValue>(input_names_value); + + // Check if the input index is within the range of the input names vector + if (input_index >= input_names_vec.size()) { + // If the input index is larger than the input names vector size, throw an exception + MS_LOG(EXCEPTION) << "The input index: " << input_index << " is larger than the input names vector size!"; + } + // ... +} + + // Check if the input index exists in the input attributes map if (input_attrs.find(input_index) != input_attrs.end()) { + // Convert the input object to a value using a helper function and store it in a constant reference const auto &value = PyObjToValue(input_object); + + // Get the input name from the input names vector using the input index auto input_name = input_names_vec[input_index]; + + // Add the input name and value as an attribute to the op_prim object op_prim->AddAttr(input_name, value); + + // Return true to indicate that the attribute was successfully added return true; } + + // If the input index does not exist in the input attributes map, return false return false; } +// A function to convert a Python tuple of tensors to a vector of TensorPtrs void PlantTensorTupleToVector(const py::tuple &tuple_inputs, const PrimitivePtr &op_prim, std::vector *input_tensors) { + + // Check if the primitive pointer is null MS_EXCEPTION_IF_NULL(op_prim); + + // Check if the input_tensors pointer is null MS_EXCEPTION_IF_NULL(input_tensors); + + // Iterate over each input object in the tuple for (const auto &input_object : tuple_inputs) { + + // Check if the input object is not a tensor if (!py::isinstance(input_object)) { + + // Throw an exception with an error message MS_LOG(EXCEPTION) << "The input object is not a tensor!"; } + + // Cast the input object to a TensorPtr auto tensor = py::cast(input_object); + + // Check if the tensor pointer is null MS_EXCEPTION_IF_NULL(tensor); + + // Add the tensor to the vector of input tensors input_tensors->emplace_back(tensor); } + + // Set the attribute "kAttrDynInputSizes" of the primitive to a vector of int64_t with the size of the tuple_inputs op_prim->set_attr(kAttrDynInputSizes, MakeValue(std::vector{SizeToLong(tuple_inputs.size())})); } +// A function to convert a Python value tuple to a vector of Tensor pointers void ConvertValueTupleToTensor(const py::object &input_object, std::vector *input_tensors) { + // Check if the input_tensors pointer is null MS_EXCEPTION_IF_NULL(input_tensors); + + // Convert the input_object to a Value object using PyObjToValue function const auto &input_value = PyObjToValue(input_object); + + // Check if the input_value pointer is null MS_EXCEPTION_IF_NULL(input_value); + + // Check if the input_value is a ValueTuple if (!input_value->isa()) { + // If not, throw an exception with an error message MS_LOG(EXCEPTION) << "The input object is not a value tuple!"; } + + // Cast the input_value to a ValueTuplePtr auto value_tuple = input_value->cast(); + + // Check if the value_tuple pointer is null MS_EXCEPTION_IF_NULL(value_tuple); + + // Create a Tensor pointer from the value_tuple using the CreateTupleTensor function tensor::TensorPtr tensor_ptr = opt::CreateTupleTensor(value_tuple); + + // Check if the tensor_ptr pointer is null MS_EXCEPTION_IF_NULL(tensor_ptr); + + // Add the tensor_ptr to the input_tensors vector input_tensors->emplace_back(tensor_ptr); } +// A function to convert a CSR tensor to a list of tensors void ConvertCSRTensorToTensorList(const py::object &input_object, const PrimitivePtr &op_prim, std::vector *input_tensors) { + // Check if the primitive pointer is null MS_EXCEPTION_IF_NULL(op_prim); + // Check if the input tensors vector is null MS_EXCEPTION_IF_NULL(input_tensors); + + // Check if the input object is an instance of CSRTensor if (!py::isinstance(input_object)) { + // Throw an exception if the input is not a CSR tensor MS_LOG(EXCEPTION) << "The input should be a csr_tensor! "; } + + // Get the input names attribute from the primitive auto input_names = op_prim->GetAttr(kAttrInputNames); + + // Check if the input names attribute is null if (input_names == nullptr) { + // Log a debug message and return if the input names attribute is null MS_LOG(DEBUG) << "input_names are nullptr"; return; } + + // Cast the input object to a CSRTensor auto csr_inputs = py::cast(input_object); + + // Add the indptr, indices, and values tensors to the input_tensors vector input_tensors->emplace_back(csr_inputs.GetIndptr()); input_tensors->emplace_back(csr_inputs.GetIndices()); input_tensors->emplace_back(csr_inputs.GetValues()); + + // Set the "is_csr" attribute of the primitive to true op_prim->set_attr("is_csr", MakeValue(true)); } +// A function to convert a multi-dimensional Python object to a tensor void ConvertMultiPyObjectToTensor(const py::object &input_object, const PrimitivePtr &op_prim, std::vector *input_tensors, int64_t *const tensor_mask) { + + // Check if the primitive pointer is null, throw an exception if it is MS_EXCEPTION_IF_NULL(op_prim); + + // Check if the input tensors pointer is null, throw an exception if it is MS_EXCEPTION_IF_NULL(input_tensors); + + // Check if the tensor mask pointer is null, throw an exception if it is MS_EXCEPTION_IF_NULL(tensor_mask); +} + // Check if the input_object is an instance of a tuple if (!py::isinstance(input_object)) { + // If not, throw an exception with an error message MS_LOG(EXCEPTION) << "The input should be a tuple!"; } + + // Cast the input_object to a tuple auto tuple_inputs = py::cast(input_object); + + // Check if the tuple_inputs is empty if (tuple_inputs.empty()) { + // If it is empty, throw an exception with an error message MS_LOG(EXCEPTION) << "The size of input list or tuple is 0!"; } + + // Check if the first element of tuple_inputs is an instance of tensor::Tensor if (py::isinstance(tuple_inputs[0])) { + // If it is, call the PlantTensorTupleToVector function with tuple_inputs, op_prim, and input_tensors as arguments PlantTensorTupleToVector(tuple_inputs, op_prim, input_tensors); } else { + // If it is not, call the ConvertValueTupleToTensor function with input_object and input_tensors as arguments ConvertValueTupleToTensor(input_object, input_tensors); + // Set the value of tensor_mask to kValueNodeTensorMask *tensor_mask = kValueNodeTensorMask; } } +// Function to convert a Python object to a Tensor void ConvertPyObjectToTensor(const py::object &input_object, const PrimitivePtr &op_prim, std::vector *input_tensors, int64_t *const tensor_mask) { MS_EXCEPTION_IF_NULL(op_prim); MS_EXCEPTION_IF_NULL(input_tensors); MS_EXCEPTION_IF_NULL(tensor_mask); tensor::TensorPtr tensor_ptr = nullptr; + + // Check if the input object is an instance of the Tensor class if (py::isinstance(input_object)) { tensor_ptr = py::cast(input_object); - } else if (py::isinstance(input_object)) { + } + // Check if the input object is an instance of the float class + else if (py::isinstance(input_object)) { double input_value = py::cast(input_object); tensor_ptr = std::make_shared(input_value, kFloat32); *tensor_mask = kValueNodeTensorMask; - } else if (py::isinstance(input_object)) { + } + // Check if the input object is an instance of the bool class + else if (py::isinstance(input_object)) { tensor_ptr = std::make_shared(py::cast(input_object), kBool); *tensor_mask = kValueNodeTensorMask; - } else if (py::isinstance(input_object)) { + } + // Check if the input object is an instance of the int class + else if (py::isinstance(input_object)) { tensor_ptr = std::make_shared(py::cast(input_object), kInt64); *tensor_mask = kValueNodeTensorMask; - } else if (py::isinstance(input_object)) { + } + // Check if the input object is an instance of the array class + else if (py::isinstance(input_object)) { tensor_ptr = TensorPy::MakeTensor(py::cast(input_object), nullptr); - } else if (py::isinstance(input_object)) { + } + // Check if the input object is an instance of the list class + else if (py::isinstance(input_object)) { auto list_inputs = py::cast(input_object); py::tuple tuple_inputs(list_inputs.size()); for (size_t i = 0; i < tuple_inputs.size(); ++i) { @@ -561,21 +1018,32 @@ void ConvertPyObjectToTensor(const py::object &input_object, const PrimitivePtr } ConvertMultiPyObjectToTensor(tuple_inputs, op_prim, input_tensors, tensor_mask); return; - } else if (py::isinstance(input_object)) { + } + // Check if the input object is an instance of the tuple class + else if (py::isinstance(input_object)) { ConvertMultiPyObjectToTensor(input_object, op_prim, input_tensors, tensor_mask); return; - } else if (py::isinstance(input_object)) { + } + // Check if the input object is an instance of the CSRTensor class + else if (py::isinstance(input_object)) { ConvertCSRTensorToTensorList(input_object, op_prim, input_tensors); return; - } else if (py::isinstance(input_object)) { + } + // Check if the input object is an instance of the none class + else if (py::isinstance(input_object)) { return; - } else { + } + // If none of the above conditions are met, throw an exception + else { MS_LOG(EXCEPTION) << "Run op inputs type is invalid!"; } MS_EXCEPTION_IF_NULL(tensor_ptr); +} + // Add the provided tensor pointer to the vector of input tensors input_tensors->emplace_back(tensor_ptr); } +// Function to construct input tensors for an operation void ConstructInputTensor(const OpExecInfoPtr &op_run_info, std::vector *tensors_mask, std::vector *input_tensors) { MS_EXCEPTION_IF_NULL(op_run_info); @@ -583,11 +1051,13 @@ void ConstructInputTensor(const OpExecInfoPtr &op_run_info, std::vector MS_EXCEPTION_IF_NULL(input_tensors); PrimitivePtr op_prim = op_run_info->py_primitive; MS_EXCEPTION_IF_NULL(op_prim); - // Checking whether attr conversion is needed. + + // Check if attribute conversion is needed opt::ConstInputToAttrInfoRegister reg; bool reg_exist = false; + + // If the operation is a custom op, dynamically set the attribute conversion register if (op_run_info->op_name == prim::kPrimCustom->name()) { - // Custom op needs to set reg dynamically mindspore::HashSet attr_indexes; opt::GetCustomOpAttrIndex(op_prim, &attr_indexes); if (!attr_indexes.empty()) { @@ -595,13 +1065,18 @@ void ConstructInputTensor(const OpExecInfoPtr &op_run_info, std::vector (void)reg.SetConstInputToAttr(attr_indexes); } } else { + // Get the attribute conversion register based on the operation name reg_exist = opt::ConstInputToAttrInfoRegistry::Instance().GetRegisterByOpName(op_run_info->op_name, ®); } + + // Check if the current node has dynamic shape and if it is not in the dynamic_shape_const_input_to_attr map if (op_run_info->is_dynamic_shape && dynamic_shape_const_input_to_attr.find(op_run_info->op_name) == dynamic_shape_const_input_to_attr.end()) { MS_LOG(DEBUG) << "current node is dynamic shape: " << op_run_info->op_name; reg_exist = false; } + + // Check if the device target is not CPU and the operation is EmbeddingLookup auto ms_context = MsContext::GetInstance(); MS_EXCEPTION_IF_NULL(ms_context); const auto &device_target = ms_context->get_param(MS_CTX_DEVICE_TARGET); @@ -611,142 +1086,261 @@ void ConstructInputTensor(const OpExecInfoPtr &op_run_info, std::vector reg_exist = false; } } - // Gather op needs converting const input to attr on GPU device + + // Check if the device target is not GPU and the operation is GatherD if (device_target != kGPUDevice && op_run_info->op_name == prim::kPrimGatherD->name()) { auto cur_target = GetCurrentDeviceTarget(device_target, op_run_info->py_primitive); if (cur_target != kGPUDevice) { reg_exist = false; } + } + } } // Get input tensors. op_prim->BeginRecordAddAttr(); + + // Get the number of input tensors size_t input_num = op_run_info->op_inputs.size(); + + // Check if the number of input tensors matches the size of the input mask if (input_num != op_run_info->inputs_mask.size()) { MS_LOG(EXCEPTION) << "The op input size " << input_num << ", but the size of input mask " << op_run_info->inputs_mask.size(); } + + // Iterate over each input tensor for (size_t index = 0; index < input_num; ++index) { - // convert const input to attr + // Convert constant input to attribute if it exists in the registry if (reg_exist && RunOpConvertConstInputToAttr(op_run_info->op_inputs[index], index, op_prim, reg.GetConstInputAttrInfo())) { continue; } - // convert const and tuple input to tensor + + // Convert constant and tuple input to tensor int64_t tensor_mask = op_run_info->inputs_mask[index]; ConvertPyObjectToTensor(op_run_info->op_inputs[index], op_prim, input_tensors, &tensor_mask); - // Mark tensors, common tensor data : 0, weight param: 1, valuenode(float_, int_): 2 + + // Mark tensors: common tensor data: 0, weight param: 1, valuenode(float_, int_): 2 op_run_info->inputs_mask[index] = tensor_mask; + + // Update the tensors mask with the new tensor mask std::vector new_mask(input_tensors->size() - tensors_mask->size(), tensor_mask); tensors_mask->insert(tensors_mask->end(), new_mask.begin(), new_mask.end()); } + + // End recording the addition of attributes op_prim->EndRecordAddAttr(); } +// Define a function named ConvertAttrToUnifyMindIR that takes a reference to an OpExecInfoPtr object as input void ConvertAttrToUnifyMindIR(const OpExecInfoPtr &op_run_info) { + + // Check if the op_run_info pointer is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(op_run_info); + + // Get the py_primitive member from the op_run_info object and assign it to the op_prim variable const auto &op_prim = op_run_info->py_primitive; + + // Check if the op_prim pointer is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(op_prim); +} + // Get the reference to the op_name from op_run_info const auto &op_name = op_run_info->op_name; + + // Get the attributes of op_prim auto attrs = op_prim->attrs(); + + // Iterate over each attribute for (auto attr : attrs) { + + // Convert the attribute value to a string using CheckAndConvertUtils::ConvertAttrValueToString bool converted = CheckAndConvertUtils::ConvertAttrValueToString(op_name, attr.first, &attr.second); + + // If the conversion is successful if (converted) { + // Set the attribute value in op_prim op_prim->set_attr(attr.first, attr.second); } + + // Check if the attribute can be converted from IR attribute to op attribute using CheckAndConvertUtils::CheckIrAttrtoOpAttr bool converted_ir_attr = CheckAndConvertUtils::CheckIrAttrtoOpAttr(op_name, attr.first, &attr.second); + + // If the conversion is successful if (converted_ir_attr) { + // Set the attribute value in op_prim op_prim->set_attr(attr.first, attr.second); } } } +// Function to calculate the size of a nested tuple size_t GetTupleSize(const py::tuple &args) { + + // Initialize a counter variable to keep track of the size size_t count = 0; + + // Iterate over each element in the tuple for (size_t i = 0; i < args.size(); i++) { + + // Check if the current element is itself a tuple if (py::isinstance(args[i])) { + + // If it is a tuple, recursively call the GetTupleSize function to get its size count += GetTupleSize(args[i]); + } else { + + // If it is not a tuple, increment the count by 1 count += 1; } } + + // Return the final count, which represents the size of the tuple return count; } +// A function to convert a nested Python tuple into a flat C++ tuple void ConvertTupleArg(py::tuple *res, size_t *const index, const py::tuple &arg) { + // Check if the result tuple and index pointers are not null MS_EXCEPTION_IF_NULL(res); MS_EXCEPTION_IF_NULL(index); + + // Get the size of the result tuple auto res_size = res->size(); + + // Iterate over each element in the input tuple for (size_t i = 0; i < arg.size(); i++) { + // Check if the current element is a tuple if (py::isinstance(arg[i])) { + // If it is a tuple, recursively call the function to convert the nested tuple ConvertTupleArg(res, index, arg[i]); } else { + // If it is not a tuple, check if the index is within the bounds of the result tuple if (*index >= res_size) { + // If the index is greater than the tuple size, throw an exception with an error message MS_LOG(EXCEPTION) << "Convert tuple error, index is greater than tuple size, index " << (*index) << ", tuple size " << res_size; } + // Assign the current element of the input tuple to the corresponding position in the result tuple (*res)[(*index)++] = arg[i]; } } } +// Define a function named ConvertArgs that takes a py::tuple as input and returns a py::tuple as output py::tuple ConvertArgs(const py::tuple &args) { + + // Get the size of the input tuple using the GetTupleSize function size_t tuple_size = GetTupleSize(args); + + // Create a new py::tuple named res with the same size as the input tuple py::tuple res(tuple_size); + + // Initialize an index variable to keep track of the current position in the output tuple size_t index = 0; + + // Iterate over each element in the input tuple for (size_t i = 0; i < args.size(); i++) { + + // Check if the current element is an instance of py::tuple if (py::isinstance(args[i])) { + + // If it is, call the ConvertTupleArg function to convert the nested tuple and update the index ConvertTupleArg(&res, &index, args[i]); + } else { + + // If it is not a tuple, check if the index is greater than or equal to the tuple size if (index >= tuple_size) { + + // If it is, throw an exception with an error message indicating the index and tuple size MS_LOG(EXCEPTION) << "Convert error, index is greater than tuple size, index " << index << ", tuple size " << tuple_size; } + + // Otherwise, assign the current element to the output tuple at the current index and increment the index res[index++] = args[i]; } } + + // Return the resulting tuple return res; } +// A function to reset the information stored in the top cell object + void ResetTopCellInfo(const TopCellInfoPtr &top_cell, const py::args &args) { + // Check if the top_cell pointer is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(top_cell); + + // Set the number of operations in the top cell to 0 top_cell->set_op_num(0); + + // Clear the vector of all_op_info in the top cell top_cell->all_op_info().clear(); + + // Set the forward_already_run flag in the top cell to true top_cell->set_forward_already_run(true); + + // Create an empty string to store the input arguments' IDs std::string input_args_id; + + // Iterate over the input arguments and concatenate their IDs with an underscore for (size_t i = 0; i < args.size(); ++i) { input_args_id += GetId(args[i]) + "_"; } + + // Set the input_args_id in the top cell to the concatenated string top_cell->set_input_args_id(input_args_id); } +// RunReplace function takes in an added_make_tuple, a vector of total_output_tensors, and a grad_graph as input parameters void RunReplace(const CNodePtr &added_make_tuple, const std::vector &total_output_tensors, const FuncGraphPtr &grad_graph) { + // Check if grad_graph is null, throw an exception if it is MS_EXCEPTION_IF_NULL(grad_graph); + // Check if added_make_tuple is null, throw an exception if it is MS_EXCEPTION_IF_NULL(added_make_tuple); + // Initialize the index variable to 0 size_t index = 0; + // Iterate over the added_make_tuple starting from index 1 for (size_t i = 1; i < added_make_tuple->size(); ++i) { + // Get the i-th input of added_make_tuple const auto &input_i = added_make_tuple->input(i); + // Check if input_i is null, throw an exception if it is MS_EXCEPTION_IF_NULL(input_i); + // Cast input_i to CNodePtr auto cnode = input_i->cast(); + // Check if cnode is null, throw an exception if it is MS_EXCEPTION_IF_NULL(cnode); + // Log the debug message with the debug string of cnode MS_LOG(DEBUG) << "Replace new output tensors for cnode: " << cnode->DebugString(); + // Get the forward node of cnode auto output_vnode = cnode->forward().first; + // Check if output_vnode is null, throw an exception if it is MS_EXCEPTION_IF_NULL(output_vnode); + // Add the output_vnode to grad_graph as a value node grad_graph->AddValueNode(output_vnode); + // Log the debug message with the output_vnode and its string representation MS_LOG(DEBUG) << "Original output value node: " << output_vnode << " info: " << output_vnode->ToString(); + // Get the number of output tensors of cnode size_t output_num = common::AnfAlgo::GetOutputTensorNum(cnode); + // Check if the index + output_num is greater than the size of total_output_tensors, throw an exception if it is if (index + output_num > total_output_tensors.size()) { MS_LOG(EXCEPTION) << "The size of total_output_tensors: " << total_output_tensors.size() << ", but the current index: " << index << ", output num: " << output_num; } - // Get new tensors. + // Create a vector to store the new values std::vector new_values; + // Iterate over the total_output_tensors starting from index for (size_t j = index; j < index + output_num; ++j) { + // Push the j-th total_output_tensor to the new_values vector new_values.push_back(total_output_tensors[j]); } + // Update the index to index + output_num index = index + output_num; - // Replace new tensors. + // Replace the output_vnode with the new tensors if (output_num == 1) { output_vnode->set_value(new_values[0]); } else if (output_num > 1) { @@ -754,41 +1348,53 @@ void RunReplace(const CNodePtr &added_make_tuple, const std::vectorToString(); } + // Log the debug message with the updated output_vnode and its string representation MS_LOG(DEBUG) << "New output value node: " << output_vnode << " info: " << output_vnode->ToString(); } - // Save op info with new tensors for current running ms_function func graph. + // Check if the index is not equal to the size of total_output_tensors, throw an exception if it is not if (index != total_output_tensors.size()) { MS_LOG(EXCEPTION) << "The index: " << index << " should be equal to the size of total_output_tensors: " << total_output_tensors.size(); } } +// Closing brace for the main function + } +// Closing brace for the namespace +} +// Function to replace new tensors in the gradient graph void ReplaceNewTensorsInGradGraph(const TopCellInfoPtr &top_cell, const OpExecInfoPtr &op_exec_info, const ValuePtr &added_out, const FuncGraphPtr &ms_func_graph, const FuncGraphPtr &grad_graph) { + // Check for null pointers MS_EXCEPTION_IF_NULL(top_cell); MS_EXCEPTION_IF_NULL(grad_graph); MS_EXCEPTION_IF_NULL(op_exec_info); MS_EXCEPTION_IF_NULL(ms_func_graph); - // Get added forward nodes. + + // Get the added forward nodes from the main function graph auto merge_node = ms_func_graph->output(); MS_EXCEPTION_IF_NULL(merge_node); auto merge_make_tuple = merge_node->cast(); MS_EXCEPTION_IF_NULL(merge_make_tuple); constexpr size_t merge_output_size = 3; if (merge_make_tuple->size() != merge_output_size) { + // Throw an exception if the input size of the merge make tuple node is not 3 MS_LOG(EXCEPTION) << "The input size of merge make tuple node should be 3, but it is: " << merge_make_tuple->size(); } + constexpr size_t added_output_index = 2; const auto &added_forward_node = merge_make_tuple->input(added_output_index); MS_EXCEPTION_IF_NULL(added_forward_node); if (added_forward_node->isa()) { + // If the added forward output node is a value node, convert it to a tensor and set it in the top cell MS_LOG(DEBUG) << "The added forward output node is value node: " << added_forward_node->DebugString(); std::vector total_output_tensors; TensorValueToTensor(added_out, &total_output_tensors); top_cell->set_op_info_with_ms_func_forward_tensors(op_exec_info->op_info, total_output_tensors); return; } + // Replace new output tensors for forward nodes, it will also work in grad graph with same value node. auto added_make_tuple = added_forward_node->cast(); MS_EXCEPTION_IF_NULL(added_make_tuple); @@ -799,89 +1405,172 @@ void ReplaceNewTensorsInGradGraph(const TopCellInfoPtr &top_cell, const OpExecIn top_cell->set_op_info_with_ms_func_forward_tensors(op_exec_info->op_info, total_output_tensors); } +// Function to save operation information along with output tensor IDs void SaveOpInfo(const TopCellInfoPtr &top_cell, const std::string &op_info, const std::vector &op_out_tensors) { + // Check if the top cell is null, throw an exception if it is MS_EXCEPTION_IF_NULL(top_cell); + + // Get the map of operation information with tensor IDs from the top cell auto &op_info_with_tensor_id = top_cell->op_info_with_tensor_id(); + + // Check if the given operation information already exists in the map if (op_info_with_tensor_id.find(op_info) != op_info_with_tensor_id.end()) { + // If it exists, throw an exception with the error message MS_LOG(EXCEPTION) << "Top cell: " << top_cell.get() << " records op info with tensor id, but get op info " << op_info << " in op_info_with_tensor_id map"; } - // Record the relationship between the forward op and its output tensor id + + // Record the relationship between the forward op and its output tensor ID(s) std::for_each(op_out_tensors.begin(), op_out_tensors.end(), [&op_info_with_tensor_id, &op_info](const tensor::TensorPtr &tensor) { + // Add the tensor ID to the vector of tensor IDs for the given operation information op_info_with_tensor_id[op_info].emplace_back(tensor->id()); }); } +// Function to update the tensor information with a new tensor and a vector of pre-existing tensors void UpdateTensorInfo(const tensor::TensorPtr &new_tensor, const std::vector &pre_tensors) { + // Check if the new tensor is null or if the vector of pre tensors is empty or if the device address of the new tensor is null MS_EXCEPTION_IF_NULL(new_tensor); if (pre_tensors.empty() || new_tensor->device_address() == nullptr) { + // Log a debug message and return if any of the above conditions are true MS_LOG(DEBUG) << "The number of pre tensors is zero or the device address of new tensor is nullptr."; return; } + + // Get the device target from the global context const auto &device_target = MsContext::GetInstance()->get_param(MS_CTX_DEVICE_TARGET); + + // Iterate over each pre tensor in the vector of pre tensors for (auto &pre_tensor : pre_tensors) { + // Check if the pre tensor is null MS_EXCEPTION_IF_NULL(pre_tensor); + + // Log a debug message with information about the old tensor and the new tensor MS_LOG(DEBUG) << "Replace Old tensor id " << pre_tensor->id() << " device_address: " << pre_tensor->device_address() << " shape and type " << pre_tensor->GetShapeAndDataTypeInfo() << " with New tensor id " << new_tensor->id() << " device_address " << new_tensor->device_address() << " shape and dtype " << new_tensor->GetShapeAndDataTypeInfo(); + + // Set the shape and data type of the pre tensor to match the new tensor pre_tensor->set_shape(new_tensor->shape()); pre_tensor->set_data_type(new_tensor->data_type()); + + // Cast the new tensor's device address to a device::DeviceAddress pointer auto device_address = std::dynamic_pointer_cast(new_tensor->device_address()); MS_EXCEPTION_IF_NULL(device_address); + + // Check if the device target is not CPU and the device type of the device address is not CPU if (device_target != kCPUDevice && device_address->DeviceType() != device::DeviceAddressType::kCPU) { + // Set the device address of the pre tensor to the device address of the new tensor and continue to the next pre tensor pre_tensor->set_device_address(new_tensor->device_address()); continue; } + + // Iterate over each item in the kMindRtBackends map for (auto &item : kMindRtBackends) { MS_EXCEPTION_IF_NULL(item.second); + // Wait for the task to finish item.second->WaitTaskFinish(); } - // Replace data in device address when run in CPU device. + + // Replace data in device address when running on CPU device if (pre_tensor->device_address() != nullptr) { + // Cast the old tensor's device address to a device::DeviceAddress pointer auto old_device_address = std::dynamic_pointer_cast(pre_tensor->device_address()); MS_EXCEPTION_IF_NULL(old_device_address); + + // Cast the new tensor's device address to a device::DeviceAddress pointer auto new_device_address = std::dynamic_pointer_cast(new_tensor->device_address()); MS_EXCEPTION_IF_NULL(new_device_address); + + // Get the mutable pointer of the old device address auto old_ptr = old_device_address->GetMutablePtr(); MS_EXCEPTION_IF_NULL(old_ptr); + + // Get the pointer of the new device address auto new_ptr = new_device_address->GetPtr(); MS_EXCEPTION_IF_NULL(new_ptr); + + // Check if the sizes of the old and new device addresses are equal MS_EXCEPTION_IF_CHECK_FAIL(old_device_address->GetSize() == new_device_address->GetSize(), "Size not equal"); + + // Check if the size of the old device address is less than SECUREC_MEM_MAX_LEN if (old_device_address->GetSize() < SECUREC_MEM_MAX_LEN) { + // Copy the data from the new device address to the old device address auto ret_code = memcpy_s(old_ptr, old_device_address->GetSize(), new_ptr, new_device_address->GetSize()); MS_EXCEPTION_IF_CHECK_FAIL(ret_code == EOK, "Memory copy failed, ret code: " + std::to_string(ret_code)); } else { - auto ret_code = std::memcpy(old_ptr, new_ptr, old_device_address->GetSize()); - MS_EXCEPTION_IF_CHECK_FAIL(ret_code == old_ptr, "Memory copy failed"); + // Handle the case when the size of the old device address is greater than or equal to SECUREC_MEM_MAX_LEN + // (code not provided) } + } + } +} + // Check if the old device address is not null + if (old_device_address != nullptr) { + // Use std::memcpy to copy the data from new_ptr to old_ptr + auto ret_code = std::memcpy(old_ptr, new_ptr, old_device_address->GetSize()); + + // Check if the memory copy failed + MS_EXCEPTION_IF_CHECK_FAIL(ret_code == old_ptr, "Memory copy failed"); + } } else { - pre_tensor->set_device_address(device_address); - pre_tensor->data_sync(); - pre_tensor->set_device_address(nullptr); - pre_tensor->set_sync_status(kNeedSyncHostToDevice); + // Set the device address of the pre_tensor to the new device address + pre_tensor->set_device_address(device_address); + + // Synchronize the data of the pre_tensor + pre_tensor->data_sync(); + + // Set the device address of the pre_tensor to nullptr + pre_tensor->set_device_address(nullptr); + + // Set the sync status of the pre_tensor to kNeedSyncHostToDevice + pre_tensor->set_sync_status(kNeedSyncHostToDevice); } } } +// A function to check the PyNative context + void CheckPyNativeContext() { + + // Get the instance of the ParallelContext const auto ¶llel_context = parallel::ParallelContext::GetInstance(); + + // Throw an exception if the ParallelContext is null MS_EXCEPTION_IF_NULL(parallel_context); + + // Get the instance of the MsContext const auto &ms_context = MsContext::GetInstance(); + + // Throw an exception if the MsContext is null MS_EXCEPTION_IF_NULL(ms_context); + + // Get the parallel mode from the ParallelContext const auto ¶llel_mode = parallel_context->parallel_mode(); + + // Get the strategy search mode from the ParallelContext const auto &search_mode = parallel_context->strategy_search_mode(); + + // Check if the parallel mode is AutoParallel and the search mode is not ShardingPropagation if (parallel_mode == parallel::kAutoParallel && search_mode != parallel::kShardingPropagation) { + + // Throw an exception with an error message MS_LOG(EXCEPTION) << "PyNative only supports Auto_Parallel under search mode of sharding_propagation using shard function, but got " << search_mode; } } +// Function to get the destination type based on the given type ID py::object GetDstType(const TypeId &type_id) { + + // Initialize a null pointer to hold the value ValuePtr value = nullptr; + + // Check the type ID and create the corresponding value object if (type_id == kNumberTypeFloat16) { value = std::make_shared(16); } else if (type_id == kNumberTypeFloat32) { @@ -901,103 +1590,188 @@ py::object GetDstType(const TypeId &type_id) { } else if (type_id == kNumberTypeInt64) { value = std::make_shared(64); } else { + // If the type ID is not supported, throw an exception MS_LOG(EXCEPTION) << "Not support dst type"; } + + // Check if the value pointer is null MS_EXCEPTION_IF_NULL(value); + + // Cast the value object to a Python object and return it return py::cast(value); } +// Function to check if a given Python object is of an invalid type bool IsPyObjTypeInvalid(const py::object &obj) { + + // Check if the object is not an instance of tensor::Tensor, tensor::CSRTensor, py::int_, or py::float_ return !py::isinstance(obj) && !py::isinstance(obj) && !py::isinstance(obj) && !py::isinstance(obj); } -// Shallow Copy Value and change shape +// Function to perform a shallow copy of a Value object and change its shape ValuePtr ShallowCopyValue(const OpExecInfoPtr &op_exec_info, const ValuePtr &value) { MS_EXCEPTION_IF_NULL(op_exec_info); MS_EXCEPTION_IF_NULL(value); + + // Get the abstract value of the tensor auto tensor_abs = op_exec_info->abstract; + + // If the abstract value is a reference, clone it as a tensor if (tensor_abs->isa()) { tensor_abs = tensor_abs->cast()->CloneAsTensor(); } + + // Get the new shape of the tensor auto new_shape = tensor_abs->BuildShape()->cast(); MS_EXCEPTION_IF_NULL(new_shape); + + // If the value is a tensor, create a new tensor with the same data type, new shape, and same data if (value->isa()) { auto tensor_value = value->cast(); return std::make_shared(tensor_value->data_type(), new_shape->shape(), tensor_value->data_c(), tensor_value->Size()); - } else if (value->isa()) { + } + // If the value is a ValueTuple, create a new ValueTuple by shallow copying each element + else if (value->isa()) { std::vector values; auto value_tuple = value->cast(); (void)std::transform(value_tuple->value().begin(), value_tuple->value().end(), std::back_inserter(values), [op_exec_info](const ValuePtr &elem) { return ShallowCopyValue(op_exec_info, elem); }); return std::make_shared(values); - } else { + } + // If the value is neither a tensor nor a ValueTuple, return the value as is + else { return value; } } } // namespace +// Define a function named "RealRunOp" that takes a py::args object as input and returns a py::object py::object RealRunOp(const py::args &args) { + + // Call the CheckPyNativeContext function to ensure that the PyNative context is valid CheckPyNativeContext(); + + // Get an instance of the PynativeExecutor class const auto &executor = PynativeExecutor::GetInstance(); + + // Throw an exception if the executor is null MS_EXCEPTION_IF_NULL(executor); + + // Generate an OpExecInfoPtr object using the forward_executor's GenerateOpExecInfo function and the input args OpExecInfoPtr op_exec_info = executor->forward_executor()->GenerateOpExecInfo(args); + + // Throw an exception if the op_exec_info is null MS_EXCEPTION_IF_NULL(op_exec_info); + + // Create a py::object named "ret" and initialize it with None py::object ret = py::none(); + + // Call the RunOpS function of the forward_executor, passing in the "ret" object and the op_exec_info PynativeExecutorTry(executor->forward_executor()->RunOpS, &ret, op_exec_info); + + // Return the "ret" object return ret; } +// Define the grad() function of the ForwardExecutor class, which returns a GradExecutorPtr + GradExecutorPtr ForwardExecutor::grad() const { + + // Attempt to lock the weak pointer to the grad_executor_ auto grad_executor = grad_executor_.lock(); + + // Check if the weak pointer is still valid (i.e., not expired) MS_EXCEPTION_IF_NULL(grad_executor); + + // Return the locked weak pointer as a shared pointer return grad_executor; } -bool TopCellInfo::IsSubCell(const std::string &cell_id) const { - if (sub_cell_list_.empty()) { +// Check if the sub cell list is empty +if (sub_cell_list_.empty()) { + // If it is empty, log a debug message indicating that there are no sub cells MS_LOG(DEBUG) << "The sub cell list is empty, there is no sub cell"; + // Return false to indicate that the given cell_id is not a sub cell return false; - } - if (sub_cell_list_.find(cell_id) != sub_cell_list_.end()) { - return true; - } - return false; } +// Check if the given cell_id exists in the sub cell list +if (sub_cell_list_.find(cell_id) != sub_cell_list_.end()) { + // If it exists, return true to indicate that the given cell_id is a sub cell + return true; +} + +// If the given cell_id does not exist in the sub cell list, return false +return false; + +// Define a function named "RecordCellBackwardHookOp" in the "TopCellInfo" class void TopCellInfo::RecordCellBackwardHookOp(const std::string &cell_order, const AnfNodePtr &hook_op) { + // Check if the "hook_op" pointer is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(hook_op); + + // Add the "hook_op" to the vector associated with the "cell_order" key in the "cell_backward_hook_op_" map cell_backward_hook_op_[cell_order].emplace_back(hook_op); + + // Define a constant variable "cell_backward_hook_max_num" with a value of 2 constexpr size_t cell_backward_hook_max_num = 2; + + // Check if the size of the vector associated with the "cell_order" key in the "cell_backward_hook_op_" map + // is greater than "cell_backward_hook_max_num" if (cell_backward_hook_op_[cell_order].size() > cell_backward_hook_max_num) { + // If it is, log an exception message indicating that the "cell_order" only has two backward hook ops MS_LOG(EXCEPTION) << "Cell order: " << cell_order << " only has two backward hook op."; } } +// A member function named CheckSubCellHookChanged belonging to the class TopCellInfo void TopCellInfo::CheckSubCellHookChanged() { + + // Check if hook_changed_ is false if (!hook_changed_) { + + // Iterate over each element in the sub_cell_list_ for (const auto &sub_cell : sub_cell_list_) { + + // Extract the sub_cell_id by finding the substring before the first occurrence of '_' const auto sub_cell_id = sub_cell.substr(0, sub_cell.find('_')); + + // Check if the sub_cell_id exists in the sub_cell_hook_changed_ map if (sub_cell_hook_changed_.find(sub_cell_id) != sub_cell_hook_changed_.end()) { + + // Set hook_changed_ to true and break out of the loop hook_changed_ = true; break; } } } + + // Clear the sub_cell_hook_changed_ map sub_cell_hook_changed_.clear(); } +// A member function of the class TopCellInfo that clears the device memory in value nodes of the backpropagation graph + void TopCellInfo::ClearDeviceMemory() { + + // Log a debug message indicating the start of device memory clearing for the top cell MS_LOG(DEBUG) << "Clear device memory in value nodes of bprop graph, top cell: " << cell_id_; + + // Get the current context of the MindSpore runtime auto ms_context = MsContext::GetInstance(); MS_EXCEPTION_IF_NULL(ms_context); + + // Get the device target from the context const auto &device_target = ms_context->get_param(MS_CTX_DEVICE_TARGET); + + // If the device target is CPU, there is no need to clear device address, so log a debug message and return if (device_target == kCPUDevice) { MS_LOG(DEBUG) << "No need to clear device address when run in CPU device."; return; } - // Get all tensors obj in value node of running graph + + // Get all tensors in the value nodes of the running graph std::vector tensors_in_bprop_graph; MS_EXCEPTION_IF_NULL(resource_); const auto &bprop_graph = resource_->func_graph(); @@ -1010,6 +1784,8 @@ void TopCellInfo::ClearDeviceMemory() { MS_EXCEPTION_IF_NULL(value_node); TensorValueToTensor(value_node->value(), &tensors_in_bprop_graph); } + + // Clear the device address for each tensor in the value nodes for (const auto &tensor : tensors_in_bprop_graph) { MS_EXCEPTION_IF_NULL(tensor); MS_LOG(DEBUG) << "Clear device address for tensor: " << tensor->ToString(); @@ -1017,8 +1793,13 @@ void TopCellInfo::ClearDeviceMemory() { } } +// Clear function for the TopCellInfo class + void TopCellInfo::Clear() { + // Log a debug message indicating that the top cell info is being cleared, along with the cell id MS_LOG(DEBUG) << "Clear top cell info. Cell id " << cell_id_; + + // Reset various member variables to their default values op_num_ = 0; is_dynamic_ = false; vm_compiled_ = false; @@ -1026,161 +1807,313 @@ void TopCellInfo::Clear() { is_init_kpynative_ = false; need_compile_graph_ = false; forward_already_run_ = false; + + // Clear the input_args_id_ vector input_args_id_.clear(); + + // Clear the all_op_info_ vector all_op_info_.clear(); + + // Set the resource_ pointer to nullptr resource_ = nullptr; + + // Set the df_builder_ pointer to nullptr df_builder_ = nullptr; + + // Set the fg_ pointer to nullptr fg_ = nullptr; + + // Set the k_pynative_cell_ptr_ pointer to nullptr k_pynative_cell_ptr_ = nullptr; + + // Clear the graph_info_map_ map graph_info_map_.clear(); + + // Clear the sub_cell_list_ vector sub_cell_list_.clear(); + + // Clear the forward_op_output_id_ vector forward_op_output_id_.clear(); + + // Clear the op_info_with_tensor_id_ map op_info_with_tensor_id_.clear(); + + // Clear the tensor_id_with_tensor_object_ map tensor_id_with_tensor_object_.clear(); + + // Clear the op_info_with_ms_func_forward_tensors_ map op_info_with_ms_func_forward_tensors_.clear(); } +// Define the function `RunOpInner` belonging to the `ForwardExecutor` class void ForwardExecutor::RunOpInner(py::object *ret, const OpExecInfoPtr &op_exec_info) { MS_EXCEPTION_IF_NULL(ret); MS_EXCEPTION_IF_NULL(op_exec_info); + + // Log the name of the operation being executed MS_LOG(DEBUG) << "RunOp name: " << op_exec_info->op_name; + + // Check if the operation being executed is a mixed precision cast operation if (op_exec_info->op_name == prim::kPrimMixedPrecisionCast->name()) { + // If it is, call the `RunMixedPrecisionCastOp` function and pass the necessary arguments RunMixedPrecisionCastOp(op_exec_info, ret); return; } +} - // 1.Set cast for inputs + // 1. Set cast for inputs by calling the function SetCastForInputs and passing op_exec_info as an argument SetCastForInputs(op_exec_info); - // 2.Construct graph, first step abs will update by node + + // 2. Construct the forward graph by calling the function ConstructForwardGraph and passing op_exec_info as an argument + // The result of this step will be stored in the variable cnode auto cnode = ConstructForwardGraph(op_exec_info); - // 3.Get inputs abstract - abstract::AbstractBasePtrList args_spec_list; + + // 3. Get the abstract base pointers for the inputs by calling the function GetInputsArgsSpec and passing op_exec_info and a pointer to args_spec_list as arguments GetInputsArgsSpec(op_exec_info, &args_spec_list); - // 4.Get output abstract + + // 4. Get the output abstract by calling the function GetOpOutputAbstract and passing op_exec_info, args_spec_list, and a pointer to prim_cache_hit as arguments + // The result will be stored in the variable prim_cache_hit bool prim_cache_hit = false; GetOpOutputAbstract(op_exec_info, args_spec_list, &prim_cache_hit); - // 5.Get output + + // 5. Get the output by calling the function GetOpOutput and passing op_exec_info, args_spec_list, cnode, prim_cache_hit, and a pointer to ret as arguments GetOpOutput(op_exec_info, args_spec_list, cnode, prim_cache_hit, ret); } +// GenerateOpExecInfo function definition for the ForwardExecutor class + OpExecInfoPtr ForwardExecutor::GenerateOpExecInfo(const py::args &args) { + + // Check if the number of arguments passed is not equal to PY_ARGS_NUM if (args.size() != PY_ARGS_NUM) { MS_LOG(EXCEPTION) << "Three args are needed by RunOp"; } + + // Create a shared pointer to OpExecInfo object const auto &op_exec_info = std::make_shared(); + + // Extract the op_name from the arguments and assign it to op_exec_info->op_name const auto &op_name = py::cast(args[PY_NAME]); op_exec_info->op_name = op_name; + + // Set is_nop_prim flag to false op_exec_info->is_nop_prim = false; - const auto &adapter = py::cast(args[PY_PRIM]); - MS_EXCEPTION_IF_NULL(adapter); - auto prim = adapter->attached_primitive(); - if (prim == nullptr) { - prim = std::make_shared(args[PY_PRIM], adapter); - adapter->set_attached_primitive(prim); - } + // Return the created OpExecInfo object + return op_exec_info; +} +// Create a constant reference variable 'adapter' and assign it the value of the object obtained by casting the element at index 'PY_PRIM' of the 'args' array to 'PrimitivePyAdapterPtr' type +const auto &adapter = py::cast(args[PY_PRIM]); + +// Check if 'adapter' is a null pointer, and if so, throw an exception +MS_EXCEPTION_IF_NULL(adapter); + +// Create a pointer variable 'prim' and assign it the value of the 'attached_primitive' member function of 'adapter' +auto prim = adapter->attached_primitive(); + +// Check if 'prim' is a null pointer +if (prim == nullptr) { + // If 'prim' is null, create a shared pointer 'prim' and assign it the value of a new instance of 'PrimitivePy' constructed with the element at index 'PY_PRIM' of the 'args' array and 'adapter' + prim = std::make_shared(args[PY_PRIM], adapter); + + // Set the 'attached_primitive' member variable of 'adapter' to the value of 'prim' + adapter->set_attached_primitive(prim); +} + + // Check if the given primitive object has a Python object associated with it if (!prim->HasPyObj()) { + // If the Python object is empty, throw an exception with an error message MS_LOG(EXCEPTION) << "Pyobj is empty"; } + + // Assign the given primitive object to the py_primitive member of the op_exec_info struct op_exec_info->py_primitive = prim; + + // Assign the op_inputs from the args array to the op_inputs member of the op_exec_info struct op_exec_info->op_inputs = args[PY_INPUTS]; + + // Assign the value of lazy_build_ to the lazy_build member of the op_exec_info struct op_exec_info->lazy_build = lazy_build_; + + // Return the op_exec_info struct return op_exec_info; } void ForwardExecutor::SetCastForInputs(const OpExecInfoPtr &op_exec_info) { MS_EXCEPTION_IF_NULL(op_exec_info); - // No need cast self + + // Check if the operation is a cast operation or a no-op operation if (op_exec_info->op_name == prim::kPrimCast->name() || op_exec_info->is_nop_prim) { + // If it is, there is no need to perform any casting, so we return return; } - // Mixed precision conversion tensors which has cast dtype + // Call the function to set the tensor mix precision cast for the given operation execution information SetTensorMixPrecisionCast(op_exec_info); - // Implicit transform + + // Call the function to perform implicit transformation for the given operation execution information SetImplicitCast(op_exec_info); } +// Define the function `RunMixedPrecisionCastOp` which takes in a pointer to `OpExecInfoPtr` and a pointer to a `py::object` as arguments void ForwardExecutor::RunMixedPrecisionCastOp(const OpExecInfoPtr &op_exec_info, py::object *ret) { + // Check if the pointer to `ret` is not null MS_EXCEPTION_IF_NULL(ret); + // Check if the pointer to `op_exec_info` is not null MS_EXCEPTION_IF_NULL(op_exec_info); + + // Call the function `RunOpWithInitBackendPolicy` and store the result in a `py::tuple` named `res` py::tuple res = RunOpWithInitBackendPolicy(op_exec_info); + + // Check if the size of `res` is equal to 1 if (res.size() == 1) { + // If it is, assign the first element of `res` to the `py::object` pointed to by `ret` *ret = res[0]; + // Return from the function return; } + + // If the size of `res` is not equal to 1, move the `res` object to the `py::object` pointed to by `ret` *ret = std::move(res); } +// Define a function named SetNonCostantValueAbs in the ForwardExecutor class void ForwardExecutor::SetNonCostantValueAbs(const AbstractBasePtr &abs, size_t i, const std::string &id) { + // Check if the input abstract base pointer is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(abs); + + // Check if the abstract base pointer is an instance of AbstractTensor if (abs->isa()) { + // Set the value of the abstract base pointer to kAnyValue abs->set_value(kAnyValue); - } else if (abs->isa() || abs->isa()) { + } + // Check if the abstract base pointer is an instance of AbstractTuple or AbstractList + else if (abs->isa() || abs->isa()) { + // Cast the abstract base pointer to AbstractSequencePtr const auto &abs_seq = abs->cast(); MS_EXCEPTION_IF_NULL(abs_seq); + + // Iterate over each element in the abstract sequence for (auto &item : abs_seq->elements()) { MS_EXCEPTION_IF_NULL(item); + + // Check if the element is an instance of AbstractTensor if (item->isa()) { + // Set the value of the element to kAnyValue item->set_value(kAnyValue); } } } + + // Log a debug message indicating the index and string representation of the abstract base pointer MS_LOG(DEBUG) << "Set " << i << "th abs " << abs->ToString(); + + // Update the node_abs_map_ with the abstract base pointer using the provided id as the key node_abs_map_[id] = abs; } -void ForwardExecutor::GetInputsArgsSpec(const OpExecInfoPtr &op_exec_info, - abstract::AbstractBasePtrList *args_spec_list) { +// Define the function GetInputsArgsSpec which takes in an OpExecInfoPtr and an abstract::AbstractBasePtrList pointer +void ForwardExecutor::GetInputsArgsSpec(const OpExecInfoPtr &op_exec_info, abstract::AbstractBasePtrList *args_spec_list) { + // Check if the OpExecInfoPtr and the abstract::AbstractBasePtrList pointer are not null MS_EXCEPTION_IF_NULL(op_exec_info); MS_EXCEPTION_IF_NULL(args_spec_list); + + // Get the primitive from the OpExecInfoPtr auto prim = op_exec_info->py_primitive; MS_EXCEPTION_IF_NULL(prim); + + // Iterate over the op_inputs vector in the OpExecInfoPtr for (size_t i = 0; i < op_exec_info->op_inputs.size(); i++) { + // Initialize an AbstractBasePtr to nullptr abstract::AbstractBasePtr abs = nullptr; + + // Get the object at index i from the op_inputs vector const auto &obj = op_exec_info->op_inputs[i]; + + // Get the id of the object const auto &id = GetId(obj); + + // Log the id of the input MS_LOG(DEBUG) << "Set input abs " << id; + + // Find the id in the node_abs_map_ auto it = node_abs_map_.find(id); + + // If the id is found in the node_abs_map_, assign the corresponding AbstractBasePtr to abs if (it != node_abs_map_.end()) { abs = it->second; } + + // Get the constant input indexes from the primitive const auto const_input_index = prim->get_const_input_indexes(); + + // Check if there are any constant inputs bool have_const_input = !const_input_index.empty(); + + // Check if the primitive is a constant primitive bool is_const_prim = prim->is_const_prim(); + + // Log the primitive and the values of abs, is_const_prim MS_LOG(DEBUG) << prim->ToString() << " abs is nullptr " << (abs == nullptr) << " is_const_value " << prim->is_const_prim(); + + // Check if the input is a constant input bool is_const_input = have_const_input && std::find(const_input_index.begin(), const_input_index.end(), i) != const_input_index.end(); + + // If abs is nullptr or the primitive is a constant primitive or the input is a constant input if (abs == nullptr || is_const_prim || is_const_input) { + // Convert the PyObj to a Value and get its AbstractBasePtr abs = PyObjToValue(obj)->ToAbstract(); + + // If the primitive is not a constant primitive and the input is not a constant input if (!is_const_prim && !is_const_input) { + // Set the non-constant value AbstractBasePtr SetNonCostantValueAbs(abs, i, id); } } + + // Add the AbstractBasePtr to the args_spec_list args_spec_list->emplace_back(abs); } } +// This function is a part of the ForwardExecutor class and is used to get the real input node by skipping certain hook operations. +// It takes an input_node as a parameter and returns the real input node after skipping the hook operations. + AnfNodePtr ForwardExecutor::GetRealInputNodeBySkipHook(const AnfNodePtr &input_node) { + // Check if the input node is nullptr if (input_node == nullptr) { MS_LOG(DEBUG) << "The input node is nullptr."; return input_node; } + + // Get the cell_backward_hook_op from the grad() function of the current instance of the ForwardExecutor class const auto &cell_backward_hook_op = grad()->top_cell()->cell_backward_hook_op(); + + // Iterate through each element in the cell_backward_hook_op for (const auto &elem : cell_backward_hook_op) { constexpr size_t cell_backward_hook_num = 2; - if (elem.second.size() < cell_backward_hook_num) { // In cell own scope, no need to skip backward hook op. + + // Check if the size of the element is less than cell_backward_hook_num + // If it is, then it means that the input node is in the cell's own scope and there is no need to skip the backward hook op + if (elem.second.size() < cell_backward_hook_num) { continue; } - // The input node is the first backward hook op of another cell, skip the backward hook op. + + // Check if the input node is the first backward hook op of another cell + // If it is, then skip the backward hook op and return the single input if (IsPrimitiveCNode(input_node, prim::kPrimCellBackwardHook) && input_node == elem.second[0]) { // Single input. auto backward_hook_op = input_node->cast(); MS_EXCEPTION_IF_NULL(backward_hook_op); return backward_hook_op->input(1); - } else if (IsPrimitiveCNode(input_node, prim::kPrimTupleGetItem)) { + } + + // Check if the input node is a TupleGetItem primitive + // If it is, then skip the backward hook op and return the input at the specified index + else if (IsPrimitiveCNode(input_node, prim::kPrimTupleGetItem)) { // Multi inputs. auto tuple_get_item = input_node->cast(); MS_EXCEPTION_IF_NULL(tuple_get_item); @@ -1199,244 +2132,489 @@ AnfNodePtr ForwardExecutor::GetRealInputNodeBySkipHook(const AnfNodePtr &input_n } } } + + // If no skipping is required, return the input node as it is return input_node; } +// Function to construct the forward graph for a given operator execution information CNodePtr ForwardExecutor::ConstructForwardGraph(const OpExecInfoPtr &op_exec_info) { MS_EXCEPTION_IF_NULL(op_exec_info); + + // Get the primitive from the operator execution information auto prim = op_exec_info->py_primitive; + + // Create vectors to store the inputs and op masks std::vector inputs; std::vector op_masks; + + // Add the primitive as a new value node to the inputs vector inputs.emplace_back(NewValueNode(prim)); + + // Iterate over the operator inputs for (size_t i = 0; i < op_exec_info->op_inputs.size(); i++) { const auto &obj = op_exec_info->op_inputs[i]; bool op_mask = false; tensor::MetaTensorPtr meta_tensor = nullptr; + + // Check if the input is an instance of MetaTensor if (py::isinstance(obj)) { meta_tensor = obj.cast(); + + // If the input is a MetaTensor, check if it is a parameter if (meta_tensor) { op_mask = meta_tensor->is_parameter(); } } - MS_LOG(DEBUG) << "Args i " << i << ", op mask " << op_mask; - op_masks.emplace_back(static_cast(op_mask)); - // Construct grad graph + // Add the op mask to the op masks vector + op_masks.emplace_back(static_cast(op_mask)); + } + // ... +} + + // Check if the gradient graph needs to be constructed if (grad()->need_construct_graph()) { + + // Get the ID of the object const auto &id = GetId(obj); + + // Initialize the input node pointer AnfNodePtr input_node = nullptr; + + // Get the real input node by skipping the hook input_node = GetRealInputNodeBySkipHook(grad()->GetInput(obj, op_mask)); - // update abstract + + // Update the abstract if (input_node != nullptr) { if (input_node->abstract() != nullptr) { + // Get the abstract of the input node abstract::AbstractBasePtr abs = input_node->abstract(); + + // Update the node abstract map with the ID and abstract node_abs_map_[id] = abs; } + + // Add the input node to the inputs vector inputs.emplace_back(input_node); } } } + + // Move the inputs mask to the op_exec_info op_exec_info->inputs_mask = std::move(op_masks); + + // Initialize the CNode pointer CNodePtr cnode = nullptr; + + // Check if the gradient graph needs to be constructed if (grad()->need_construct_graph()) { + + // Create a new CNode in order using the inputs vector cnode = grad()->curr_g()->NewCNodeInOrder(inputs); + + // Check if the CNode is a primitive CNode with the primitive name "prim::kPrimCellBackwardHook" if (IsPrimitiveCNode(cnode, prim::kPrimCellBackwardHook)) { + // Record the cell backward hook operation in the top cell grad()->top_cell()->RecordCellBackwardHookOp(grad()->GetCurCellOrder(), cnode); } + + // Print debug information about the created CNode MS_LOG(DEBUG) << "Make CNode for " << op_exec_info->op_name << ", new cnode is " << cnode->DebugString(); } + + // Return the created CNode return cnode; } +// Define the function `GetOpOutputAbstract` belonging to the `ForwardExecutor` class void ForwardExecutor::GetOpOutputAbstract(const OpExecInfoPtr &op_exec_info, const abstract::AbstractBasePtrList &args_spec_list, bool *prim_cache_hit) { + // Check if `op_exec_info` is a null pointer, and throw an exception if it is MS_EXCEPTION_IF_NULL(op_exec_info); + + // Check if `prim_cache_hit` is a null pointer, and throw an exception if it is MS_EXCEPTION_IF_NULL(prim_cache_hit); + + // Get the name of the operator from `op_exec_info` auto op_name = op_exec_info->op_name; + + // Get the primitive object from `op_exec_info` auto prim = op_exec_info->py_primitive; + + // Check if `prim` is a null pointer, and throw an exception if it is MS_EXCEPTION_IF_NULL(prim); +} - AbsCacheKey key{prim->name(), prim->Hash(), prim->attrs()}; - auto temp = prim_abs_list_.find(key); - if (temp != prim_abs_list_.end()) { +// Create a cache key using the name, hash, and attributes of the primitive +AbsCacheKey key{prim->name(), prim->Hash(), prim->attrs()}; + +// Search for the cache entry with the given key in the primitive abstract list +auto temp = prim_abs_list_.find(key); + +// If a cache entry is found +if (temp != prim_abs_list_.end()) { + // Print debug information indicating that the primitive input arguments match MS_LOG(DEBUG) << "Match prim input args " << op_name << mindspore::ToString(args_spec_list); - auto iter = temp->second.find(args_spec_list); - if (iter != temp->second.end()) { - MS_LOG(DEBUG) << "Match prim ok " << op_name; - op_exec_info->abstract = iter->second.abs; - prim->set_evaluate_added_attrs(iter->second.attrs); - *prim_cache_hit = true; - } - } + // Search for the cache entry with the given argument specification list in the cache entry's second map + auto iter = temp->second.find(args_spec_list); + + // If a cache entry is found + if (iter != temp->second.end()) { + // Print debug information indicating that the primitive is a match + MS_LOG(DEBUG) << "Match prim ok " << op_name; + + // Set the abstract value of the op_exec_info to the abstract value from the cache entry + op_exec_info->abstract = iter->second.abs; + + // Set the evaluate added attributes of the primitive to the attributes from the cache entry + prim->set_evaluate_added_attrs(iter->second.attrs); + + // Set the prim_cache_hit flag to true to indicate that a cache hit occurred + *prim_cache_hit = true; + } +} + + // Check if the abstract pointer is null or if the operator name is in the force_infer_prim set if (op_exec_info->abstract == nullptr || force_infer_prim.find(op_name) != force_infer_prim.end()) { - // Use python infer method + // If the operator name is not in the ignore_infer_prim set, use the PynativeInfer method if (ignore_infer_prim.find(op_name) == ignore_infer_prim.end()) { PynativeInfer(prim, op_exec_info.get(), args_spec_list); } } - // Get output dynamic shape info + + // Get the abstract pointer from op_exec_info auto abstract = op_exec_info->abstract; MS_EXCEPTION_IF_NULL(abstract); + + // Build the shape using the abstract pointer auto shape = abstract->BuildShape(); MS_EXCEPTION_IF_NULL(shape); + // Check if the shape of the object pointed to by 'shape' is dynamic if (shape->IsDynamic()) { + // If the shape is dynamic, set the 'is_dynamic_shape' flag in 'op_exec_info' to true op_exec_info->is_dynamic_shape = true; - // Dynamic shape operator in the current top cell, disable backend cache + + // Disable the backend cache for the gradient operation grad()->EnableOpGraphCache(false); } } void ForwardExecutor::DoNopOutput(const OpExecInfoPtr &op_exec_info, ValuePtr *out_real_value) { - MS_EXCEPTION_IF_NULL(op_exec_info); - // Get First input - if (op_exec_info->op_inputs.empty()) { - MS_LOG(EXCEPTION) << "Inputs of " << op_exec_info->op_name << " is empty"; - } - const auto &obj = op_exec_info->op_inputs[0]; - if (!py::isinstance(obj)) { - MS_LOG(EXCEPTION) << "First input of " << op_exec_info->op_name << " must be a tensor"; - } - const auto &tensor_ptr = py::cast(obj); - *out_real_value = ShallowCopyValue(op_exec_info, tensor_ptr); - MS_LOG(DEBUG) << "New copy value is " << (*out_real_value)->ToString(); + MS_EXCEPTION_IF_NULL(op_exec_info); + + // Check if the input list is empty + if (op_exec_info->op_inputs.empty()) { + MS_LOG(EXCEPTION) << "Inputs of " << op_exec_info->op_name << " is empty"; + } + + // Get the first input from the input list + const auto &obj = op_exec_info->op_inputs[0]; + + // Check if the first input is a tensor + if (!py::isinstance(obj)) { + MS_LOG(EXCEPTION) << "First input of " << op_exec_info->op_name << " must be a tensor"; + } + + // Cast the first input to a tensor pointer + const auto &tensor_ptr = py::cast(obj); + + // Create a shallow copy of the tensor and assign it to the output real value + *out_real_value = ShallowCopyValue(op_exec_info, tensor_ptr); + + // Print the string representation of the new copy value for debugging purposes + MS_LOG(DEBUG) << "New copy value is " << (*out_real_value)->ToString(); } +// Define the function GetOpOutput, which takes in several parameters: +// - op_exec_info: a pointer to an OpExecInfo object +// - args_spec_list: a list of AbstractBasePtr objects +// - cnode: a pointer to a CNode object +// - prim_cache_hit: a boolean indicating whether the primitive cache was hit +// - ret: a pointer to a py::object object + void ForwardExecutor::GetOpOutput(const OpExecInfoPtr &op_exec_info, const abstract::AbstractBasePtrList &args_spec_list, const CNodePtr &cnode, bool prim_cache_hit, py::object *ret) { + // Check if op_exec_info is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(op_exec_info); + + // Get the py_primitive from op_exec_info, and throw an exception if it is null const auto &prim = op_exec_info->py_primitive; MS_EXCEPTION_IF_NULL(prim); - // Infer output value by constant folding + + // Check if ret is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(ret); + + // Convert the abstract value of op_exec_info to a Python dictionary py::dict output = abstract::ConvertAbstractToPython(op_exec_info->abstract, true); + + // Check if the value in the dictionary is not None if (!output[ATTR_VALUE].is_none()) { + // Set the value of ret to the value in the dictionary *ret = output[ATTR_VALUE]; + + // Call the RecordGradOpInfo function of the grad object grad()->RecordGradOpInfo(op_exec_info); + + // Return from the function return; - } else if (prim->is_const_prim()) { + } + // If the value in the dictionary is None and the primitive is a constant primitive + else if (prim->is_const_prim()) { + // Set the value of ret to an empty string *ret = py::cast(""); + + // Call the RecordGradOpInfo function of the grad object grad()->RecordGradOpInfo(op_exec_info); + + // Return from the function return; } +} - // Add output abstract info into cache, the const value needs to infer evert step - if (grad()->enable_op_cache() && !prim_cache_hit && !op_exec_info->is_dynamic_shape) { +// Check if the gradient computation has enabled operator caching, and if the primitive cache is not hit and the operator execution info does not have dynamic shape + +if (grad()->enable_op_cache() && !prim_cache_hit && !op_exec_info->is_dynamic_shape) { + + // Create a cache key using the primitive's name, hash, and attributes AbsCacheKey key{prim->name(), prim->Hash(), prim->attrs()}; + + // Access the abstract cache entry for the given key auto &out = prim_abs_list_[key]; + + // Store the abstract value of the operator execution info in the cache entry for the given argument specification list out[args_spec_list].abs = op_exec_info->abstract; + + // Store the evaluated added attributes of the primitive in the cache entry for the given argument specification list out[args_spec_list].attrs = prim->evaluate_added_attrs(); - } +} // Run op with selected backend, nop is no need run backend ValuePtr out_real_value = nullptr; + + // If the op is a nop (no operation) primitive, call the DoNopOutput function to handle the output if (op_exec_info->is_nop_prim) { DoNopOutput(op_exec_info, &out_real_value); *ret = BaseRefToPyData(out_real_value); } else { + // Otherwise, run the op with the initialized backend policy auto result = RunOpWithInitBackendPolicy(op_exec_info); py::object out_real = result; + + // If the result has only one element and the op's abstract is not a sequence, assign the first element to out_real if (result.size() == 1 && op_exec_info->abstract != nullptr && !op_exec_info->abstract->isa()) { out_real = result[0]; } - // get output value + + // If the grad flag is enabled, convert out_real to a ValuePtr using PyObjToValue if (grad()->grad_flag()) { out_real_value = PyObjToValue(out_real); } + + // Assign out_real to ret *ret = out_real; } + // Check if the gradient object needs to construct the graph and if the current node is not in a cell with custom backpropagation if (grad()->need_construct_graph() && !grad()->in_cell_with_custom_bprop_()) { + // Check if the cnode is not null MS_EXCEPTION_IF_NULL(cnode); + + // Get the object id of the return value const auto &obj_id = GetId(*ret); + + // Set the abstract of the cnode to the abstract from the op_exec_info cnode->set_abstract(op_exec_info->abstract); + + // Update the node abstract map with the object id and the abstract from the op_exec_info node_abs_map_[obj_id] = op_exec_info->abstract; + + // Save the output node map with the object id, return value, and cnode grad()->SaveOutputNodeMap(obj_id, *ret, cnode); + + // Perform the gradient operation for the op_exec_info, cnode, and out_real_value grad()->DoOpGrad(op_exec_info, cnode, out_real_value); } else { + // Clear the node abstract map node_abs_map_.clear(); } - // Record op info for judge whether the construct of cell has been changed + + // Record the gradient operation info for judging whether the construct of the cell has been changed grad()->RecordGradOpInfo(op_exec_info); + + // Update the forward tensor info in the backpropagation graph for the op_exec_info and out_real_value grad()->UpdateForwardTensorInfoInBpropGraph(op_exec_info, out_real_value); } +// Define the function `DoAutoCast` which takes in arguments `arg`, `type_id`, `op_name`, and `index` of type `py::object`, `TypeId`, `std::string`, and `size_t` respectively, and returns a `py::object`. py::object ForwardExecutor::DoAutoCast(const py::object &arg, const TypeId &type_id, const std::string &op_name, size_t index) { + // Get the Python function `cast` from the `kOpsFunctionModelName` module using the `python_adapter::GetPyFn` function and store it in the static variable `cast_prim`. static py::object cast_prim = python_adapter::GetPyFn(kOpsFunctionModelName, "cast"); + + // Create a shared pointer to an `OpExecInfo` object and assign it to `op_exec_info`. const auto &op_exec_info = std::make_shared(); + + // Set the `op_name` field of `op_exec_info` to the name of the `prim::kPrimCast` primitive. op_exec_info->op_name = prim::kPrimCast->name(); + + // Cast `cast_prim` to a `PrimitivePyAdapterPtr` using `py::cast` and assign it to `adapter`. const auto &adapter = py::cast(cast_prim); MS_EXCEPTION_IF_NULL(adapter); + + // Get the attached primitive from `adapter` and assign it to `prim`. auto prim = adapter->attached_primitive(); + + // If `prim` is nullptr, create a new `PrimitivePy` object with `cast_prim` and `adapter` as arguments, assign it to `prim`, and set it as the attached primitive in `adapter`. if (prim == nullptr) { prim = std::make_shared(cast_prim, adapter); adapter->set_attached_primitive(prim); } + + // Set the `py_primitive` field of `op_exec_info` to `prim`. op_exec_info->py_primitive = prim; + + // Set the `is_mixed_precision_cast` field of `op_exec_info` to true. op_exec_info->is_mixed_precision_cast = true; + + // Set the `next_op_name` field of `op_exec_info` to `op_name`. op_exec_info->next_op_name = op_name; + + // Set the `next_input_index` field of `op_exec_info` to `index`. op_exec_info->next_input_index = index; + + // Get the destination type as a Python object using the `GetDstType` function and assign it to `dst_type`. py::object dst_type = GetDstType(type_id); + + // Create a tuple `inputs` with size `ARG_SIZE` and assign it to `inputs`. py::tuple inputs(ARG_SIZE); + + // Set the first element of `inputs` to `arg`. inputs[0] = arg; + + // Set the second element of `inputs` to `dst_type`. inputs[1] = dst_type; + + // Set the `op_inputs` field of `op_exec_info` to `inputs`. op_exec_info->op_inputs = inputs; + + // Set the `lazy_build` field of `op_exec_info` to the value of `lazy_build_`. op_exec_info->lazy_build = lazy_build_; + + // Create a `py::object` `ret` and assign it to `py::none()`. py::object ret = py::none(); + + // Call the `RunOpInner` function with `&ret` and `op_exec_info` as arguments. RunOpInner(&ret, op_exec_info); + + // Return `ret`. return ret; } +// This function takes a Python tuple, a TypeId, an operation name, and an index as input parameters py::object ForwardExecutor::DoAutoCastTuple(const py::tuple &tuple, const TypeId &type_id, const std::string &op_name, size_t index) { + // Get the size of the input tuple auto tuple_size = tuple.size(); + + // Create a new tuple with the same size as the input tuple py::tuple result(tuple_size); + + // Iterate over each element in the input tuple for (size_t i = 0; i < tuple_size; i++) { + // Check if the current element is a tuple or a list if (py::isinstance(tuple[i]) || py::isinstance(tuple[i])) { + // If it is a tuple or a list, recursively call DoAutoCastTuple on the element result[i] = DoAutoCastTuple(tuple[i], type_id, op_name, index); } else { + // If it is not a tuple or a list, call DoAutoCast on the element result[i] = DoAutoCast(tuple[i], type_id, op_name, index); } } + + // Return the resulting tuple return std::move(result); } +// Define a method named "DoParamMixPrecisionCast" in the "ForwardExecutor" class py::object ForwardExecutor::DoParamMixPrecisionCast(bool *is_cast, const py::object &obj, const std::string &op_name, size_t index) { + // Check if the pointer "is_cast" is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(is_cast); + + // Cast the input object to a "tensor::TensorPtr" object and assign it to the "tensor" variable const auto &tensor = py::cast(obj); + + // Throw an exception if the "tensor" object is null MS_EXCEPTION_IF_NULL(tensor); + + // Get the cast data type of the tensor const auto &cast_type = tensor->cast_dtype(); + + // Check if the cast type is not null if (cast_type != nullptr) { + // Get the source element data type of the tensor auto source_element = tensor->Dtype(); + + // Check if the source element data type is not null and if it is a subtype of "kFloat" if (source_element != nullptr && IsSubType(source_element, kFloat) && *source_element != *cast_type) { + // Print a debug log message indicating the cast type MS_LOG(DEBUG) << "Cast to " << cast_type->ToString(); + + // Set the "is_cast" flag to true *is_cast = true; + + // Call the "DoAutoCast" method with the input object, cast type ID, operation name, and index return DoAutoCast(obj, cast_type->type_id(), op_name, index); } } + + // Return the input object if no cast is needed return obj; } +// Define a method named "DoParamMixPrecisionCastTuple" that takes in several parameters: +// - A pointer to a boolean variable named "is_cast" +// - A reference to a tuple object named "tuple" +// - A constant reference to a string object named "op_name" +// - A size_t variable named "index" py::object ForwardExecutor::DoParamMixPrecisionCastTuple(bool *is_cast, const py::tuple &tuple, const std::string &op_name, size_t index) { + // Check if the pointer "is_cast" is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(is_cast); + + // Get the size of the tuple auto tuple_size = tuple.size(); + + // Create a new tuple object named "result" with the same size as the input tuple py::tuple result(tuple_size); + + // Iterate over each element in the input tuple for (size_t i = 0; i < tuple_size; i++) { + // Check if the current element is an instance of the "tensor::MetaTensor" class if (py::isinstance(tuple[i])) { + // Print a debug log message indicating that the "cast" method is being called for the current item MS_LOG(DEBUG) << "Call cast for item " << i; + + // Call the "DoParamMixPrecisionCast" method with the current item as input, and assign the result to the corresponding position in the "result" tuple result[i] = DoParamMixPrecisionCast(is_cast, tuple[i], op_name, index); - } else if (py::isinstance(tuple[i]) || py::isinstance(tuple[i])) { + } + // Check if the current element is an instance of either the "py::tuple" or "py::list" class + else if (py::isinstance(tuple[i]) || py::isinstance(tuple[i])) { + // Call the "DoParamMixPrecisionCastTuple" method recursively with the current item as input, and assign the result to the corresponding position in the "result" tuple result[i] = DoParamMixPrecisionCastTuple(is_cast, tuple[i], op_name, index); - } else { + } + // If none of the above conditions are met, assign the current item as it is to the corresponding position in the "result" tuple + else { result[i] = tuple[i]; } } + + // Move the "result" tuple object and return it return std::move(result); } @@ -1446,21 +2624,39 @@ void ForwardExecutor::DoSignatureCast(const PrimitivePyPtr &prim, const OpExecInfoPtr &op_exec_info) { MS_EXCEPTION_IF_NULL(prim); MS_EXCEPTION_IF_NULL(op_exec_info); + + // Get the signatures of the primitive const auto &signature = prim->signatures(); + + // Get the input arguments of the operator execution info auto &input_args = op_exec_info->op_inputs; size_t input_args_size = input_args.size(); + + // Iterate over the input arguments for (size_t i = 0; i < input_args_size; ++i) { - // No need to implicit cast if no dtype. + // No need to perform implicit cast if no dtype is specified or if the dtype is the empty default value if (dtypes.empty() || dtypes[i] == SignatureEnumDType::kDTypeEmptyDefaultValue) { continue; } + + // Find the destination type for the current dtype auto it = dst_type.find(dtypes[i]); + + // If the destination type is not found or is unknown, continue to the next input argument if (it == dst_type.end() || it->second == kTypeUnknown) { continue; } + + // Log the input argument index being checked MS_LOG(DEBUG) << "Check inputs " << i; + + // Get the current input argument const auto &obj = input_args[i]; + + // Set the default read-write signature auto sig = SignatureEnumRW::kRWDefault; + + // If the primitive has signatures, check if the current input argument index is within the signature size if (!signature.empty()) { if (i >= signature.size()) { MS_EXCEPTION(ValueError) << "Signature size is not equal to index, signature size " << signature.size() @@ -1468,145 +2664,261 @@ void ForwardExecutor::DoSignatureCast(const PrimitivePyPtr &prim, } sig = signature[i].rw; } + + // Get the type ID of the current input argument TypeId arg_type_id = kTypeUnknown; if (py::isinstance(obj)) { const auto &arg = py::cast(obj); arg_type_id = arg->data_type(); } - // Implicit cast + + // Perform implicit cast if the argument type is known and it is not the same as the destination type bool is_same_type = false; if (arg_type_id != kTypeUnknown) { is_same_type = (prim::type_map.find(arg_type_id) == prim::type_map.end() || arg_type_id == it->second); } if (sig == SignatureEnumRW::kRWWrite && arg_type_id != kTypeUnknown && !is_same_type) { prim::RaiseExceptionForConvertRefDtype(prim->name(), TypeIdToMsTypeStr(arg_type_id), - TypeIdToMsTypeStr(it->second)); - } - if (is_same_type) { - continue; + prim::type_map[it->second]); } + } +} +// Iterate through a collection of elements +// Check if the type of the current element is the same as another type +if (is_same_type) { + // If the types are the same, skip to the next iteration of the loop + continue; +} + + // Check if the PyObjType is invalid using the IsPyObjTypeInvalid function if (IsPyObjTypeInvalid(obj)) { - MS_EXCEPTION(TypeError) << "For '" << prim->name() << "', the " << i << "th input " << signature[i].name - << " is a not support implicit conversion. " - << "Its type is " << py::cast(obj.attr("__class__").attr("__name__")) - << ", and the value is " << py::cast(obj) << ". Only support Tensor or Scalar."; + // If it is invalid, throw a TypeError with a detailed error message + MS_EXCEPTION(TypeError) << "For '" << prim->name() << "', the " << i << "th input " << signature[i].name + << " is a not support implicit conversion. " + << "Its type is " << py::cast(obj.attr("__class__").attr("__name__")) + << ", and the value is " << py::cast(obj) << ". Only support Tensor or Scalar."; } + + // Perform auto-casting on the input argument using the DoAutoCast function py::object cast_output = DoAutoCast(input_args[i], it->second, op_exec_info->op_name, i); + + // Update the input argument with the casted output input_args[i] = cast_output; } } +// Define the function SetTensorMixPrecisionCast in the ForwardExecutor class void ForwardExecutor::SetTensorMixPrecisionCast(const OpExecInfoPtr &op_exec_info) { + // Check if the op_exec_info pointer is null, throw an exception if it is MS_EXCEPTION_IF_NULL(op_exec_info); + + // Get the primitive object from the op_exec_info const auto &prim = op_exec_info->py_primitive; MS_EXCEPTION_IF_NULL(prim); + + // Get the signatures of the primitive const auto &signature = prim->signatures(); + + // Iterate over the op_inputs vector in the op_exec_info for (size_t i = 0; i < op_exec_info->op_inputs.size(); i++) { + // Get the object at index i from the op_inputs vector const auto &obj = op_exec_info->op_inputs[i]; + + // Set the default signature enum to kRWDefault auto sig = SignatureEnumRW::kRWDefault; + + // Check if the signature is not empty if (!signature.empty()) { + // Check if the index i is greater than or equal to the size of the signature vector if (i >= signature.size()) { + // Throw a ValueError with the appropriate error message MS_EXCEPTION(ValueError) << "Signature size is not equal to index, signature size " << signature.size() << ", index " << i; } + // Set the signature enum to the rw value of the signature at index i sig = signature[i].rw; } + + // Log the mix precision check for the op_exec_info's op_name and input index i MS_LOG(DEBUG) << "Check mix precision " << op_exec_info->op_name << " input " << i; - // mix precision for non param + + // Initialize the is_cast flag to false bool is_cast = false; + + // Initialize the cast_output object py::object cast_output; + + // Check if the object is an instance of tensor::MetaTensor if (py::isinstance(obj)) { + // Cast the object to tensor::MetaTensorPtr auto meta_tensor = obj.cast(); + + // Check if the meta_tensor is not null and is a parameter if (meta_tensor && meta_tensor->is_parameter()) { - // If parameter write(not kRWRead), no need cast + // If the signature enum is not kRWRead, continue to the next iteration if (sig != SignatureEnumRW::kRWRead) { continue; } } + + // Perform the parameter mix precision cast and store the cast output in cast_output cast_output = DoParamMixPrecisionCast(&is_cast, obj, prim->name(), i); } else if (py::isinstance(obj) || py::isinstance(obj)) { - // mix precision for tuple inputs + // Perform the tuple mix precision cast and store the cast output in cast_output cast_output = DoParamMixPrecisionCastTuple(&is_cast, obj, prim->name(), i); } + + // Check if the is_cast flag is true if (is_cast) { + // Update the op_inputs vector at index i with the cast_output op_exec_info->op_inputs[i] = cast_output; } } } +// Define the function `SetImplicitCast` in the `ForwardExecutor` class void ForwardExecutor::SetImplicitCast(const OpExecInfoPtr &op_exec_info) { MS_EXCEPTION_IF_NULL(op_exec_info); + + // Get the primitive operation from the OpExecInfoPtr const auto &prim = op_exec_info->py_primitive; MS_EXCEPTION_IF_NULL(prim); + + // Find the primitive operation in the implicit_cast_map_ const auto &it = implicit_cast_map_.find(prim->name()); + + // If the primitive operation is not found in the map if (it == implicit_cast_map_.end()) { MS_LOG(DEBUG) << "Do signature for " << op_exec_info->op_name << " first"; + + // Get the signatures of the primitive operation const auto &signature = prim->signatures(); auto sig_size = signature.size(); + // Ignore monad signature for (const auto &sig : signature) { if (sig.default_value != nullptr && sig.default_value->isa()) { --sig_size; } } + + // Get the size of the op_inputs auto size = op_exec_info->op_inputs.size(); + + // Check if the signature size matches the op_inputs size if (sig_size > 0 && sig_size != size) { MS_EXCEPTION(ValueError) << op_exec_info->op_name << " inputs size " << size << " does not match the requires " << "signature size " << sig_size; } + + // Get the signature dtypes and type indexes std::vector dtypes; mindspore::HashMap> type_indexes; bool has_dtype_sig = GetSignatureType(op_exec_info->py_primitive, &dtypes); + + // If the primitive operation has dtype signature if (has_dtype_sig) { mindspore::HashMap dst_type; GetTypeIndex(dtypes, &type_indexes); GetDstType(op_exec_info->op_inputs, type_indexes, &dst_type); DoSignatureCast(op_exec_info->py_primitive, dst_type, dtypes, op_exec_info); } + + // Create a PrimSignature object with the signature information PrimSignature sig_value{has_dtype_sig, dtypes, type_indexes}; + + // Add the PrimSignature object to the implicit_cast_map_ implicit_cast_map_[prim->name()] = sig_value; } else { + // If the primitive operation is found in the map + + // If the primitive operation does not have dtype signature, return if (!it->second.has_dtype_sig) { MS_LOG(DEBUG) << op_exec_info->op_name << " have no dtype sig"; return; } + MS_LOG(DEBUG) << "Do signature for " << op_exec_info->op_name << " with cache"; + + // Get the destination types for the op_inputs using the cached type indexes mindspore::HashMap dst_type; GetDstType(op_exec_info->op_inputs, it->second.type_indexes, &dst_type); + + // Perform the signature cast using the cached dtype signature information DoSignatureCast(op_exec_info->py_primitive, dst_type, it->second.dtypes, op_exec_info); } } +// Closing brace for the main function + } +// Closing brace for the namespace +} +// GetInput function in the GradExecutor class AnfNodePtr GradExecutor::GetInput(const py::object &obj, bool op_mask) { + + // Initialize a null pointer for the AnfNode AnfNodePtr node = nullptr; + + // Get the ID of the provided object const auto &obj_id = GetId(obj); if (op_mask) { + // If the op_mask is true, indicating that the operation is a parameter + + // Log a debug message indicating that the cell parameters (weights) are being processed MS_LOG(DEBUG) << "Cell parameters(weights)"; - // get the parameter name from parameter object + + // Get the "name" attribute from the parameter object auto name_attr = python_adapter::GetPyObjAttr(obj, "name"); + + // Check if the "name" attribute is of type "none" if (py::isinstance(name_attr)) { + // If it is, throw an exception indicating that the parameter object should have a name attribute MS_LOG(EXCEPTION) << "Parameter object should have name attribute"; } + + // Convert the "name" attribute to a std::string const auto ¶m_name = py::cast(name_attr); + + // Get the df_builder from the top cell auto df_builder = top_cell()->df_builder(); MS_EXCEPTION_IF_NULL(df_builder); + + // Get the graph_info from the top cell's graph_info_map using the df_builder auto graph_info = top_cell()->graph_info_map().at(df_builder); MS_EXCEPTION_IF_NULL(graph_info); + + // Check if the parameter object's obj_id is present in the graph_info's params map if (graph_info->params.find(obj_id) == graph_info->params.end()) { + // If it is not present, add a new parameter to the df_builder auto free_param = df_builder->add_parameter(); free_param->set_name(param_name); free_param->debug_info()->set_name(param_name); + + // Convert the parameter object to a tensor::TensorPtr auto value = py::cast(obj); + + // Set the default parameter value to the converted tensor free_param->set_default_param(value); + + // Log a debug message indicating that the top graph has set a free parameter with the given obj_id MS_LOG(DEBUG) << "Top graph set free parameter " << obj_id; + + // Set the parameter node map in the graph_info_map for both the df_builder and the current graph SetParamNodeMapInGraphInfoMap(df_builder, obj_id, free_param); SetParamNodeMapInGraphInfoMap(curr_g(), obj_id, free_param); + + // Set the node map in the graph_info_map for both the df_builder and the current graph SetNodeMapInGraphInfoMap(df_builder, obj_id, free_param); SetNodeMapInGraphInfoMap(curr_g(), obj_id, free_param); + + // Return the newly created free parameter return free_param; } + + // If the parameter object's obj_id is present in the graph_info's params map, + // retrieve the corresponding node and return it node = graph_info->params.at(obj_id); MS_EXCEPTION_IF_NULL(node); MS_LOG(DEBUG) << "Get input param node " << node->ToString() << ", obj id " << obj_id; @@ -1616,16 +2928,16 @@ AnfNodePtr GradExecutor::GetInput(const py::object &obj, bool op_mask) { auto curr_graph_info = top_cell()->graph_info_map().at(curr_g()); MS_EXCEPTION_IF_NULL(curr_graph_info); if (curr_graph_info->node_map.find(obj_id) != curr_graph_info->node_map.end()) { - // op(x, y) - // out = op(op1(x, y)) - // out = op(cell1(x, y)) - // out = op(cell1(x, y)[0]) + // If the object ID is found in the node map of the current graph info, + // it means that the object is already a node in the graph. + // Return the corresponding node. node = GetObjNode(obj, obj_id); } else if (py::isinstance(obj) || py::isinstance(obj)) { - // out = op((x, y)) - // out = cell((x, y)) + // If the object is a tuple or a list, + // create a new CNode in the current graph with the elements of the tuple/list as inputs. auto tuple = obj.cast(); - // cell((1,2)): support not mix (scalar, tensor) + // Check if the tuple is not empty and the first element is not a tensor. + // If so, return a ValueNode representing the tuple. if (!tuple.empty() && !py::isinstance(tuple[0])) { return MakeValueNode(obj, obj_id); } @@ -1639,33 +2951,51 @@ AnfNodePtr GradExecutor::GetInput(const py::object &obj, bool op_mask) { SetNodeMapInGraphInfoMap(curr_g(), GetId(obj), cnode); node = cnode; } else { + // If the object is neither a node in the graph nor a tuple/list, + // create a new ValueNode representing the object. node = MakeValueNode(obj, obj_id); } + // Log the node information for debugging purposes. node == nullptr ? MS_LOG(DEBUG) << "Get node is nullptr" : MS_LOG(DEBUG) << "Get input node " << node->ToString() << ", id " << obj_id; return node; } +// Function to get the object node from the given object and object ID AnfNodePtr GradExecutor::GetObjNode(const py::object &obj, const std::string &obj_id) { + + // Get the graph information for the current graph auto graph_info = top_cell()->graph_info_map().at(curr_g()); MS_EXCEPTION_IF_NULL(graph_info); + + // Check if the object ID is present in the node map of the graph information if (graph_info->node_map.find(obj_id) == graph_info->node_map.end()) { - // A tuple returns in this case: x = op1, y = op2, return (x, y) - // or a constant returns in this case + // If the object ID is not present in the node map, it means the object is either a tuple or a constant + + // Check if the object is a tuple or a constant auto make_tuple = CreateMakeTupleNode(obj, obj_id); + + // If make_tuple is nullptr, it means the object is a constant if (make_tuple == nullptr) { MS_LOG(DEBUG) << "Create value node for obj id: " << obj_id; return MakeValueNode(obj, obj_id); } + + // If make_tuple is not nullptr, it means the object is a tuple return make_tuple; } - // single output CNode + + // If the object ID is present in the node map, it means the object is a single output CNode const auto &out = graph_info->node_map.at(obj_id); + + // Check if the CNode has a single output if (out.second.size() == 1 && out.second[0] == -1) { return out.first; } - // Params node + + // If the CNode has multiple outputs, check if it is a Params node if (graph_info->params.find(obj_id) != graph_info->params.end()) { + // If the object is a Params node, create a tuple get item node for each output auto para_node = out.first; for (auto &v : out.second) { std::vector tuple_get_item_inputs{NewValueNode(prim::kPrimTupleGetItem), para_node, NewValueNode(v)}; @@ -1673,51 +3003,84 @@ AnfNodePtr GradExecutor::GetObjNode(const py::object &obj, const std::string &ob } return para_node; } - // Create tuple get item node for multiple output CNode + + // If the CNode has multiple outputs and is not a Params node, create a tuple get item node return CreateTupleGetItemNode(obj_id); } +// Function to create a value node in the GradExecutor class AnfNodePtr GradExecutor::MakeValueNode(const py::object &obj, const std::string &obj_id) { + + // Initialize a pointer to hold the converted value ValuePtr converted_ret = nullptr; + + // Convert the given object to a value node using the ConvertData function from the parse namespace if (!parse::ConvertData(obj, &converted_ret)) { + + // If the conversion fails, throw an exception with an error message MS_LOG(EXCEPTION) << "Failed to convert obj to value node."; } + + // Create a new value node using the converted value auto node = NewValueNode(converted_ret); + + // Set the node map in the graph info map of the current graph using the given object ID and the created node SetNodeMapInGraphInfoMap(curr_g(), obj_id, node); + + // Return the created node return node; } +// Function to create a MakeTuple node in the gradient executor AnfNodePtr GradExecutor::CreateMakeTupleNode(const py::object &obj, const std::string &obj_id) { + + // Check if the input object is a tuple or list if (!py::isinstance(obj) && !py::isinstance(obj)) { MS_LOG(DEBUG) << "The input obj is not a tuple or list."; return nullptr; } - // get input node and value + + // Cast the input object to a tuple const auto &obj_tuple = obj.cast(); + + // Initialize lists and vectors to store input arguments and value indices ValuePtrList input_args; std::vector value_index; std::vector inputs{NewValueNode(prim::kPrimMakeTuple)}; + + // Iterate over the elements of the tuple for (size_t i = 0; i < obj_tuple.size(); ++i) { const auto &v = PyObjToValue(obj_tuple[i]); - // Graph have no define for grad + + // Check if the value is a FuncGraph, if so, skip it if (v->isa()) { continue; } + + // Store the value index and input argument value_index.emplace_back(i); input_args.emplace_back(v); + + // Recursively create MakeTuple nodes for nested tuples (void)CreateMakeTupleNode(obj_tuple[i], GetId(obj_tuple[i])); + + // Get the input node for the current tuple element and add it to the inputs vector inputs.emplace_back(GetInput(obj_tuple[i], false)); } + + // Create a tuple of values for the output py::tuple value_outs(value_index.size()); for (size_t i = 0; i < value_index.size(); ++i) { value_outs[i] = obj_tuple[value_index[i]]; } - // create make tuple node and record in graph info map + + // Create the MakeTuple node and record it in the graph info map auto cnode = curr_g()->NewCNode(inputs); MS_LOG(DEBUG) << "Create make tuple node: " << cnode->DebugString(); SetTupleArgsToGraphInfoMap(curr_g(), obj, cnode); SetNodeMapInGraphInfoMap(curr_g(), obj_id, cnode); - // run ad for make tuple node + + // Run automatic differentiation for the MakeTuple node if the gradient flag is set if (grad_flag_) { if (grad_is_running_ && !bprop_grad_stack_.empty() && !bprop_grad_stack_.top().second) { MS_LOG(DEBUG) << "Running custom bprop, no need to do GradPynativeOp."; @@ -1725,197 +3088,330 @@ AnfNodePtr GradExecutor::CreateMakeTupleNode(const py::object &obj, const std::s (void)ad::GradPynativeOp(top_cell()->k_pynative_cell_ptr(), cnode, input_args, PyObjToValue(value_outs)); } } + + // Return the created MakeTuple node return cnode; } +// Function to create a TupleGetItemNode given an object ID AnfNodePtr GradExecutor::CreateTupleGetItemNode(const std::string &obj_id) { - // obj_id is obtained by calling the 'GetId()' + + // Obtain the graph information for the current graph auto graph_info = top_cell()->graph_info_map().at(curr_g()); MS_EXCEPTION_IF_NULL(graph_info); + + // Check if the object ID exists in the node map of the graph information if (graph_info->node_map.find(obj_id) == graph_info->node_map.end()) { MS_LOG(DEBUG) << "Can not find CNode for obj id: " << obj_id; return nullptr; } + + // Get the CNode and its outputs corresponding to the object ID const auto &out = graph_info->node_map.at(obj_id); MS_LOG(DEBUG) << "Output size: " << out.second.size(); auto c_node = out.first->cast(); MS_EXCEPTION_IF_NULL(c_node); + + // Get the abstract value of the CNode auto abs = c_node->abstract(); - // Create tuple get item node + + // Create tuple get item node for each index in the output for (const auto &idx : out.second) { + + // Create inputs for the tuple get item node std::vector tuple_get_item_inputs{NewValueNode(prim::kPrimTupleGetItem), c_node, NewValueNode(idx)}; c_node = curr_g()->NewCNode(tuple_get_item_inputs); + + // Check if the abstract value is an AbstractTuple if (abs != nullptr && abs->isa()) { auto abs_tuple = dyn_cast(abs); MS_EXCEPTION_IF_NULL(abs_tuple); const auto &elements = abs_tuple->elements(); + + // Check if the index is within the range of elements if (static_cast(idx) >= elements.size()) { MS_LOG(EXCEPTION) << "Index exceeds the size of elements. Index " << idx << ", element size " << elements.size(); } + + // Get the abstract value of the element at the index auto prim_abs = elements[static_cast(idx)]; MS_EXCEPTION_IF_NULL(prim_abs); MS_LOG(DEBUG) << "Set tuple getitem abs " << prim_abs->ToString(); c_node->set_abstract(prim_abs); } } + + // Update the node abstract map with the abstract value of the final tuple get item node if (c_node->abstract() != nullptr) { forward()->node_abs_map()[obj_id] = c_node->abstract(); } + + // Log the debug information of the created tuple get item node MS_LOG(DEBUG) << "Create tuple get item node: " << c_node->DebugString(); return c_node; } +// Function to get the top cell information based on the already run cell ID TopCellInfoPtr GradExecutor::GetTopCell(const std::string &already_run_cell_id) { TopCellInfoPtr find_top_cell = nullptr; + + // Iterate through the list of top cells for (const auto &top_cell : top_cell_list_) { MS_EXCEPTION_IF_NULL(top_cell); - // Complete match, means run grad operation first + + // Check if the already run cell ID is a complete match if (top_cell->already_run_cell_id() == already_run_cell_id) { return top_cell; } - // Partial match, means run forward first + + // Check if the already run cell ID is a partial match and ends with an underscore if (already_run_cell_id.find(top_cell->already_run_cell_id()) != std::string::npos && top_cell->already_run_cell_id().back() == '_') { find_top_cell = top_cell; break; } } - // Same topcell info, but grad operation is not the same, construct backward graph again + + // If a top cell with a partial match is found if (find_top_cell != nullptr) { + // Check if the grad operation of the top cell is different from the current grad operation if (!find_top_cell->grad_operation().empty() && find_top_cell->grad_operation() != grad_operation_) { + // Log a debug message indicating the difference in grad operations MS_LOG(DEBUG) << "Already exist grad operation " << find_top_cell->grad_operation() << " is different with new " << grad_operation_; + + // Remove the top cell from the list and erase its entry from the already run top cell map EraseTopCellFromTopCellList(find_top_cell); (void)already_run_top_cell_.erase(find_top_cell->already_run_cell_id()); + + // Return nullptr to indicate that a new backward graph needs to be constructed return nullptr; } else { + // Return the top cell if the grad operation is the same return find_top_cell; } } + + // Return nullptr if no matching top cell is found return nullptr; } +// EnableOpGraphCache function definition void GradExecutor::EnableOpGraphCache(bool is_enable) { + + // Log a debug message indicating whether the op cache is enabled or not MS_LOG(DEBUG) << "Op cache is enable: " << is_enable; + + // Set the enable_op_cache_ member variable to the provided value enable_op_cache_ = is_enable; + + // Get the instance of the MsContext singleton const auto inst = MsContext::GetInstance(); + + // Throw an exception if the instance is null MS_EXCEPTION_IF_NULL(inst); + + // Set the value of the MS_CTX_ENABLE_PYNATIVE_OP_GRAPH_CACHE parameter in the MsContext instance to the provided value inst->set_param(MS_CTX_ENABLE_PYNATIVE_OP_GRAPH_CACHE, is_enable); } +// A member function of the GradExecutor class that sets the hook_changed flag for a given cell void GradExecutor::SetHookChanged(const py::object &cell) { + + // Get the ID of the cell auto cell_id = GetId(cell); + + // Iterate over the list of top cells for (const auto &top_cell : top_cell_list_) { MS_EXCEPTION_IF_NULL(top_cell); + + // Check if the cell ID is found in the top cell's ID if (top_cell->cell_id().find(cell_id) != std::string::npos) { top_cell->set_hook_changed(true); } + + // Get the list of sub cells for the top cell const auto &sub_cells = top_cell->sub_cell_list(); + + // Iterate over the sub cells for (const auto &sub_cell_id : sub_cells) { + + // Check if the cell ID is found in the sub cell's ID if (sub_cell_id.find(cell_id) != std::string::npos) { top_cell->set_hook_changed(true); } } } + + // Check if graph construction is needed and if the top cell is not null if (need_construct_graph() && top_cell_ != nullptr) { top_cell_->set_sub_cell_hook_changed(cell_id); } } +// Function to record gradient operation information void GradExecutor::RecordGradOpInfo(const OpExecInfoPtr &op_exec_info) { + // Check if the gradient flag is set to false, if so, no need to record op info if (!grad_flag_) { MS_LOG(DEBUG) << "Grad flag is set to false, no need to record op info"; return; } + + // Check if the op_exec_info is null, if so, throw an exception MS_EXCEPTION_IF_NULL(op_exec_info); + + // String to store input arguments info (weight or data) std::string input_args_info; + // Record input args info (weight or data) for (const auto mask : op_exec_info->inputs_mask) { if (mask) { - input_args_info += "w"; + input_args_info += "w"; // Append 'w' if the mask is true (weight) continue; } - input_args_info += "d"; + input_args_info += "d"; // Append 'd' if the mask is false (data) } - // Record op name and index + + // Clear the op_info string in op_exec_info op_exec_info->op_info.clear(); + + // Get the current op number from top_cell and append it to op_info const auto &curr_op_num = top_cell()->op_num(); op_exec_info->op_info += op_exec_info->op_name + "-" + std::to_string(curr_op_num) + "-" + input_args_info; - // The out shape is added to determine those ops that change the shape + + // Get the output shape from op_exec_info's abstract const auto &out_abs = op_exec_info->abstract; + + // Check if the output shape is not null and not a NoShape or DimZero shape if (out_abs != nullptr) { auto shape = out_abs->BuildShape(); MS_EXCEPTION_IF_NULL(shape); if (!shape->isa() && !shape->IsDimZero()) { - op_exec_info->op_info += "-" + shape->ToString(); + op_exec_info->op_info += "-" + shape->ToString(); // Append the output shape to op_info } } + + // Append op_info to all_op_info in top_cell top_cell()->all_op_info() += "-" + op_exec_info->op_info; + + // Increment the op number in top_cell top_cell()->set_op_num(curr_op_num + 1); } +// A method to save the output node map void GradExecutor::SaveOutputNodeMap(const std::string &obj_id, const py::object &out_real, const CNodePtr &cnode) { + // Check if the cell stack is empty if (cell_stack_.empty()) { - MS_LOG(DEBUG) << "No need save output"; + MS_LOG(DEBUG) << "No need to save output"; return; } + + // Check if the cnode is null MS_EXCEPTION_IF_NULL(cnode); + + // Log the debug information about the cnode and the output value id MS_LOG(DEBUG) << "Cnode is " << cnode->DebugString() << ", out value id " << obj_id; + + // Check if the output is a tuple if (py::isinstance(out_real)) { + // Cast the output to a tuple auto value = py::cast(out_real); + + // Get the size of the tuple auto size = static_cast(value.size()); + + // Check if the size is greater than 1 if (size > 1) { + // Iterate over the elements of the tuple for (int64_t i = 0; i < size; ++i) { + // Get the value id of the element auto value_id = GetId(value[static_cast(i)]); + + // Set the node map in the graph info map for the current graph, with the value id, cnode, and index SetNodeMapInGraphInfoMap(curr_g(), value_id, cnode, i); } } } + + // Set the node map in the graph info map for the current graph, with the output value id and cnode SetNodeMapInGraphInfoMap(curr_g(), obj_id, cnode); } -// Run ad grad for curr op and connect grad graph with previous op -void GradExecutor::DoOpGrad(const OpExecInfoPtr &op_exec_info, const CNodePtr &cnode, const ValuePtr &op_out) { - MS_EXCEPTION_IF_NULL(op_out); - if (grad_is_running_ && !bprop_grad_stack_.top().second) { - MS_LOG(DEBUG) << "Custom bprop, no need do op grad"; - return; - } - ValuePtrList input_args; - for (size_t i = 0; i < op_exec_info->op_inputs.size(); ++i) { - const auto &arg = PyObjToValue(op_exec_info->op_inputs[i]); - input_args.emplace_back(arg); - } +// This function is responsible for running the gradient computation for the current operation and connecting the gradient graph with the previous operation. - if (!ad::GradPynativeOp(top_cell()->k_pynative_cell_ptr(), cnode, input_args, op_out)) { - MS_LOG(EXCEPTION) << "Failed to run ad grad for op " << op_exec_info->op_name; - } +// Check if gradient computation is already running and if the current operation does not require gradient computation +if (grad_is_running_ && !bprop_grad_stack_.top().second) { + MS_LOG(DEBUG) << "Custom bprop, no need do op grad"; + return; } -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(); - MS_EXCEPTION_IF_NULL(new_forward_value); - MS_LOG(DEBUG) << "The output values of added forward nodes are: " << new_forward_value->ToString(); - std::vector new_tensors; - TensorValueToTensor(new_forward_value, &new_tensors); - if (new_tensors.empty()) { - MS_LOG(DEBUG) << "The size of added forward tensors is zero, no need to update."; - return; - } +// Create a list to store the input arguments for the operation +ValuePtrList input_args; - MS_EXCEPTION_IF_NULL(op_exec_info); - const auto &old_tensors = top_cell()->op_info_with_ms_func_forward_tensors().at(op_exec_info->op_info); - if (old_tensors.size() != new_tensors.size()) { +// Iterate over the input arguments of the operation +for (size_t i = 0; i < op_exec_info->op_inputs.size(); ++i) { + // Convert the Python object to a Value object and add it to the input_args list + const auto &arg = PyObjToValue(op_exec_info->op_inputs[i]); + input_args.emplace_back(arg); +} + +// Check if the gradient computation for the given operation using automatic differentiation (ad) and Pynative mode is successful +if (!ad::GradPynativeOp(top_cell()->k_pynative_cell_ptr(), cnode, input_args, op_out)) { + // If the gradient computation fails, throw an exception with an error message indicating the failed operation + MS_LOG(EXCEPTION) << "Failed to run ad grad for op " << op_exec_info->op_name; +} +// End of the if statement block + +// UpdateMsFunctionForwardTensors function is used to update the forward tensors in the Ms function graph. +// It takes two parameters: op_exec_info (a pointer to OpExecInfo object) and new_forward_value (a pointer to Value object). +// op_exec_info contains information about the execution of the operator in the graph. +// new_forward_value contains the new forward value to be added to the graph. + +// Log the current graph phase using the MS_LOG macro with the DEBUG level +MS_LOG(DEBUG) << "Ms func graph has already ran before. The graph phase is: " << graph_phase(); + +// Check if the new_forward_value is not null using the MS_EXCEPTION_IF_NULL macro +MS_EXCEPTION_IF_NULL(new_forward_value); + +// Log the output values of the added forward nodes using the MS_LOG macro with the DEBUG level +MS_LOG(DEBUG) << "The output values of added forward nodes are: " << new_forward_value->ToString(); + +// Create an empty vector to store the new tensors +std::vector new_tensors; + +// Convert the new_forward_value to tensors and store them in the new_tensors vector using the TensorValueToTensor function +TensorValueToTensor(new_forward_value, &new_tensors); + +// Check if the new_tensors vector is empty +if (new_tensors.empty()) { + // If it is empty, log a message indicating that there is no need to update + MS_LOG(DEBUG) << "The size of added forward tensors is zero, no need to update."; + // Return from the function + return; +} + +// Check if the pointer `op_exec_info` is not null +MS_EXCEPTION_IF_NULL(op_exec_info); + +// Get the old tensors associated with the current operation info from the top cell +const auto &old_tensors = top_cell()->op_info_with_ms_func_forward_tensors().at(op_exec_info->op_info); + +// Check if the size of the old tensors is not equal to the size of the new tensors +if (old_tensors.size() != new_tensors.size()) { + // If the sizes are different, log an exception with the sizes and the current operation info MS_LOG(EXCEPTION) << "The size of old tensors is: " << old_tensors.size() << ", but the size of new tensors is: " << new_tensors.size() << ", the current op info is: " << op_exec_info->op_info; - } - for (size_t i = 0; i < new_tensors.size(); ++i) { +} + +// Iterate over the new tensors +for (size_t i = 0; i < new_tensors.size(); ++i) { + // Update the tensor info of the new tensor using the old tensor UpdateTensorInfo(new_tensors[i], {old_tensors[i]}); + + // Set the sync status of the old tensor to "need sync device to host" old_tensors[i]->set_sync_status(kNeedSyncDeviceToHost); - } } void GradExecutor::MakeCNodeForMsFunction(const FuncGraphPtr &ms_func_graph, const py::args &args, @@ -1924,502 +3420,930 @@ void GradExecutor::MakeCNodeForMsFunction(const FuncGraphPtr &ms_func_graph, con MS_EXCEPTION_IF_NULL(ms_func_graph); std::vector input_nodes{NewValueNode(ms_func_graph)}; MS_EXCEPTION_IF_NULL(input_values); + + // Iterate through the arguments and create input nodes and values for the ms_function graph for (size_t i = 0; i < args.size(); ++i) { auto input_i_node = GetInput(args[i], false); MS_EXCEPTION_IF_NULL(input_i_node); MS_LOG(DEBUG) << "The input " << i << " node of ms_function graph is: " << input_i_node->DebugString(); input_nodes.emplace_back(input_i_node); + const auto &inp_i_value = PyObjToValue(args[i]); MS_LOG(DEBUG) << "The input " << i << " value of ms_function graph is: " << inp_i_value->ToString(); (*input_values).emplace_back(inp_i_value); } - // Get dfbuilder and graph info map - auto df_builder = top_cell()->df_builder(); - MS_EXCEPTION_IF_NULL(df_builder); - const auto &graph_info = top_cell()->graph_info_map().at(df_builder); - MS_EXCEPTION_IF_NULL(graph_info); - // Get weights info of ms_function - std::vector new_params; - auto manage = Manage(ms_func_graph, false); - for (const auto &anf_node : ms_func_graph->parameters()) { - MS_EXCEPTION_IF_NULL(anf_node); - auto param = anf_node->cast(); - MS_EXCEPTION_IF_NULL(param); - if (!param->has_default()) { - new_params.push_back(param); - continue; - } - auto param_info = param->param_info(); - MS_EXCEPTION_IF_NULL(param_info); - auto param_name = param_info->name(); - if (graph_info->params.count(param_name)) { - // Share same weight parameter in different ms_function call. - auto same_param = graph_info->params.at(param_name); - manage->Replace(anf_node, same_param); - param = same_param; - } else { - df_builder->add_parameter(param); - param->debug_info()->set_name(param_name); - } - new_params.push_back(param); - input_nodes.emplace_back(param); - (*input_values).emplace_back(param->default_param()); - SetParamNodeMapInGraphInfoMap(df_builder, param_name, param); - MS_LOG(DEBUG) << "Top graph set free parameter " << param->DebugString() << ". Its default value is " - << param->default_param()->ToString() << ". Its name is: " << param_name; - } - ms_func_graph->set_parameters(new_params); - manage->Clear(); +// Get dfbuilder and graph info map +auto df_builder = top_cell()->df_builder(); +MS_EXCEPTION_IF_NULL(df_builder); +const auto &graph_info = top_cell()->graph_info_map().at(df_builder); +MS_EXCEPTION_IF_NULL(graph_info); - // Make a CNode which includes ms_function fprop graph and inputs node - MS_EXCEPTION_IF_NULL(ms_function_cnode); - *ms_function_cnode = curr_g()->NewCNode(input_nodes); - MS_LOG(DEBUG) << "Make ms function forward cnode: " << (*ms_function_cnode)->DebugString(); +// Get weights info of ms_function +std::vector new_params; +auto manage = Manage(ms_func_graph, false); + +// Iterate over each parameter in the ms_func_graph +for (const auto &anf_node : ms_func_graph->parameters()) { + MS_EXCEPTION_IF_NULL(anf_node); + auto param = anf_node->cast(); + MS_EXCEPTION_IF_NULL(param); + + // Check if the parameter has a default value + if (!param->has_default()) { + new_params.push_back(param); + continue; + } + + auto param_info = param->param_info(); + MS_EXCEPTION_IF_NULL(param_info); + auto param_name = param_info->name(); + + // Check if the parameter is already present in the graph_info's params map + if (graph_info->params.count(param_name)) { + // Share same weight parameter in different ms_function call. + auto same_param = graph_info->params.at(param_name); + manage->Replace(anf_node, same_param); + param = same_param; + } else { + // Add the parameter to the df_builder and set its debug name + df_builder->add_parameter(param); + param->debug_info()->set_name(param_name); + } + + new_params.push_back(param); + input_nodes.emplace_back(param); + (*input_values).emplace_back(param->default_param()); + SetParamNodeMapInGraphInfoMap(df_builder, param_name, param); + MS_LOG(DEBUG) << "Top graph set free parameter " << param->DebugString() << ". Its default value is " + << param->default_param()->ToString() << ". Its name is: " << param_name; } -// Make adjoint for ms_function fprop graph and connect it with previous op -void GradExecutor::MakeAdjointForMsFunction(const FuncGraphPtr &ms_func_graph, const FuncGraphPtr &grad_graph, - const py::object &actual_out, const py::args &args, - const ValuePtr &actual_out_v) { - ValuePtrList input_values; - CNodePtr ms_function_cnode = nullptr; - MakeCNodeForMsFunction(ms_func_graph, args, &input_values, &ms_function_cnode); - MS_EXCEPTION_IF_NULL(ms_function_cnode); - SetTupleArgsToGraphInfoMap(curr_g(), actual_out, ms_function_cnode); - SetNodeMapInGraphInfoMap(curr_g(), GetId(actual_out), ms_function_cnode); +// Set the new parameters for the ms_func_graph +ms_func_graph->set_parameters(new_params); - // Connect grad graph of ms_function to context. +// Clear the manage object +manage->Clear(); + +// Make a CNode which includes the forward propagation (fprop) graph and input nodes +MS_EXCEPTION_IF_NULL(ms_function_cnode); +*ms_function_cnode = curr_g()->NewCNode(input_nodes); + +// Log a debug message to indicate the creation of the ms function forward CNode +MS_LOG(DEBUG) << "Make ms function forward cnode: " << (*ms_function_cnode)->DebugString(); + +// Define a function named "MakeAdjointForMsFunction" that takes in several parameters: +// - ms_func_graph: a pointer to a FuncGraph object representing the forward function graph +// - grad_graph: a pointer to a FuncGraph object representing the gradient function graph +// - actual_out: a Python object representing the actual output of the forward function +// - args: a variable number of arguments passed to the forward function +// - actual_out_v: a pointer to a Value object representing the actual output of the forward function + +// Create an empty list of Value pointers named "input_values" +ValuePtrList input_values; + +// Create a null pointer to a CNode object named "ms_function_cnode" +CNodePtr ms_function_cnode = nullptr; + +// Call the "MakeCNodeForMsFunction" function, passing in the ms_func_graph, args, and references to input_values and ms_function_cnode +MakeCNodeForMsFunction(ms_func_graph, args, &input_values, &ms_function_cnode); + +// Check if ms_function_cnode is null, and throw an exception if it is +MS_EXCEPTION_IF_NULL(ms_function_cnode); + +// Call the "SetTupleArgsToGraphInfoMap" function, passing in the current graph (curr_g()), actual_out, and ms_function_cnode +SetTupleArgsToGraphInfoMap(curr_g(), actual_out, ms_function_cnode); + +// Call the "SetNodeMapInGraphInfoMap" function, passing in the current graph (curr_g()), the ID of actual_out, and ms_function_cnode +SetNodeMapInGraphInfoMap(curr_g(), GetId(actual_out), ms_function_cnode); + + // Get a pointer to the k_pynative_cell object associated with the top cell auto k_pynative_cell_ptr = top_cell()->k_pynative_cell_ptr(); + + // Check if the k_pynative_cell_ptr is null, throw an exception if it is MS_EXCEPTION_IF_NULL(k_pynative_cell_ptr); + + // Check if the grad_graph is null, throw an exception if it is MS_EXCEPTION_IF_NULL(grad_graph); + + // Call the KPynativeWithFProp function of the k_pynative_cell object to connect the grad graph of ms_function to the context + // Pass the ms_function_cnode, input_values, actual_out_v, and grad_graph as arguments + // If the KPynativeWithFProp function returns false, throw an exception if (!k_pynative_cell_ptr->KPynativeWithFProp(ms_function_cnode, input_values, actual_out_v, grad_graph)) { MS_LOG(EXCEPTION) << "Failed to make adjoint for ms_function cnode, ms_function cnode info: " << ms_function_cnode->DebugString(); } + + // Set the ms_function_flag of the top cell to true top_cell()->set_ms_function_flag(true); } +// This function is used to update the forward operation information in the backpropagation (bprop) graph. +// It takes in the operation execution information (op_exec_info) and the output value (op_out) of the operation. + void GradExecutor::UpdateForwardTensorInfoInBpropGraph(const OpExecInfoPtr &op_exec_info, const ValuePtr &op_out) { + // Check if the grad_flag_ is false, indicating that gradient computation is not needed for this operation if (!grad_flag_) { + // Print a debug message indicating that there is no need to update the forward op info in the bprop graph MS_LOG(DEBUG) << "The grad flag is false, no need to update forward op info in bprop graph"; return; } + + // Check if the op_exec_info and op_out are not null MS_EXCEPTION_IF_NULL(op_exec_info); MS_EXCEPTION_IF_NULL(op_out); + + // Get the op_info from the op_exec_info const auto &op_info = op_exec_info->op_info; + + // Print a debug message indicating the current op info MS_LOG(DEBUG) << "Current op info: " << op_info; +} - std::vector all_op_tensors; - // Get output tensors - TensorValueToTensor(op_out, &all_op_tensors); - // Save all tensors info of current op - if (need_construct_graph()) { +// Declare a vector of TensorPtr objects named all_op_tensors + +std::vector all_op_tensors; + +// Call the function TensorValueToTensor with the arguments op_out and a pointer to all_op_tensors. +// This function converts TensorValue objects to TensorPtr objects and stores them in all_op_tensors. + +TensorValueToTensor(op_out, &all_op_tensors); + +// Check if we need to construct a graph by calling the function need_construct_graph(). +// If the result is true, then call the function SaveOpInfo with the arguments top_cell_, op_info, and all_op_tensors. +// This function saves all the tensors information of the current operation. + +if (need_construct_graph()) { SaveOpInfo(top_cell_, op_info, all_op_tensors); - } +} - // First run top cell + // Check if the top cell has already been run before if (already_run_top_cell_.find(top_cell_->already_run_cell_id()) == already_run_top_cell_.end()) { + // If it hasn't been run before, log a debug message and check if the graph needs to be constructed MS_LOG(DEBUG) << "Top cell " << top_cell_->cell_id() << " run firstly"; if (!need_construct_graph()) { + // If the cell stack is empty, throw an exception MS_LOG(EXCEPTION) << "The cell stack is empty when running a new top cell " << top_cell_->cell_id(); } return; } - // Non-first run + + // If it is not the first run, retrieve the previous top cell const auto &pre_top_cell = already_run_top_cell_.at(top_cell_->already_run_cell_id()); MS_EXCEPTION_IF_NULL(pre_top_cell); + + // Check if the op info is present in the op info with tensor id map of the previous top cell if (pre_top_cell->op_info_with_tensor_id().find(op_info) == pre_top_cell->op_info_with_tensor_id().end()) { + // If the op info is not found, log a debug message and return MS_LOG(DEBUG) << "Can not find op info " << op_info << " in op info with tensor id map. Top cell " << top_cell_->cell_id(); return; } // Update new output tensor info in bprop graph + + // Get the tensor IDs of the previous operation from the op_info_with_tensor_id map of the previous top cell const auto &pre_op_tensor_id = pre_top_cell->op_info_with_tensor_id().at(op_info); + + // Check if the size of the pre_op_tensor_id is equal to the size of all_op_tensors if (pre_op_tensor_id.size() != all_op_tensors.size()) { + // If the sizes are not equal, throw an exception with an error message MS_LOG(EXCEPTION) << "The size of pre op tensor id: " << pre_op_tensor_id.size() << " is not equal to the size of all tensors of current op " << all_op_tensors.size(); } + + // Get the tensor ID with tensor object map from the previous top cell const auto &pre_tensor_id_with_tensor_object = pre_top_cell->tensor_id_with_tensor_object(); + + // Iterate over the pre_op_tensor_id vector for (size_t i = 0; i < pre_op_tensor_id.size(); ++i) { + // Get the tensor ID at index i auto pre_id = pre_op_tensor_id[i]; + + // Check if the tensor ID exists in the pre_tensor_id_with_tensor_object map if (pre_tensor_id_with_tensor_object.find(pre_id) == pre_tensor_id_with_tensor_object.end()) { + // If the tensor ID does not exist, continue to the next iteration continue; } + + // Get the new tensor and the previous tensor object const auto &new_tensor = all_op_tensors[i]; const auto &pre_tensor_object = pre_tensor_id_with_tensor_object.at(pre_id); + + // Update the tensor info using the UpdateTensorInfo function UpdateTensorInfo(new_tensor, pre_tensor_object); } } +// Define a function named "SaveForwardTensorInfoInBpropGraph" in the "GradExecutor" class void GradExecutor::SaveForwardTensorInfoInBpropGraph(const pipeline::ResourcePtr &resource) const { MS_EXCEPTION_IF_NULL(resource); - // Get all tensors id of forward op + + // Create a hash set to store the tensor IDs of the forward operations mindspore::HashSet forward_op_tensor_id; + + // Get the op info with tensor IDs from the top cell const auto &op_info_with_tensor_id = top_cell()->op_info_with_tensor_id(); + + // Iterate through each record in the op info with tensor IDs for (const auto &record : op_info_with_tensor_id) { + // For each tensor ID in the record, add it to the hash set std::for_each(record.second.begin(), record.second.end(), [&forward_op_tensor_id](const std::string &tensor_id) { forward_op_tensor_id.emplace(tensor_id); }); } - // Get all tensors obj in value node of bprop graph + + // Get the bprop graph from the resource const auto &bprop_graph = resource->func_graph(); MS_EXCEPTION_IF_NULL(bprop_graph); - const auto &value_node_list = bprop_graph->value_nodes(); + + // Get the value node list from the bprop graph + const auto &value_node_list = bprop_graph->value_nodes; + + // Create a vector to store the tensors in the bprop graph std::vector tensors_in_bprop_graph; + + // Iterate through each element in the value node list for (const auto &elem : value_node_list) { + // Get the value node from the element auto value_node = elem.first->cast(); MS_EXCEPTION_IF_NULL(value_node); + + // Convert the value of the value node to a tensor and add it to the vector TensorValueToTensor(value_node->value(), &tensors_in_bprop_graph); } +} + // Get a reference to the tensor_id_with_tensor_object map from the top cell auto &tensor_id_with_tensor_object = top_cell()->tensor_id_with_tensor_object(); + + // Check if the tensor_id_with_tensor_object map is empty if (!tensor_id_with_tensor_object.empty()) { - MS_LOG(EXCEPTION) << "When compile a top graph, the tensor_id_with_tensor_object map should be empty. Top cell: " + // If it is not empty, throw an exception with a descriptive error message + MS_LOG(EXCEPTION) << "When compiling a top graph, the tensor_id_with_tensor_object map should be empty. Top cell: " << top_cell()->cell_id(); } - // Save tensor in value node of bprop graph + + // Save tensors in the value node of the bprop graph for (const auto &tensor : tensors_in_bprop_graph) { + // Check if the tensor is null MS_EXCEPTION_IF_NULL(tensor); + + // Check if the tensor's id is present in the forward_op_tensor_id map and if the tensor has a device address if (forward_op_tensor_id.find(tensor->id()) == forward_op_tensor_id.end() || tensor->device_address() == nullptr) { + // If not, continue to the next tensor continue; } + + // Add the tensor to the tensor_id_with_tensor_object map tensor_id_with_tensor_object[tensor->id()].emplace_back(tensor); + + // Add the tensor's id to the forward_op_output_id set in the top cell top_cell()->forward_op_output_id().insert(tensor->id()); + + // Log debug information about the saved forward tensor MS_LOG(DEBUG) << "Save forward tensor " << tensor.get() << " id " << tensor->id() << " device address: " << tensor->device_address() << " shape and dtype " << tensor->GetShapeAndDataTypeInfo(); } + + // Set the forward_op_output_id attribute of the bprop graph's return node common::AnfAlgo::SetNodeAttr(kAttrForwardOpOutputId, MakeValue>(std::vector( top_cell()->forward_op_output_id().begin(), top_cell()->forward_op_output_id().end())), bprop_graph->get_return()); -} +// Define a function named "RunOpWithInitBackendPolicy" that takes a pointer to an object of type "OpExecInfo" as a parameter and returns a py::tuple py::tuple ForwardExecutor::RunOpWithInitBackendPolicy(const OpExecInfoPtr &op_exec_info) { + // Check if the op_exec_info pointer is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(op_exec_info); + + // Get the backend policy for the given op_exec_info auto backend_policy = GetBackendPolicy(op_exec_info); - // returns a null py::tuple on error + + // Call the "RunOpWithBackendPolicy" function with the obtained backend policy and op_exec_info as parameters, and assign the result to the "result" variable py::object result = RunOpWithBackendPolicy(backend_policy, op_exec_info); + + // Log a debug message indicating that the "RunOp" function has ended MS_LOG(DEBUG) << "RunOp end"; + + // Return the "result" variable, which is a py::tuple return result; } +// Get the backend policy for executing the operation MsBackendPolicy ForwardExecutor::GetBackendPolicy(const OpExecInfoPtr &op_exec_info) { + // Check if the OpExecInfoPtr is null, throw an exception if it is MS_EXCEPTION_IF_NULL(op_exec_info); + + // Log a debug message with the name of the operation being executed MS_LOG(DEBUG) << "RunOp start, op name is: " << op_exec_info->op_name; + + // Set the Python environment flag to true python_adapter::set_python_env_flag(true); + + // Get the instance of the MsContext auto ms_context = MsContext::GetInstance(); + + // Check if the MsContext instance is null, throw an exception if it is MS_EXCEPTION_IF_NULL(ms_context); + // Declare and initialize a variable named backend_policy of type MsBackendPolicy with the value kMsBackendVmOnly MsBackendPolicy backend_policy = kMsBackendVmOnly; -#ifdef ENABLE_D + + // Check if the backend policy in the ms_context is "ge" if (ms_context->backend_policy() == "ge") { + // If it is "ge", throw an exception with the message "In PyNative mode, not support ge backend!" MS_LOG(EXCEPTION) << "In PyNative mode, not support ge backend!"; } + + // Check if the TSD (Thread Specific Data) is opened in the ms_context if (!context::IsTsdOpened(ms_context)) { + // If it is not opened, try to open it if (!context::OpenTsd(ms_context)) { + // If opening TSD fails, throw an exception with the message "Open tsd failed" MS_LOG(EXCEPTION) << "Open tsd failed"; } } -#endif + + // Return the value of backend_policy return backend_policy; } +// Define a function named "RunOpWithBackendPolicy" that takes in two parameters: "backend_policy" of type "MsBackendPolicy" and "op_exec_info" of type "const OpExecInfoPtr&" py::object ForwardExecutor::RunOpWithBackendPolicy(MsBackendPolicy backend_policy, const OpExecInfoPtr &op_exec_info) { + + // Declare a variable named "result" of type "py::object" py::object result; + + // Check if the value of "backend_policy" is equal to "kMsBackendVmOnly" if (backend_policy == kMsBackendVmOnly) { -#ifndef ENABLE_TEST - if (kVmOperators.find(op_exec_info->op_name) != kVmOperators.end()) { + + // Check if the macro "ENABLE_TEST" is not defined + #ifndef ENABLE_TEST + + // Check if the value of "op_exec_info->op_name" is present in the set "kVmOperators" + if (kVmOperators.find(op_exec_info->op_name) != kVmOperators.end()) { + + // If the condition is true, call the function "RunOpInVM" with "op_exec_info" as the argument and assign the returned value to "result" + result = RunOpInVM(op_exec_info); + } else { + + // If the condition is false, call the function "RunOpInMs" with "op_exec_info" as the argument and assign the returned value to "result" + result = RunOpInMs(op_exec_info); + } + + // If the macro "ENABLE_TEST" is defined + #else + + // Call the function "RunOpInVM" with "op_exec_info" as the argument and assign the returned value to "result" result = RunOpInVM(op_exec_info); - } else { - result = RunOpInMs(op_exec_info); - } -#else - result = RunOpInVM(op_exec_info); -#endif + + // End of the "#ifndef ENABLE_TEST" block + #endif } - - return result; + + // Continue with the rest of the code outside the if statement } -py::object ForwardExecutor::RunOpInVM(const OpExecInfoPtr &op_exec_info) { - MS_LOG(DEBUG) << "RunOpInVM start"; - MS_EXCEPTION_IF_NULL(op_exec_info); - MS_EXCEPTION_IF_NULL(op_exec_info->py_primitive); +// Return the value of the variable "result" to indicate the result of the function +return result; +} - auto &op_inputs = op_exec_info->op_inputs; - if (op_exec_info->op_name == prim::kPrimInsertGradientOf->name() || - op_exec_info->op_name == prim::kPrimStopGradient->name() || - op_exec_info->op_name == prim::kPrimHookBackward->name() || - op_exec_info->op_name == prim::kPrimCellBackwardHook->name()) { - py::tuple result(op_inputs.size()); - for (size_t i = 0; i < op_inputs.size(); i++) { - py::object input = op_inputs[i]; - auto tensor = py::cast(input); - MS_EXCEPTION_IF_NULL(tensor); - if (op_exec_info->op_name == prim::kPrimHookBackward->name() || - op_exec_info->op_name == prim::kPrimCellBackwardHook->name()) { - // the input object is not a output of forward cnode, eg: parameter - result[i] = tensor; - } else { - // the input object is a output of forward cnode - auto new_tensor = std::make_shared(tensor->data_type(), tensor->shape(), tensor->data_ptr()); - new_tensor->set_device_address(tensor->device_address()); - new_tensor->set_sync_status(tensor->sync_status()); - result[i] = new_tensor; - } +// RunOpInVM function of the ForwardExecutor class is called to execute an operation in a virtual machine +// It takes an OpExecInfoPtr object as input, which contains information about the operation to be executed + +// Log a debug message indicating the start of the RunOpInVM function +MS_LOG(DEBUG) << "RunOpInVM start"; + +// Check if the op_exec_info object is null, and throw an exception if it is +MS_EXCEPTION_IF_NULL(op_exec_info); + +// Check if the py_primitive member of the op_exec_info object is null, and throw an exception if it is +MS_EXCEPTION_IF_NULL(op_exec_info->py_primitive); + +// Create a reference to the `op_inputs` vector from `op_exec_info` +auto &op_inputs = op_exec_info->op_inputs; + +// Check if the `op_name` of `op_exec_info` matches any of the specified names +if (op_exec_info->op_name == prim::kPrimInsertGradientOf->name() || + op_exec_info->op_name == prim::kPrimStopGradient->name() || + op_exec_info->op_name == prim::kPrimHookBackward->name() || + op_exec_info->op_name == prim::kPrimCellBackwardHook->name()) { + + // Create a Python tuple with the same size as `op_inputs` + py::tuple result(op_inputs.size()); + + // Iterate over each element in `op_inputs` + for (size_t i = 0; i < op_inputs.size(); i++) { + + // Get the current input object + py::object input = op_inputs[i]; + + // Cast the input object to a `tensor::TensorPtr` + auto tensor = py::cast(input); + MS_EXCEPTION_IF_NULL(tensor); + + // Check if the `op_name` matches either "prim::kPrimHookBackward" or "prim::kPrimCellBackwardHook" + if (op_exec_info->op_name == prim::kPrimHookBackward->name() || + op_exec_info->op_name == prim::kPrimCellBackwardHook->name()) { + + // If the input object is not an output of a forward cnode, e.g., a parameter, assign it to the result tuple + result[i] = tensor; + } else { + + // If the input object is an output of a forward cnode, create a new tensor with the same properties + auto new_tensor = std::make_shared(tensor->data_type(), tensor->shape(), tensor->data_ptr()); + new_tensor->set_device_address(tensor->device_address()); + new_tensor->set_sync_status(tensor->sync_status()); + + // Assign the new tensor to the result tuple + result[i] = new_tensor; } - MS_LOG(DEBUG) << "RunOpInVM end"; - return std::move(result); } - auto primitive = op_exec_info->py_primitive; - MS_EXCEPTION_IF_NULL(primitive); - auto result = primitive->RunPyComputeFunction(op_inputs); + // Log a debug message MS_LOG(DEBUG) << "RunOpInVM end"; + + // Return the result tuple + return std::move(result); +} + + // Get the primitive from the op_exec_info and assign it to the variable "primitive" + auto primitive = op_exec_info->py_primitive; + + // Check if the primitive is null, and throw an exception if it is + MS_EXCEPTION_IF_NULL(primitive); + + // Call the RunPyComputeFunction method of the primitive, passing in the op_inputs + auto result = primitive->RunPyComputeFunction(op_inputs); + + // Log a debug message indicating that the RunOpInVM function has ended + MS_LOG(DEBUG) << "RunOpInVM end"; + + // Check if the result is an instance of "py::none" if (py::isinstance(result)) { + // If it is, log an exception message and return an empty tuple MS_LOG(EXCEPTION) << "VM op " << op_exec_info->op_name << " run failed!"; py::tuple err_ret(0); return std::move(err_ret); } + + // Check if the result is an instance of "py::tuple" if (py::isinstance(result)) { + // If it is, return the result as is return result; } + + // If the result is not an instance of "py::tuple", create a tuple containing the result py::tuple tuple_result = py::make_tuple(result); + + // Return the tuple_result return std::move(tuple_result); } +// A member function of the ForwardExecutor class that checks if synchronization is needed for heterogeneous execution void ForwardExecutor::CheckIfNeedSyncForHeterogeneous(const std::string &cur_target) { + + // Check if the last target is not "Unknown" and is different from the current target if (last_target_ != "Unknown" && last_target_ != cur_target) { + + // Get an instance of the PynativeExecutor class auto executor = PynativeExecutor::GetInstance(); + + // Call the Sync() function of the PynativeExecutor instance to synchronize executor->Sync(); } + + // Update the last target to the current target last_target_ = cur_target; } +// Define a function named "RunOpInMs" that takes a pointer to an object of type "OpExecInfo" as a parameter and returns a "py::object" py::object ForwardExecutor::RunOpInMs(const OpExecInfoPtr &op_exec_info) { + // Check if the provided pointer is not null, throw an exception if it is null MS_EXCEPTION_IF_NULL(op_exec_info); + + // Enable the MindRT backend compile::SetMindRTEnable(); + + // Log a debug message indicating the start of running the operation with the backend policy "ms" MS_LOG(DEBUG) << "Start run op [" << op_exec_info->op_name << "] with backend policy ms"; + + // Get the instance of the "MsContext" class auto ms_context = MsContext::GetInstance(); + + // Throw an exception if the "ms_context" is null MS_EXCEPTION_IF_NULL(ms_context); + + // Set the "MS_CTX_ENABLE_PYNATIVE_INFER" parameter of the "ms_context" to true ms_context->set_param(MS_CTX_ENABLE_PYNATIVE_INFER, true); + + // Get the value of the "MS_CTX_DEVICE_TARGET" parameter from the "ms_context" and assign it to the "device_target" variable const std::string &device_target = ms_context->get_param(MS_CTX_DEVICE_TARGET); + + // Get the value of the "MS_CTX_DEVICE_ID" parameter from the "ms_context" and assign it to the "device_id" variable uint32_t device_id = ms_context->get_param(MS_CTX_DEVICE_ID); + + // Get the value of the "MS_CTX_ENABLE_MINDRT" parameter from the "ms_context" and assign it to the "enable_mind_rt" variable auto enable_mind_rt = ms_context->get_param(MS_CTX_ENABLE_MINDRT); + // ... +} - std::string cur_target = GetCurrentDeviceTarget(device_target, op_exec_info->py_primitive); - CheckIfNeedSyncForHeterogeneous(cur_target); +// Declare a variable named "cur_target" of type std::string and assign it the value returned by the function GetCurrentDeviceTarget +std::string cur_target = GetCurrentDeviceTarget(device_target, op_exec_info->py_primitive); - std::vector input_tensors; - std::vector tensors_mask; - std::string graph_info; - ConstructInputTensor(op_exec_info, &tensors_mask, &input_tensors); - ConvertAttrToUnifyMindIR(op_exec_info); - // get graph info for checking it whether existing in the cache - GetSingleOpGraphInfo(op_exec_info, input_tensors, tensors_mask, &graph_info); +// Call the function CheckIfNeedSyncForHeterogeneous and pass the "cur_target" variable as an argument +CheckIfNeedSyncForHeterogeneous(cur_target); + +// Declare a vector of tensor pointers named input_tensors +std::vector input_tensors; + +// Declare a vector of int64_t named tensors_mask +std::vector tensors_mask; + +// Declare a string variable named graph_info +std::string graph_info; + +// Call the function ConstructInputTensor and pass the arguments op_exec_info, tensors_mask, and input_tensors by reference +ConstructInputTensor(op_exec_info, &tensors_mask, &input_tensors); + +// Call the function ConvertAttrToUnifyMindIR and pass the argument op_exec_info +ConvertAttrToUnifyMindIR(op_exec_info); + +// Call the function GetSingleOpGraphInfo and pass the arguments op_exec_info, input_tensors, tensors_mask, and graph_info by reference +GetSingleOpGraphInfo(op_exec_info, input_tensors, tensors_mask, &graph_info); + +// Check if the __APPLE__ macro is defined #if defined(__APPLE__) - session::OpRunInfo op_run_info = {false, - op_exec_info->op_name, - op_exec_info->py_primitive.get(), - op_exec_info->abstract, - op_exec_info->is_dynamic_shape, - op_exec_info->is_mixed_precision_cast, - false, - op_exec_info->next_op_name, - static_cast(op_exec_info->next_input_index), - graph_info, - tensors_mask, - input_tensors, - cur_target}; + + // Declare a session::OpRunInfo struct named op_run_info and initialize its members + session::OpRunInfo op_run_info = { + false, + op_exec_info->op_name, + op_exec_info->py_primitive.get(), + op_exec_info->abstract, + op_exec_info->is_dynamic_shape, + op_exec_info->is_mixed_precision_cast, + false, + op_exec_info->next_op_name, + static_cast(op_exec_info->next_input_index), + graph_info, + tensors_mask, + input_tensors, + cur_target + }; + +// If the __APPLE__ macro is not defined #else - session::OpRunInfo op_run_info = {false, - op_exec_info->op_name, - op_exec_info->py_primitive.get(), - op_exec_info->abstract, - op_exec_info->is_dynamic_shape, - op_exec_info->is_mixed_precision_cast, - op_exec_info->lazy_build, - op_exec_info->next_op_name, - op_exec_info->next_input_index, - graph_info, - tensors_mask, - input_tensors, - cur_target}; + + // Declare a session::OpRunInfo struct named op_run_info and initialize its members + session::OpRunInfo op_run_info = { + false, + op_exec_info->op_name, + op_exec_info->py_primitive.get(), + op_exec_info->abstract, + op_exec_info->is_dynamic_shape, + op_exec_info->is_mixed_precision_cast, + op_exec_info->lazy_build, + op_exec_info->next_op_name, + op_exec_info->next_input_index, + graph_info, + tensors_mask, + input_tensors, + cur_target + }; + #endif - VectorRef outputs; - if (!enable_mind_rt) { - auto cur_session = GetCurrentSession(cur_target, device_id); - MS_EXCEPTION_IF_NULL(cur_session); - cur_session->RunOp(&op_run_info, &outputs); - } else { - auto cur_mind_rt_backend = GetMindRtBackend(cur_target, device_id); - MS_EXCEPTION_IF_NULL(cur_mind_rt_backend); - mindspore::ScopedLongRunning long_running; - cur_mind_rt_backend->RunOp(&op_run_info, &outputs); - } +// Create a vector to store the output values +VectorRef outputs; +// Check if the "enable_mind_rt" flag is false +if (!enable_mind_rt) { + // Get the current session for the specified target and device ID + auto cur_session = GetCurrentSession(cur_target, device_id); + // Throw an exception if the current session is null + MS_EXCEPTION_IF_NULL(cur_session); + // Run the operation using the current session and store the outputs in the "outputs" vector + cur_session->RunOp(&op_run_info, &outputs); +} else { + // Get the current MindRt backend for the specified target and device ID + auto cur_mind_rt_backend = GetMindRtBackend(cur_target, device_id); + // Throw an exception if the current MindRt backend is null + MS_EXCEPTION_IF_NULL(cur_mind_rt_backend); + // Create a scoped long running object to handle long running operations + mindspore::ScopedLongRunning long_running; + // Run the operation using the current MindRt backend and store the outputs in the "outputs" vector + cur_mind_rt_backend->RunOp(&op_run_info, &outputs); +} + + // Check if the operation has dynamic shape if (op_exec_info->is_dynamic_shape) { + // If it does, assign the abstract value from op_run_info to op_exec_info op_exec_info->abstract = op_run_info.abstract; } + + // Convert the outputs to Python data auto result = BaseRefToPyData(outputs); + + // Disable PyNative inference in the MindSpore context ms_context->set_param(MS_CTX_ENABLE_PYNATIVE_INFER, false); + + // Log a debug message indicating the end of running the operation with the backend policy "ms" MS_LOG(DEBUG) << "End run op [" << op_exec_info->op_name << "] with backend policy ms"; + + // Return the result return result; } +// A member function of the ForwardExecutor class that clears the resources used during forward execution + void ForwardExecutor::ClearRes() { + + // Log a debug message indicating that the forward results are being cleared MS_LOG(DEBUG) << "Clear forward res"; + + // Set the lazy_build_ flag to false, indicating that lazy building is not enabled lazy_build_ = false; + + // Clear the implicit_cast_map_, which is a map used for implicit type casting implicit_cast_map_.clear(); + + // Clear the prim_abs_list_, which is a list of primitive abstract values prim_abs_list_.clear(); + + // Clear the node_abs_map_, which is a map of node to abstract value node_abs_map_.clear(); } +// Define a function named "forward" that returns a pointer to a ForwardExecutor object ForwardExecutorPtr GradExecutor::forward() const { + + // Attempt to lock the weak pointer to the forward_executor_ object auto forward_executor = forward_executor_.lock(); + + // Check if the weak pointer is still valid (i.e., not expired) MS_EXCEPTION_IF_NULL(forward_executor); + + // Return the locked pointer to the forward_executor_ object return forward_executor; } +// Return a pointer to the top cell of the GradExecutor object TopCellInfoPtr GradExecutor::top_cell() const { + + // Check if the top_cell_ member variable is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(top_cell_); + + // Return the top_cell_ member variable return top_cell_; } +// Define the function `curr_g()` of the `GradExecutor` class, which returns a `FuncGraphPtr` object. FuncGraphPtr GradExecutor::curr_g() const { + + // Get the function graph from the top cell of the `GradExecutor` object auto fg = top_cell()->fg(); + + // Throw an exception if the function graph is null MS_EXCEPTION_IF_NULL(fg); + + // Return the function graph return fg; } +// A member function of the GradExecutor class that pushes a cell ID onto the cell stack void GradExecutor::PushCellStack(const std::string &cell_id) { + + // Push the given cell ID onto the cell stack cell_stack_.push(cell_id); + + // Increment the cell order counter ++cell_order_; } +// Definition of the function "PopCellStack" in the class "GradExecutor" + void GradExecutor::PopCellStack() { + + // Check if the cell stack is empty if (cell_stack_.empty()) { + + // If the cell stack is empty, throw an exception with an error message MS_LOG(EXCEPTION) << "Stack cell_stack_ is empty"; } + + // If the cell stack is not empty, remove the top element cell_stack_.pop(); } +// Define a member function named GetCurCellOrder in the GradExecutor class that returns a string std::string GradExecutor::GetCurCellOrder() const { + + // Check if the cell stack is empty if (cell_stack_.empty()) { + + // If the cell stack is empty, throw an exception with an error message MS_LOG(EXCEPTION) << "The cell_stack_ is empty!"; } + + // Return the top element of the cell stack concatenated with an underscore and the cell order converted to a string return cell_stack_.top() + "_" + std::to_string(cell_order_); } -void GradExecutor::PushHighOrderGraphStack(const TopCellInfoPtr &top_cell) { high_order_stack_.push(top_cell); } +// This function is a member function of the GradExecutor class +// It is used to push a TopCellInfoPtr object onto the high_order_stack_ +void GradExecutor::PushHighOrderGraphStack(const TopCellInfoPtr &top_cell) { + // Push the provided top_cell onto the high_order_stack_ + high_order_stack_.push(top_cell); +} + +// Function to pop the top element from the high_order_stack_ and return it as a TopCellInfoPtr TopCellInfoPtr GradExecutor::PopHighOrderGraphStack() { + + // Check if the high_order_stack_ is empty if (high_order_stack_.empty()) { + + // If the stack is empty, throw an exception with an error message MS_LOG(EXCEPTION) << "Stack high_order_stack_ is empty"; } + + // Pop the top element from the high_order_stack_ high_order_stack_.pop(); + + // Create a pointer to a TopCellInfo object and initialize it to nullptr TopCellInfoPtr top_cell = nullptr; + + // Check if the high_order_stack_ is still not empty after popping the top element if (!high_order_stack_.empty()) { + + // If the stack is not empty, assign the top element to the top_cell pointer top_cell = high_order_stack_.top(); } + + // Return the top_cell pointer return top_cell; } +// This function is a member function of the GradExecutor class and returns a string representing the cell ID. +// The cell ID is constructed based on the given cell object and arguments. + std::string GradExecutor::GetCellId(const py::object &cell, const py::args &args) { + // Get the ID of the cell object auto cell_id = GetId(cell); + + // Iterate over the arguments for (size_t i = 0; i < args.size(); i++) { + // Get the ID of the current argument const auto &arg_id = GetId(args[i]); + + // Check if the argument ID exists in the node_abs_map of the forward executor auto it = forward()->node_abs_map().find(arg_id); if (it != forward()->node_abs_map().end()) { + // If the argument ID exists, get the corresponding AbstractValue auto &abs = it->second; MS_EXCEPTION_IF_NULL(abs); + + // Build the shape and type of the AbstractValue auto shape = abs->BuildShape(); MS_EXCEPTION_IF_NULL(shape); auto type = abs->BuildType(); MS_EXCEPTION_IF_NULL(type); + + // Append the shape and type information to the cell ID cell_id += "_" + shape->ToString(); cell_id += type->ToString(); } else { + // If the argument ID does not exist, convert the argument to a Value and create an AbstractValue from it auto value = PyObjToValue(args[i]); MS_EXCEPTION_IF_NULL(value); auto abs = value->ToAbstract(); MS_EXCEPTION_IF_NULL(abs); + + // If the AbstractValue is an AbstractTensor, set its value to kAnyValue if (abs->isa()) { abs->set_value(kAnyValue); } + + // Add the argument ID and its corresponding AbstractValue to the node_abs_map of the forward executor forward()->node_abs_map()[arg_id] = abs; + + // Build the shape and type of the AbstractValue auto shape = abs->BuildShape(); MS_EXCEPTION_IF_NULL(shape); auto type = abs->BuildType(); MS_EXCEPTION_IF_NULL(type); + + // Append the shape and type information to the cell ID cell_id += "_" + shape->ToString(); cell_id += type->ToString(); } } + + // Return the constructed cell ID return cell_id; } +// A function named "DumpGraphIR" defined in the "GradExecutor" class void GradExecutor::DumpGraphIR(const std::string &filename, const FuncGraphPtr &graph) { + + // Check if the ENABLE_DUMP_IR macro is defined #ifdef ENABLE_DUMP_IR - auto ms_context = MsContext::GetInstance(); - MS_EXCEPTION_IF_NULL(ms_context); - if (ms_context->get_param(MS_CTX_SAVE_GRAPHS_FLAG)) { - DumpIR(filename, graph); - } + + // Get the instance of the MsContext class + auto ms_context = MsContext::GetInstance(); + + // Throw an exception if the MsContext instance is null + MS_EXCEPTION_IF_NULL(ms_context); + + // Check if the MS_CTX_SAVE_GRAPHS_FLAG parameter is set to true + if (ms_context->get_param(MS_CTX_SAVE_GRAPHS_FLAG)) { + + // Call the DumpIR function to dump the intermediate representation of the graph to a file + DumpIR(filename, graph); + } #endif } +// Check if the current gradient execution is nested inline bool GradExecutor::IsNestedGrad() const { + + // Output a debug log message indicating the nested order of the gradient MS_LOG(DEBUG) << "Grad nested order is " << grad_order_; + + // Return true if the nested order of the gradient is greater than 1, otherwise return false return grad_order_ > 1; } +// A member function of the GradExecutor class that compares two cell object IDs bool GradExecutor::IsCellObjIdEq(const std::string &l_cell_id, const std::string &r_cell_id) const { - // just compare obj_id, ignore args id + + // Compare the first PTR_LEN characters of l_cell_id and r_cell_id + // If they are equal, return true; otherwise, return false return l_cell_id.compare(0, PTR_LEN, r_cell_id, 0, PTR_LEN) == 0; } +// Check if the GradExecutor's top_cell_ is nullptr bool GradExecutor::IsBpropGraph(const std::string &cell_id) { if (top_cell_ == nullptr) { return false; } + + // Check if the given cell_id is present in the bprop_cell_list_ return std::any_of(bprop_cell_list_.begin(), bprop_cell_list_.end(), [&cell_id](const std::string &value) { return cell_id.find(value) != std::string::npos; }); } +// A function to update the top cell information void GradExecutor::UpdateTopCellInfo(bool forward_already_run, bool need_compile_graph, bool vm_compiled) { + + // Set the vm_compiled flag of the top cell to the provided value top_cell()->set_vm_compiled(vm_compiled); + + // Set the need_compile_graph flag of the top cell to the provided value top_cell()->set_need_compile_graph(need_compile_graph); + + // Set the forward_already_run flag of the top cell to the provided value top_cell()->set_forward_already_run(forward_already_run); } void GradExecutor::ClearCellRes(const std::string &cell_id) { static bool clear_all_cell_res = false; - // Grad clean + + // If cell_id is empty, clear all cell resources if (cell_id.empty()) { MS_LOG(DEBUG) << "Clear all cell resources"; clear_all_cell_res = true; + + // Clear resources for each top cell in top_cell_list_ for (const auto &iter : top_cell_list_) { MS_EXCEPTION_IF_NULL(iter); iter->Clear(); } + + // Clear top_cell_list_ and already_run_top_cell_ top_cell_list_.clear(); already_run_top_cell_.clear(); + clear_all_cell_res = false; return; } + + // If clear_all_cell_res is true, it means we are already in the process of clearing all cell resources, + // so there is no need to clear resources for a single cell again if (clear_all_cell_res) { MS_LOG(DEBUG) << "In process of clearing all cell resources, so no need to clear single cell resource again"; return; } - // clear when cell destruction + + // Clear resources when a cell is being destroyed for (auto it = top_cell_list_.begin(); it != top_cell_list_.end();) { MS_EXCEPTION_IF_NULL(*it); const auto &top_cell_id = (*it)->cell_id(); const auto &already_run_cell_id = (*it)->already_run_cell_id(); + + // If the cell_id matches the top_cell_id, clear the top cell resource if (IsCellObjIdEq(cell_id, top_cell_id)) { MS_LOG(DEBUG) << "Clear top cell resource. Top cell id " << top_cell_id; (*it)->Clear(); @@ -2427,49 +4351,57 @@ void GradExecutor::ClearCellRes(const std::string &cell_id) { (void)already_run_top_cell_.erase(already_run_cell_id); continue; } + ++it; } } void GradExecutor::HandleInputArgsForTopCell(const py::args &args, bool is_bprop_top) { - if (is_bprop_top) { - // Convert input args to parameters for top cell graph in bprop. - for (size_t i = 0; i < args.size(); ++i) { - auto param = args[i]; - auto new_param = curr_g()->add_parameter(); - const auto ¶m_id = GetId(param); - SetTupleArgsToGraphInfoMap(curr_g(), param, new_param, true); - SetNodeMapInGraphInfoMap(curr_g(), param_id, new_param); - SetParamNodeMapInGraphInfoMap(curr_g(), param_id, new_param); + if (is_bprop_top) { + // Convert input args to parameters for top cell graph in bprop. + for (size_t i = 0; i < args.size(); ++i) { + auto param = args[i]; + auto new_param = curr_g()->add_parameter(); + const auto ¶m_id = GetId(param); + SetTupleArgsToGraphInfoMap(curr_g(), param, new_param, true); + SetNodeMapInGraphInfoMap(curr_g(), param_id, new_param); + SetParamNodeMapInGraphInfoMap(curr_g(), param_id, new_param); + } + return; } - return; - } - // Convert input args to parameters for top cell graph in construct. - std::vector input_param_values; - const auto &only_tensors = FilterTensorArgs(args); - for (size_t i = 0; i < only_tensors.size(); ++i) { - auto new_param = curr_g()->add_parameter(); - auto param_i = only_tensors[i]; - const auto ¶m_i_value = PyObjToValue(param_i); - input_param_values.emplace_back(param_i_value); - auto param_i_abs = param_i_value->ToAbstract(); - MS_EXCEPTION_IF_NULL(param_i_abs); - new_param->set_abstract(param_i_abs->Broaden()); - const auto ¶m_i_id = GetId(param_i); - SetTupleArgsToGraphInfoMap(curr_g(), param_i, new_param, true); - SetNodeMapInGraphInfoMap(curr_g(), param_i_id, new_param); - SetParamNodeMapInGraphInfoMap(curr_g(), param_i_id, new_param); - SetParamNodeMapInGraphInfoMap(top_cell_->df_builder(), param_i_id, new_param); - } - top_cell()->set_k_pynative_cell_ptr(ad::GradPynativeCellBegin(curr_g()->parameters(), input_param_values)); + // Convert input args to parameters for top cell graph in construct. + std::vector input_param_values; + const auto &only_tensors = FilterTensorArgs(args); + for (size_t i = 0; i < only_tensors.size(); ++i) { + auto new_param = curr_g()->add_parameter(); + auto param_i = only_tensors[i]; + const auto ¶m_i_value = PyObjToValue(param_i); + input_param_values.emplace_back(param_i_value); + auto param_i_abs = param_i_value->ToAbstract(); + MS_EXCEPTION_IF_NULL(param_i_abs); + new_param->set_abstract(param_i_abs->Broaden()); + const auto ¶m_i_id = GetId(param_i); + SetTupleArgsToGraphInfoMap(curr_g(), param_i, new_param, true); + SetNodeMapInGraphInfoMap(curr_g(), param_i_id, new_param); + SetParamNodeMapInGraphInfoMap(curr_g(), param_i_id, new_param); + SetParamNodeMapInGraphInfoMap(top_cell_->df_builder(), param_i_id, new_param); + } + top_cell()->set_k_pynative_cell_ptr(ad::GradPynativeCellBegin(curr_g()->parameters(), input_param_values)); } +// Initialize the resources and dataflow builder for the GradExecutor class void GradExecutor::InitResourceAndDfBuilder(const std::string &cell_id, const py::object &cell, const py::args &args) { + + // Check if the cell stack is empty or if we are in a nested gradient computation if (cell_stack_.empty() || IsNestedGrad()) { + + // If the cell stack is empty and gradient computation is not running, create a new top-level graph if (cell_stack_.empty() && !grad_is_running_) { MS_LOG(DEBUG) << "Make new topest graph"; MakeNewTopGraph(cell_id, cell, args, true); - } else if (grad_is_running_ && IsBpropGraph(cell_id)) { + } + // If gradient computation is running and the current cell is a bprop graph, run the bprop cell + else if (grad_is_running_ && IsBpropGraph(cell_id)) { MS_LOG(DEBUG) << "Run bprop cell"; auto fg = std::make_shared(); top_cell()->set_fg(fg); @@ -2477,75 +4409,128 @@ void GradExecutor::InitResourceAndDfBuilder(const std::string &cell_id, const py top_cell()->graph_info_map()[fg] = graph_info_cg; HandleInputArgsForTopCell(args, true); bprop_grad_stack_.push(std::make_pair(cell_id, false)); - } else if (grad_is_running_ && top_cell()->grad_order() != grad_order_) { + } + // If gradient computation is running and the current cell is not a bprop graph, but the grad order is different from the top cell's grad order + else if (grad_is_running_ && top_cell()->grad_order() != grad_order_) { MS_LOG(DEBUG) << "Nested grad graph existed in bprop"; MakeNewTopGraph(cell_id, cell, args, false); bprop_grad_stack_.push(std::make_pair(cell_id, true)); - } else if (!cell_stack_.empty() && IsNestedGrad() && top_cell()->grad_order() != grad_order_) { + } + // If the cell stack is not empty, we are in a nested gradient computation, and the grad order is different from the top cell's grad order + else if (!cell_stack_.empty() && IsNestedGrad() && top_cell()->grad_order() != grad_order_) { MS_LOG(DEBUG) << "Nested grad graph existed in construct"; auto cur_top_is_dynamic = top_cell()->is_dynamic(); MakeNewTopGraph(cell_id, cell, args, false); top_cell()->set_is_dynamic(cur_top_is_dynamic); } } +} PushCellStack(cell_id); - // Init kPynativeCellPtr with input parameters of top cell + // Initialize kPynativeCellPtr with input parameters of the top cell if (!top_cell()->is_init_kpynative()) { + // Create a new GraphInfo object for the current cell and add it to the graph_info_map of the top cell auto graph_info_cg = std::make_shared(cell_id); top_cell()->graph_info_map()[curr_g()] = graph_info_cg; + + // Create another GraphInfo object for the current cell and add it to the graph_info_map of the df_builder of the top cell auto graph_info_df = std::make_shared(cell_id); top_cell()->graph_info_map()[top_cell_->df_builder()] = graph_info_df; + + // Handle input arguments for the top cell HandleInputArgsForTopCell(args, false); + + // Set the need_compile_graph flag to true for the top cell top_cell()->set_need_compile_graph(true); + + // Set the init_kpynative flag to true for the top cell top_cell()->set_init_kpynative(true); } else { // Non-top cell + // Add the current cell to the sub_cell_list of the top cell top_cell()->sub_cell_list().emplace(cell_id); } } +// Define the function `NewGraphInner` of the `GradExecutor` class void GradExecutor::NewGraphInner(py::object *ret, const py::object &cell, const py::args &args) { MS_EXCEPTION_IF_NULL(ret); + + // Get the cell ID using the `GetCellId` function const auto &cell_id = GetCellId(cell, args); + + // Log the start of the `NewGraphInner` function along with the number of arguments and the cell ID MS_LOG(DEBUG) << "NewGraphInner start " << args.size() << " " << cell_id; + + // Check if the top cell is not null and the cell stack is empty if (top_cell_ != nullptr && cell_stack_.empty()) { // Already run top cell need distinguish high order; high order add "0" otherwise "1" const auto &already_run_cell_id = GetAlreadyRunCellId(cell_id); + + // Find the top cell in the already run top cells map auto top_it = already_run_top_cell_.find(already_run_cell_id); + + // Check if the top cell is found in the already run top cells map if (top_it != already_run_top_cell_.end()) { // Top cell forward run. const auto &pre_top_cell = top_it->second; MS_EXCEPTION_IF_NULL(pre_top_cell); + + // Check if the hook has changed for the previous top cell if (pre_top_cell->hook_changed()) { + // If the hook has changed, erase the top cell from the already run top cells map already_run_top_cell_.erase(top_it); + + // Erase the top cell from the top cell list EraseTopCellFromTopCellList(pre_top_cell); } else if (!pre_top_cell->is_dynamic()) { + // If the previous top cell is not dynamic, no need to run `NewGraphInner` again MS_LOG(DEBUG) << "Top cell " << cell_id << " is not dynamic, no need to run NewGraphInner again"; + + // Reset the top cell information ResetTopCellInfo(pre_top_cell, args); + + // Push the high order graph stack with the previous top cell PushHighOrderGraphStack(pre_top_cell); + + // Set the top cell to the previous top cell set_top_cell(pre_top_cell); + + // Set the grad order to the grad order of the previous top cell grad_order_ = pre_top_cell->grad_order(); + + // Return from the function return; } } else if ((top_cell()->IsSubCell(cell_id) || GetHighOrderStackSize() >= 1) && !IsCellObjIdEq(cell_id, check_graph_cell_id_)) { // Sub cell ( or may be a temporary cell, but must be non top) forward run in cache process. MS_LOG(DEBUG) << "Sub cell no need to run NewGraphInner again"; + + // Return from the function return; } } - // When the cell has custom bprop, in_custom_bprop_cell is lager than 0 + + // When the cell has custom bprop, in_custom_bprop_cell is larger than 0 if (py::hasattr(cell, parse::CUSTOM_BPROP_NAME)) { custom_bprop_cell_count_ += 1; } - // Make top graph and init resource for resource and df_builder + + // Initialize the resource and df_builder for the top graph InitResourceAndDfBuilder(cell_id, cell, args); - // Check whether cell has dynamic construct + + // Check whether the cell has dynamic construct if (!top_cell()->is_dynamic()) { bool is_dynamic = parse::DynamicParser::IsDynamicCell(cell); MS_LOG(DEBUG) << "Current cell dynamic " << is_dynamic; + // ... + } +} + // Check if the variable is_dynamic is true if (is_dynamic) { + + // If it is true, call the set_is_dynamic function on the top cell top_cell()->set_is_dynamic(is_dynamic); } } @@ -2553,393 +4538,667 @@ void GradExecutor::NewGraphInner(py::object *ret, const py::object &cell, const void GradExecutor::MakeNewTopGraph(const string &cell_id, const py::object &cell, const py::args &args, bool is_topest) { - pipeline::CheckArgsValid(cell, args); - // Record input args info - std::string input_args_id; + pipeline::CheckArgsValid(cell, args); // Check if the arguments are valid using the pipeline module + + std::string input_args_id; // Initialize an empty string to store the input arguments' IDs + for (size_t i = 0; i < args.size(); ++i) { - input_args_id += GetId(args[i]) + "_"; + input_args_id += GetId(args[i]) + "_"; // Concatenate the IDs of the input arguments with an underscore } - // Run forward first need plus 1 - if (grad_order_ == 0) { - ++grad_order_; + + if (grad_order_ == 0) { // If the gradient order is 0 + ++grad_order_; // Increment the gradient order by 1 } - // The number of top cell exceeds MAX_TOP_CELL_COUNTS, delete the last one to keep the maximum length of the list, - // disable backend cache - if (top_cell_list_.size() >= MAX_TOP_CELL_COUNTS) { - EnableOpGraphCache(false); - const auto last_top_cell = top_cell_list_.back(); - top_cell_list_.pop_back(); - MS_EXCEPTION_IF_NULL(last_top_cell); - last_top_cell->Clear(); - (void)already_run_top_cell_.erase(last_top_cell->already_run_cell_id()); + + if (top_cell_list_.size() >= MAX_TOP_CELL_COUNTS) { // If the number of top cells exceeds the maximum count + EnableOpGraphCache(false); // Disable the backend cache + + const auto last_top_cell = top_cell_list_.back(); // Get the last top cell from the list + top_cell_list_.pop_back(); // Remove the last top cell from the list + + MS_EXCEPTION_IF_NULL(last_top_cell); // Throw an exception if the last top cell is null + + last_top_cell->Clear(); // Clear the resources associated with the last top cell + (void)already_run_top_cell_.erase(last_top_cell->already_run_cell_id()); // Remove the last top cell from the already run top cell set } - // Create top cell - auto fg = std::make_shared(); - auto df_builder = std::make_shared(); - auto resource = std::make_shared(); - const auto &already_run_cell_id = GetAlreadyRunCellId(cell_id); + + auto fg = std::make_shared(); // Create a new function graph + auto df_builder = std::make_shared(); // Create a new function graph for the gradient + auto resource = std::make_shared(); // Create a new resource object + + const auto &already_run_cell_id = GetAlreadyRunCellId(cell_id); // Get the ID of the already run cell + auto top_cell = - std::make_shared(is_topest, grad_order_, resource, fg, df_builder, cell_id, already_run_cell_id); - top_cell->set_forward_already_run(true); - top_cell->set_input_args_id(input_args_id); - top_cell_list_.emplace_back(top_cell); - PushHighOrderGraphStack(top_cell); - set_top_cell(top_cell); - MS_LOG(DEBUG) << "New top graph, fg ptr " << fg.get() << " resource ptr " << resource.get(); + std::make_shared(is_topest, grad_order_, resource, fg, df_builder, cell_id, already_run_cell_id); // Create a new top cell with the given parameters + + top_cell->set_forward_already_run(true); // Set the forward already run flag to true + top_cell->set_input_args_id(input_args_id); // Set the input arguments ID for the top cell + + top_cell_list_.emplace_back(top_cell); // Add the new top cell to the top cell list + PushHighOrderGraphStack(top_cell); // Push the new top cell to the high order graph stack + set_top_cell(top_cell); // Set the new top cell as the current top cell + + MS_LOG(DEBUG) << "New top graph, fg ptr " << fg.get() << " resource ptr " << resource.get(); // Log the information about the new top graph } void GradExecutor::SetTupleArgsToGraphInfoMap(const FuncGraphPtr &g, const py::object &args, const AnfNodePtr &node, bool is_param) { - if (!py::isinstance(args) && !py::isinstance(args)) { - return; - } - auto tuple = args.cast(); - auto tuple_size = static_cast(tuple.size()); - for (int64_t i = 0; i < tuple_size; ++i) { - // tuple slice used size_t - auto id = GetId(tuple[static_cast(i)]); - if (is_param && node->isa()) { - auto param = node->cast(); - MS_EXCEPTION_IF_NULL(param); - SetParamNodeMapInGraphInfoMap(g, id, param); + // Check if the input args is a tuple or a list + if (!py::isinstance(args) && !py::isinstance(args)) { + return; + } + + // Cast the input args to a tuple + auto tuple = args.cast(); + + // Get the size of the tuple + auto tuple_size = static_cast(tuple.size()); + + // Iterate over each element in the tuple + for (int64_t i = 0; i < tuple_size; ++i) { + // Get the id of the current tuple element + auto id = GetId(tuple[static_cast(i)]); + + // Check if the node is a parameter and if is_param is true + if (is_param && node->isa()) { + auto param = node->cast(); + MS_EXCEPTION_IF_NULL(param); + SetParamNodeMapInGraphInfoMap(g, id, param); + } + + // Set the node map in the graph info map + SetNodeMapInGraphInfoMap(g, id, node, i); + + // Recursively call SetTupleItemArgsToGraphInfoMap for the current tuple element + SetTupleItemArgsToGraphInfoMap(g, tuple[i], node, std::vector{i}, is_param); } - SetNodeMapInGraphInfoMap(g, id, node, i); - SetTupleItemArgsToGraphInfoMap(g, tuple[i], node, std::vector{i}, is_param); - } } void GradExecutor::SetTupleItemArgsToGraphInfoMap(const FuncGraphPtr &g, const py::object &args, const AnfNodePtr &node, const std::vector &index_sequence, bool is_param) { - if (!py::isinstance(args) && !py::isinstance(args)) { - return; - } - MS_EXCEPTION_IF_NULL(node); - auto tuple = args.cast(); - auto tuple_size = static_cast(tuple.size()); - for (int64_t i = 0; i < tuple_size; ++i) { - std::vector tmp = index_sequence; - tmp.emplace_back(i); - // tuple slice used size_t - auto id = GetId(tuple[static_cast(i)]); - if (is_param && node->isa()) { - auto param = node->cast(); - MS_EXCEPTION_IF_NULL(param); - SetParamNodeMapInGraphInfoMap(g, id, param); + // Check if the input args is a tuple or a list + if (!py::isinstance(args) && !py::isinstance(args)) { + return; + } + MS_EXCEPTION_IF_NULL(node); + auto tuple = args.cast(); + auto tuple_size = static_cast(tuple.size()); + for (int64_t i = 0; i < tuple_size; ++i) { + std::vector tmp = index_sequence; + tmp.emplace_back(i); + // Get the id of the current tuple item + auto id = GetId(tuple[static_cast(i)]); + if (is_param && node->isa()) { + auto param = node->cast(); + MS_EXCEPTION_IF_NULL(param); + // Set the parameter node in the graph info map + SetParamNodeMapInGraphInfoMap(g, id, param); + } + // Set the node in the graph info map with its id and index sequence + SetNodeMapInGraphInfoMap(g, id, node, tmp); + // Recursively call the function to process nested tuple items + SetTupleItemArgsToGraphInfoMap(g, tuple[i], node, tmp, is_param); } - SetNodeMapInGraphInfoMap(g, id, node, tmp); - SetTupleItemArgsToGraphInfoMap(g, tuple[i], node, tmp, is_param); - } } +// The `EndGraphInner` function is a member function of the `GradExecutor` class. + void GradExecutor::EndGraphInner(py::object *ret, const py::object &cell, const py::object &out, const py::args &args) { MS_EXCEPTION_IF_NULL(ret); + + // Get the cell ID based on the given cell and arguments const auto &cell_id = GetCellId(cell, args); + + // Log the start of the `EndGraphInner` function with the number of arguments and the cell ID MS_LOG(DEBUG) << "EndGraphInner start " << args.size() << " " << cell_id; + + // Check if the cell stack is empty if (cell_stack_.empty()) { + // If the cell stack is empty, check if the current cell ID matches the top cell ID if (cell_id == top_cell()->cell_id()) { + // If the current cell is the top cell and it is the topmost cell, set the grad flag to false if (top_cell()->is_topest()) { set_grad_flag(false); } + + // Check if the high-order stack size is less than ARG_SIZE if (GetHighOrderStackSize() < ARG_SIZE) { + // Pop the top cell from the high-order graph stack and set it as the new top cell auto outer_top_cell = PopHighOrderGraphStack(); if (outer_top_cell != nullptr) { set_top_cell(outer_top_cell); } } } + + // Log that the current cell does not need to run `EndGraphInner` again MS_LOG(DEBUG) << "Current cell " << cell_id << " no need to run EndGraphInner again"; + + // Return from the function return; } + + // Perform gradient computation for custom bprop DoGradForCustomBprop(cell, out, args); + + // Pop the top cell from the cell stack PopCellStack(); + + // Check if gradient is running and the bprop gradient stack is not empty if (grad_is_running_ && !bprop_grad_stack_.empty()) { + // Check if the top of the bprop gradient stack is not a forward graph if (!bprop_grad_stack_.top().second) { + // Set the output of the current graph to the object node of the output and return curr_g()->set_output(GetObjNode(out, GetId(out))); bprop_grad_stack_.pop(); return; } else if (bprop_grad_stack_.top().first == cell_id) { + // Check if the top of the bprop gradient stack matches the current cell ID and pop it bprop_grad_stack_.pop(); } } - // Just only dump the last forward graph + + // Check if saving graphs is enabled and the current cell is the top cell bool is_top_cell_end = cell_id == top_cell()->cell_id(); if (MsContext::GetInstance()->get_param(MS_CTX_SAVE_GRAPHS_FLAG) && is_top_cell_end) { + // Set the output of the current graph to the object node of the output curr_g()->set_output(GetObjNode(out, GetId(out))); + + // Dump the IR of the current graph #ifdef ENABLE_DUMP_IR DumpIR("fg.ir", curr_g()); #endif } - // Reset grad flag and update output node of the outermost cell + + // Check if the cell stack is empty and the current cell is the top cell if (cell_stack_.empty() && is_top_cell_end) { - MS_LOG(DEBUG) << "Cur top last cell " << cell_id; - PopHighOrderGraphStack(); - auto k_pynative_cell_ptr = top_cell()->k_pynative_cell_ptr(); - MS_EXCEPTION_IF_NULL(k_pynative_cell_ptr); - k_pynative_cell_ptr->UpdateOutputNodeOfTopCell(GetObjNode(out, GetId(out))); - top_cell()->ClearCellHookOp(); - cell_order_ = 0; - set_grad_flag(false); + // Reset the grad flag and update the output node of the outermost cell + // ... } +} + MS_LOG(DEBUG) << "Cur top last cell " << cell_id; // Print debug message with the current top cell ID + PopHighOrderGraphStack(); // Pop the top cell from the high order graph stack + auto k_pynative_cell_ptr = top_cell()->k_pynative_cell_ptr(); // Get the k_pynative_cell_ptr of the current top cell + MS_EXCEPTION_IF_NULL(k_pynative_cell_ptr); // Throw an exception if k_pynative_cell_ptr is null + k_pynative_cell_ptr->UpdateOutputNodeOfTopCell(GetObjNode(out, GetId(out))); // Update the output node of the current top cell with the given output and its ID + top_cell()->ClearCellHookOp(); // Clear the cell hook operation of the current top cell + cell_order_ = 0; // Reset the cell order to 0 + set_grad_flag(false); // Set the grad flag to false + + } // End of if statement + // Checkout whether need to compile graph when each top cell has ran finished if (is_top_cell_end) { // In high grad cases, the output of the internal graph may be a tuple, and node needs to be created in the getobj if (!cell_stack_.empty()) { - (void)GetObjNode(out, GetId(out)); + (void)GetObjNode(out, GetId(out)); // Create a node in the getobj for the output if the cell stack is not empty } - top_cell()->CheckSubCellHookChanged(); - CheckNeedCompileGraph(); - } -} + top_cell()->CheckSubCellHookChanged(); // Check if the subcell hook has changed in the current top cell + CheckNeedCompileGraph(); // Check if the graph needs to be compiled + } // End of if statement +// This function is responsible for performing gradient computation for a custom backward propagation function of a neural network cell. +// It takes in three arguments: 'cell' (the neural network cell object), 'out' (the output of the forward pass), and 'args' (additional arguments). void GradExecutor::DoGradForCustomBprop(const py::object &cell, const py::object &out, const py::args &args) { + + // Check if the 'cell' object has the attribute 'CUSTOM_BPROP_NAME' (indicating the presence of a custom backward propagation function) if (!py::hasattr(cell, parse::CUSTOM_BPROP_NAME)) { - return; + return; // If not, return from the function } + + // Decrement the count of custom backward propagation cells custom_bprop_cell_count_ -= 1; + + // Check if there are more custom backward propagation cells remaining if (custom_bprop_cell_count_ != 0) { - return; + return; // If yes, return from the function } + + // Log a debug message indicating that gradient computation is being performed for custom backward propagation MS_LOG(DEBUG) << "Do grad for custom bprop"; + + // Get the number of parameters in the 'cell' object size_t par_number = py::tuple(python_adapter::CallPyObjMethod(cell, "get_parameters")).size(); + + // Check if there are any parameters in the 'cell' object if (par_number > 0) { + // If yes, throw an exception indicating that the 'Parameter' data type is not supported in the network when user defines the backward propagation MS_LOG(EXCEPTION) << "When user defines the net bprop, the 'Parameter' data type is not supported in the net."; } + + // Get the custom backward propagation function from the 'cell' object py::function bprop_func = py::getattr(cell, parse::CUSTOM_BPROP_NAME); + + // Get the ID of the custom backward propagation function auto bprop_func_cellid = GetId(bprop_func); + + // Add the ID of the custom backward propagation function to the list of backward propagation cells bprop_cell_list_.emplace_back(bprop_func_cellid); + + // Create a fake primitive with the name 'kPrimHookBackward' auto fake_prim = std::make_shared(prim::kPrimHookBackward->name()); + + // Check if the 'cell' object is an instance of the 'Cell' class if (py::isinstance(cell)) { + // If yes, cast the 'cell' object to a 'CellPtr' and set the 'bprop_cls_name' of the fake primitive to the name of the 'cell' object auto cell_ptr = py::cast(cell); fake_prim->set_bprop_cls_name(cell_ptr->name()); } + + // Add the backward hook function to the fake primitive fake_prim->AddBackwardHookFn(0, bprop_func); +} - const auto &cell_id = GetCellId(cell, args); - (void)fake_prim->AddAttr("cell_id", MakeValue(cell_id)); - (void)fake_prim->AddAttr(parse::CUSTOM_BPROP_NAME, MakeValue(true)); +// Create a constant reference variable 'cell_id' and assign it the value returned by the function 'GetCellId' with arguments 'cell' and 'args' +const auto &cell_id = GetCellId(cell, args); - py::object code_obj = py::getattr(bprop_func, "__code__"); - py::object co_name = py::getattr(code_obj, "co_name"); - if (std::string(py::str(co_name)) == "staging_specialize") { +// Add an attribute to the 'fake_prim' object with the name "cell_id" and the value of 'cell_id' +(void)fake_prim->AddAttr("cell_id", MakeValue(cell_id)); + +// Add an attribute to the 'fake_prim' object with the name stored in 'parse::CUSTOM_BPROP_NAME' and the value 'true' +(void)fake_prim->AddAttr(parse::CUSTOM_BPROP_NAME, MakeValue(true)); + +// Get the "__code__" attribute of the bprop_func object +py::object code_obj = py::getattr(bprop_func, "__code__"); + +// Get the "co_name" attribute of the code_obj object +py::object co_name = py::getattr(code_obj, "co_name"); + +// Check if the name of the code object is "staging_specialize" +if (std::string(py::str(co_name)) == "staging_specialize") { + // If it is, throw an exception with an error message MS_LOG(EXCEPTION) << "Decorating bprop with '@ms_function' is not supported."; - } - // Three parameters self, out and dout need to be excluded - const size_t inputs_num = py::cast(py::getattr(code_obj, "co_argcount")) - 3; - if (inputs_num != args.size()) { +} + +// Calculate the number of inputs for the bprop function +// Subtract 3 from the "co_argcount" attribute of the code_obj object +const size_t inputs_num = py::cast(py::getattr(code_obj, "co_argcount")) - 3; + +// Check if the calculated number of inputs is equal to the size of the args vector +if (inputs_num != args.size()) { + // If it is not, throw a TypeError with an error message MS_EXCEPTION(TypeError) << "Size of bprop func inputs[" << inputs_num << "] is not equal to the size of cell inputs[" << args.size() << "]"; - } +} + // Create an empty list to store cell inputs py::list cell_inputs; + + // Iterate over the inputs and append them to the cell_inputs list for (size_t i = 0; i < inputs_num; i += 1) { cell_inputs.append(args[i]); } + + // Create a shared pointer to an OpExecInfo object OpExecInfoPtr op_exec_info = std::make_shared(); + + // Set the op_name of the OpExecInfo object to the name of the fake_prim op_exec_info->op_name = fake_prim->name(); + + // Set the py_primitive of the OpExecInfo object to the fake_prim op_exec_info->py_primitive = fake_prim; + + // Set the op_inputs of the OpExecInfo object to the cell_inputs list op_exec_info->op_inputs = cell_inputs; + + // Call the ConstructForwardGraph function of the forward object and store the result in cnode auto cnode = forward()->ConstructForwardGraph(op_exec_info); + + // Convert the out object to a Value object and store it in v_out const auto &v_out = PyObjToValue(out); + + // Call the DoOpGrad function with the op_exec_info, cnode, and v_out as arguments DoOpGrad(op_exec_info, cnode, v_out); + + // Get the object ID of the out object and store it in out_obj_id const auto &out_obj_id = GetId(out); + + // Call the SaveOutputNodeMap function with the out_obj_id, out, and cnode as arguments SaveOutputNodeMap(out_obj_id, out, cnode); } +// This function is a member function of the GradExecutor class +// It takes a const reference to a string called cell_id as input and returns a string + std::string GradExecutor::GetAlreadyRunCellId(const std::string &cell_id) { + + // Create a new string called already_run_cell_id and initialize it with the value of cell_id std::string already_run_cell_id(cell_id); + + // Append the value of grad_order_ (if it is 0, append 1, otherwise append grad_order_) to already_run_cell_id already_run_cell_id += std::to_string(grad_order_ == 0 ? 1 : grad_order_); + + // Append "_" and the value of grad_operation_ to already_run_cell_id already_run_cell_id += "_" + grad_operation_; + + // Print a debug message using the MS_LOG macro, indicating the value of already_run_cell_id MS_LOG(DEBUG) << "Get already run top cell id " << already_run_cell_id; + + // Return the value of already_run_cell_id return already_run_cell_id; } +// Function to get the gradient cell ID std::string GradExecutor::GetGradCellId(bool has_sens, const py::object &cell, const py::args &args) { + + // Get the size of the forward arguments size_t forward_args_size = args.size(); + + // Create a temporary copy of the arguments py::args tmp = args; + + // If the function has sensitivity, reduce the forward arguments size by 1 if (has_sens) { forward_args_size--; + + // Create a new tuple to store the reduced forward arguments py::tuple f_args(forward_args_size); + + // Copy the forward arguments to the new tuple for (size_t i = 0; i < forward_args_size; ++i) { f_args[i] = args[i]; } + + // Update the temporary arguments to the reduced forward arguments tmp = f_args; } + + // Get the cell ID using the updated arguments const auto &cell_id = GetCellId(cell, tmp); + + // Return the cell ID return cell_id; } +// This function marks the nodes in a function graph that correspond to parameters with default values +// The function takes a resource pointer as input void GradExecutor::MarkMsFunctionNodes(const pipeline::ResourcePtr &resource) { + + // Get the function graph from the resource auto func_graph = resource->func_graph(); + + // Create a vector to store the indices of parameters that are in a function marked with `ms_function_params_` std::vector in_ms_function; + + // Get the parameters of the function graph auto parameters = func_graph->parameters(); + + // Iterate over the parameters for (size_t i = 0; i < parameters.size(); i++) { + + // Get the current parameter auto param = parameters[i]->cast(); + + // Check if the parameter has a default value if (!param->has_default()) { continue; } + + // Check if the parameter's name is in the `ms_function_params_` vector auto iter = std::find(ms_function_params_.begin(), ms_function_params_.end(), param->name()); + + // If the parameter's name is found in `ms_function_params_`, push 1 to `in_ms_function` if (iter != ms_function_params_.end()) { in_ms_function.push_back(1); - } else { + } + // If the parameter's name is not found in `ms_function_params_`, push 0 to `in_ms_function` + else { in_ms_function.push_back(0); } } +} - auto ret = func_graph->get_return(); - auto ret_cnode = ret->cast(); - auto grads = ret_cnode->input(1)->cast(); - for (size_t i = 1; i < grads->inputs().size(); i++) { - if (in_ms_function[i - 1]) { - auto node = grads->input(i); - if (!node->isa()) { - continue; + auto ret = func_graph->get_return(); // Get the return node of the function graph + auto ret_cnode = ret->cast(); // Cast the return node to a CNode pointer + auto grads = ret_cnode->input(1)->cast(); // Get the second input of the return node and cast it to a CNode pointer + for (size_t i = 1; i < grads->inputs().size(); i++) { // Iterate over the inputs of the grads CNode + if (in_ms_function[i - 1]) { // Check if the corresponding in_ms_function flag is true + auto node = grads->input(i); // Get the i-th input of the grads CNode + if (!node->isa()) { // Check if the input is not a CNode + continue; // Skip to the next iteration if the input is not a CNode } - auto cnode = node->cast(); - cnode->set_parallel(true); + auto cnode = node->cast(); // Cast the input to a CNode pointer + cnode->set_parallel(true); // Set the parallel flag of the CNode to true } } } +// The GradExecutor class has a method called GradNetInner which takes several arguments +// and returns nothing (void). + 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); MS_EXCEPTION_IF_NULL(grad); + + // Get the number of arguments passed to the function auto size = args.size(); + + // Get the unique identifier for the current gradient cell const auto &cell_id = GetGradCellId(grad->sens_param(), cell, args); + + // Log the start of the GradNet execution with the number of arguments and the cell identifier MS_LOG(DEBUG) << "GradNet start " << size << " " << cell_id; + + // Check if the top cell needs to compile the graph if (!top_cell()->need_compile_graph()) { MS_LOG(DEBUG) << "No need compile graph"; + + // If the cell stack is not empty, update the top cell information accordingly if (!cell_stack_.empty()) { UpdateTopCellInfo(false, false, true); } else { UpdateTopCellInfo(false, false, false); } + + // Return from the function return; } + + // Set the gradient operation for the top cell top_cell()->set_grad_operation(grad_operation_); + + // Get the resource and df_builder for the top cell auto resource = top_cell()->resource(); MS_EXCEPTION_IF_NULL(resource); auto df_builder = top_cell()->df_builder(); MS_EXCEPTION_IF_NULL(df_builder); + + // Log the current gradient and resource pointers MS_LOG(DEBUG) << "fg ptr " << curr_g().get() << " resource ptr " << resource.get(); +} // Get params(weights) require derivative - auto w_args = GetWeightsArgs(weights, df_builder); - auto p_args = GetGradPositionArgs(grad_position); - if (w_args.empty() && !df_builder->parameters().empty()) { - MS_LOG(DEBUG) << "Add weights params to w_args"; - w_args.insert(w_args.end(), df_builder->parameters().begin(), df_builder->parameters().end()); + auto w_args = GetWeightsArgs(weights, df_builder); // Get the arguments for weights, based on the provided weights and the derivative function builder + auto p_args = GetGradPositionArgs(grad_position); // Get the arguments for gradient position, based on the provided gradient position + if (w_args.empty() && !df_builder->parameters().empty()) { // If the weights arguments are empty and the derivative function builder has parameters + MS_LOG(DEBUG) << "Add weights params to w_args"; // Log a debug message indicating that weights parameters are being added to w_args + w_args.insert(w_args.end(), df_builder->parameters().begin(), df_builder->parameters().end()); // Add the parameters of the derivative function builder to w_args } // Get bprop graph of top cell - auto bprop_graph = GetBpropGraph(grad, cell, w_args, p_args, size, args); - MS_EXCEPTION_IF_NULL(bprop_graph); - bprop_graph->set_is_bprop(true); - resource->set_func_graph(bprop_graph); - auto manager = resource->manager(); - MS_EXCEPTION_IF_NULL(manager); - manager->AddFuncGraph(bprop_graph, true); - DumpGraphIR("launch_bprop_graph.ir", bprop_graph); + auto bprop_graph = GetBpropGraph(grad, cell, w_args, p_args, size, args); // Get the backpropagation graph of the top cell, based on the provided gradient, cell, w_args, p_args, size, and args + MS_EXCEPTION_IF_NULL(bprop_graph); // Throw an exception if the bprop_graph is null + bprop_graph->set_is_bprop(true); // Set the is_bprop flag of the bprop_graph to true + resource->set_func_graph(bprop_graph); // Set the func_graph of the resource to the bprop_graph + auto manager = resource->manager(); // Get the manager from the resource + MS_EXCEPTION_IF_NULL(manager); // Throw an exception if the manager is null + manager->AddFuncGraph(bprop_graph, true); // Add the bprop_graph to the manager + DumpGraphIR("launch_bprop_graph.ir", bprop_graph); // Dump the graph IR of the bprop_graph to a file named "launch_bprop_graph.ir" // Launch bprop graph to backend - SaveForwardTensorInfoInBpropGraph(resource); - compile::SetMindRTEnable(); - resource->SetResult(pipeline::kBackend, compile::CreateBackend()); - MS_LOG(DEBUG) << "Start task emit action"; - auto parallel_mode = parallel::ParallelContext::GetInstance()->parallel_mode(); - if (parallel_mode == parallel::kSemiAutoParallel || parallel_mode == parallel::kAutoParallel) { - MarkMsFunctionNodes(resource); + SaveForwardTensorInfoInBpropGraph(resource); // Save the forward tensor information in the bprop_graph + compile::SetMindRTEnable(); // Enable MindRT compilation + resource->SetResult(pipeline::kBackend, compile::CreateBackend()); // Set the backend of the resource to the created backend + MS_LOG(DEBUG) << "Start task emit action"; // Log a debug message indicating that the task emit action is starting + auto parallel_mode = parallel::ParallelContext::GetInstance()->parallel_mode(); // Get the parallel mode from the ParallelContext + if (parallel_mode == parallel::kSemiAutoParallel || parallel_mode == parallel::kAutoParallel) { // If the parallel mode is semi-automatic or automatic parallel + MarkMsFunctionNodes(resource); // Mark the MS function nodes in the resource } - TaskEmitAction(resource); - MS_LOG(DEBUG) << "Start execute action"; - ExecuteAction(resource); - MS_LOG(DEBUG) << "Start update top cell info when run finish"; - UpdateTopCellInfo(false, false, true); - resource->Clean(); - abstract::AnalysisContext::ClearContext(); + TaskEmitAction(resource); // Perform the task emit action using the resource + MS_LOG(DEBUG) << "Start execute action"; // Log a debug message indicating that the execute action is starting + ExecuteAction(resource); // Perform the execute action using the resource + MS_LOG(DEBUG) << "Start update top cell info when run finish"; // Log a debug message indicating that the top cell info update is starting + UpdateTopCellInfo(false, false, true); // Update the top cell info with the provided flags + resource->Clean(); // Clean up the resource + abstract::AnalysisContext::ClearContext(); // Clear the analysis context // Clean cache used for parse. As static variable is released after // Python threads is released. - parse::data_converter::ClearObjectCache(); - parse::Parser::CleanParserResource(); - parse::CleanDataClassToClassMap(); - trace::ClearTraceStack(); -} + parse::data_converter::ClearObjectCache(); // Clear the object cache used for parsing + parse::Parser::CleanParserResource(); // Clean up the parser resource + parse::CleanDataClassToClassMap(); // Clean up the data class to class map + trace::ClearTraceStack(); // Clear the trace stack +// Define a function named "GetWeightsArgs" which takes two parameters: "weights" of type py::object and "df_builder" of type FuncGraphPtr std::vector GradExecutor::GetWeightsArgs(const py::object &weights, const FuncGraphPtr &df_builder) { + + // Check if "df_builder" is a null pointer, and throw an exception if it is MS_EXCEPTION_IF_NULL(df_builder); + + // Check if the "weights" object has an attribute named "__parameter_tuple__" if (!py::hasattr(weights, "__parameter_tuple__")) { + + // If the attribute is not found, print a debug message MS_LOG(DEBUG) << "No parameter tuple get"; + + // Return an empty vector return {}; } - - const auto &tuple = weights.cast(); - MS_LOG(DEBUG) << "Get weights tuple size " << tuple.size(); - std::vector w_args; - for (size_t it = 0; it < tuple.size(); ++it) { - auto param = tuple[it]; - auto param_id = GetId(param); - auto &graph_info_map = top_cell()->graph_info_map(); - if (graph_info_map.find(df_builder) == graph_info_map.end()) { - MS_LOG(EXCEPTION) << "Can not find df_builder " << df_builder.get() << " Top cell " << top_cell().get() - << " cell id " << top_cell()->cell_id(); - } - auto graph_info = graph_info_map.at(df_builder); - MS_EXCEPTION_IF_NULL(graph_info); - AnfNodePtr para_node = nullptr; - if (graph_info->params.find(param_id) != graph_info->params.end()) { - para_node = graph_info->params.at(param_id); - w_args.emplace_back(para_node); - continue; - } - const auto &name_attr = python_adapter::GetPyObjAttr(param, "name"); - if (py::isinstance(name_attr)) { - MS_LOG(EXCEPTION) << "Parameter object should have name attribute"; - } - const auto ¶m_name = py::cast(name_attr); - MS_LOG(DEBUG) << "The input " << it << " parameter weight name " << param_name; - if (graph_info->params.find(param_name) != graph_info->params.end()) { - para_node = graph_info->params.at(param_name); - } else { - MS_LOG(DEBUG) << "Can not find input param in graph info map, make a new parameter"; - auto free_param = df_builder->add_parameter(); - free_param->set_name(param_name); - auto value = py::cast(param); - free_param->set_default_param(value); - free_param->debug_info()->set_name(param_name); - para_node = free_param; - } - w_args.emplace_back(para_node); - } - return w_args; } +// Create a constant reference to the weights tuple obtained from casting the 'weights' object +const auto &tuple = weights.cast(); + +// Log the size of the weights tuple for debugging purposes +MS_LOG(DEBUG) << "Get weights tuple size " << tuple.size(); + +// Create a vector to store the AnfNodePtr objects representing the weights +std::vector w_args; + +// Iterate over each element in the weights tuple +for (size_t it = 0; it < tuple.size(); ++it) { + // Get the current parameter object from the tuple + auto param = tuple[it]; + + // Get the unique identifier for the parameter + auto param_id = GetId(param); + + // Get the graph info map from the top cell + auto &graph_info_map = top_cell()->graph_info_map(); + + // Check if the df_builder is present in the graph info map + if (graph_info_map.find(df_builder) == graph_info_map.end()) { + // If not found, log an exception with relevant information + MS_LOG(EXCEPTION) << "Can not find df_builder " << df_builder.get() << " Top cell " << top_cell().get() + << " cell id " << top_cell()->cell_id(); + } + + // Get the graph info object corresponding to the df_builder + auto graph_info = graph_info_map.at(df_builder); + MS_EXCEPTION_IF_NULL(graph_info); + + // Create a pointer to the AnfNode representing the parameter + AnfNodePtr para_node = nullptr; + + // Check if the parameter is already present in the graph info's params map + if (graph_info->params.find(param_id) != graph_info->params.end()) { + // If found, get the corresponding AnfNode and add it to the w_args vector + para_node = graph_info->params.at(param_id); + w_args.emplace_back(para_node); + continue; + } + + // Get the 'name' attribute of the parameter object + const auto &name_attr = python_adapter::GetPyObjAttr(param, "name"); + + // Check if the 'name' attribute is of type 'none' + if (py::isinstance(name_attr)) { + // If it is, log an exception indicating that the parameter object should have a 'name' attribute + MS_LOG(EXCEPTION) << "Parameter object should have name attribute"; + } + + // Convert the 'name' attribute to a std::string + const auto ¶m_name = py::cast(name_attr); + + // Log the name of the parameter weight for debugging purposes + MS_LOG(DEBUG) << "The input " << it << " parameter weight name " << param_name; + + // Check if the parameter name is present in the graph info's params map + if (graph_info->params.find(param_name) != graph_info->params.end()) { + // If found, get the corresponding AnfNode + para_node = graph_info->params.at(param_name); + } else { + // If not found, create a new parameter and add it to the graph + MS_LOG(DEBUG) << "Can not find input param in graph info map, make a new parameter"; + auto free_param = df_builder->add_parameter(); + free_param->set_name(param_name); + auto value = py::cast(param); + free_param->set_default_param(value); + free_param->debug_info()->set_name(param_name); + para_node = free_param; + } + + // Add the parameter node to the w_args vector + w_args.emplace_back(para_node); +} + +// Return the vector of AnfNodePtr objects representing the weights +return w_args; + +// Function to get the gradient position arguments from a Python object std::vector GradExecutor::GetGradPositionArgs(const py::object &grad_position) { + + // Create an empty vector to store the position arguments std::vector pos_args; + + // Check if the grad_position object is an instance of a tuple if (py::isinstance(grad_position)) { + + // Cast the grad_position object to a tuple const auto &tuple = grad_position.cast(); + + // Iterate over each element in the tuple for (size_t it = 0; it < tuple.size(); ++it) { + + // Get the current parameter from the tuple auto param = tuple[it]; + + // Get the ID of the parameter auto param_id = GetId(param); + + // Convert the parameter ID to an integer and add it to the position arguments vector pos_args.push_back(std::stoi(param_id)); } + + // Return the position arguments vector return pos_args; } + + // If the grad_position object is not a tuple, throw an exception MS_LOG(EXCEPTION) << "Grad position only support tuple."; } +// A function to shallow copy the sensitivity values from the input arguments to the run arguments void GradExecutor::ShallowCopySensValue(const py::tuple &input_args, bool has_sens, VectorRef *run_args) { + // If the input arguments do not have sensitivity values, return if (!has_sens) { return; } - // Get index and number of sens args. + + // Get the index and number of sensitivity arguments size_t sens_index = input_args.size() - 1; size_t sens_num = 1; + + // If the sensitivity argument is a tuple, get the number of elements in the tuple if (py::isinstance(input_args[sens_index])) { py::tuple tuple_sens = py::cast(input_args[sens_index]); sens_num = ConvertArgs(tuple_sens).size(); } - // Shallow copy sens args to new sens args. + + // Shallow copy the sensitivity arguments to new sensitivity arguments MS_EXCEPTION_IF_NULL(run_args); for (size_t i = sens_index; i < sens_index + sens_num; ++i) { const auto &original_sens = (*run_args)[i]; + + // If the original sensitivity value is a ValuePtr, perform shallow copy if (utils::isa(original_sens)) { auto sens_value = utils::cast(original_sens); MS_EXCEPTION_IF_NULL(sens_value); @@ -2952,319 +5211,610 @@ void GradExecutor::ShallowCopySensValue(const py::tuple &input_args, bool has_se } } -void GradExecutor::UpdateParamAbsByArgs(const py::list &args, const FuncGraphPtr &bprop_graph) { - MS_EXCEPTION_IF_NULL(bprop_graph); - const auto &bprop_params = bprop_graph->parameters(); - // bprop_params include inputs, parameters, more than size(inputs) - if (bprop_params.size() < args.size()) { - MS_LOG(EXCEPTION) << "Df parameters size " << bprop_params.size() << " less than " << args.size(); +// Update the abstract information of parameters based on the provided arguments and the backpropagation graph + +// Check if the backpropagation parameters size is less than the size of the provided arguments +if (bprop_params.size() < args.size()) { + MS_LOG(EXCEPTION) << "Df parameters size " << bprop_params.size() << " less than " << args.size(); +} + +// Initialize an index variable to keep track of the current parameter +size_t index = 0; + +// Iterate over each parameter in the backpropagation graph +for (const auto ¶m : bprop_params) { + // Cast the parameter to a ParameterPtr + auto param_node = param->cast(); + MS_EXCEPTION_IF_NULL(param_node); + + // Check if the parameter has a default value + if (param_node->has_default()) { + // Update the abstract information for weights + ValuePtr value = param_node->default_param(); + MS_EXCEPTION_IF_NULL(value); + auto ptr = value->ToAbstract(); + MS_EXCEPTION_IF_NULL(ptr); + param_node->set_abstract(ptr->Broaden()); + } else { + // Update the abstract information for input parameters + + // Convert the Python object to a Value and create an AbstractValue from it + auto input_abs = abstract::FromValue(PyObjToValue(args[index]), true); + + // Check if the parameter already has an abstract value + if (param_node->abstract() != nullptr) { + auto input_shape = input_abs->BuildShape()->ToString(); + auto param_tensor_abs = param_node->abstract(); + + // Check if the parameter's abstract value is an AbstractRef + if (param_tensor_abs->isa()) { + param_tensor_abs = param_tensor_abs->cast()->CloneAsTensor(); + } + + auto ir_shape = param_tensor_abs->BuildShape()->ToString(); + + // Exclude const input + if (input_shape != "()" && ir_shape != "()") { + // Check if the input shape matches the expected shape + if (input_shape != ir_shape) { + MS_EXCEPTION(ValueError) << "The shape should be " << ir_shape << ", but got " << input_shape << ", " + << param->DebugString(); + } + + auto ir_dtype = param_tensor_abs->BuildType()->ToString(); + auto input_dtype = input_abs->BuildType()->ToString(); + + // Check if the input dtype matches the expected dtype + if (input_dtype != ir_dtype) { + MS_EXCEPTION(TypeError) << "The dtype should be " << ir_dtype << ", but got " << input_dtype << ", " + << param->DebugString(); + } + } + } + + // Increment the index to move to the next parameter + index++; } - size_t index = 0; - for (const auto ¶m : bprop_params) { - auto param_node = param->cast(); - MS_EXCEPTION_IF_NULL(param_node); - if (param_node->has_default()) { - // update abstract info for weights - ValuePtr value = param_node->default_param(); - MS_EXCEPTION_IF_NULL(value); - auto ptr = value->ToAbstract(); - MS_EXCEPTION_IF_NULL(ptr); - param_node->set_abstract(ptr->Broaden()); - } else { - // update abstract info for input params - auto input_abs = abstract::FromValue(PyObjToValue(args[index]), true); - if (param_node->abstract() != nullptr) { - auto input_shape = input_abs->BuildShape()->ToString(); - auto param_tensor_abs = param_node->abstract(); - if (param_tensor_abs->isa()) { - param_tensor_abs = param_tensor_abs->cast()->CloneAsTensor(); - } - auto ir_shape = param_tensor_abs->BuildShape()->ToString(); - // Exclude const input - if (input_shape != "()" && ir_shape != "()") { - if (input_shape != ir_shape) { - MS_EXCEPTION(ValueError) << "The shape should be " << ir_shape << ", but got " << input_shape << ", " - << param->DebugString(); - } - auto ir_dtype = param_tensor_abs->BuildType()->ToString(); - auto input_dtype = input_abs->BuildType()->ToString(); - if (input_dtype != ir_dtype) { - MS_EXCEPTION(TypeError) << "The dtype should be " << ir_dtype << ", but got " << input_dtype << ", " - << param->DebugString(); - } +} } + // Check if the debug info name of the parameter node is "sens" and if the shape of the intermediate representation (ir_shape) is not equal to the input shape if (param_node->debug_info()->name() == "sens" && ir_shape != input_shape) { + // Set the flag need_renormalize_ to true need_renormalize_ = true; } } + // Set the abstract of the parameter node to the broadened input abstract param_node->set_abstract(input_abs->Broaden()); + // Increment the index index++; } } } -FuncGraphPtr GradExecutor::GetBpropGraph(const prim::GradOperationPtr &grad, const py::object &cell, - const std::vector &weights, - const std::vector &grad_position, size_t arg_size, - const py::args &args) { - bool build_formal_param = false; - if (!py::hasattr(cell, parse::CUSTOM_BPROP_NAME) && !cell_stack_.empty() && IsNestedGrad()) { +// GetBpropGraph function definition for the GradExecutor class + +// This function takes in several parameters: +// - grad: a GradOperationPtr object representing the gradient operation +// - cell: a py::object representing the cell +// - weights: a vector of AnfNodePtr objects representing the weights +// - grad_position: a vector of size_t representing the gradient positions +// - arg_size: a size_t representing the argument size +// - args: a py::args object representing additional arguments + +// Initialize a boolean variable build_formal_param to false +bool build_formal_param = false; + +// Check if the cell does not have the CUSTOM_BPROP_NAME attribute and the cell stack is not empty and IsNestedGrad() returns true +if (!py::hasattr(cell, parse::CUSTOM_BPROP_NAME) && !cell_stack_.empty() && IsNestedGrad()) { + // Set build_formal_param to true build_formal_param = true; + + // Set need_renormalize_ to true need_renormalize_ = true; - } - if (top_cell()->ms_function_flag()) { +} + +// Check if the top cell has the ms_function_flag() set +if (top_cell()->ms_function_flag()) { + // Set need_renormalize_ to true need_renormalize_ = true; - } +} - auto k_pynative_cell_ptr = top_cell()->k_pynative_cell_ptr(); - MS_EXCEPTION_IF_NULL(k_pynative_cell_ptr); - MS_EXCEPTION_IF_NULL(grad); - FuncGraphPtr bprop_graph = ad::GradPynativeCellEnd(k_pynative_cell_ptr, weights, grad_position, grad->get_all_, - grad->get_by_list_, grad->sens_param_, build_formal_param); - MS_EXCEPTION_IF_NULL(bprop_graph); +// Get the pointer to the `k_pynative_cell_ptr` object from the `top_cell()` function and assign it to `k_pynative_cell_ptr` variable +auto k_pynative_cell_ptr = top_cell()->k_pynative_cell_ptr(); - MS_LOG(DEBUG) << "Top graph input params size " << arg_size; - std::ostringstream ss; - ss << "grad{" << arg_size << "}"; - bprop_graph->set_flag(FUNC_GRAPH_FLAG_CORE, true); - bprop_graph->debug_info()->set_name(ss.str()); - // Get the parameters items and add the value to args_spec - UpdateParamAbsByArgs(FilterTensorArgs(args, grad->sens_param_), bprop_graph); +// Check if `k_pynative_cell_ptr` is null, if it is, throw an exception +MS_EXCEPTION_IF_NULL(k_pynative_cell_ptr); - // Do opt for final bprop graph +// Check if `grad` is null, if it is, throw an exception +MS_EXCEPTION_IF_NULL(grad); + +// Call the `GradPynativeCellEnd` function from the `ad` namespace with the arguments `k_pynative_cell_ptr`, `weights`, `grad_position`, `grad->get_all_`, `grad->get_by_list_`, `grad->sens_param_`, `build_formal_param` and assign the returned `FuncGraphPtr` to `bprop_graph` variable +FuncGraphPtr bprop_graph = ad::GradPynativeCellEnd(k_pynative_cell_ptr, weights, grad_position, grad->get_all_, grad->get_by_list_, grad->sens_param_, build_formal_param); + +// Check if `bprop_graph` is null, if it is, throw an exception +MS_EXCEPTION_IF_NULL(bprop_graph); + +// Log a debug message using the MS_LOG macro, printing the size of the argument +MS_LOG(DEBUG) << "Top graph input params size " << arg_size; + +// Create an output string stream object +std::ostringstream ss; + +// Append the string "grad{" followed by the value of arg_size to the output string stream +ss << "grad{" << arg_size << "}"; + +// Set the FUNC_GRAPH_FLAG_CORE flag of the bprop_graph to true +bprop_graph->set_flag(FUNC_GRAPH_FLAG_CORE, true); + +// Set the name of the debug info of the bprop_graph to the value of the output string stream +bprop_graph->debug_info()->set_name(ss.str()); + +// Filter the tensor arguments by excluding the ones present in grad->sens_param_ and update the parameter abstracts in bprop_graph +UpdateParamAbsByArgs(FilterTensorArgs(args, grad->sens_param_), bprop_graph); + + // Create a resource object to hold the bprop graph pipeline::ResourcePtr resource = std::make_shared(); + + // Set the bprop graph as the function graph in the resource resource->set_func_graph(bprop_graph); + + // Get the function graph manager from the resource auto manager = resource->manager(); MS_EXCEPTION_IF_NULL(manager); + + // Add the bprop graph to the function graph manager manager->AddFuncGraph(bprop_graph); + + // Use the PrimBpropOptimizer singleton instance to perform final optimization on the bprop graph auto optimized_bg = ad::PrimBpropOptimizer::GetPrimBpropOptimizerInst().BpropGraphFinalOpt(resource); + // Check if the cell stack is empty if (cell_stack_.empty()) { + // If it is empty, set the need_renormalize_ flag to false need_renormalize_ = false; } + + // Dump the graph IR to a file named "after_final_opt.ir" using the DumpGraphIR function DumpGraphIR("after_final_opt.ir", optimized_bg); + + // Return the optimized background graph return optimized_bg; } +// Define a method named "CheckGraph" that takes a Python object "cell" and a variable number of arguments "args" py::object GradExecutor::CheckGraph(const py::object &cell, const py::args &args) { + + // Initialize a variable "ret" with the value "false" of type BaseRef BaseRef ret = false; + + // Set the value of "check_graph_cell_id_" by calling the method "GetCellId" with "cell" and "args" as arguments check_graph_cell_id_ = GetCellId(cell, args); + + // Check if the conditions for executing the code block are met if (!(top_cell_ != nullptr && check_graph_cell_id_.find(top_cell_->cell_id()) != std::string::npos && grad_order_ >= 1)) { + + // Increment the value of "grad_order_" by 1 ++grad_order_; } + + // Check if "grad_is_running_" is false if (!grad_is_running_) { + + // Print a debug message indicating that "Grad" is not running yet MS_LOG(DEBUG) << "Grad not running yet"; + + // Convert "ret" to a Python object and return it return BaseRefToPyData(ret); } + + // Print a debug message indicating the value of "check_graph_cell_id_" MS_LOG(DEBUG) << "Key is " << check_graph_cell_id_; + + // Check if "top_cell_" is not nullptr if (top_cell_ != nullptr) { + + // Iterate over the elements in the "sub_cell_list()" of "top_cell_" for (auto it = top_cell_->sub_cell_list().begin(); it != top_cell_->sub_cell_list().end(); ++it) { + + // Print a debug message indicating the current cell id MS_LOG(DEBUG) << "Cur cell id " << *it; + + // Check if the current cell id is not equal to "check_graph_cell_id_" if (!IsCellObjIdEq(*it, check_graph_cell_id_)) { continue; } + + // Print a debug message indicating that the cell id is being deleted from the cell graph list MS_LOG(DEBUG) << "Delete cellid from cell graph list, top cell is " << top_cell_; + + // Remove the current cell id from the "sub_cell_list()" of "top_cell_" top_cell_->sub_cell_list().erase(it); + + // Set the value of "ret" to true ret = true; + + // Break out of the loop break; } } + + // Convert "ret" to a Python object and return it return BaseRefToPyData(ret); } -py::object GradExecutor::CheckAlreadyRun(const prim::GradOperationPtr &grad, const py::object &cell, - const py::args &args) { - bool forward_run = false; - // Get cell id and input args info - const auto &cell_id = GetCellId(cell, args); - grad_operation_ = std::to_string(static_cast(grad->get_all_)) + - std::to_string(static_cast(grad->get_by_list_)) + grad->grad_position_; +// Check if the forward run has already been executed +bool forward_run = false; - std::string input_args_id; - for (size_t i = 0; i < args.size(); ++i) { - input_args_id += GetId(args[i]) + "_"; - } - // Under the condition that the stack is empty (forward process completed or no forward process), - // check whether need to run forward process - if (cell_stack_.empty() && top_cell_ != nullptr) { - const auto &check_already_run_cell_id = GetAlreadyRunCellId(cell_id); - auto find_top_cell = GetTopCell(check_already_run_cell_id); - if (find_top_cell != nullptr) { - MS_LOG(DEBUG) << "Find already run top cell"; - forward_run = find_top_cell->forward_already_run(); - auto curr_top_cell = top_cell(); - set_top_cell(find_top_cell); - bool input_args_changed = - !find_top_cell->input_args_id().empty() && find_top_cell->input_args_id() != input_args_id; - if (forward_run && input_args_changed && find_top_cell->is_dynamic()) { - MS_LOG(WARNING) << "The construct of running cell is dynamic and the input info of this cell has changed, " - "forward process will run again"; - forward_run = false; - } - if (forward_run && GetHighOrderStackSize() >= 1) { - PushHighOrderGraphStack(curr_top_cell); - } - } - } - MS_LOG(DEBUG) << "Graph have already ran " << forward_run << " top cell id " << cell_id; - return BaseRefToPyData(forward_run); +// Get the cell ID and input arguments information +const auto &cell_id = GetCellId(cell, args); + +// Generate a string representation of the grad operation +grad_operation_ = std::to_string(static_cast(grad->get_all_)) + + std::to_string(static_cast(grad->get_by_list_)) + grad->grad_position_; + +// Declare a variable to store the concatenated IDs of the input arguments +std::string input_args_id; + +// Iterate over the 'args' vector and concatenate the IDs of each argument with an underscore +for (size_t i = 0; i < args.size(); ++i) { + input_args_id += GetId(args[i]) + "_"; } -void GradExecutor::CheckNeedCompileGraph() { - auto new_top_cell = top_cell(); - const auto &already_top_cell_id = new_top_cell->already_run_cell_id(); - // Update top cell by current cell op info - if (already_run_top_cell_.find(already_top_cell_id) == already_run_top_cell_.end()) { +// Check if the stack is empty and if the top cell is not null +if (cell_stack_.empty() && top_cell_ != nullptr) { + + // Get the ID of the cell that has already been run + const auto &check_already_run_cell_id = GetAlreadyRunCellId(cell_id); + + // Get the top cell with the same ID as the already run cell + auto find_top_cell = GetTopCell(check_already_run_cell_id); + + // If a top cell with the same ID is found + if (find_top_cell != nullptr) { + MS_LOG(DEBUG) << "Find already run top cell"; + + // Get the forward run status of the found top cell + forward_run = find_top_cell->forward_already_run(); + + // Get the current top cell and set the found top cell as the new top cell + auto curr_top_cell = top_cell(); + set_top_cell(find_top_cell); + + // Check if the input arguments have changed for the found top cell + bool input_args_changed = + !find_top_cell->input_args_id().empty() && find_top_cell->input_args_id() != input_args_id; + + // If the forward run is true, the input arguments have changed, and the found top cell is dynamic + if (forward_run && input_args_changed && find_top_cell->is_dynamic()) { + MS_LOG(WARNING) << "The construct of running cell is dynamic and the input info of this cell has changed, " + "forward process will run again"; + forward_run = false; + } + + // If the forward run is true and the high order stack size is greater than or equal to 1, + // push the current top cell to the high order graph stack + if (forward_run && GetHighOrderStackSize() >= 1) { + PushHighOrderGraphStack(curr_top_cell); + } + } +} + +// Print the forward run status and the ID of the top cell +MS_LOG(DEBUG) << "Graph have already ran " << forward_run << " top cell id " << cell_id; + +// Convert the forward run status to a Python object and return it +return BaseRefToPyData(forward_run); + +// Check if the current top cell needs to be compiled + +// Get the new top cell +auto new_top_cell = top_cell(); + +// Get the ID of the already run top cell +const auto &already_top_cell_id = new_top_cell->already_run_cell_id(); + +// Check if the already run top cell ID is not found in the already run top cell map +if (already_run_top_cell_.find(already_top_cell_id) == already_run_top_cell_.end()) { + // Print debug message indicating that the top cell has never been run and needs to be compiled MS_LOG(DEBUG) << "Top cell " << new_top_cell->cell_id() << " has never been ran, need compile graph"; - already_run_top_cell_[already_top_cell_id] = new_top_cell; - return; - } - MS_LOG(DEBUG) << "Top cell " << new_top_cell->cell_id() << " has been ran"; - auto pre_top_cell = already_run_top_cell_.at(already_top_cell_id); - MS_EXCEPTION_IF_NULL(pre_top_cell); - const auto &pre_all_op_info = pre_top_cell->all_op_info(); - const auto &new_all_op_info = new_top_cell->all_op_info(); - MS_LOG(DEBUG) << "Pre all op info : " << pre_all_op_info; - MS_LOG(DEBUG) << "New all op info : " << new_all_op_info; - if (pre_all_op_info != new_all_op_info) { - MS_LOG(DEBUG) << "The op info has been changed, need to compile graph again"; - // The top cell switches exceeds MAX_TOP_CELL_COUNTS under the control flow, disable backend cache - if (top_cell_switch_counts_ >= MAX_TOP_CELL_COUNTS) { - EnableOpGraphCache(false); - } else { - // Increase top cell switches counts - ++top_cell_switch_counts_; - } - EraseTopCellFromTopCellList(pre_top_cell); - pre_top_cell->Clear(); + // Add the new top cell to the already run top cell map already_run_top_cell_[already_top_cell_id] = new_top_cell; - g_pyobj_id_cache.clear(); - } else { - MS_LOG(DEBUG) << "The op info has not been changed, no need to compile graph again"; - pre_top_cell->set_input_args_id(new_top_cell->input_args_id()); - // In high order situations, the internal top cell remains unchanged, but the external top cell has changed. Then - // the graph info of the internal top cell needs to be updated so that the external top cell can perceive it. - if (!cell_stack_.empty()) { - pre_top_cell->graph_info_map()[pre_top_cell->df_builder()] = - new_top_cell->graph_info_map()[new_top_cell->df_builder()]; - } - EraseTopCellFromTopCellList(new_top_cell); - new_top_cell->Clear(); - pre_top_cell->set_forward_already_run(true); - set_top_cell(pre_top_cell); - } + + // Return from the function + return; } +// Log a debug message indicating that the top cell with the given cell ID has been run +MS_LOG(DEBUG) << "Top cell " << new_top_cell->cell_id() << " has been ran"; + +// Get the previously run top cell with the same cell ID as the current top cell +auto pre_top_cell = already_run_top_cell_.at(already_top_cell_id); + +// Check if the previously run top cell is null +MS_EXCEPTION_IF_NULL(pre_top_cell); + +// Get the op info of the previously run top cell and the op info of the current top cell +const auto &pre_all_op_info = pre_top_cell->all_op_info(); +const auto &new_all_op_info = new_top_cell->all_op_info(); + +// Log the op info of the previously run top cell and the op info of the current top cell +MS_LOG(DEBUG) << "Pre all op info : " << pre_all_op_info; +MS_LOG(DEBUG) << "New all op info : " << new_all_op_info; + +// Check if the op info has been changed +if (pre_all_op_info != new_all_op_info) { + // Log a debug message indicating that the op info has been changed and the graph needs to be compiled again + + MS_LOG(DEBUG) << "The op info has been changed, need to compile graph again"; + + // Check if the number of top cell switches exceeds the maximum allowed count + if (top_cell_switch_counts_ >= MAX_TOP_CELL_COUNTS) { + // Disable the backend cache if the maximum count has been reached + EnableOpGraphCache(false); + } else { + // Increase the count of top cell switches + ++top_cell_switch_counts_; + } + + // Erase the previously run top cell from the top cell list + EraseTopCellFromTopCellList(pre_top_cell); + + // Clear the previously run top cell + pre_top_cell->Clear(); + + // Update the already run top cell map with the new top cell + already_run_top_cell_[already_top_cell_id] = new_top_cell; + + // Clear the Python object ID cache + g_pyobj_id_cache.clear(); +} else { + // Log a debug message indicating that the op info has not been changed and there is no need to compile the graph again + + MS_LOG(DEBUG) << "The op info has not been changed, no need to compile graph again"; + + // Set the input arguments ID of the previously run top cell to the input arguments ID of the current top cell + pre_top_cell->set_input_args_id(new_top_cell->input_args_id()); + + // Check if the cell stack is not empty + if (!cell_stack_.empty()) { + // Update the graph info of the previously run top cell with the graph info of the current top cell + pre_top_cell->graph_info_map()[pre_top_cell->df_builder()] = + new_top_cell->graph_info_map()[new_top_cell->df_builder()]; + } + + // Erase the current top cell from the top cell list + EraseTopCellFromTopCellList(new_top_cell); + + // Clear the current top cell + new_top_cell->Clear(); + + // Set the forward already run flag of the previously run top cell to true + pre_top_cell->set_forward_already_run(true); + + // Set the top cell to the previously run top cell + set_top_cell(pre_top_cell); +} + +// Run the gradient graph for a given cell and arguments void GradExecutor::RunGradGraph(py::object *ret, const py::object &cell, const py::tuple &args) { MS_EXCEPTION_IF_NULL(ret); + + // Get the unique identifier for the cell and its arguments const auto &cell_id = GetCellId(cell, args); + + // Log the start of the gradient graph execution for the cell MS_LOG(DEBUG) << "Run start cell id " << cell_id; + + // Check if any of the top cells have sensitivity and are not the current cell auto has_sens = std::any_of(top_cell_list_.begin(), top_cell_list_.end(), [&cell_id](const TopCellInfoPtr &value) { return cell_id.find(value->cell_id()) != std::string::npos && cell_id != value->cell_id(); }); + + // Log whether the current cell has sensitivity and its cell id MS_LOG(DEBUG) << "Run has sens " << has_sens << " cell id " << cell_id; + + // Get the resource pointer for the top cell auto resource = top_cell()->resource(); MS_EXCEPTION_IF_NULL(resource); + + // Log the resource pointer for the top cell MS_LOG(DEBUG) << "Run resource ptr " << resource.get(); +} - VectorRef arg_list; - auto filter_args = FilterTensorArgs(args, has_sens); - py::tuple converted_args = ConvertArgs(filter_args); - pipeline::ProcessVmArgInner(converted_args, resource, &arg_list); - ShallowCopySensValue(filter_args, has_sens, &arg_list); - MS_LOG(DEBUG) << "Convert args size " << converted_args.size() << ", graph param size " << arg_list.size(); - compile::VmEvalFuncPtr run = resource->GetResult(pipeline::kOutput).cast(); - MS_EXCEPTION_IF_NULL(run); +// Create an empty VectorRef object named arg_list +VectorRef arg_list; +// Filter the tensor arguments by removing any arguments that are not needed and store the result in filter_args +auto filter_args = FilterTensorArgs(args, has_sens); + +// Convert the filtered arguments into a Python tuple and store the result in converted_args +py::tuple converted_args = ConvertArgs(filter_args); + +// Process the converted_args using the ProcessVmArgInner function, passing in the resource and arg_list as parameters +pipeline::ProcessVmArgInner(converted_args, resource, &arg_list); + +// Shallow copy the sensitivity value from filter_args to arg_list, if has_sens is true +ShallowCopySensValue(filter_args, has_sens, &arg_list); + +// Print the size of converted_args and arg_list using the MS_LOG macro with the DEBUG level +MS_LOG(DEBUG) << "Convert args size " << converted_args.size() << ", graph param size " << arg_list.size(); + +// Get the VmEvalFuncPtr object named run from the resource's result, which is casted to VmEvalFuncPtr +compile::VmEvalFuncPtr run = resource->GetResult(pipeline::kOutput).cast(); + +// Throw an exception if run is null +MS_EXCEPTION_IF_NULL(run); + + // Get the backend policy from the instance of MsContext and store it in a constant reference variable called backend const auto &backend = MsContext::GetInstance()->backend_policy(); + + // Log a debug message indicating that the evaluation run is starting, along with the backend policy MS_LOG(DEBUG) << "Eval run " << backend; + + // Set the grad_is_running_ flag to true grad_is_running_ = true; + + // Set the k_pynative_cell_ptr of the top cell to nullptr top_cell()->set_k_pynative_cell_ptr(nullptr); + + // Call the run function and store the result in a BaseRef variable called value BaseRef value = (*run)(arg_list); + + // Set the grad_is_running_ flag to false grad_is_running_ = false; + + // Get the function graph from the resource and store it in a FuncGraphPtr variable called fg FuncGraphPtr fg = resource->func_graph(); + + // Throw an exception if the function graph is null MS_EXCEPTION_IF_NULL(fg); + + // Get the abstract value of the output of the function graph and store it in a variable called output_abs auto output_abs = fg->output()->abstract(); + + // Log a debug message indicating that the evaluation run has ended, along with the string representation of the value MS_LOG(DEBUG) << "Eval run end " << value.ToString(); + + // Convert the value to a Python object using BaseRefToPyData and store it in the ret pointer *ret = BaseRefToPyData(value, output_abs); - // Clear device memory resource of top cell when it has been ran. + + // Check if there are any higher order cells in the top cell list auto has_higher_order = std::any_of(top_cell_list_.begin(), top_cell_list_.end(), [](const TopCellInfoPtr &value) { return !value->is_topest(); }); + + // Check if the current top cell is the topmost cell and there are no higher order cells if (top_cell()->is_topest() && !has_higher_order) { + // Clear the device memory resource of the top cell top_cell()->ClearDeviceMemory(); + + // Check if the cell is of function type and clear its resources if (IsFunctionType(cell)) { ClearCellRes(cell_id); } } - // High order + + // Check if the top cell is VM compiled if (top_cell()->vm_compiled()) { + // Make a nested CNode for the cell using MakeNestedCnode MakeNestedCnode(cell, converted_args, resource, *ret); } else if (GetHighOrderStackSize() >= ARG_SIZE) { + // Check if the high order stack size is greater than or equal to ARG_SIZE and switch the top cell SwitchTopcell(); } } +// Define a function named "SwitchTopcell" in the "GradExecutor" class + void GradExecutor::SwitchTopcell() { + + // Get a reference to the "all_op_info" member variable of the "top_cell" object and assign it to "inner_top_cell_all_op_info" const auto &inner_top_cell_all_op_info = top_cell()->all_op_info(); + + // Get the value of the "is_dynamic" member variable of the "top_cell" object and assign it to "inner_top_cell_is_dynamic" bool inner_top_cell_is_dynamic = top_cell()->is_dynamic(); - // Get outer top cell + // Get the outer top cell from the high order graph stack auto outer_top_cell = PopHighOrderGraphStack(); MS_EXCEPTION_IF_NULL(outer_top_cell); + + // Append the all_op_info of the inner top cell to the all_op_info of the outer top cell outer_top_cell->all_op_info() += inner_top_cell_all_op_info; - // If inner is dynamic, outer set dynamic too + + // If the inner top cell is dynamic, set the outer top cell as dynamic too if (inner_top_cell_is_dynamic) { outer_top_cell->set_is_dynamic(inner_top_cell_is_dynamic); } + + // Set the outer top cell as the new top cell set_top_cell(outer_top_cell); } -void GradExecutor::DoParameterReplace(const FuncGraphPtr &first_grad_fg, const py::tuple &forward_args, - std::vector *inputs, ValuePtrList *weights_args) { - MS_EXCEPTION_IF_NULL(inputs); - MS_EXCEPTION_IF_NULL(weights_args); +// Define the function `DoParameterReplace` which takes in a `FuncGraphPtr` object `first_grad_fg`, a `py::tuple` object `forward_args`, +// a pointer to a vector of `AnfNodePtr` objects `inputs`, and a pointer to a `ValuePtrList` object `weights_args`. +// Check if the `inputs` pointer is null, and throw an exception if it is. +MS_EXCEPTION_IF_NULL(inputs); + +// Check if the `weights_args` pointer is null, and throw an exception if it is. +MS_EXCEPTION_IF_NULL(weights_args); + + // Get the df_builder of the top cell and assign it to the variable first_df_builder auto first_df_builder = top_cell()->df_builder(); + + // Check if first_df_builder is null, if it is, throw an exception MS_EXCEPTION_IF_NULL(first_df_builder); + + // Get the graph_info associated with first_df_builder from the graph_info_map of the top cell and assign it to the variable first_graph_info auto first_graph_info = top_cell()->graph_info_map().at(first_df_builder); + + // Check if first_graph_info is null, if it is, throw an exception MS_EXCEPTION_IF_NULL(first_graph_info); + + // Switch the top cell to a different cell SwitchTopcell(); + + // Get the df_builder of the new top cell and assign it to the variable second_df_builder auto second_df_builder = top_cell()->df_builder(); + + // Check if second_df_builder is null, if it is, throw an exception MS_EXCEPTION_IF_NULL(second_df_builder); + + // Get the graph_info associated with second_df_builder from the graph_info_map of the top cell and assign it to the variable second_graph_info auto second_graph_info = top_cell()->graph_info_map().at(second_df_builder); + + // Check if second_graph_info is null, if it is, throw an exception MS_EXCEPTION_IF_NULL(second_graph_info); - mindspore::HashSet params_weights_set; - mindspore::HashSet params_inputs_set; - for (const auto &sec : second_graph_info->params) { +// Create an empty hash set of strings to store weights of parameters +mindspore::HashSet params_weights_set; + +// Create an empty hash set of strings to store inputs of parameters +mindspore::HashSet params_inputs_set; + +// Iterate over the parameters in the second graph +for (const auto &sec : second_graph_info->params) { + // Check if the parameter has a default value if (sec.second->has_default()) { - params_weights_set.emplace(sec.first); + // If it has a default value, add it to the weights set + params_weights_set.emplace(sec.first); } else { - params_inputs_set.insert(sec.first); + // If it doesn't have a default value, add it to the inputs set + params_inputs_set.insert(sec.first); } - } - auto manager = Manage({first_grad_fg}, false); - // Replace inputs param - for (size_t i = 0; i < forward_args.size(); ++i) { +} + +// Create a manager to manage the first gradient function graph +auto manager = Manage({first_grad_fg}, false); + +// Replace input parameters +for (size_t i = 0; i < forward_args.size(); ++i) { + // Get the ID of the forward argument const auto &id = GetId(forward_args[i]); + + // Check if the ID is present in the inputs set if (params_inputs_set.count(id)) { - // Can find in second graph - const auto &input_param_second = second_graph_info->params.at(id); - manager->Replace(first_graph_info->params.at(id), input_param_second); - inputs->emplace_back(input_param_second); + // If it is present in the inputs set, get the corresponding input parameter from the second graph + const auto &input_param_second = second_graph_info->params.at(id); + + // Replace the input parameter in the first graph with the input parameter from the second graph + manager->Replace(first_graph_info->params.at(id), input_param_second); + + // Add the input parameter from the second graph to the inputs vector + inputs->emplace_back(input_param_second); } else { - inputs->emplace_back(GetInput(forward_args[i], false)); + // If it is not present in the inputs set, get the input from the forward arguments + inputs->emplace_back(GetInput(forward_args[i], false)); } - } +} // Replace weights param for (const auto &fir : first_graph_info->params) { if (!fir.second->has_default()) { continue; } - // Second graph no this weight param, need add to second graph + // Second graph does not have this weight param, need to add it to the second graph if (!params_weights_set.count(fir.first)) { MS_LOG(DEBUG) << "Can't find " << fir.first << " in outer graph, add it"; second_df_builder->add_parameter(fir.second); @@ -3272,7 +5822,7 @@ void GradExecutor::DoParameterReplace(const FuncGraphPtr &first_grad_fg, const p inputs->emplace_back(fir.second); weights_args->emplace_back(fir.second->default_param()); } else { - // Need replace + // Need to replace MS_LOG(DEBUG) << "Param name " << fir.first << " ptr " << fir.second.get(); auto it = std::find_if(second_graph_info->params.begin(), second_graph_info->params.end(), [&fir](const std::pair &sec) { @@ -3287,41 +5837,93 @@ void GradExecutor::DoParameterReplace(const FuncGraphPtr &first_grad_fg, const p } } +// This function is a member function of the GradExecutor class. +// It takes in several parameters: a py::object called "cell", a py::tuple called "forward_args", +// a pipeline::ResourcePtr called "resource", and a py::object called "out". + void GradExecutor::MakeNestedCnode(const py::object &cell, const py::tuple &forward_args, const pipeline::ResourcePtr &resource, const py::object &out) { + // Check if the cell stack is empty if (cell_stack_.empty()) { + // If it is empty, log a debug message and return MS_LOG(DEBUG) << "No nested grad find"; return; } + + // Declare a pointer to a FuncGraph object called "first_grad_fg" and initialize it to nullptr FuncGraphPtr first_grad_fg = nullptr; + + // Check if the "cell" object has an attribute called "CUSTOM_BPROP_NAME" if (py::hasattr(cell, parse::CUSTOM_BPROP_NAME)) { + // If it does, set "first_grad_fg" to the current graph (curr_g) and log a debug message first_grad_fg = curr_g(); MS_LOG(DEBUG) << "Bprop nested"; } else { + // If it doesn't, set "first_grad_fg" to the func_graph stored in the "resource" object first_grad_fg = resource->func_graph(); } + + // Check if "first_grad_fg" is null MS_EXCEPTION_IF_NULL(first_grad_fg); + + // Dump the graph IR of "first_grad_fg" to a file called "first_grad_fg.ir" DumpGraphIR("first_grad_fg.ir", first_grad_fg); +} - std::vector inputs{NewValueNode(first_grad_fg)}; - ValuePtrList weights_args; - DoParameterReplace(first_grad_fg, forward_args, &inputs, &weights_args); +// Create a vector named "inputs" of type "std::vector" and initialize it with a single element, which is a "NewValueNode" object constructed with the "first_grad_fg" argument. - pipeline::ResourcePtr r = std::make_shared(); - r->manager()->AddFuncGraph(first_grad_fg); - set_eliminate_forward(false); - first_grad_fg->transforms().erase(kGrad); - FuncGraphPtr second_grad_fg = ad::Grad(first_grad_fg, opt::Optimizer::MakeEmptyOptimizer(r)); - set_eliminate_forward(true); - DumpGraphIR("second_grad_fg.ir", second_grad_fg); - r->Clean(); +std::vector inputs{NewValueNode(first_grad_fg)}; - MS_LOG(DEBUG) << "Get pre graph ptr " << curr_g().get(); - auto cnode = curr_g()->NewCNode(inputs); - auto out_id = GetId(out); - SetTupleArgsToGraphInfoMap(curr_g(), out, cnode); - SetNodeMapInGraphInfoMap(curr_g(), out_id, cnode); - MS_LOG(DEBUG) << "Nested make cnode is " << cnode->DebugString(); +// Create an empty list named "weights_args" of type "ValuePtrList". + +ValuePtrList weights_args; + +// Call the function "DoParameterReplace" with the arguments "first_grad_fg", "forward_args", "inputs", and "weights_args". The function will modify the "inputs" and "weights_args" lists. + +DoParameterReplace(first_grad_fg, forward_args, &inputs, &weights_args); + +// Create a shared pointer to a new instance of the `pipeline::Resource` class and assign it to `r` +pipeline::ResourcePtr r = std::make_shared(); + +// Add the `first_grad_fg` function graph to the function graph manager associated with `r` +r->manager()->AddFuncGraph(first_grad_fg); + +// Disable the elimination of forward operations +set_eliminate_forward(false); + +// Remove the `kGrad` transform from the `first_grad_fg` function graph's list of transforms +first_grad_fg->transforms().erase(kGrad); + +// Create a new function graph `second_grad_fg` by applying automatic differentiation to the `first_grad_fg` function graph +// using an empty optimizer +FuncGraphPtr second_grad_fg = ad::Grad(first_grad_fg, opt::Optimizer::MakeEmptyOptimizer(r)); + +// Enable the elimination of forward operations +set_eliminate_forward(true); + +// Dump the intermediate representation (IR) of the `second_grad_fg` function graph to a file named "second_grad_fg.ir" +DumpGraphIR("second_grad_fg.ir", second_grad_fg); + +// Clean up the resources associated with `r` +r->Clean(); + +// Log a debug message using the MS_LOG macro, printing the current graph pointer +MS_LOG(DEBUG) << "Get pre graph ptr " << curr_g().get(); + +// Create a new CNode in the current graph, using the inputs provided +auto cnode = curr_g()->NewCNode(inputs); + +// Get the ID of the output node +auto out_id = GetId(out); + +// Set the tuple arguments to the graph info map for the current graph and the output node +SetTupleArgsToGraphInfoMap(curr_g(), out, cnode); + +// Set the node map in the graph info map for the current graph and the output ID +SetNodeMapInGraphInfoMap(curr_g(), out_id, cnode); + +// Log a debug message using the MS_LOG macro, printing the debug string representation of the created CNode +MS_LOG(DEBUG) << "Nested make cnode is " << cnode->DebugString(); // Get input values ValuePtrList input_args; @@ -3330,6 +5932,7 @@ void GradExecutor::MakeNestedCnode(const py::object &cell, const py::tuple &forw input_args.emplace_back(arg); } input_args.insert(input_args.end(), weights_args.begin(), weights_args.end()); + // Get output values py::object new_out; if (py::hasattr(cell, parse::CUSTOM_BPROP_NAME) && !py::isinstance(out)) { @@ -3338,357 +5941,768 @@ void GradExecutor::MakeNestedCnode(const py::object &cell, const py::tuple &forw new_out = out; } const auto &out_value = PyObjToValue(new_out); + + // Run automatic differentiation (ad) grad for second grad graph if (!top_cell()->k_pynative_cell_ptr()->KPynativeWithFProp(cnode, input_args, out_value, second_grad_fg)) { MS_LOG(EXCEPTION) << "Failed to run ad grad for second grad graph " << cnode->ToString(); } + + // Set the flag to indicate that renormalization is needed need_renormalize_ = true; } +// A member function of the GradExecutor class that erases a given top cell from the top cell list void GradExecutor::EraseTopCellFromTopCellList(const TopCellInfoPtr &top_cell) { + // Check if the given top cell is null MS_EXCEPTION_IF_NULL(top_cell); + + // Find the iterator pointing to the first occurrence of the given top cell in the top cell list auto iter = std::find_if(top_cell_list_.begin(), top_cell_list_.end(), [&](const TopCellInfoPtr &elem) { return elem.get() == top_cell.get(); }); + + // If the iterator reaches the end of the top cell list, it means the given top cell was not found if (iter == top_cell_list_.end()) { + // Log a warning message indicating that the top cell was not found in the top cell list MS_LOG(WARNING) << "Can not find top cell " << top_cell.get() << " cell id " << top_cell->cell_id() << " from top cell list"; } else { + // Erase the top cell from the top cell list using the iterator (void)top_cell_list_.erase(iter); } } -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. - if (!py::isinstance(out)) { +// This function is a member function of the GradExecutor class. +// It takes several input parameters: phase (a string), out (a Python object), args (a variable number of Python arguments), +// ms_func_graph (a pointer to a FuncGraph object), and grad_graph (a pointer to a FuncGraph object). + +// The purpose of this function is to process the output of a ms_function func graph and extract the actual output value and added output value. + +// Check if the output value is a tuple +if (!py::isinstance(out)) { MS_LOG(EXCEPTION) << "The output value of ms_function func graph should be a tuple."; - } - auto tuple_out = py::cast(out); - constexpr size_t tuple_out_size = 2; - if (tuple_out.size() != tuple_out_size) { +} + +// Cast the output value to a tuple +auto tuple_out = py::cast(out); + +// Check if the tuple size is 2 +constexpr size_t tuple_out_size = 2; +if (tuple_out.size() != tuple_out_size) { MS_LOG(EXCEPTION) << "The tuple size of output value of ms_function func graph should be 2."; - } - py::object actual_out = tuple_out[0]; - auto actual_out_v = PyObjToValue(actual_out); - auto added_out = PyObjToValue(tuple_out[1]); - MS_LOG(DEBUG) << "Added output value is: " << added_out->ToString(); +} - // Identity op info for current running ms_func graph. - OpExecInfoPtr op_exec_info = std::make_shared(); - op_exec_info->op_name = phase; - op_exec_info->abstract = actual_out_v->ToAbstract(); - RecordGradOpInfo(op_exec_info); - MS_LOG(DEBUG) << "ms_function cnode op info: " << op_exec_info->op_info; +// Extract the actual output value from the tuple +py::object actual_out = tuple_out[0]; - // Step 1: Update actual output tensors used in grad graph. - MS_LOG(DEBUG) << "ms_function actual output value: " << actual_out_v->ToString(); - UpdateForwardTensorInfoInBpropGraph(op_exec_info, actual_out_v); +// Convert the actual output value to a Value object +auto actual_out_v = PyObjToValue(actual_out); + +// Extract the added output value from the tuple +auto added_out = PyObjToValue(tuple_out[1]); + +// Print the added output value for debugging purposes +MS_LOG(DEBUG) << "Added output value is: " << added_out->ToString(); + +// Create a shared pointer to an OpExecInfo object +OpExecInfoPtr op_exec_info = std::make_shared(); + +// Set the op_name field of the OpExecInfo object to the value of the phase variable +op_exec_info->op_name = phase; + +// Set the abstract field of the OpExecInfo object to the abstract representation of the actual_out_v variable +op_exec_info->abstract = actual_out_v->ToAbstract(); + +// Call the RecordGradOpInfo function with the OpExecInfo object as an argument +RecordGradOpInfo(op_exec_info); + +// Print the op_info field of the OpExecInfo object to the debug log +MS_LOG(DEBUG) << "ms_function cnode op info: " << op_exec_info->op_info; + +// Step 1: Update actual output tensors used in grad graph. + +// Print the actual output value of the `ms_function` tensor using the DEBUG log level +MS_LOG(DEBUG) << "ms_function actual output value: " << actual_out_v->ToString(); + +// Update the forward tensor information in the backpropagation graph using the `op_exec_info` and `actual_out_v` tensors +UpdateForwardTensorInfoInBpropGraph(op_exec_info, actual_out_v); // Step 2: Update output tensors of added forward nodes, which are added to return node of ms_function func graph. if (top_cell()->op_info_with_ms_func_forward_tensors().count(op_exec_info->op_info)) { + // If the op_info is present in the op_info_with_ms_func_forward_tensors set of the top cell, + // update the output tensors of the added forward nodes UpdateMsFunctionForwardTensors(op_exec_info, added_out); return; } + + // If the execution reaches here, it means that the op_info is not present in the op_info_with_ms_func_forward_tensors set + // Log the graph phase of the ms func graph MS_LOG(DEBUG) << "Ms func graph run firstly. The graph phase is: " << graph_phase(); + + // Check if the flag of need construct graph is false if (!need_construct_graph()) { + // If the flag is false, throw an exception with an error message MS_LOG(EXCEPTION) << "The flag of need construct graph is False."; } + + // Replace new tensors in the grad graph using the top cell, op_exec_info, added_out, ms_func_graph, and grad_graph ReplaceNewTensorsInGradGraph(top_cell(), op_exec_info, added_out, ms_func_graph, grad_graph); - // Clone new ms_function func graph and grad graph. + // Clone the ms_func_graph and grad_graph using the BasicClone function auto new_ms_func_graph = BasicClone(ms_func_graph); auto new_grad_graph = BasicClone(grad_graph, true); + + // Cast the output of new_ms_func_graph to a CNodePtr auto new_make_tuple = new_ms_func_graph->output()->cast(); MS_EXCEPTION_IF_NULL(new_make_tuple); + + // Set the output of new_ms_func_graph to the second input of new_make_tuple new_ms_func_graph->set_output(new_make_tuple->input(1)); - // Make Adjoint for grad graph + // Call the function MakeAdjointForMsFunction to create the adjoint for the gradient graph + // Pass the new_ms_func_graph, new_grad_graph, actual_out, args, and actual_out_v as arguments MakeAdjointForMsFunction(new_ms_func_graph, new_grad_graph, actual_out, args, actual_out_v); } +// Define a method named "GradMsFunction" that takes in a py::object "out" and a py::args "args" as parameters and returns a py::object py::object GradExecutor::GradMsFunction(const py::object &out, const py::args &args) { - // Get actual forward output object. + + // Check if the graph phase is empty if (graph_phase().empty()) { + // If it is empty, throw an exception with an error message MS_LOG(EXCEPTION) << "The graph phase is empty, can not obtain ms_function func graph."; } + + // Get the current graph phase const auto &phase = graph_phase(); + + // Print the graph phase for debugging purposes MS_LOG(DEBUG) << "ms_function func graph phase: " << phase; + + // Get the instance of the GraphExecutorPy class auto executor = pipeline::GraphExecutorPy::GetInstance(); + + // Throw an exception if the executor is null MS_EXCEPTION_IF_NULL(executor); + + // Get the FuncGraphPtr associated with the current graph phase FuncGraphPtr ms_func_graph = executor->GetFuncGraph(phase); + + // Throw an exception if the FuncGraphPtr is null MS_EXCEPTION_IF_NULL(ms_func_graph); + + // Set the return object to the input "out" py::object ret = out; + + // Check if the FuncGraphPtr has modified the output if (ms_func_graph->modify_output()) { + // If it has, cast the output to a tuple and set the return object to the first element of the tuple auto tuple_out = py::cast(out); ret = tuple_out[0]; } - // Make Adjoint for grad graph of ms_function. + // Check if the grad_flag_ is set to true, indicating the need to construct the gradient graph if (!grad_flag_) { + // If not, log a debug message and return without constructing the grad graph MS_LOG(DEBUG) << "Only run forward infer computation, no need to construct grad graph."; set_graph_phase(""); return ret; } + + // Get the gradient graph from the executor for the given phase FuncGraphPtr grad_graph = executor->GetGradGraph(phase); MS_EXCEPTION_IF_NULL(grad_graph); + + // Call the GradMsFunctionInner function to construct the gradient graph GradMsFunctionInner(phase, out, args, ms_func_graph, grad_graph); + + // Get the parallel mode from the ParallelContext singleton instance auto parallel_mode = parallel::ParallelContext::GetInstance()->parallel_mode(); + + // Check if the parallel mode is either SemiAutoParallel or AutoParallel if (parallel_mode == parallel::kSemiAutoParallel || parallel_mode == parallel::kAutoParallel) { + // Iterate over the parameters of the ms_func_graph for (auto ¶meter : ms_func_graph->parameters()) { auto param = parameter->cast(); + // Check if the parameter has a default value if (param->has_default()) { + // If yes, add the parameter name to the ms_function_params_ vector ms_function_params_.push_back(param->name()); } } } + + // Reset the graph phase to an empty string set_graph_phase(""); + + // Return the value of ret return ret; } +// A member function named "ClearGrad" of the class "GradExecutor" is defined here + void GradExecutor::ClearGrad(const py::object &cell, const py::args &args) { + + // Log a debug message indicating that the top cell grad resource is being cleared MS_LOG(DEBUG) << "Clear top cell grad resource " << GetCellId(cell, args); + + // If the grad_order_ is greater than 0, decrement it by 1 if (grad_order_ > 0) { --grad_order_; } + + // Clear the check_graph_cell_id_ container check_graph_cell_id_.clear(); + + // Clear the grad_operation_ container grad_operation_.clear(); + + // Clear the node_abs_map_ container of the forward computation graph forward()->node_abs_map().clear(); + + // Clean up the resources used by the automatic differentiation framework ad::CleanRes(); + + // Reclaim the optimizer resources used by the pipeline pipeline::ReclaimOptimizer(); } +// Function to clear the results of gradient execution void GradExecutor::ClearRes() { + + // Log a debug message indicating that the gradient results are being cleared MS_LOG(DEBUG) << "Clear grad res"; + + // Set the flag indicating whether gradient calculation is enabled to false grad_flag_ = false; + + // Set the flag indicating whether operator caching is enabled to true enable_op_cache_ = true; + + // Set the flag indicating whether gradient calculation is currently running to false grad_is_running_ = false; + + // Set the flag indicating whether renormalization is needed to false need_renormalize_ = false; + + // Set the flag indicating whether forward elimination is enabled to true eliminate_forward_ = true; + + // Reset the count of custom backpropagation cells to 0 custom_bprop_cell_count_ = 0; + + // Reset the gradient order to 0 grad_order_ = 0; + + // Reset the count of top cell switches to 0 top_cell_switch_counts_ = 0; - check_graph_cell_id_.clear(); - grad_operation_.clear(); - top_cell_ = nullptr; - bprop_cell_list_.clear(); - already_run_top_cell_.clear(); - ClearCellRes(); - std::stack>().swap(bprop_grad_stack_); - std::stack().swap(cell_stack_); - std::stack().swap(high_order_stack_); -} +// Clear the check_graph_cell_id_ vector +check_graph_cell_id_.clear(); +// Clear the grad_operation_ vector +grad_operation_.clear(); + +// Set the top_cell_ pointer to nullptr +top_cell_ = nullptr; + +// Clear the bprop_cell_list_ vector +bprop_cell_list_.clear(); + +// Clear the already_run_top_cell_ vector +already_run_top_cell_.clear(); + +// Clear the cell resources by calling the ClearCellRes() function +ClearCellRes(); + +// Clear the bprop_grad_stack_ stack by creating a new empty stack and swapping it with the existing one +std::stack>().swap(bprop_grad_stack_); + +// Clear the cell_stack_ stack by creating a new empty stack and swapping it with the existing one +std::stack().swap(cell_stack_); + +// Clear the high_order_stack_ stack by creating a new empty stack and swapping it with the existing one +std::stack().swap(high_order_stack_); + +// Define the grad_executor() function of the PynativeExecutor class GradExecutorPtr PynativeExecutor::grad_executor() const { + + // Check if the grad_executor_ member variable is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(grad_executor_); + + // Return the value of the grad_executor_ member variable return grad_executor_; } + +// Define the forward_executor() function of the PynativeExecutor class ForwardExecutorPtr PynativeExecutor::forward_executor() const { + + // Check if the forward_executor_ member variable is null, and throw an exception if it is MS_EXCEPTION_IF_NULL(forward_executor_); + + // Return the value of the forward_executor_ member variable return forward_executor_; } -bool PynativeExecutor::grad_flag() const { return grad_executor()->grad_flag(); } +// Define a member function named "grad_flag" for the class "PynativeExecutor" that returns a boolean value +bool PynativeExecutor::grad_flag() const { -void PynativeExecutor::set_grad_flag(bool flag) { grad_executor()->set_grad_flag(flag); } + // Call the "grad_flag" function of the "grad_executor" object and return its result + return grad_executor()->grad_flag(); +} +// Define a function named "set_grad_flag" in the class "PynativeExecutor" that takes a boolean parameter named "flag" +void PynativeExecutor::set_grad_flag(bool flag) { + + // Call the "set_grad_flag" function of the "grad_executor" object and pass the "flag" parameter to it + grad_executor()->set_grad_flag(flag); +} + +// Define the function SetHookChanged in the PynativeExecutor class, which takes a py::object as input void PynativeExecutor::SetHookChanged(const py::object &cell) { + + // Check if the input object is an instance of the Cell class if (!py::isinstance(cell)) { + + // If it is not an instance of the Cell class, throw an exception with an error message MS_LOG(EXCEPTION) << "The 'set_hook_changed' function is only supported on Cell object!"; } + + // Call the SetHookChanged function of the grad_executor() object, passing the cell object as an argument grad_executor()->SetHookChanged(cell); } +// Define a function named "set_graph_phase" in the class "PynativeExecutor" that takes a constant reference to a string as a parameter void PynativeExecutor::set_graph_phase(const std::string &graph_phase) { + + // Call the "set_graph_phase" function of the "grad_executor" object, passing the "graph_phase" parameter grad_executor()->set_graph_phase(graph_phase); } +// Define the function "set_py_exe_path" in the class "PynativeExecutor" void PynativeExecutor::set_py_exe_path(const py::object &py_exe_path) { + + // Check if the input "py_exe_path" is an instance of the "str" class in Python if (!py::isinstance(py_exe_path)) { + + // If it is not an instance of "str", throw an exception with an error message MS_LOG(EXCEPTION) << "Failed, py_exe_path input is not a str"; } + + // Convert the input "py_exe_path" to a C++ string auto py_exe_path_s = py::cast(py_exe_path); + + // Get the instance of the "MsContext" class auto ms_context = MsContext::GetInstance(); + + // Set the value of the "MS_CTX_PYTHON_EXE_PATH" parameter in the "MsContext" instance to the converted string ms_context->set_param(MS_CTX_PYTHON_EXE_PATH, py_exe_path_s); } +// Define a function named "set_kernel_build_server_dir" that takes a py::object as input void PynativeExecutor::set_kernel_build_server_dir(const py::object &kernel_build_server_dir) { + + // Check if the input is an instance of py::str if (!py::isinstance(kernel_build_server_dir)) { + + // If not, throw an exception with an error message MS_LOG(EXCEPTION) << "Failed, kernel_build_server_dir input is not a str"; } + + // Convert the py::object to a std::string auto kernel_build_server_dir_s = py::cast(kernel_build_server_dir); + + // Get the instance of MsContext auto ms_context = MsContext::GetInstance(); + + // Set the value of the MS_CTX_KERNEL_BUILD_SERVER_DIR parameter in MsContext to the converted std::string ms_context->set_param(MS_CTX_KERNEL_BUILD_SERVER_DIR, kernel_build_server_dir_s); } +// Define a method named "CheckGraph" in the class "PynativeExecutor" that takes a Python object "cell" and a variable number of arguments "args" py::object PynativeExecutor::CheckGraph(const py::object &cell, const py::args &args) { + + // Call the "CheckGraph" method of the "grad_executor" object and pass the "cell" and "args" as arguments return grad_executor()->CheckGraph(cell, args); } +// Define a function named "set_grad_position" in the "PynativeExecutor" class void PynativeExecutor::set_grad_position(const prim::GradOperationPtr &grad, const py::object &grad_position) { + + // Call the "set_grad_position" method of the "grad" object, passing a string representation of "grad_position" grad->set_grad_position(std::string(py::str(grad_position))); } +// Define a function named "CheckAlreadyRun" in the "PynativeExecutor" class +// The function takes three parameters: a pointer to a "GradOperation" object, a Python object named "cell", and a variable number of arguments py::object PynativeExecutor::CheckAlreadyRun(const prim::GradOperationPtr &grad, const py::object &cell, const py::args &args) { + + // Call the "CheckAlreadyRun" function of the "grad_executor" object and return its result return grad_executor()->CheckAlreadyRun(grad, cell, args); } +// Define the Run function of the PynativeExecutor class, which takes a cell object and a tuple of arguments as input py::object PynativeExecutor::Run(const py::object &cell, const py::tuple &args) { + + // Declare a py::object variable to store the return value py::object ret; + + // Call the RunGraph function of the grad_executor object, passing the ret variable, cell, and args as arguments PynativeExecutorTry(grad_executor()->RunGraph, &ret, cell, args); + + // Return the value stored in the ret variable return ret; } +// A member function named "ClearCell" of the class "PynativeExecutor" is defined here void PynativeExecutor::ClearCell(const std::string &cell_id) { + + // Log a debug message using the MS_LOG macro, indicating the cell ID being cleared MS_LOG(DEBUG) << "Clear cell res, cell id " << cell_id; + + // Call the "ClearCellRes" function of the "grad_executor" object, passing the cell ID as an argument grad_executor()->ClearCellRes(cell_id); } +// Define a function named "ClearGrad" that takes a py::object named "cell" and a py::args named "args" as parameters void PynativeExecutor::ClearGrad(const py::object &cell, const py::args &args) { + + // Output a debug log message using the MS_LOG macro, indicating that the grad is being cleared MS_LOG(DEBUG) << "Clear grad"; + + // Call the ClearGrad function of the grad_executor object and return its result return grad_executor()->ClearGrad(cell, args); } -void PynativeExecutor::ClearRes() { - MS_LOG(DEBUG) << "Clear all res"; - session::PynativeTaskManager::GetInstance().Reset(); - runtime::OpExecutor::GetInstance().Reset(); - for (auto &item : kMindRtBackends) { - MS_EXCEPTION_IF_NULL(item.second); - item.second->ClearOpExecutorResource(); - } - SetLazyBuild(false); - cell_depth_ = 0; +// A function to clear resources used by the PynativeExecutor - // Maybe exit in runop step +// Log a debug message indicating that all resources are being cleared +MS_LOG(DEBUG) << "Clear all res"; + +// Reset the PynativeTaskManager instance +session::PynativeTaskManager::GetInstance().Reset(); + +// Reset the OpExecutor instance +runtime::OpExecutor::GetInstance().Reset(); + +// Iterate over each item in the kMindRtBackends map +for (auto &item : kMindRtBackends) { + // Check if the item is null, and throw an exception if it is + MS_EXCEPTION_IF_NULL(item.second); + // Clear the OpExecutor resource for the current item + item.second->ClearOpExecutorResource(); +} + +// Set the lazy build flag to false +SetLazyBuild(false); + +// Reset the cell depth counter to 0 +cell_depth_ = 0; + + // Check if the `ms_context` is not null auto ms_context = MsContext::GetInstance(); if (ms_context != nullptr) { + // Set the `MS_CTX_ENABLE_PYNATIVE_INFER` parameter of the `ms_context` to false ms_context->set_param(MS_CTX_ENABLE_PYNATIVE_INFER, false); } + + // Reset the iteration number in the `ConfigManager` ConfigManager::GetInstance().ResetIterNum(); + + // Clear the resources in the `forward_executor_` if it is not null if (forward_executor_ != nullptr) { forward_executor_->ClearRes(); } + + // Clear the resources in the `grad_executor_` if it is not null if (grad_executor_ != nullptr) { grad_executor_->ClearRes(); } + + // Clean up the resources in the `ad` module ad::CleanRes(); + + // Reclaim the optimizer resources in the `pipeline` module pipeline::ReclaimOptimizer(); + + // Clear the `kSessionBackends` vector kSessionBackends.clear(); + + // Clear the `kMindRtBackends` vector kMindRtBackends.clear(); + + // Clear the `g_pyobj_id_cache` map g_pyobj_id_cache.clear(); } void PynativeExecutor::NewGraph(const py::object &cell, const py::args &args) { - // Make a flag for new cell - if (!grad_executor()->grad_flag()) { - MS_LOG(DEBUG) << "Grad flag is false"; - return; - } - py::object ret; - PynativeExecutorTry(grad_executor()->InitGraph, &ret, cell, args); + // Check if the grad flag is false + if (!grad_executor()->grad_flag()) { + // Print a debug message indicating that the grad flag is false + MS_LOG(DEBUG) << "Grad flag is false"; + // Return from the function + return; + } + + // Declare a py::object variable named ret + py::object ret; + + // Call the PynativeExecutorTry function with grad_executor()->InitGraph as the first argument, + // &ret as the second argument, cell as the third argument, and args as the fourth argument + PynativeExecutorTry(grad_executor()->InitGraph, &ret, cell, args); } +// Definition of the function "EndGraph" in the class "PynativeExecutor" + void PynativeExecutor::EndGraph(const py::object &cell, const py::object &out, const py::args &args) { + + // Check if the gradient flag is false if (!grad_executor()->grad_flag()) { + + // Print a debug message indicating that the gradient flag is false MS_LOG(DEBUG) << "Grad flag is false"; + + // Return from the function return; } + + // Print a debug message indicating that the end graph process has started MS_LOG(DEBUG) << "Enter end graph process."; + + // Declare a py::object variable named "ret" py::object ret; + + // Call the function "LinkGraph" of the "grad_executor" object, passing "ret", "cell", "out", and "args" as arguments PynativeExecutorTry(grad_executor()->LinkGraph, &ret, cell, out, args); + + // Print a debug message indicating that the end graph process has finished MS_LOG(DEBUG) << "Leave end graph process."; } +// Define a function named "GradMsFunction" that takes two arguments: "out" of type py::object and "args" of type py::args py::object PynativeExecutor::GradMsFunction(const py::object &out, const py::args &args) { + + // Call the "GradMsFunction" method of the "grad_executor" object and pass in the "out" and "args" arguments + // Return the result of this method call return grad_executor()->GradMsFunction(out, args); } +// Define a function named "GradNet" with the following parameters: +// - grad: a shared pointer to a GradOperation object +// - cell: a Python object +// - weights: a Python object +// - grad_position: a Python object +// - args: a variable number of Python arguments + void PynativeExecutor::GradNet(const prim::GradOperationPtr &grad, const py::object &cell, const py::object &weights, const py::object &grad_position, const py::args &args) { + + // Declare a Python object named "ret" py::object ret; + + // Call the GradGraph function of the grad_executor object, passing the "ret" object and the provided arguments PynativeExecutorTry(grad_executor()->GradGraph, &ret, grad, cell, weights, grad_position, args); } -void PynativeExecutor::Sync() { - ExecuteLazyTask(); +// Definition of the Sync function in the PynativeExecutor class - mindspore::ScopedLongRunning long_running; - auto ms_context = MsContext::GetInstance(); - MS_EXCEPTION_IF_NULL(ms_context); - if (!ms_context->get_param(MS_CTX_ENABLE_MINDRT)) { - for (auto &item : kSessionBackends) { - MS_EXCEPTION_IF_NULL(item.second); - item.second->SyncStream(); - } - } else { - for (auto &item : kMindRtBackends) { - MS_EXCEPTION_IF_NULL(item.second); - item.second->SyncStream(); - } - for (auto &item : kSessionBackends) { - MS_EXCEPTION_IF_NULL(item.second); - item.second->SyncStream(); - } - } +void PynativeExecutor::Sync() { + + // Call the ExecuteLazyTask function to execute the lazy task + ExecuteLazyTask(); } -void PynativeExecutor::SetLazyBuild(bool enable) { forward_executor()->set_lazy_build(enable); } +// Create a ScopedLongRunning object named "long_running" to manage long-running operations + +mindspore::ScopedLongRunning long_running; + +// Get the instance of the MsContext class +auto ms_context = MsContext::GetInstance(); + +// Throw an exception if the MsContext instance is null +MS_EXCEPTION_IF_NULL(ms_context); + +// Check if the MS_CTX_ENABLE_MINDRT parameter is set to true +if (!ms_context->get_param(MS_CTX_ENABLE_MINDRT)) { + + // Iterate through each item in the kSessionBackends map + for (auto &item : kSessionBackends) { + + // Throw an exception if the item is null + MS_EXCEPTION_IF_NULL(item.second); + + // Call the SyncStream function of the item + item.second->SyncStream(); + } +} else { + + // Iterate through each item in the kMindRtBackends map + for (auto &item : kMindRtBackends) { + + // Throw an exception if the item is null + MS_EXCEPTION_IF_NULL(item.second); + + // Call the SyncStream function of the item + item.second->SyncStream(); + } + + // Iterate through each item in the kSessionBackends map + for (auto &item : kSessionBackends) { + + // Throw an exception if the item is null + MS_EXCEPTION_IF_NULL(item.second); + + // Call the SyncStream function of the item + item.second->SyncStream(); + } +} +} + +// Define the function "SetLazyBuild" belonging to the class "PynativeExecutor" +void PynativeExecutor::SetLazyBuild(bool enable) { + + // Call the "set_lazy_build" function of the "forward_executor" object and pass the "enable" parameter + forward_executor()->set_lazy_build(enable); +} + +// Definition of the EnterCell function in the PynativeExecutor class void PynativeExecutor::EnterCell() { + + // Check if the cell depth is less than the maximum value of an unsigned 32-bit integer if (cell_depth_ < UINT32_MAX) { + + // Increment the cell depth by 1 ++cell_depth_; + } else { + + // If the cell depth is equal to or exceeds the maximum value, log an error message MS_LOG(ERROR) << "Cell call stack too deep"; } } +// Define the function "ExitCell" belonging to the class "PynativeExecutor" void PynativeExecutor::ExitCell() { + + // Check if the value of "cell_depth_" is greater than 0 if (cell_depth_ > 0) { + + // Decrement the value of "cell_depth_" by 1 --cell_depth_; } } -bool PynativeExecutor::IsTopCell() const { return cell_depth_ == 0; } +// A member function of the PynativeExecutor class that checks if the current cell is the top cell +bool PynativeExecutor::IsTopCell() const { + + // Return true if the cell depth is 0, indicating that it is the top cell + return cell_depth_ == 0; +} + +// Definition of the function `ExecuteLazyTask` in the `PynativeExecutor` class void PynativeExecutor::ExecuteLazyTask() { + + // Create a scoped object `long_running` of type `ScopedLongRunning` from the `mindspore` namespace + // This object is used to indicate that the following code is a long-running task mindspore::ScopedLongRunning long_running; + + // Execute the remaining tasks in the `PynativeTaskManager` singleton instance session::PynativeTaskManager::GetInstance().ExecuteRemainingTasks(); + + // Iterate over each item in the `kMindRtBackends` map for (auto &item : kMindRtBackends) { + + // Check if the value of the current item is null MS_EXCEPTION_IF_NULL(item.second); + + // Wait for the task associated with the current backend to finish item.second->WaitTaskFinish(); } } +// Register the PynativeExecutor_ class with the pybind module REGISTER_PYBIND_DEFINE(PynativeExecutor_, ([](const py::module *m) { - (void)py::class_>(*m, "PynativeExecutor_") - .def_static("get_instance", &PynativeExecutor::GetInstance, "PynativeExecutor get_instance.") - .def("enter_cell", &PynativeExecutor::EnterCell, "enter cell.") - .def("exit_cell", &PynativeExecutor::ExitCell, "exit cell.") - .def("is_top_cell", &PynativeExecutor::IsTopCell, "check top cell.") - .def("new_graph", &PynativeExecutor::NewGraph, "pynative new a graph.") - .def("end_graph", &PynativeExecutor::EndGraph, "pynative end a graph.") - .def("check_graph", &PynativeExecutor::CheckGraph, "pynative check a grad graph.") - .def("check_run", &PynativeExecutor::CheckAlreadyRun, "pynative check graph run before.") - .def("grad_ms_function", &PynativeExecutor::GradMsFunction, "pynative grad for ms_function.") - .def("grad_net", &PynativeExecutor::GradNet, "pynative grad graph.") - .def("clear_cell", &PynativeExecutor::ClearCell, "pynative clear status.") - .def("clear_res", &PynativeExecutor::ClearRes, "pynative clear exception res.") - .def("clear_grad", &PynativeExecutor::ClearGrad, "pynative clear grad status.") - .def("sync", &PynativeExecutor::Sync, "pynative sync stream.") - .def("set_lazy_build", &PynativeExecutor::SetLazyBuild, "pynative build kernel async") - .def("execute_lazy_task", &PynativeExecutor::ExecuteLazyTask, "clear all task") - .def("__call__", &PynativeExecutor::Run, "pynative executor run grad graph.") - .def("set_graph_phase", &PynativeExecutor::set_graph_phase, "pynative set graph phase") - .def("grad_flag", &PynativeExecutor::grad_flag, "pynative grad flag") - .def("set_hook_changed", &PynativeExecutor::SetHookChanged, "set pynative hook changed") - .def("set_grad_position", &PynativeExecutor::set_grad_position, "set pynative grad position") - .def("set_grad_flag", &PynativeExecutor::set_grad_flag, py::arg("flag") = py::bool_(false), - "Executor set grad flag.") - .def("set_py_exe_path", &PynativeExecutor::set_py_exe_path, - py::arg("py_exe_path") = py::str(""), "set python executable path.") - .def("set_kernel_build_server_dir", &PynativeExecutor::set_kernel_build_server_dir, - py::arg("kernel_build_server_dir") = py::str(""), - "set kernel build server directory path."); - })); -} // namespace mindspore::pynative + + // Define the PynativeExecutor_ class and bind it to the "PynativeExecutor_" name + (void)py::class_>(*m, "PynativeExecutor_") + + // Define the static method "get_instance" that returns an instance of PynativeExecutor + .def_static("get_instance", &PynativeExecutor::GetInstance, "PynativeExecutor get_instance.") + + // Define the method "enter_cell" that represents entering a cell + .def("enter_cell", &PynativeExecutor::EnterCell, "enter cell.") + + // Define the method "exit_cell" that represents exiting a cell + .def("exit_cell", &PynativeExecutor::ExitCell, "exit cell.") + + // Define the method "is_top_cell" that checks if the current cell is the top cell + .def("is_top_cell", &PynativeExecutor::IsTopCell, "check top cell.") + + // Define the method "new_graph" that creates a new graph in PynativeExecutor + .def("new_graph", &PynativeExecutor::NewGraph, "pynative new a graph.") + + // Define the method "end_graph" that ends the current graph in PynativeExecutor + .def("end_graph", &PynativeExecutor::EndGraph, "pynative end a graph.") + + // Define the method "check_graph" that checks a gradient graph in PynativeExecutor + .def("check_graph", &PynativeExecutor::CheckGraph, "pynative check a grad graph.") + + // Define the method "check_run" that checks if a graph has already been run in PynativeExecutor + .def("check_run", &PynativeExecutor::CheckAlreadyRun, "pynative check graph run before.") + + // Define the method "grad_ms_function" that performs gradient computation for a ms_function in PynativeExecutor + .def("grad_ms_function", &PynativeExecutor::GradMsFunction, "pynative grad for ms_function.") + + // Define the method "grad_net" that performs gradient computation for a graph in PynativeExecutor + .def("grad_net", &PynativeExecutor::GradNet, "pynative grad graph.") + + // Define the method "clear_cell" that clears the status of PynativeExecutor + .def("clear_cell", &PynativeExecutor::ClearCell, "pynative clear status.") + + // Define the method "clear_res" that clears the exception result in PynativeExecutor + .def("clear_res", &PynativeExecutor::ClearRes, "pynative clear exception res.") + + // Define the method "clear_grad" that clears the gradient status in PynativeExecutor + .def("clear_grad", &PynativeExecutor::ClearGrad, "pynative clear grad status.") + + // Define the method "sync" that synchronizes the stream in PynativeExecutor + .def("sync", &PynativeExecutor::Sync, "pynative sync stream.") + + // Define the method "set_lazy_build" that sets the lazy build flag in PynativeExecutor + .def("set_lazy_build", &PynativeExecutor::SetLazyBuild, "pynative build kernel async") + + // Define the method "execute_lazy_task" that executes all lazy tasks in PynativeExecutor + .def("execute_lazy_task", &PynativeExecutor::ExecuteLazyTask, "clear all task") + + // Define the method "__call__" that runs the gradient graph in PynativeExecutor + .def("__call__", &PynativeExecutor::Run, "pynative executor run grad graph.") + + // Define the method "set_graph_phase" that sets the graph phase in PynativeExecutor + .def("set_graph_phase", &PynativeExecutor::set_graph_phase, "pynative set graph phase") + + // Define the method "grad_flag" that returns the gradient flag in PynativeExecutor + .def("grad_flag", &PynativeExecutor::grad_flag, "pynative grad flag") + + // Define the method "set_hook_changed" that sets the hook changed flag in PynativeExecutor + .def("set_hook_changed", &PynativeExecutor::SetHookChanged, "set pynative hook changed") + + // Define the method "set_grad_position" that sets the gradient position in PynativeExecutor + .def("set_grad_position", &PynativeExecutor::set_grad_position, "set pynative grad position") + + // Define the method "set_grad_flag" that sets the gradient flag in PynativeExecutor + .def("set_grad_flag", &PynativeExecutor::set_grad_flag, py::arg("flag") = py::bool_(false), + "Executor set grad flag.") + + // Define the method "set_py_exe_path" that sets the python executable path in PynativeExecutor + .def("set_py_exe_path", &PynativeExecutor::set_py_exe_path, + py::arg("py_exe_path") = py::str(""), "set python executable path.") + + // Define the method "set_kernel_build_server_dir" that sets the kernel build server directory path in PynativeExecutor + .def("set_kernel_build_server_dir", &PynativeExecutor::set_kernel_build_server_dir, + py::arg("kernel_build_server_dir") = py::str(""), + "set kernel build server directory path."); +})); + +// End of the namespace mindspore::pynative \ No newline at end of file -- 2.34.1