From e47d72e62bc5fd42bea96fd628ee17bdcbda06b2 Mon Sep 17 00:00:00 2001 From: huajsj Date: Fri, 24 Sep 2021 13:54:18 -0700 Subject: [PATCH 01/23] [pipeline executor] Add configuration load function and pipeline executor export,import function. --- python/tvm/contrib/pipeline_executor.py | 164 ++++++++++++++++--- src/runtime/pipeline/pipeline_executor.cc | 45 +++-- src/runtime/pipeline/pipeline_executor.h | 71 +++++++- src/runtime/pipeline/pipeline_function.cc | 105 ++++++++++++ src/runtime/pipeline/pipeline_function.h | 59 +++++++ src/runtime/pipeline/pipeline_struct.h | 152 +++++++++++++++++ tests/python/relay/test_pipeline_executor.py | 10 ++ 7 files changed, 566 insertions(+), 40 deletions(-) create mode 100644 src/runtime/pipeline/pipeline_function.cc create mode 100644 src/runtime/pipeline/pipeline_function.h create mode 100644 src/runtime/pipeline/pipeline_struct.h diff --git a/python/tvm/contrib/pipeline_executor.py b/python/tvm/contrib/pipeline_executor.py index 36c03891d210..0fb1390a49cf 100644 --- a/python/tvm/contrib/pipeline_executor.py +++ b/python/tvm/contrib/pipeline_executor.py @@ -16,6 +16,7 @@ # under the License. """Pipeline executor that executes a series of modules in a pipeline fashion.""" import json +import os import tvm._ffi from tvm import relay from tvm.relay.transform import InferType @@ -47,13 +48,13 @@ def build(pipe_configs): ret: PipelineExecutorFactoryModule Common interface for pipeline executor factory modules. """ - mods = {} + libs = {} mod_n_configs = pipe_configs.get_config() config_len = len(mod_n_configs) string_config = [{} for _ in range(config_len)] for ir_mod, mod_config in mod_n_configs.items(): mconf = mod_config["pipeline"].copy() - mod_idx = mconf["mod_idx"] - 1 + mod_idx = mconf["mod_idx"] dev = mod_config["dev"] target = mod_config["target"] build_func = relay.build @@ -61,7 +62,7 @@ def build(pipe_configs): if "build" in mod_config and mod_config["build"]: build_func = mod_config["build"] - mod = build_func( + lib = build_func( ir_mod, target, params=mod_config["params"], @@ -71,10 +72,10 @@ def build(pipe_configs): mconf["dev"] = "{},{}".format(dev.device_type, dev.device_id) # Create a pipeline configuration. - string_config[mod_idx] = mconf - mods[mod] = {"dev": dev} + string_config[mod_idx - 1] = mconf + libs[mod_idx] = {"lib": lib, "dev": dev} - return PipelineExecutorFactoryModule(mods, string_config) + return PipelineExecutorFactoryModule(libs, string_config) class PipelineModule(object): @@ -86,8 +87,43 @@ class PipelineModule(object): Common interface for pipeline executor factory modules. """ - def __init__(self, module): - self.module = module.module + def __init__(self, module=None): + self.module = module.module if module else None + self._get_num_outputs = None + # Get pack functions from pipeline executor. + self.load_functions() + + def import_from_library(self, config_file_name): + """Create pipeline executor from files. + + Parameters + ---------- + config_file_name : str + The configuration file path, the configuration file contains the + disk path of the parameter file, library file and JSON file。 + """ + # Create empty PipelineExecutorFactoryModule. + pipeline_factory = PipelineExecutorFactoryModule() + # Load configuration file to initialize PipelineExecutorFactoryModule. + pipeline_factory.import_from_library(config_file_name) + self.module = pipeline_factory.module + # Get pack functions from pipeline executor. + self.load_functions() + + def load_functions(self): + # Get functions from pipeline executor C++ modules + self._get_num_outputs = self.module["get_num_outputs"] if self.module else None + + def get_num_outputs(self): + """Get the number of outputs. + Returns + ------- + count : int + The number of outputs. + """ + if self._get_num_outputs: + return self._get_num_outputs() + return 0 class PipelineConfig(object): @@ -501,17 +537,18 @@ class PipelineExecutorFactoryModule(object): """ - def __init__(self, pipeline_mods, mods_config): - mods, config = self.graph_executor_create(pipeline_mods, mods_config) - assert ( - pipeline_executor_enabled() - ), "Pipeline executor is not enabled. Please \ - re-build TVM with USE_PIPELINE_EXECUTOR=ON" - pipeline_create = tvm._ffi.get_global_func( + def __init__(self, pipeline_libs=None, mods_config=None): + self.pipeline_libs = pipeline_libs + self.mods_config = mods_config + self.pipeline_create = tvm._ffi.get_global_func( "tvm.pipeline_executor.create", allow_missing=False ) - assert pipeline_create - self.module = pipeline_create(mods, config) + self.module = None + # Only create pipeline executor when pipeline_libs, mods_config and + # self.pipeline_create are not None. + if pipeline_libs and mods_config and self.pipeline_create: + graph_executors, config = self.graph_executor_create(pipeline_libs, mods_config) + self.module = self.pipeline_create(graph_executors, config) def graph_executor_create(self, pipeline_mods, mod_config): """Create graph_executor list and return configuration as a json string. @@ -533,11 +570,92 @@ def graph_executor_create(self, pipeline_mods, mod_config): The Modudle configuration. """ - mods = [] - for pipeline_mod in pipeline_mods: - mod = graph_executor.GraphModule( - pipeline_mod["default"](pipeline_mods[pipeline_mod]["dev"]) - ) - mods.append(mod.module) + # The module in pipeline_mods has a index information to identify the modules order + # in pipe line, the pipe line executor follow such asend order to run each module, + # but asend order of module is not guaranteed in pipeline_mod, here need to pre-allocate + # module list then put module in correct place follow the index value. + mods = [None for _ in range(len(pipeline_mods))] + for lib_index in pipeline_mods: + pipeline_lib = pipeline_mods[lib_index]["lib"] + dev = pipeline_mods[lib_index]["dev"] + lib = graph_executor.GraphModule(pipeline_lib["default"](dev)) + # Return a module list sorted by lib_index. + mods[lib_index - 1] = lib.module return mods, json.dumps(mod_config) + + def export_library(self, directory_path=None): + """Export pipeline runtime into disk. + + Parameters + ---------- + directory_path : str + The directory to which these files are exported. + """ + if not self.pipeline_libs: + raise RuntimeError(f"The pipeline executor has not been initialized.") + + # If directory_path is not set, use the temporary path as the file storage directory_path. + if not directory_path: + directory_path = tvm.contrib.utils.tempdir().temp_dir + + # Create if the directory does not exist. + if not os.path.exists(directory_path): + os.makedirs(directory_path) + # Get the initial version of the configuration for export. + export_conf = self.mods_config.copy() + # Export library, JSON file and parameter file, and export the corresponding + # relationship between the files and the pipeline modules. + for lib_index in self.pipeline_libs: + mconf = export_conf[lib_index - 1] + mconf["lib_name"] = "{}/lib{}.so".format(directory_path, lib_index) + mconf["json_name"] = "{}/json{}".format(directory_path, lib_index) + mconf["params_name"] = "{}/params{}".format(directory_path, lib_index) + mconf["dev"] = "{},{}".format( + self.pipeline_libs[lib_index]["dev"].device_type, + self.pipeline_libs[lib_index]["dev"].device_id, + ) + + # Get graph, lib and parameters from GraphExecutorFactoryModule. + graph, lib, params = self.pipeline_libs[lib_index]["lib"] + # Export lib, graph and parameters to disk. + lib.export_library(mconf["lib_name"]) + with open(mconf["json_name"], "w") as file_handle: + file_handle.write(graph) + with open(mconf["params_name"], "wb") as file_handle: + file_handle.write(relay.save_param_dict(params)) + + # Check whether the output is successful. + if not os.path.exists(mconf["json_name"]): + raise RuntimeError("File {} export failure.".format(mconf["json_name"])) + if not os.path.exists(mconf["lib_name"]): + raise RuntimeError("File {} export failure.".format(mconf["lib_name"])) + if not os.path.exists(mconf["params_name"]): + raise RuntimeError("File {} export failure.".format(mconf["params_name"])) + + # Export configuration file to disk. + conf_file_name = "{}/config".format(directory_path) + with open(conf_file_name, "w") as file_handle: + file_handle.write(json.dumps(export_conf)) + if not os.path.exists(conf_file_name): + raise RuntimeError(f"File {conf_file_name} export failure.") + + return conf_file_name + + def import_from_library(self, configure_file_name): + """Load configuration file to create and initialize pipeline executor. + + Parameters + ---------- + configure_file_name : str + The configuration file path, the configuration file contains the + disk path of the parameter file, library file and JSON file。 + """ + if self.module: + raise RuntimeError(f"The pipeline executor has already been initialized.") + + self.pipe_configure = "" + with open(configure_file_name, "r") as file_handle: + self.pipe_configure = file_handle.read() + + self.module = self.pipeline_create([], self.pipe_configure) diff --git a/src/runtime/pipeline/pipeline_executor.cc b/src/runtime/pipeline/pipeline_executor.cc index 41f867057282..081838e2b9fe 100644 --- a/src/runtime/pipeline/pipeline_executor.cc +++ b/src/runtime/pipeline/pipeline_executor.cc @@ -21,31 +21,48 @@ * \file pipeline_executor.cc */ #include "pipeline_executor.h" - namespace tvm { namespace runtime { - -void PipelineRuntime::Init(const Array& modules, - const std::string& pipeline_json) { - return; -} - -/* GetFunction can not be pure abstract function, implement an empty function for now. +/*! + * \brief Give frontends an access to packed functions. + * \param name The name of the function. + * \param sptr_to_self The pointer to the module node. + * \return The corresponding packed function. */ -PackedFunc PipelineRuntime::GetFunction(const std::string& name, - const ObjectPtr& sptr_to_self) { +PackedFunc PipelineExecutor::GetFunction(const std::string& name, + const ObjectPtr& sptr_to_self) { + if (name == "get_num_outputs") { + return PackedFunc( + [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { *rv = this->NumOutputs(); }); + } else { + return PackedFunc(); + } return nullptr; } +/*! + * \brief Initialize the pipeline executor with module array and json text. + * \param modules The module list used for building pipeline. + * \param pipeline_json The configuration of modules dependencies. + */ +void PipelineExecutor::Init(const Array& modules, const std::string& pipeline_json) { + // Use JSONReader to load pipe line configuration from file. + std::istringstream is(pipeline_json); + dmlc::JSONReader reader(&is); + this->Load(&reader); + // Initialize the pipeline function class used for pipeline thread pool management + // and schedule etc. This function return output number of whole pipeline. + output_number_ = pipeline_function_.PipelineInit(modules, pipeline_configure_, mod_configure_); + return; +} -Module PipelineRuntimeCreate(const Array& m, - const std::string& pipeline_json) { - auto exec = make_object(); +Module PipelineExecutorCreate(const Array& m, const std::string& pipeline_json) { + auto exec = make_object(); exec->Init(m, pipeline_json); return Module(exec); } TVM_REGISTER_GLOBAL("tvm.pipeline_executor.create").set_body([](TVMArgs args, TVMRetValue* rv) { - *rv = PipelineRuntimeCreate(args[0], args[1]); + *rv = PipelineExecutorCreate(args[0], args[1]); }); } // namespace runtime } // namespace tvm diff --git a/src/runtime/pipeline/pipeline_executor.h b/src/runtime/pipeline/pipeline_executor.h index c7625c62b724..8e5ea121dcb2 100644 --- a/src/runtime/pipeline/pipeline_executor.h +++ b/src/runtime/pipeline/pipeline_executor.h @@ -23,9 +23,16 @@ */ #ifndef TVM_RUNTIME_PIPELINE_PIPELINE_EXECUTOR_H_ #define TVM_RUNTIME_PIPELINE_PIPELINE_EXECUTOR_H_ + #include +#include +#include +#include #include +#include + +#include "pipeline_function.h" namespace tvm { namespace runtime { /*! @@ -36,18 +43,18 @@ namespace runtime { * * This executor can be accessed by various language via TVM runtime PackedFunc API. */ -class TVM_DLL PipelineRuntime : public ModuleNode { +class TVM_DLL PipelineExecutor : public ModuleNode { public: /*! * \Return the type key of the executor. */ - const char* type_key() const final { return "PipelineRuntime"; } + const char* type_key() const final { return "PipelineExecutor"; } /*! * \brief Initialize the pipeline executor with module array and json text. * \param modules The module list used for building pipeline. * \param pipeline_json The configuration of modules dependencies. */ - void Init(const Array& modules, const std::string& pipeline_json); + void Init(const Array& modules, const std::string& pipeline_json); /*! * \brief Give frontends an access to packed functions. * \param name The name of the function. @@ -55,6 +62,64 @@ class TVM_DLL PipelineRuntime : public ModuleNode { * \return The corresponding packed function. */ virtual PackedFunc GetFunction(const std::string& name, const ObjectPtr& sptr_to_self); + + /*! + * \brief Get the number of outputs. + * + * \return The number of outputs. + */ + int NumOutputs() const { return output_number_; } + + private: + /*!\brief The class used to execute pipeline logic*/ + PipelineFunction pipeline_function_; + /*!\brief Dependency information of each graph runtime module of pipe line.*/ + PipelineConfigure pipeline_configure_; + /*!\brief Module information that can get use to create graph runtime.*/ + ModuleConfigure mod_configure_; + /*!\birief How many outputs are in this pipeline executor.*/ + size_t output_number_ = 0; + /*!\brief Json loader.*/ + void Load(dmlc::JSONReader* reader) { + reader->BeginArray(); + while (reader->NextArrayItem()) { + std::string key; + reader->BeginObject(); + int mod_idx = 0; + std::string libName; + std::string jsonName; + std::string paramsName; + std::string dev; + OutputMap output; + while (reader->NextObjectItem(&key)) { + if (key == "mod_idx") { + reader->Read(&mod_idx); + } + if (key == "lib_name") { + reader->Read(&libName); + } + + if (key == "json_name") { + reader->Read(&jsonName); + } + + if (key == "params_name") { + reader->Read(¶msName); + } + + if (key == "dev") { + reader->Read(&dev); + } + + if (key == "output") { + reader->Read(&output); + } + } + pipeline_configure_[mod_idx] = output; + mod_configure_[mod_idx] = { + {"lib_name", libName}, {"json_name", jsonName}, {"params", paramsName}, {"dev", dev}}; + } + } }; } // namespace runtime } // namespace tvm diff --git a/src/runtime/pipeline/pipeline_function.cc b/src/runtime/pipeline/pipeline_function.cc new file mode 100644 index 000000000000..45074a156474 --- /dev/null +++ b/src/runtime/pipeline/pipeline_function.cc @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include "pipeline_function.h" + +#include +#include +namespace tvm { +namespace runtime { +/*! + * \brief Initialize pipeline. + * \param modules List of graph runtime module. + * \param pipeline_conf Dependency relation of each graph runtime module. + * \param mod_configure Configure information that generate by export library function call. + */ +size_t PipelineFunction::PipelineInit(Array modules, const PipelineConfigure& pipeline_conf, + const ModuleConfigure& mod_configure) { + int outputNum = pipeline_conf.GetGlobalOutputNum(); + std::vector graphRuntimes = PipelineCreateGraphruntime(modules, mod_configure); + return outputNum; +} +/*! + * \brief There are two mode to create graph runtime list, first is to use modules that + * are the module list already created by caller, when modules is empty these information + * from mod_configure will get use to create graph runtime list. + * \param modules List of graph runtime module. + * \param mod_configure Configure information that generate by export library function call. + */ +std::vector PipelineFunction::PipelineCreateGraphruntime( + Array modules, const ModuleConfigure& mod_configure) { + const PackedFunc* graphRuntimeCreate = Registry::Get("tvm.graph_executor.create"); + std::vector ret; + // if modules not empty just return in vector container + if (!modules.empty()) { + for (auto mod : modules) { + ret.push_back(mod); + } + + // if modules is empty, need to build the graph runtime from mod_conf + } else { + ret.resize(mod_configure.size()); + for (auto configure : mod_configure) { + // load lib + auto lib = Module::LoadFromFile(configure.second["lib_name"].c_str()); + + // read json + std::ifstream ifJson(configure.second["json_name"].c_str()); + if (ifJson.fail()) { + throw std::runtime_error("json file not found!"); + } + const std::string json((std::istreambuf_iterator(ifJson)), + std::istreambuf_iterator()); + + // create graph runtime + std::istringstream istr(configure.second["dev"]); + std::string str; + int deviceType = 1, deviceId = 0; + while (getline(istr, str, ';')) { + std::istringstream istrDev(str); + std::string stemp; + if (getline(istrDev, stemp)) { + deviceType = stoi(stemp); + } + if (getline(istrDev, stemp)) { + deviceId = stoi(stemp); + } + } + Module graphModule = (*graphRuntimeCreate)(json, lib, deviceType, deviceId); + + // load parameter + TVMByteArray params_arr; + std::ifstream ifParam(configure.second["params"].c_str()); + if (ifParam.fail()) { + throw std::runtime_error("params file not found!"); + } + const std::string params((std::istreambuf_iterator(ifParam)), + std::istreambuf_iterator()); + params_arr.data = params.c_str(); + params_arr.size = params.length(); + auto load_params = graphModule.GetFunction("load_params"); + load_params(params_arr); + + // put into return vector + ret[configure.first - 1] = graphModule; + } + } + return ret; +} +} // namespace runtime +} // namespace tvm diff --git a/src/runtime/pipeline/pipeline_function.h b/src/runtime/pipeline/pipeline_function.h new file mode 100644 index 000000000000..18e0220efd98 --- /dev/null +++ b/src/runtime/pipeline/pipeline_function.h @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef TVM_RUNTIME_PIPELINE_PIPELINE_FUNCTION_H_ +#define TVM_RUNTIME_PIPELINE_PIPELINE_FUNCTION_H_ +#include +#include +#include + +#include +#include +#include +#include + +#include "pipeline_struct.h" +namespace tvm { +namespace runtime { +/*! + * \brief The class that executes pipeline logic is used to initialize the thread pool, + execute and schedule pipeline tasks, allocate and manage memory, etc. + */ +class PipelineFunction { + public: + /*! + * \brief There are two mode to create graph runtime list, first is to use modules that + * are the module list already created by caller, when modules is empty these information + * from mod_configure will get use to create graph runtime list. + * \param modules List of graph runtime module. + * \param mod_configure Configure information that generate by export library function call. + */ + std::vector PipelineCreateGraphruntime(Array modules, + const ModuleConfigure& mod_configure); + /*! + * \brief Initialize pipeline. + * \param modules List of graph runtime module. + * \param pipeline_conf Dependency relation of each graph runtime module. + * \param mod_configure Configure information that generate by export library function call. + */ + size_t PipelineInit(Array modules, const PipelineConfigure& pipeline_conf, + const ModuleConfigure& mod_configure); +}; +} // namespace runtime +} // namespace tvm +#endif // TVM_RUNTIME_PIPELINE_PIPELINE_FUNCTION_H_ diff --git a/src/runtime/pipeline/pipeline_struct.h b/src/runtime/pipeline/pipeline_struct.h new file mode 100644 index 000000000000..f1144242a8ec --- /dev/null +++ b/src/runtime/pipeline/pipeline_struct.h @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef TVM_RUNTIME_PIPELINE_PIPELINE_STRUCT_H_ +#define TVM_RUNTIME_PIPELINE_PIPELINE_STRUCT_H_ +#include +#include +#include + +#include +#include +#include +/*! + * \brief Store the corresponding dependencies between the input of other modules + and the current outputs. + */ +struct OutputBindings { + /*! \brief All module interface binding with current output. */ + std::unordered_map bindings; + /*! + * \brief If there is one global binding in bindings, then current output is + * global interface. + * \return Whether this output interface is global output interface. + */ + bool IsGlobalOutputNum() const { + int outputNum = 0; + for (auto binding : bindings) { + /* output is global output when value is 0. + */ + outputNum += (binding.first == 0); + } + /* If this output is a global output then there is only one such output in map.*/ + assert(outputNum <= 1); + return outputNum == 1; + } + /*! + * \brief Create module interface map from JSONReader. + * \param reader Json reader. + */ + void Load(dmlc::JSONReader* reader) { + reader->BeginArray(); + while (reader->NextArrayItem()) { + std::string key; + reader->BeginObject(); + std::string inputName; + int mod_idx; + while (reader->NextObjectItem(&key)) { + if (key == "mod_idx") { + reader->Read(&mod_idx); + } + if (key == "input_name") { + reader->Read(&inputName); + } + } + bindings[mod_idx] = inputName; + } + } +}; +/*! + * \brief Binding information of the outputs by each module. + */ +struct OutputMap { + /*! \brief output and output binding map. */ + std::unordered_map output_binding_map; + OutputMap& operator=(const OutputMap& output) { + output_binding_map = output.output_binding_map; + return *this; + } + /*! \brief Global output is the final outputs of pipeline, this function use to + * get how many global outputs are in this Outputmap + * \return Number of global outputs. + */ + size_t GetGlobalOutputNum(void) const { + size_t outputNum = 0; + for (auto bindings : output_binding_map) { + outputNum += bindings.second.IsGlobalOutputNum() ? 1 : 0; + } + return outputNum; + } + + /*! + * \brief Create output and output binding map from JSONReader. + * \param reader Json reader. + */ + void Load(dmlc::JSONReader* reader) { + reader->BeginArray(); + while (reader->NextArrayItem()) { + std::string key; + reader->BeginObject(); + int output_idx; + OutputBindings binding; + while (reader->NextObjectItem(&key)) { + if (key == "output_idx") { + reader->Read(&output_idx); + } + if (key == "dependent") { + reader->Read(&binding); + } + } + output_binding_map[output_idx] = binding; + } + } +}; +/*! + * \brief Binding or dependency information of each module output interface. + */ +struct PipelineConfigure { + /*! */ + std::unordered_map config; + OutputMap& operator[](const int key) { return config[key]; } + /*! + * \brief Get total global outputs number. + * \return Global outputs number. + */ + size_t GetGlobalOutputNum() const { + size_t output_num = 0; + for (auto mod_output : config) { + output_num += mod_output.second.GetGlobalOutputNum(); + } + return output_num; + } +}; +/*! + * \brief Informations used to initialize the graph executor module, these + information comming from export library function call. + */ +struct ModuleInformation { + std::unordered_map info; + const std::string& operator[](const std::string& key) { return info[key]; } + ModuleInformation& operator=(const std::unordered_map& umap) { + info = umap; + return *this; + } +}; +/*! Module information of each module. */ +typedef std::unordered_map ModuleConfigure; +#endif // TVM_RUNTIME_PIPELINE_PIPELINE_STRUCT_H_ diff --git a/tests/python/relay/test_pipeline_executor.py b/tests/python/relay/test_pipeline_executor.py index d9411c92c375..7c423b3229ab 100644 --- a/tests/python/relay/test_pipeline_executor.py +++ b/tests/python/relay/test_pipeline_executor.py @@ -231,9 +231,19 @@ def test_pipeline(): with tvm.transform.PassContext(opt_level=3): pipeline_mod_factory = pipeline_executor.build(pipe_config) + # Export parameter configuration to file. + config_file_name = pipeline_mod_factory.export_library() + + # The test is to create and initialized class PipelineModule using build return. pipeline_module = pipeline_executor.PipelineModule(pipeline_mod_factory) assert pipeline_module + # The test is to initialized class PipelineModule using import function. + pipeline_module_test = pipeline_executor.PipelineModule() + pipeline_module_test.import_from_library(config_file_name) + # Check whether the number of outputs is two. + assert pipeline_module_test.get_num_outputs() == 2 + if __name__ == "__main__": pytest.main([__file__]) From a6029757e9547617ebed9ca28c9a1b3a3f2f75fc Mon Sep 17 00:00:00 2001 From: huajsj Date: Tue, 28 Sep 2021 17:49:34 -0700 Subject: [PATCH 02/23] address review comments. --- python/tvm/contrib/pipeline_executor.py | 23 +--- src/runtime/pipeline/pipeline_executor.cc | 6 +- src/runtime/pipeline/pipeline_executor.h | 32 +++--- src/runtime/pipeline/pipeline_function.cc | 115 +++++++++---------- src/runtime/pipeline/pipeline_function.h | 22 ++-- src/runtime/pipeline/pipeline_struct.h | 55 +++++---- tests/python/relay/test_pipeline_executor.py | 5 +- 7 files changed, 130 insertions(+), 128 deletions(-) diff --git a/python/tvm/contrib/pipeline_executor.py b/python/tvm/contrib/pipeline_executor.py index 0fb1390a49cf..47b4f0c86214 100644 --- a/python/tvm/contrib/pipeline_executor.py +++ b/python/tvm/contrib/pipeline_executor.py @@ -90,7 +90,7 @@ class PipelineModule(object): def __init__(self, module=None): self.module = module.module if module else None self._get_num_outputs = None - # Get pack functions from pipeline executor. + # Get packed functions from pipeline executor. self.load_functions() def import_from_library(self, config_file_name): @@ -107,7 +107,7 @@ def import_from_library(self, config_file_name): # Load configuration file to initialize PipelineExecutorFactoryModule. pipeline_factory.import_from_library(config_file_name) self.module = pipeline_factory.module - # Get pack functions from pipeline executor. + # Get packed functions from pipeline executor. self.load_functions() def load_functions(self): @@ -121,9 +121,9 @@ def get_num_outputs(self): count : int The number of outputs. """ - if self._get_num_outputs: - return self._get_num_outputs() - return 0 + if not self._get_num_outputs: + raise RuntimeError(f"The pipeline executor has not been initialized.") + return self._get_num_outputs() class PipelineConfig(object): @@ -570,10 +570,7 @@ def graph_executor_create(self, pipeline_mods, mod_config): The Modudle configuration. """ - # The module in pipeline_mods has a index information to identify the modules order - # in pipe line, the pipe line executor follow such asend order to run each module, - # but asend order of module is not guaranteed in pipeline_mod, here need to pre-allocate - # module list then put module in correct place follow the index value. + # Modules need to be stored in the list named 'mods' in index order. mods = [None for _ in range(len(pipeline_mods))] for lib_index in pipeline_mods: pipeline_lib = pipeline_mods[lib_index]["lib"] @@ -625,14 +622,6 @@ def export_library(self, directory_path=None): with open(mconf["params_name"], "wb") as file_handle: file_handle.write(relay.save_param_dict(params)) - # Check whether the output is successful. - if not os.path.exists(mconf["json_name"]): - raise RuntimeError("File {} export failure.".format(mconf["json_name"])) - if not os.path.exists(mconf["lib_name"]): - raise RuntimeError("File {} export failure.".format(mconf["lib_name"])) - if not os.path.exists(mconf["params_name"]): - raise RuntimeError("File {} export failure.".format(mconf["params_name"])) - # Export configuration file to disk. conf_file_name = "{}/config".format(directory_path) with open(conf_file_name, "w") as file_handle: diff --git a/src/runtime/pipeline/pipeline_executor.cc b/src/runtime/pipeline/pipeline_executor.cc index 081838e2b9fe..57775d9fa887 100644 --- a/src/runtime/pipeline/pipeline_executor.cc +++ b/src/runtime/pipeline/pipeline_executor.cc @@ -45,13 +45,13 @@ PackedFunc PipelineExecutor::GetFunction(const std::string& name, * \param pipeline_json The configuration of modules dependencies. */ void PipelineExecutor::Init(const Array& modules, const std::string& pipeline_json) { - // Use JSONReader to load pipe line configuration from file. + // Use JSONReader to load pipeline configuration from file. std::istringstream is(pipeline_json); dmlc::JSONReader reader(&is); this->Load(&reader); // Initialize the pipeline function class used for pipeline thread pool management - // and schedule etc. This function return output number of whole pipeline. - output_number_ = pipeline_function_.PipelineInit(modules, pipeline_configure_, mod_configure_); + // and schedule etc. This function return the number of output. + num_outputs_ = pipeline_function_.PipelineInit(modules, pipeline_configure_, mod_configure_); return; } diff --git a/src/runtime/pipeline/pipeline_executor.h b/src/runtime/pipeline/pipeline_executor.h index 8e5ea121dcb2..2666077e9477 100644 --- a/src/runtime/pipeline/pipeline_executor.h +++ b/src/runtime/pipeline/pipeline_executor.h @@ -68,17 +68,17 @@ class TVM_DLL PipelineExecutor : public ModuleNode { * * \return The number of outputs. */ - int NumOutputs() const { return output_number_; } + int NumOutputs() const { return num_outputs_; } private: /*!\brief The class used to execute pipeline logic*/ PipelineFunction pipeline_function_; - /*!\brief Dependency information of each graph runtime module of pipe line.*/ - PipelineConfigure pipeline_configure_; - /*!\brief Module information that can get use to create graph runtime.*/ - ModuleConfigure mod_configure_; + /*!\brief Dependency information of each graph runtime module of pipeline.*/ + PipelineConfig pipeline_configure_; + /*!\brief Module information that can get used to create graph runtime.*/ + ModuleConfig mod_configure_; /*!\birief How many outputs are in this pipeline executor.*/ - size_t output_number_ = 0; + size_t num_outputs_ = 0; /*!\brief Json loader.*/ void Load(dmlc::JSONReader* reader) { reader->BeginArray(); @@ -86,9 +86,9 @@ class TVM_DLL PipelineExecutor : public ModuleNode { std::string key; reader->BeginObject(); int mod_idx = 0; - std::string libName; - std::string jsonName; - std::string paramsName; + std::string lib_name; + std::string json_name; + std::string params_name; std::string dev; OutputMap output; while (reader->NextObjectItem(&key)) { @@ -96,15 +96,15 @@ class TVM_DLL PipelineExecutor : public ModuleNode { reader->Read(&mod_idx); } if (key == "lib_name") { - reader->Read(&libName); + reader->Read(&lib_name); } if (key == "json_name") { - reader->Read(&jsonName); + reader->Read(&json_name); } if (key == "params_name") { - reader->Read(¶msName); + reader->Read(¶ms_name); } if (key == "dev") { @@ -115,9 +115,15 @@ class TVM_DLL PipelineExecutor : public ModuleNode { reader->Read(&output); } } + // Check if mod_idx is read successfully. + assert(mod_idx > 0); + // Check if the output is read successfully. + assert(!output.empty()); pipeline_configure_[mod_idx] = output; + // Check if lib, json and params are read successfully. + assert(!lib_name.empty() && !json_name.empty() && !params_name.empty()); mod_configure_[mod_idx] = { - {"lib_name", libName}, {"json_name", jsonName}, {"params", paramsName}, {"dev", dev}}; + {"lib_name", lib_name}, {"json_name", json_name}, {"params", params_name}, {"dev", dev}}; } } }; diff --git a/src/runtime/pipeline/pipeline_function.cc b/src/runtime/pipeline/pipeline_function.cc index 45074a156474..866277c40436 100644 --- a/src/runtime/pipeline/pipeline_function.cc +++ b/src/runtime/pipeline/pipeline_function.cc @@ -26,78 +26,73 @@ namespace runtime { * \brief Initialize pipeline. * \param modules List of graph runtime module. * \param pipeline_conf Dependency relation of each graph runtime module. - * \param mod_configure Configure information that generate by export library function call. + * \param mod_config Config information that generate by export library function call. */ -size_t PipelineFunction::PipelineInit(Array modules, const PipelineConfigure& pipeline_conf, - const ModuleConfigure& mod_configure) { - int outputNum = pipeline_conf.GetGlobalOutputNum(); - std::vector graphRuntimes = PipelineCreateGraphruntime(modules, mod_configure); - return outputNum; -} -/*! - * \brief There are two mode to create graph runtime list, first is to use modules that - * are the module list already created by caller, when modules is empty these information - * from mod_configure will get use to create graph runtime list. - * \param modules List of graph runtime module. - * \param mod_configure Configure information that generate by export library function call. - */ -std::vector PipelineFunction::PipelineCreateGraphruntime( - Array modules, const ModuleConfigure& mod_configure) { - const PackedFunc* graphRuntimeCreate = Registry::Get("tvm.graph_executor.create"); - std::vector ret; +size_t PipelineFunction::PipelineInit(Array modules, const PipelineConfig& pipeline_config, + const ModuleConfig& mod_config) { + int num_output = pipeline_config.GetGlobalOutputNum(); // if modules not empty just return in vector container if (!modules.empty()) { for (auto mod : modules) { - ret.push_back(mod); + graph_executors_.push_back(mod); } - - // if modules is empty, need to build the graph runtime from mod_conf } else { - ret.resize(mod_configure.size()); - for (auto configure : mod_configure) { - // load lib - auto lib = Module::LoadFromFile(configure.second["lib_name"].c_str()); + // if modules is empty, need to build the graph runtime from mod_config. + graph_executors_ = PipelineCreateGraphExecutors(mod_config); + } + return num_output; +} +/*! + * \brief Use mod_config information to create a graph runtime list. + * \param mod_configure Config information that generate by export library function call. + */ +std::vector PipelineFunction::PipelineCreateGraphExecutors(const ModuleConfig& mod_config) { + const PackedFunc* graph_executor_create = Registry::Get("tvm.graph_executor.create"); + std::vector ret; + ret.resize(mod_config.size()); + for (auto config : mod_config) { + // load lib + auto lib = Module::LoadFromFile(config.second["lib_name"].c_str()); - // read json - std::ifstream ifJson(configure.second["json_name"].c_str()); - if (ifJson.fail()) { - throw std::runtime_error("json file not found!"); - } - const std::string json((std::istreambuf_iterator(ifJson)), - std::istreambuf_iterator()); + // read json + std::ifstream ifJson(config.second["json_name"].c_str()); + if (ifJson.fail()) { + throw std::runtime_error("json file not found!"); + } + const std::string json((std::istreambuf_iterator(ifJson)), + std::istreambuf_iterator()); - // create graph runtime - std::istringstream istr(configure.second["dev"]); - std::string str; - int deviceType = 1, deviceId = 0; - while (getline(istr, str, ';')) { - std::istringstream istrDev(str); - std::string stemp; - if (getline(istrDev, stemp)) { - deviceType = stoi(stemp); - } - if (getline(istrDev, stemp)) { - deviceId = stoi(stemp); - } + // create graph runtime + std::istringstream istr(config.second["dev"]); + std::string str; + int device_type = 1, device_id = 0; + while (getline(istr, str, ';')) { + std::istringstream istr_dev(str); + std::string str_temp; + if (getline(istr_dev, str_temp)) { + device_type = stoi(str_temp); } - Module graphModule = (*graphRuntimeCreate)(json, lib, deviceType, deviceId); - - // load parameter - TVMByteArray params_arr; - std::ifstream ifParam(configure.second["params"].c_str()); - if (ifParam.fail()) { - throw std::runtime_error("params file not found!"); + if (getline(istr_dev, str_temp)) { + device_id = stoi(str_temp); } - const std::string params((std::istreambuf_iterator(ifParam)), - std::istreambuf_iterator()); - params_arr.data = params.c_str(); - params_arr.size = params.length(); - auto load_params = graphModule.GetFunction("load_params"); - load_params(params_arr); + } + Module graph_module = (*graph_executor_create)(json, lib, device_type, device_id); - // put into return vector - ret[configure.first - 1] = graphModule; + // load parameter + TVMByteArray params_arr; + std::ifstream if_param(config.second["params"].c_str()); + if (if_param.fail()) { + throw std::runtime_error("params file not found!"); } + const std::string params((std::istreambuf_iterator(if_param)), + std::istreambuf_iterator()); + params_arr.data = params.c_str(); + params_arr.size = params.length(); + auto load_params = graph_module.GetFunction("load_params"); + load_params(params_arr); + + // put into return vector + ret[config.first - 1] = graph_module; } return ret; } diff --git a/src/runtime/pipeline/pipeline_function.h b/src/runtime/pipeline/pipeline_function.h index 18e0220efd98..8d3417765383 100644 --- a/src/runtime/pipeline/pipeline_function.h +++ b/src/runtime/pipeline/pipeline_function.h @@ -37,22 +37,22 @@ namespace runtime { class PipelineFunction { public: /*! - * \brief There are two mode to create graph runtime list, first is to use modules that - * are the module list already created by caller, when modules is empty these information - * from mod_configure will get use to create graph runtime list. - * \param modules List of graph runtime module. - * \param mod_configure Configure information that generate by export library function call. + * \brief Use the information of mod_config to create graph runtime list. + * \param mod_config Configuration information that generate by export library function call. */ - std::vector PipelineCreateGraphruntime(Array modules, - const ModuleConfigure& mod_configure); + std::vector PipelineCreateGraphExecutors(const ModuleConfig& mod_config); /*! * \brief Initialize pipeline. * \param modules List of graph runtime module. - * \param pipeline_conf Dependency relation of each graph runtime module. - * \param mod_configure Configure information that generate by export library function call. + * \param pipeline_config Dependency relation of each graph runtime module. + * \param mod_config Config information that generate by export library function call. */ - size_t PipelineInit(Array modules, const PipelineConfigure& pipeline_conf, - const ModuleConfigure& mod_configure); + size_t PipelineInit(Array modules, const PipelineConfig& pipeline_config, + const ModuleConfig& mod_config); + + private: + /*!\brief List of graph executors.*/ + std::vector graph_executors_; }; } // namespace runtime } // namespace tvm diff --git a/src/runtime/pipeline/pipeline_struct.h b/src/runtime/pipeline/pipeline_struct.h index f1144242a8ec..778d2114e7a4 100644 --- a/src/runtime/pipeline/pipeline_struct.h +++ b/src/runtime/pipeline/pipeline_struct.h @@ -30,23 +30,25 @@ and the current outputs. */ struct OutputBindings { - /*! \brief All module interface binding with current output. */ + /*!\brief Output interface binding information, 'int' is the index of the bond module. + * 'string' is the interface name of the bond module. + */ std::unordered_map bindings; /*! * \brief If there is one global binding in bindings, then current output is * global interface. * \return Whether this output interface is global output interface. */ - bool IsGlobalOutputNum() const { - int outputNum = 0; + bool IsGlobalOutput() const { + int num_output = 0; for (auto binding : bindings) { /* output is global output when value is 0. */ - outputNum += (binding.first == 0); + num_output += (binding.first == 0); } /* If this output is a global output then there is only one such output in map.*/ - assert(outputNum <= 1); - return outputNum == 1; + assert(num_output <= 1); + return num_output == 1; } /*! * \brief Create module interface map from JSONReader. @@ -57,17 +59,17 @@ struct OutputBindings { while (reader->NextArrayItem()) { std::string key; reader->BeginObject(); - std::string inputName; + std::string input_name; int mod_idx; while (reader->NextObjectItem(&key)) { if (key == "mod_idx") { reader->Read(&mod_idx); } if (key == "input_name") { - reader->Read(&inputName); + reader->Read(&input_name); } } - bindings[mod_idx] = inputName; + bindings[mod_idx] = input_name; } } }; @@ -81,16 +83,21 @@ struct OutputMap { output_binding_map = output.output_binding_map; return *this; } + + /*!\brief Check that OutMap is empty. + * \return True or False. + */ + bool Empty() { return output_binding_map.empty(); } /*! \brief Global output is the final outputs of pipeline, this function use to * get how many global outputs are in this Outputmap * \return Number of global outputs. */ size_t GetGlobalOutputNum(void) const { - size_t outputNum = 0; + size_t num_output = 0; for (auto bindings : output_binding_map) { - outputNum += bindings.second.IsGlobalOutputNum() ? 1 : 0; + num_output += bindings.second.IsGlobalOutput() ? 1 : 0; } - return outputNum; + return num_output; } /*! @@ -102,7 +109,7 @@ struct OutputMap { while (reader->NextArrayItem()) { std::string key; reader->BeginObject(); - int output_idx; + int output_idx = -1; OutputBindings binding; while (reader->NextObjectItem(&key)) { if (key == "output_idx") { @@ -112,6 +119,7 @@ struct OutputMap { reader->Read(&binding); } } + assert(output_idx >= 0); output_binding_map[output_idx] = binding; } } @@ -119,8 +127,10 @@ struct OutputMap { /*! * \brief Binding or dependency information of each module output interface. */ -struct PipelineConfigure { - /*! */ +struct PipelineConfig { + /*!\brief The module index is the key, this variable record all module pipeline configuration + * information. + */ std::unordered_map config; OutputMap& operator[](const int key) { return config[key]; } /*! @@ -128,18 +138,21 @@ struct PipelineConfigure { * \return Global outputs number. */ size_t GetGlobalOutputNum() const { - size_t output_num = 0; + size_t num_output = 0; for (auto mod_output : config) { - output_num += mod_output.second.GetGlobalOutputNum(); + num_output += mod_output.second.GetGlobalOutputNum(); } - return output_num; + return num_output; } }; /*! * \brief Informations used to initialize the graph executor module, these - information comming from export library function call. + information come from export library function call. */ struct ModuleInformation { + /*\brief The first string is information type such as "lib_name",the second string is + * information detail such as "/src/lib1.so" for "lib_name". + */ std::unordered_map info; const std::string& operator[](const std::string& key) { return info[key]; } ModuleInformation& operator=(const std::unordered_map& umap) { @@ -147,6 +160,6 @@ struct ModuleInformation { return *this; } }; -/*! Module information of each module. */ -typedef std::unordered_map ModuleConfigure; +/*! Module information of each module.The int is module index. */ +using ModuleConfig = std::unordered_map; #endif // TVM_RUNTIME_PIPELINE_PIPELINE_STRUCT_H_ diff --git a/tests/python/relay/test_pipeline_executor.py b/tests/python/relay/test_pipeline_executor.py index 7c423b3229ab..49a44bae907b 100644 --- a/tests/python/relay/test_pipeline_executor.py +++ b/tests/python/relay/test_pipeline_executor.py @@ -234,14 +234,13 @@ def test_pipeline(): # Export parameter configuration to file. config_file_name = pipeline_mod_factory.export_library() - # The test is to create and initialized class PipelineModule using build return. + # Use the output of build to create and initialize PipelineModule. pipeline_module = pipeline_executor.PipelineModule(pipeline_mod_factory) assert pipeline_module - # The test is to initialized class PipelineModule using import function. + # Use the import function to create and initialize PipelineModule. pipeline_module_test = pipeline_executor.PipelineModule() pipeline_module_test.import_from_library(config_file_name) - # Check whether the number of outputs is two. assert pipeline_module_test.get_num_outputs() == 2 From d3f15954e8d6f96ab119cb868c071778cfa793ec Mon Sep 17 00:00:00 2001 From: huajsj Date: Wed, 29 Sep 2021 17:54:17 -0700 Subject: [PATCH 03/23] polish comments and doc string. --- python/tvm/contrib/pipeline_executor.py | 31 ++++++++++---------- src/runtime/pipeline/pipeline_executor.h | 4 +-- src/runtime/pipeline/pipeline_function.cc | 26 ++++++++-------- src/runtime/pipeline/pipeline_function.h | 17 ++++++----- src/runtime/pipeline/pipeline_struct.h | 23 +++++++-------- tests/python/relay/test_pipeline_executor.py | 2 +- 6 files changed, 52 insertions(+), 51 deletions(-) diff --git a/python/tvm/contrib/pipeline_executor.py b/python/tvm/contrib/pipeline_executor.py index 47b4f0c86214..4e2eed55232b 100644 --- a/python/tvm/contrib/pipeline_executor.py +++ b/python/tvm/contrib/pipeline_executor.py @@ -90,11 +90,11 @@ class PipelineModule(object): def __init__(self, module=None): self.module = module.module if module else None self._get_num_outputs = None - # Get packed functions from pipeline executor. + # Get the packed functions from the pipeline executor. self.load_functions() def import_from_library(self, config_file_name): - """Create pipeline executor from files. + """Import files to create pipeline executor. Parameters ---------- @@ -102,16 +102,16 @@ def import_from_library(self, config_file_name): The configuration file path, the configuration file contains the disk path of the parameter file, library file and JSON file。 """ - # Create empty PipelineExecutorFactoryModule. + # Create a empty PipelineExecutorFactoryModule. pipeline_factory = PipelineExecutorFactoryModule() - # Load configuration file to initialize PipelineExecutorFactoryModule. + # Load the configuration file to initialize a PipelineExecutorFactoryModule. pipeline_factory.import_from_library(config_file_name) self.module = pipeline_factory.module - # Get packed functions from pipeline executor. + # Get packed functions from the pipeline executor. self.load_functions() def load_functions(self): - # Get functions from pipeline executor C++ modules + # Get functions from the pipeline executor. self._get_num_outputs = self.module["get_num_outputs"] if self.module else None def get_num_outputs(self): @@ -582,7 +582,7 @@ def graph_executor_create(self, pipeline_mods, mod_config): return mods, json.dumps(mod_config) def export_library(self, directory_path=None): - """Export pipeline runtime into disk. + """Export the pipeline executor into disk files. Parameters ---------- @@ -592,17 +592,18 @@ def export_library(self, directory_path=None): if not self.pipeline_libs: raise RuntimeError(f"The pipeline executor has not been initialized.") - # If directory_path is not set, use the temporary path as the file storage directory_path. + # If the directory_path is not set, use the temporary path as the file storage + # directory_path. if not directory_path: directory_path = tvm.contrib.utils.tempdir().temp_dir - # Create if the directory does not exist. + # If the directory does not exist, create the directory. if not os.path.exists(directory_path): os.makedirs(directory_path) - # Get the initial version of the configuration for export. + # Create a configuration copy for export. export_conf = self.mods_config.copy() - # Export library, JSON file and parameter file, and export the corresponding - # relationship between the files and the pipeline modules. + # Export the library, JSON and parameter into files, then export these files path + # into a configuraton file. for lib_index in self.pipeline_libs: mconf = export_conf[lib_index - 1] mconf["lib_name"] = "{}/lib{}.so".format(directory_path, lib_index) @@ -613,16 +614,16 @@ def export_library(self, directory_path=None): self.pipeline_libs[lib_index]["dev"].device_id, ) - # Get graph, lib and parameters from GraphExecutorFactoryModule. + # Get the graph, lib and parameters from GraphExecutorFactoryModule. graph, lib, params = self.pipeline_libs[lib_index]["lib"] - # Export lib, graph and parameters to disk. + # Export the lib, graph and parameters to disk. lib.export_library(mconf["lib_name"]) with open(mconf["json_name"], "w") as file_handle: file_handle.write(graph) with open(mconf["params_name"], "wb") as file_handle: file_handle.write(relay.save_param_dict(params)) - # Export configuration file to disk. + # Export the configuration file to disk. conf_file_name = "{}/config".format(directory_path) with open(conf_file_name, "w") as file_handle: file_handle.write(json.dumps(export_conf)) diff --git a/src/runtime/pipeline/pipeline_executor.h b/src/runtime/pipeline/pipeline_executor.h index 2666077e9477..3b47bb9f5414 100644 --- a/src/runtime/pipeline/pipeline_executor.h +++ b/src/runtime/pipeline/pipeline_executor.h @@ -73,9 +73,9 @@ class TVM_DLL PipelineExecutor : public ModuleNode { private: /*!\brief The class used to execute pipeline logic*/ PipelineFunction pipeline_function_; - /*!\brief Dependency information of each graph runtime module of pipeline.*/ + /*!\brief The Dependency information of each graph runtime module of pipeline.*/ PipelineConfig pipeline_configure_; - /*!\brief Module information that can get used to create graph runtime.*/ + /*!\brief The Module information that can get used to create graph runtime.*/ ModuleConfig mod_configure_; /*!\birief How many outputs are in this pipeline executor.*/ size_t num_outputs_ = 0; diff --git a/src/runtime/pipeline/pipeline_function.cc b/src/runtime/pipeline/pipeline_function.cc index 866277c40436..5571a65040fc 100644 --- a/src/runtime/pipeline/pipeline_function.cc +++ b/src/runtime/pipeline/pipeline_function.cc @@ -23,38 +23,38 @@ namespace tvm { namespace runtime { /*! - * \brief Initialize pipeline. - * \param modules List of graph runtime module. - * \param pipeline_conf Dependency relation of each graph runtime module. - * \param mod_config Config information that generate by export library function call. + * \brief Initialize the pipeline. + * \param modules The List of graph executor module. + * \param pipeline_conf The Dependency information of each graph executor module. + * \param mod_config The config information that generate by the export library function call. */ size_t PipelineFunction::PipelineInit(Array modules, const PipelineConfig& pipeline_config, const ModuleConfig& mod_config) { int num_output = pipeline_config.GetGlobalOutputNum(); - // if modules not empty just return in vector container + // If 'modules' is not empty just return in vector container if (!modules.empty()) { for (auto mod : modules) { graph_executors_.push_back(mod); } } else { - // if modules is empty, need to build the graph runtime from mod_config. + // If 'modules' is empty, need to build the graph exectuor from mod_config. graph_executors_ = PipelineCreateGraphExecutors(mod_config); } return num_output; } /*! - * \brief Use mod_config information to create a graph runtime list. - * \param mod_configure Config information that generate by export library function call. + * \brief Use the mod_config information to create a graph runtime list. + * \param mod_configure The config information that generate by export library function call. */ std::vector PipelineFunction::PipelineCreateGraphExecutors(const ModuleConfig& mod_config) { const PackedFunc* graph_executor_create = Registry::Get("tvm.graph_executor.create"); std::vector ret; ret.resize(mod_config.size()); for (auto config : mod_config) { - // load lib + // Load library. auto lib = Module::LoadFromFile(config.second["lib_name"].c_str()); - // read json + // Read json. std::ifstream ifJson(config.second["json_name"].c_str()); if (ifJson.fail()) { throw std::runtime_error("json file not found!"); @@ -62,7 +62,7 @@ std::vector PipelineFunction::PipelineCreateGraphExecutors(const ModuleC const std::string json((std::istreambuf_iterator(ifJson)), std::istreambuf_iterator()); - // create graph runtime + // Create graph executor. std::istringstream istr(config.second["dev"]); std::string str; int device_type = 1, device_id = 0; @@ -78,7 +78,7 @@ std::vector PipelineFunction::PipelineCreateGraphExecutors(const ModuleC } Module graph_module = (*graph_executor_create)(json, lib, device_type, device_id); - // load parameter + // Load parameters. TVMByteArray params_arr; std::ifstream if_param(config.second["params"].c_str()); if (if_param.fail()) { @@ -91,7 +91,7 @@ std::vector PipelineFunction::PipelineCreateGraphExecutors(const ModuleC auto load_params = graph_module.GetFunction("load_params"); load_params(params_arr); - // put into return vector + // Put a graph executor module into the vector. ret[config.first - 1] = graph_module; } return ret; diff --git a/src/runtime/pipeline/pipeline_function.h b/src/runtime/pipeline/pipeline_function.h index 8d3417765383..dbd1b584ae68 100644 --- a/src/runtime/pipeline/pipeline_function.h +++ b/src/runtime/pipeline/pipeline_function.h @@ -31,27 +31,28 @@ namespace tvm { namespace runtime { /*! - * \brief The class that executes pipeline logic is used to initialize the thread pool, + * \brief The class that executes the pipeline logic is used to initialize the thread pool, execute and schedule pipeline tasks, allocate and manage memory, etc. */ class PipelineFunction { public: /*! - * \brief Use the information of mod_config to create graph runtime list. - * \param mod_config Configuration information that generate by export library function call. + * \brief Use the information of mod_config to create graph executor list. + * \param mod_config The Configuration information that generate by the library export library + * function call. */ std::vector PipelineCreateGraphExecutors(const ModuleConfig& mod_config); /*! - * \brief Initialize pipeline. - * \param modules List of graph runtime module. - * \param pipeline_config Dependency relation of each graph runtime module. - * \param mod_config Config information that generate by export library function call. + * \brief Initialize the pipeline. + * \param modules The List of graph executor module. + * \param pipeline_config The Dependency information of each graph executor module. + * \param mod_config The config information that generate by the export library function call. */ size_t PipelineInit(Array modules, const PipelineConfig& pipeline_config, const ModuleConfig& mod_config); private: - /*!\brief List of graph executors.*/ + /*!\brief The list of graph executors.*/ std::vector graph_executors_; }; } // namespace runtime diff --git a/src/runtime/pipeline/pipeline_struct.h b/src/runtime/pipeline/pipeline_struct.h index 778d2114e7a4..637fe6bc74a1 100644 --- a/src/runtime/pipeline/pipeline_struct.h +++ b/src/runtime/pipeline/pipeline_struct.h @@ -26,8 +26,7 @@ #include #include /*! - * \brief Store the corresponding dependencies between the input of other modules - and the current outputs. + * \brief All binding information of a output interface. */ struct OutputBindings { /*!\brief Output interface binding information, 'int' is the index of the bond module. @@ -74,10 +73,10 @@ struct OutputBindings { } }; /*! - * \brief Binding information of the outputs by each module. + * \brief The binding information of all outputs of a module. */ struct OutputMap { - /*! \brief output and output binding map. */ + /*! \brief Output binding map, 'int' is output interface index.*/ std::unordered_map output_binding_map; OutputMap& operator=(const OutputMap& output) { output_binding_map = output.output_binding_map; @@ -101,7 +100,7 @@ struct OutputMap { } /*! - * \brief Create output and output binding map from JSONReader. + * \brief Create output binding map from JSONReader. * \param reader Json reader. */ void Load(dmlc::JSONReader* reader) { @@ -134,8 +133,8 @@ struct PipelineConfig { std::unordered_map config; OutputMap& operator[](const int key) { return config[key]; } /*! - * \brief Get total global outputs number. - * \return Global outputs number. + * \brief Get the total global outputs number. + * \return The global outputs number. */ size_t GetGlobalOutputNum() const { size_t num_output = 0; @@ -146,12 +145,12 @@ struct PipelineConfig { } }; /*! - * \brief Informations used to initialize the graph executor module, these - information come from export library function call. + * \brief The informations used to initialize the graph executor module, the information + * come from the export library function call. */ struct ModuleInformation { - /*\brief The first string is information type such as "lib_name",the second string is - * information detail such as "/src/lib1.so" for "lib_name". + /*\brief The first string is the information type such as "lib_name",the second string is + * the information detail such as "/src/lib1.so" for "lib_name". */ std::unordered_map info; const std::string& operator[](const std::string& key) { return info[key]; } @@ -160,6 +159,6 @@ struct ModuleInformation { return *this; } }; -/*! Module information of each module.The int is module index. */ +/*! The Module information of each module.The 'int' is module index. */ using ModuleConfig = std::unordered_map; #endif // TVM_RUNTIME_PIPELINE_PIPELINE_STRUCT_H_ diff --git a/tests/python/relay/test_pipeline_executor.py b/tests/python/relay/test_pipeline_executor.py index 49a44bae907b..e9be37207e41 100644 --- a/tests/python/relay/test_pipeline_executor.py +++ b/tests/python/relay/test_pipeline_executor.py @@ -231,7 +231,7 @@ def test_pipeline(): with tvm.transform.PassContext(opt_level=3): pipeline_mod_factory = pipeline_executor.build(pipe_config) - # Export parameter configuration to file. + # Export the parameter configuration to a file. config_file_name = pipeline_mod_factory.export_library() # Use the output of build to create and initialize PipelineModule. From 06d4338284249224bfff0b784727cde17041342f Mon Sep 17 00:00:00 2001 From: huajsj Date: Fri, 1 Oct 2021 00:28:20 -0700 Subject: [PATCH 04/23] address review comments. --- python/tvm/contrib/pipeline_executor.py | 22 +++++------ src/runtime/pipeline/pipeline_executor.cc | 4 +- src/runtime/pipeline/pipeline_executor.h | 18 ++++----- src/runtime/pipeline/pipeline_function.cc | 21 +++++----- src/runtime/pipeline/pipeline_function.h | 10 ++--- src/runtime/pipeline/pipeline_struct.h | 48 +++++++++++++---------- 6 files changed, 65 insertions(+), 58 deletions(-) diff --git a/python/tvm/contrib/pipeline_executor.py b/python/tvm/contrib/pipeline_executor.py index 4e2eed55232b..7f94bcd31c85 100644 --- a/python/tvm/contrib/pipeline_executor.py +++ b/python/tvm/contrib/pipeline_executor.py @@ -71,7 +71,8 @@ def build(pipe_configs): ) mconf["dev"] = "{},{}".format(dev.device_type, dev.device_id) - # Create a pipeline configuration. + # Create a pipeline configuration, 'mod_idx' start from 1, use 'mod_idx - 1' here + # to get correct index of 'string_config' which is a list. string_config[mod_idx - 1] = mconf libs[mod_idx] = {"lib": lib, "dev": dev} @@ -546,7 +547,7 @@ def __init__(self, pipeline_libs=None, mods_config=None): self.module = None # Only create pipeline executor when pipeline_libs, mods_config and # self.pipeline_create are not None. - if pipeline_libs and mods_config and self.pipeline_create: + if pipeline_libs and mods_config: graph_executors, config = self.graph_executor_create(pipeline_libs, mods_config) self.module = self.pipeline_create(graph_executors, config) @@ -576,7 +577,8 @@ def graph_executor_create(self, pipeline_mods, mod_config): pipeline_lib = pipeline_mods[lib_index]["lib"] dev = pipeline_mods[lib_index]["dev"] lib = graph_executor.GraphModule(pipeline_lib["default"](dev)) - # Return a module list sorted by lib_index. + # Return a module list sorted by lib_index, because lib_index start from 1, use + # 'lib_index - 1' here to get the correct index value of module in list. mods[lib_index - 1] = lib.module return mods, json.dumps(mod_config) @@ -627,25 +629,23 @@ def export_library(self, directory_path=None): conf_file_name = "{}/config".format(directory_path) with open(conf_file_name, "w") as file_handle: file_handle.write(json.dumps(export_conf)) - if not os.path.exists(conf_file_name): - raise RuntimeError(f"File {conf_file_name} export failure.") return conf_file_name - def import_from_library(self, configure_file_name): + def import_from_library(self, config_file_name): """Load configuration file to create and initialize pipeline executor. Parameters ---------- - configure_file_name : str + config_file_name : str The configuration file path, the configuration file contains the disk path of the parameter file, library file and JSON file。 """ if self.module: raise RuntimeError(f"The pipeline executor has already been initialized.") - self.pipe_configure = "" - with open(configure_file_name, "r") as file_handle: - self.pipe_configure = file_handle.read() + self.pipe_config = "" + with open(config_file_name, "r") as file_handle: + self.pipe_config = file_handle.read() - self.module = self.pipeline_create([], self.pipe_configure) + self.module = self.pipeline_create([], self.pipe_config) diff --git a/src/runtime/pipeline/pipeline_executor.cc b/src/runtime/pipeline/pipeline_executor.cc index 57775d9fa887..ec6cbe41c6c6 100644 --- a/src/runtime/pipeline/pipeline_executor.cc +++ b/src/runtime/pipeline/pipeline_executor.cc @@ -50,8 +50,8 @@ void PipelineExecutor::Init(const Array& modules, const std::string& pip dmlc::JSONReader reader(&is); this->Load(&reader); // Initialize the pipeline function class used for pipeline thread pool management - // and schedule etc. This function return the number of output. - num_outputs_ = pipeline_function_.PipelineInit(modules, pipeline_configure_, mod_configure_); + // and schedule etc. This function returns the number of output. + num_outputs_ = pipeline_function_.PipelineInit(modules, pipeline_config_, mod_config_); return; } diff --git a/src/runtime/pipeline/pipeline_executor.h b/src/runtime/pipeline/pipeline_executor.h index 3b47bb9f5414..ed214198bfb5 100644 --- a/src/runtime/pipeline/pipeline_executor.h +++ b/src/runtime/pipeline/pipeline_executor.h @@ -74,9 +74,9 @@ class TVM_DLL PipelineExecutor : public ModuleNode { /*!\brief The class used to execute pipeline logic*/ PipelineFunction pipeline_function_; /*!\brief The Dependency information of each graph runtime module of pipeline.*/ - PipelineConfig pipeline_configure_; + PipelineConfig pipeline_config_; /*!\brief The Module information that can get used to create graph runtime.*/ - ModuleConfig mod_configure_; + ModuleConfig mod_config_; /*!\birief How many outputs are in this pipeline executor.*/ size_t num_outputs_ = 0; /*!\brief Json loader.*/ @@ -116,14 +116,14 @@ class TVM_DLL PipelineExecutor : public ModuleNode { } } // Check if mod_idx is read successfully. - assert(mod_idx > 0); + ICHECK(mod_idx > 0); // Check if the output is read successfully. - assert(!output.empty()); - pipeline_configure_[mod_idx] = output; - // Check if lib, json and params are read successfully. - assert(!lib_name.empty() && !json_name.empty() && !params_name.empty()); - mod_configure_[mod_idx] = { - {"lib_name", lib_name}, {"json_name", json_name}, {"params", params_name}, {"dev", dev}}; + ICHECK(!output.Empty()); + pipeline_config_.Insert(mod_idx, output); + // Check if there is lib, json and params information. + if (!lib_name.empty() && !json_name.empty() && !params_name.empty()) { + mod_config_[mod_idx] = ModuleInformation(lib_name, json_name, params_name, dev); + } } } }; diff --git a/src/runtime/pipeline/pipeline_function.cc b/src/runtime/pipeline/pipeline_function.cc index 5571a65040fc..743d02a4322f 100644 --- a/src/runtime/pipeline/pipeline_function.cc +++ b/src/runtime/pipeline/pipeline_function.cc @@ -24,9 +24,9 @@ namespace tvm { namespace runtime { /*! * \brief Initialize the pipeline. - * \param modules The List of graph executor module. + * \param modules The list of graph executor module. * \param pipeline_conf The Dependency information of each graph executor module. - * \param mod_config The config information that generate by the export library function call. + * \param mod_config The config information that generated by the export library function call. */ size_t PipelineFunction::PipelineInit(Array modules, const PipelineConfig& pipeline_config, const ModuleConfig& mod_config) { @@ -44,7 +44,7 @@ size_t PipelineFunction::PipelineInit(Array modules, const PipelineConfi } /*! * \brief Use the mod_config information to create a graph runtime list. - * \param mod_configure The config information that generate by export library function call. + * \param mod_config The config information that generate by export library function call. */ std::vector PipelineFunction::PipelineCreateGraphExecutors(const ModuleConfig& mod_config) { const PackedFunc* graph_executor_create = Registry::Get("tvm.graph_executor.create"); @@ -52,18 +52,18 @@ std::vector PipelineFunction::PipelineCreateGraphExecutors(const ModuleC ret.resize(mod_config.size()); for (auto config : mod_config) { // Load library. - auto lib = Module::LoadFromFile(config.second["lib_name"].c_str()); + auto lib = Module::LoadFromFile(config.second.lib_name.c_str()); // Read json. - std::ifstream ifJson(config.second["json_name"].c_str()); + std::ifstream ifJson(config.second.json_name.c_str()); if (ifJson.fail()) { - throw std::runtime_error("json file not found!"); + LOG(FATAL) << "json file not found!"; } const std::string json((std::istreambuf_iterator(ifJson)), std::istreambuf_iterator()); // Create graph executor. - std::istringstream istr(config.second["dev"]); + std::istringstream istr(config.second.dev); std::string str; int device_type = 1, device_id = 0; while (getline(istr, str, ';')) { @@ -80,9 +80,9 @@ std::vector PipelineFunction::PipelineCreateGraphExecutors(const ModuleC // Load parameters. TVMByteArray params_arr; - std::ifstream if_param(config.second["params"].c_str()); + std::ifstream if_param(config.second.params_name.c_str()); if (if_param.fail()) { - throw std::runtime_error("params file not found!"); + LOG(FATAL) << "params file not found!"; } const std::string params((std::istreambuf_iterator(if_param)), std::istreambuf_iterator()); @@ -91,7 +91,8 @@ std::vector PipelineFunction::PipelineCreateGraphExecutors(const ModuleC auto load_params = graph_module.GetFunction("load_params"); load_params(params_arr); - // Put a graph executor module into the vector. + // Put a graph executor module into the vector. because 'config.first' start from 1, use + // 'config.first - 1' here to get the correct index value of module in the vector. ret[config.first - 1] = graph_module; } return ret; diff --git a/src/runtime/pipeline/pipeline_function.h b/src/runtime/pipeline/pipeline_function.h index dbd1b584ae68..98d0cab61632 100644 --- a/src/runtime/pipeline/pipeline_function.h +++ b/src/runtime/pipeline/pipeline_function.h @@ -31,22 +31,22 @@ namespace tvm { namespace runtime { /*! - * \brief The class that executes the pipeline logic is used to initialize the thread pool, + * \brief The class that executes the pipeline logic,it is used to initialize the thread pool, execute and schedule pipeline tasks, allocate and manage memory, etc. */ class PipelineFunction { public: /*! * \brief Use the information of mod_config to create graph executor list. - * \param mod_config The Configuration information that generate by the library export library + * \param mod_config The configuration information generated by the library export library * function call. */ std::vector PipelineCreateGraphExecutors(const ModuleConfig& mod_config); /*! * \brief Initialize the pipeline. - * \param modules The List of graph executor module. - * \param pipeline_config The Dependency information of each graph executor module. - * \param mod_config The config information that generate by the export library function call. + * \param modules The list of graph executor module. + * \param pipeline_config The dependency information of each graph executor module. + * \param mod_config The config information generated by the export library function call. */ size_t PipelineInit(Array modules, const PipelineConfig& pipeline_config, const ModuleConfig& mod_config); diff --git a/src/runtime/pipeline/pipeline_struct.h b/src/runtime/pipeline/pipeline_struct.h index 637fe6bc74a1..b47fd14b76e0 100644 --- a/src/runtime/pipeline/pipeline_struct.h +++ b/src/runtime/pipeline/pipeline_struct.h @@ -29,8 +29,9 @@ * \brief All binding information of a output interface. */ struct OutputBindings { - /*!\brief Output interface binding information, 'int' is the index of the bond module. - * 'string' is the interface name of the bond module. + /*!\brief Output interface binding information, 'int' is the index of the module that + * use this output data as the input interface data, 'string' is the input interface name + * of the module. */ std::unordered_map bindings; /*! @@ -46,7 +47,7 @@ struct OutputBindings { num_output += (binding.first == 0); } /* If this output is a global output then there is only one such output in map.*/ - assert(num_output <= 1); + ICHECK(num_output <= 1); return num_output == 1; } /*! @@ -59,7 +60,7 @@ struct OutputBindings { std::string key; reader->BeginObject(); std::string input_name; - int mod_idx; + int mod_idx = -1; while (reader->NextObjectItem(&key)) { if (key == "mod_idx") { reader->Read(&mod_idx); @@ -68,6 +69,7 @@ struct OutputBindings { reader->Read(&input_name); } } + ICHECK(mod_idx >= 0); bindings[mod_idx] = input_name; } } @@ -83,11 +85,11 @@ struct OutputMap { return *this; } - /*!\brief Check that OutMap is empty. - * \return True or False. + /*!\brief This function is used to verify whether OutputMap is loaded successfully. + * \return Return true to indicate that this class has not been successfully loaded. */ bool Empty() { return output_binding_map.empty(); } - /*! \brief Global output is the final outputs of pipeline, this function use to + /*! \brief Global output is the final outputs of pipeline, this function is used to * get how many global outputs are in this Outputmap * \return Number of global outputs. */ @@ -118,7 +120,7 @@ struct OutputMap { reader->Read(&binding); } } - assert(output_idx >= 0); + ICHECK(output_idx >= 0); output_binding_map[output_idx] = binding; } } @@ -127,14 +129,19 @@ struct OutputMap { * \brief Binding or dependency information of each module output interface. */ struct PipelineConfig { - /*!\brief The module index is the key, this variable record all module pipeline configuration + /*!\brief The module index is the key, this variable records all module pipeline configuration * information. */ std::unordered_map config; - OutputMap& operator[](const int key) { return config[key]; } + OutputMap& operator[](int key) { + ICHECK(config.find(key) != config.end()); + return config[key]; + } + + void Insert(int key, const OutputMap& map) { config[key] = map; } /*! - * \brief Get the total global outputs number. - * \return The global outputs number. + * \brief Get the number of global outputs that is the outputs of entire pipeline. + * \return How much output does the entire pipeline have. */ size_t GetGlobalOutputNum() const { size_t num_output = 0; @@ -149,15 +156,14 @@ struct PipelineConfig { * come from the export library function call. */ struct ModuleInformation { - /*\brief The first string is the information type such as "lib_name",the second string is - * the information detail such as "/src/lib1.so" for "lib_name". - */ - std::unordered_map info; - const std::string& operator[](const std::string& key) { return info[key]; } - ModuleInformation& operator=(const std::unordered_map& umap) { - info = umap; - return *this; - } + ModuleInformation(const std::string& lib, const std::string& json, const std::string& params, + const std::string& device) + : lib_name(lib), json_name(json), params_name(params), dev(device) {} + ModuleInformation() { ; } + std::string lib_name; + std::string json_name; + std::string params_name; + std::string dev; }; /*! The Module information of each module.The 'int' is module index. */ using ModuleConfig = std::unordered_map; From ebb29ef9f157182282b7c37c03429f4f36f9e83a Mon Sep 17 00:00:00 2001 From: huajsj Date: Tue, 5 Oct 2021 00:04:12 -0700 Subject: [PATCH 05/23] address review comments. --- python/tvm/contrib/pipeline_executor.py | 23 ++++++-------- src/runtime/pipeline/pipeline_executor.cc | 4 ++- src/runtime/pipeline/pipeline_executor.h | 31 +++++++------------ ...line_function.cc => pipeline_scheduler.cc} | 16 +++++----- ...peline_function.h => pipeline_scheduler.h} | 12 +++---- src/runtime/pipeline/pipeline_struct.h | 10 +++--- tests/python/relay/test_pipeline_executor.py | 9 ++++-- 7 files changed, 49 insertions(+), 56 deletions(-) rename src/runtime/pipeline/{pipeline_function.cc => pipeline_scheduler.cc} (85%) rename src/runtime/pipeline/{pipeline_function.h => pipeline_scheduler.h} (86%) diff --git a/python/tvm/contrib/pipeline_executor.py b/python/tvm/contrib/pipeline_executor.py index 7f94bcd31c85..96ce376eff78 100644 --- a/python/tvm/contrib/pipeline_executor.py +++ b/python/tvm/contrib/pipeline_executor.py @@ -94,7 +94,7 @@ def __init__(self, module=None): # Get the packed functions from the pipeline executor. self.load_functions() - def import_from_library(self, config_file_name): + def load_library(self, config_file_name): """Import files to create pipeline executor. Parameters @@ -103,10 +103,10 @@ def import_from_library(self, config_file_name): The configuration file path, the configuration file contains the disk path of the parameter file, library file and JSON file。 """ - # Create a empty PipelineExecutorFactoryModule. + # Create an empty PipelineExecutorFactoryModule. pipeline_factory = PipelineExecutorFactoryModule() # Load the configuration file to initialize a PipelineExecutorFactoryModule. - pipeline_factory.import_from_library(config_file_name) + pipeline_factory.load_library(config_file_name) self.module = pipeline_factory.module # Get packed functions from the pipeline executor. self.load_functions() @@ -542,12 +542,12 @@ def __init__(self, pipeline_libs=None, mods_config=None): self.pipeline_libs = pipeline_libs self.mods_config = mods_config self.pipeline_create = tvm._ffi.get_global_func( - "tvm.pipeline_executor.create", allow_missing=False + "tvm.pipeline_executor.create", allow_missing=True ) self.module = None # Only create pipeline executor when pipeline_libs, mods_config and # self.pipeline_create are not None. - if pipeline_libs and mods_config: + if pipeline_libs and mods_config and self.pipeline_create: graph_executors, config = self.graph_executor_create(pipeline_libs, mods_config) self.module = self.pipeline_create(graph_executors, config) @@ -583,7 +583,7 @@ def graph_executor_create(self, pipeline_mods, mod_config): return mods, json.dumps(mod_config) - def export_library(self, directory_path=None): + def export_library(self, directory_path): """Export the pipeline executor into disk files. Parameters @@ -594,14 +594,9 @@ def export_library(self, directory_path=None): if not self.pipeline_libs: raise RuntimeError(f"The pipeline executor has not been initialized.") - # If the directory_path is not set, use the temporary path as the file storage - # directory_path. - if not directory_path: - directory_path = tvm.contrib.utils.tempdir().temp_dir - - # If the directory does not exist, create the directory. + # Check if the directory_path exists. if not os.path.exists(directory_path): - os.makedirs(directory_path) + raise RuntimeError(f"The directory {directory_path} does not exist.") # Create a configuration copy for export. export_conf = self.mods_config.copy() # Export the library, JSON and parameter into files, then export these files path @@ -632,7 +627,7 @@ def export_library(self, directory_path=None): return conf_file_name - def import_from_library(self, config_file_name): + def load_library(self, config_file_name): """Load configuration file to create and initialize pipeline executor. Parameters diff --git a/src/runtime/pipeline/pipeline_executor.cc b/src/runtime/pipeline/pipeline_executor.cc index ec6cbe41c6c6..a8ac6c2ec194 100644 --- a/src/runtime/pipeline/pipeline_executor.cc +++ b/src/runtime/pipeline/pipeline_executor.cc @@ -35,12 +35,14 @@ PackedFunc PipelineExecutor::GetFunction(const std::string& name, return PackedFunc( [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { *rv = this->NumOutputs(); }); } else { + LOG(FATAL) << "Unknown packed function: " << name; return PackedFunc(); } return nullptr; } /*! - * \brief Initialize the pipeline executor with module array and json text. + * \brief Initialize the pipeline executor with a list of modules to be pipelined + * and config in JSON format. * \param modules The module list used for building pipeline. * \param pipeline_json The configuration of modules dependencies. */ diff --git a/src/runtime/pipeline/pipeline_executor.h b/src/runtime/pipeline/pipeline_executor.h index ed214198bfb5..71fa53a67eaa 100644 --- a/src/runtime/pipeline/pipeline_executor.h +++ b/src/runtime/pipeline/pipeline_executor.h @@ -32,7 +32,7 @@ #include #include -#include "pipeline_function.h" +#include "pipeline_scheduler.h" namespace tvm { namespace runtime { /*! @@ -71,13 +71,13 @@ class TVM_DLL PipelineExecutor : public ModuleNode { int NumOutputs() const { return num_outputs_; } private: - /*!\brief The class used to execute pipeline logic*/ - PipelineFunction pipeline_function_; + /*!\brief The class used to execute pipeline logic.*/ + PipelineScheduler pipeline_function_; /*!\brief The Dependency information of each graph runtime module of pipeline.*/ PipelineConfig pipeline_config_; - /*!\brief The Module information that can get used to create graph runtime.*/ + /*!\brief The Module information used to create graph runtime.*/ ModuleConfig mod_config_; - /*!\birief How many outputs are in this pipeline executor.*/ + /*!\brief How many outputs are in this pipeline executor.*/ size_t num_outputs_ = 0; /*!\brief Json loader.*/ void Load(dmlc::JSONReader* reader) { @@ -94,24 +94,15 @@ class TVM_DLL PipelineExecutor : public ModuleNode { while (reader->NextObjectItem(&key)) { if (key == "mod_idx") { reader->Read(&mod_idx); - } - if (key == "lib_name") { + } else if (key == "lib_name") { reader->Read(&lib_name); - } - - if (key == "json_name") { + } else if (key == "json_name") { reader->Read(&json_name); - } - - if (key == "params_name") { + } else if (key == "params_name") { reader->Read(¶ms_name); - } - - if (key == "dev") { + } else if (key == "dev") { reader->Read(&dev); - } - - if (key == "output") { + } else if (key == "output") { reader->Read(&output); } } @@ -122,7 +113,7 @@ class TVM_DLL PipelineExecutor : public ModuleNode { pipeline_config_.Insert(mod_idx, output); // Check if there is lib, json and params information. if (!lib_name.empty() && !json_name.empty() && !params_name.empty()) { - mod_config_[mod_idx] = ModuleInformation(lib_name, json_name, params_name, dev); + mod_config_[mod_idx] = GraphModuleLoadInfo(lib_name, json_name, params_name, dev); } } } diff --git a/src/runtime/pipeline/pipeline_function.cc b/src/runtime/pipeline/pipeline_scheduler.cc similarity index 85% rename from src/runtime/pipeline/pipeline_function.cc rename to src/runtime/pipeline/pipeline_scheduler.cc index 743d02a4322f..625381734d28 100644 --- a/src/runtime/pipeline/pipeline_function.cc +++ b/src/runtime/pipeline/pipeline_scheduler.cc @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -#include "pipeline_function.h" +#include "pipeline_scheduler.h" #include #include @@ -25,20 +25,20 @@ namespace runtime { /*! * \brief Initialize the pipeline. * \param modules The list of graph executor module. - * \param pipeline_conf The Dependency information of each graph executor module. - * \param mod_config The config information that generated by the export library function call. + * \param pipeline_conf The dependency information of each graph executor module. + * \param mod_config The config information generated by the export library function call. */ -size_t PipelineFunction::PipelineInit(Array modules, const PipelineConfig& pipeline_config, - const ModuleConfig& mod_config) { +size_t PipelineScheduler::PipelineInit(Array modules, const PipelineConfig& pipeline_config, + const ModuleConfig& mod_config) { int num_output = pipeline_config.GetGlobalOutputNum(); // If 'modules' is not empty just return in vector container if (!modules.empty()) { for (auto mod : modules) { - graph_executors_.push_back(mod); + graph_modules_.push_back(mod); } } else { // If 'modules' is empty, need to build the graph exectuor from mod_config. - graph_executors_ = PipelineCreateGraphExecutors(mod_config); + graph_modules_ = PipelineCreateGraphModules(mod_config); } return num_output; } @@ -46,7 +46,7 @@ size_t PipelineFunction::PipelineInit(Array modules, const PipelineConfi * \brief Use the mod_config information to create a graph runtime list. * \param mod_config The config information that generate by export library function call. */ -std::vector PipelineFunction::PipelineCreateGraphExecutors(const ModuleConfig& mod_config) { +std::vector PipelineScheduler::PipelineCreateGraphModules(const ModuleConfig& mod_config) { const PackedFunc* graph_executor_create = Registry::Get("tvm.graph_executor.create"); std::vector ret; ret.resize(mod_config.size()); diff --git a/src/runtime/pipeline/pipeline_function.h b/src/runtime/pipeline/pipeline_scheduler.h similarity index 86% rename from src/runtime/pipeline/pipeline_function.h rename to src/runtime/pipeline/pipeline_scheduler.h index 98d0cab61632..b774da275ba1 100644 --- a/src/runtime/pipeline/pipeline_function.h +++ b/src/runtime/pipeline/pipeline_scheduler.h @@ -16,8 +16,8 @@ * specific language governing permissions and limitations * under the License. */ -#ifndef TVM_RUNTIME_PIPELINE_PIPELINE_FUNCTION_H_ -#define TVM_RUNTIME_PIPELINE_PIPELINE_FUNCTION_H_ +#ifndef TVM_RUNTIME_PIPELINE_PIPELINE_SCHEDULER_H_ +#define TVM_RUNTIME_PIPELINE_PIPELINE_SCHEDULER_H_ #include #include #include @@ -34,14 +34,14 @@ namespace runtime { * \brief The class that executes the pipeline logic,it is used to initialize the thread pool, execute and schedule pipeline tasks, allocate and manage memory, etc. */ -class PipelineFunction { +class PipelineScheduler { public: /*! * \brief Use the information of mod_config to create graph executor list. * \param mod_config The configuration information generated by the library export library * function call. */ - std::vector PipelineCreateGraphExecutors(const ModuleConfig& mod_config); + std::vector PipelineCreateGraphModules(const ModuleConfig& mod_config); /*! * \brief Initialize the pipeline. * \param modules The list of graph executor module. @@ -53,8 +53,8 @@ class PipelineFunction { private: /*!\brief The list of graph executors.*/ - std::vector graph_executors_; + std::vector graph_modules_; }; } // namespace runtime } // namespace tvm -#endif // TVM_RUNTIME_PIPELINE_PIPELINE_FUNCTION_H_ +#endif // TVM_RUNTIME_PIPELINE_PIPELINE_SCHEDULER_H_ diff --git a/src/runtime/pipeline/pipeline_struct.h b/src/runtime/pipeline/pipeline_struct.h index b47fd14b76e0..df77c4725462 100644 --- a/src/runtime/pipeline/pipeline_struct.h +++ b/src/runtime/pipeline/pipeline_struct.h @@ -155,16 +155,16 @@ struct PipelineConfig { * \brief The informations used to initialize the graph executor module, the information * come from the export library function call. */ -struct ModuleInformation { - ModuleInformation(const std::string& lib, const std::string& json, const std::string& params, - const std::string& device) +struct GraphModuleLoadInfo { + GraphModuleLoadInfo(const std::string& lib, const std::string& json, const std::string& params, + const std::string& device) : lib_name(lib), json_name(json), params_name(params), dev(device) {} - ModuleInformation() { ; } + GraphModuleLoadInfo() { ; } std::string lib_name; std::string json_name; std::string params_name; std::string dev; }; /*! The Module information of each module.The 'int' is module index. */ -using ModuleConfig = std::unordered_map; +using ModuleConfig = std::unordered_map; #endif // TVM_RUNTIME_PIPELINE_PIPELINE_STRUCT_H_ diff --git a/tests/python/relay/test_pipeline_executor.py b/tests/python/relay/test_pipeline_executor.py index e9be37207e41..de80ae3d09f6 100644 --- a/tests/python/relay/test_pipeline_executor.py +++ b/tests/python/relay/test_pipeline_executor.py @@ -16,6 +16,7 @@ # under the License. import pytest +import os import numpy as np import tvm import tvm.testing @@ -232,7 +233,11 @@ def test_pipeline(): pipeline_mod_factory = pipeline_executor.build(pipe_config) # Export the parameter configuration to a file. - config_file_name = pipeline_mod_factory.export_library() + directory_path = tvm.contrib.utils.tempdir().temp_dir + # If the directory does not exist, create the directory. + if not os.path.exists(directory_path): + os.makedirs(directory_path) + config_file_name = pipeline_mod_factory.export_library(directory_path) # Use the output of build to create and initialize PipelineModule. pipeline_module = pipeline_executor.PipelineModule(pipeline_mod_factory) @@ -240,7 +245,7 @@ def test_pipeline(): # Use the import function to create and initialize PipelineModule. pipeline_module_test = pipeline_executor.PipelineModule() - pipeline_module_test.import_from_library(config_file_name) + pipeline_module_test.load_library(config_file_name) assert pipeline_module_test.get_num_outputs() == 2 From ab3fbc779a31aebe91a4ff406fcf9597553b013e Mon Sep 17 00:00:00 2001 From: huajsj Date: Tue, 5 Oct 2021 00:36:31 -0700 Subject: [PATCH 06/23] Change mod_idx start from 0, remove mod_idx - 1 logic. --- python/tvm/contrib/pipeline_executor.py | 9 ++++----- src/runtime/pipeline/pipeline_executor.h | 4 ++-- src/runtime/pipeline/pipeline_scheduler.cc | 5 ++--- src/runtime/pipeline/pipeline_struct.h | 12 ++++++++---- tests/python/relay/test_pipeline_executor.py | 16 ++++++++-------- 5 files changed, 24 insertions(+), 22 deletions(-) diff --git a/python/tvm/contrib/pipeline_executor.py b/python/tvm/contrib/pipeline_executor.py index 96ce376eff78..4ecbc09b1692 100644 --- a/python/tvm/contrib/pipeline_executor.py +++ b/python/tvm/contrib/pipeline_executor.py @@ -71,9 +71,8 @@ def build(pipe_configs): ) mconf["dev"] = "{},{}".format(dev.device_type, dev.device_id) - # Create a pipeline configuration, 'mod_idx' start from 1, use 'mod_idx - 1' here - # to get correct index of 'string_config' which is a list. - string_config[mod_idx - 1] = mconf + # Create a pipeline configuration, 'mod_idx' start from 0. + string_config[mod_idx] = mconf libs[mod_idx] = {"lib": lib, "dev": dev} return PipelineExecutorFactoryModule(libs, string_config) @@ -176,7 +175,7 @@ def get_owner_idx(self): if isinstance(self.io_owner, PipelineConfig.ModuleWrapper): return self.io_owner.idx - return 0 + return -1 def is_global_interface(self): """The global interface is the interface visible to the caller which use a pipeline @@ -509,7 +508,7 @@ def dag_topology_sort(self): mlist += temp_list for mod, i in zip(mlist, range(len(mlist))): - self.mod_wrapper[mod].set_idx_name(i + 1) + self.mod_wrapper[mod].set_idx_name(i) def get_mod_idx(self, mod): # Return the module index. diff --git a/src/runtime/pipeline/pipeline_executor.h b/src/runtime/pipeline/pipeline_executor.h index 71fa53a67eaa..d4bbb3c8042d 100644 --- a/src/runtime/pipeline/pipeline_executor.h +++ b/src/runtime/pipeline/pipeline_executor.h @@ -85,7 +85,7 @@ class TVM_DLL PipelineExecutor : public ModuleNode { while (reader->NextArrayItem()) { std::string key; reader->BeginObject(); - int mod_idx = 0; + int mod_idx = -1; std::string lib_name; std::string json_name; std::string params_name; @@ -107,7 +107,7 @@ class TVM_DLL PipelineExecutor : public ModuleNode { } } // Check if mod_idx is read successfully. - ICHECK(mod_idx > 0); + ICHECK(mod_idx >= 0); // Check if the output is read successfully. ICHECK(!output.Empty()); pipeline_config_.Insert(mod_idx, output); diff --git a/src/runtime/pipeline/pipeline_scheduler.cc b/src/runtime/pipeline/pipeline_scheduler.cc index 625381734d28..b20d9a9d4ecd 100644 --- a/src/runtime/pipeline/pipeline_scheduler.cc +++ b/src/runtime/pipeline/pipeline_scheduler.cc @@ -91,9 +91,8 @@ std::vector PipelineScheduler::PipelineCreateGraphModules(const ModuleCo auto load_params = graph_module.GetFunction("load_params"); load_params(params_arr); - // Put a graph executor module into the vector. because 'config.first' start from 1, use - // 'config.first - 1' here to get the correct index value of module in the vector. - ret[config.first - 1] = graph_module; + // Put a graph executor module into the vector. + ret[config.first] = graph_module; } return ret; } diff --git a/src/runtime/pipeline/pipeline_struct.h b/src/runtime/pipeline/pipeline_struct.h index df77c4725462..d7860c963f38 100644 --- a/src/runtime/pipeline/pipeline_struct.h +++ b/src/runtime/pipeline/pipeline_struct.h @@ -22,9 +22,11 @@ #include #include +#include #include #include #include +#define GLOBAL_MODULE_INDEX -1 /*! * \brief All binding information of a output interface. */ @@ -42,9 +44,9 @@ struct OutputBindings { bool IsGlobalOutput() const { int num_output = 0; for (auto binding : bindings) { - /* output is global output when value is 0. + /* output is global output when value is GLOBAL_MODULE_INDEX. */ - num_output += (binding.first == 0); + num_output += (binding.first == GLOBAL_MODULE_INDEX); } /* If this output is a global output then there is only one such output in map.*/ ICHECK(num_output <= 1); @@ -60,7 +62,7 @@ struct OutputBindings { std::string key; reader->BeginObject(); std::string input_name; - int mod_idx = -1; + int mod_idx = std::numeric_limits::min(); while (reader->NextObjectItem(&key)) { if (key == "mod_idx") { reader->Read(&mod_idx); @@ -69,7 +71,9 @@ struct OutputBindings { reader->Read(&input_name); } } - ICHECK(mod_idx >= 0); + // mod_idx == -1 means this module is global module, then the input or output + // is the whole pipeline input or output. + ICHECK(mod_idx >= -1); bindings[mod_idx] = input_name; } } diff --git a/tests/python/relay/test_pipeline_executor.py b/tests/python/relay/test_pipeline_executor.py index de80ae3d09f6..a0244195ac1c 100644 --- a/tests/python/relay/test_pipeline_executor.py +++ b/tests/python/relay/test_pipeline_executor.py @@ -77,11 +77,11 @@ def get_manual_conf(mods, target): # The third output is the final output, the second output is for mod3, the first output # is for mod2 input. pipe_config1 = { - "mod_idx": 1, + "mod_idx": 0, "output": [ - {"output_idx": 0, "dependent": [{"mod_idx": 2, "input_name": "data_0"}]}, - {"output_idx": 1, "dependent": [{"mod_idx": 3, "input_name": "data_0"}]}, - {"output_idx": 2, "dependent": [{"mod_idx": 0, "input_name": "0"}]}, + {"output_idx": 0, "dependent": [{"mod_idx": 1, "input_name": "data_0"}]}, + {"output_idx": 1, "dependent": [{"mod_idx": 2, "input_name": "data_0"}]}, + {"output_idx": 2, "dependent": [{"mod_idx": -1, "input_name": "0"}]}, ], } mod_config[mods[0]] = { @@ -95,9 +95,9 @@ def get_manual_conf(mods, target): } pipe_config2 = { - "mod_idx": 2, + "mod_idx": 1, "output": [ - {"output_idx": 0, "dependent": [{"mod_idx": 3, "input_name": "data_1"}]}, + {"output_idx": 0, "dependent": [{"mod_idx": 2, "input_name": "data_1"}]}, ], } mod_config[mods[1]] = { @@ -111,8 +111,8 @@ def get_manual_conf(mods, target): } pipe_config3 = { - "mod_idx": 3, - "output": [{"output_idx": 0, "dependent": [{"mod_idx": 0, "input_name": "1"}]}], + "mod_idx": 2, + "output": [{"output_idx": 0, "dependent": [{"mod_idx": -1, "input_name": "1"}]}], } mod_config[mods[2]] = { "pipeline": pipe_config3, From 4dc4832bc80a54056e65ffe2eaffbbff60dcdf30 Mon Sep 17 00:00:00 2001 From: huajsj Date: Tue, 5 Oct 2021 11:50:32 -0700 Subject: [PATCH 07/23] address review comments. --- python/tvm/contrib/pipeline_executor.py | 33 ++++++++++---------- src/runtime/pipeline/pipeline_executor.h | 4 +-- src/runtime/pipeline/pipeline_struct.h | 12 +++---- tests/python/relay/test_pipeline_executor.py | 12 ++++--- 4 files changed, 32 insertions(+), 29 deletions(-) diff --git a/python/tvm/contrib/pipeline_executor.py b/python/tvm/contrib/pipeline_executor.py index 4ecbc09b1692..ed29bba383f4 100644 --- a/python/tvm/contrib/pipeline_executor.py +++ b/python/tvm/contrib/pipeline_executor.py @@ -114,7 +114,8 @@ def load_functions(self): # Get functions from the pipeline executor. self._get_num_outputs = self.module["get_num_outputs"] if self.module else None - def get_num_outputs(self): + @property + def num_outputs(self): """Get the number of outputs. Returns ------- @@ -177,10 +178,10 @@ def get_owner_idx(self): return -1 - def is_global_interface(self): - """The global interface is the interface visible to the caller which use a pipeline - executor, the global input interface is responsible for passing parameters to the - internal module interface, and the global output interface is responsible for + def is_pipeline_executor_interface(self): + """The pipeline interface is the interface visible to the caller which use a pipeline + executor, the pipeline input interface is responsible for passing parameters to the + internal module interface, and the pipeline output interface is responsible for outputting the results computed by the pipeline executor to a caller. """ return not isinstance(self.io_owner, PipelineConfig.ModuleWrapper) @@ -218,8 +219,8 @@ def check_dag_acyclic(self, start, inputs): def connect(self, binding): """Connect the current interface to the destination interface. - Correct connections are as follows: 1. global input connected to module input, - 2. module output connected to global output, 3. module output connected to + Correct connections are as follows: 1. pipeline input connected to module input, + 2. module output connected to pipline output, 3. module output connected to module input. Parameters @@ -232,31 +233,31 @@ def connect(self, binding): if self.io_owner == binding.io_owner: raise RuntimeError(f"Can not bind itself.") - if not self.is_global_interface() and self.io_type == "input": + if not self.is_pipeline_executor_interface() and self.io_type == "input": raise RuntimeError(f"Module can only bind from output interface!") if ( - not self.is_global_interface() - and not binding.is_global_interface() + not self.is_pipeline_executor_interface() + and not binding.is_pipeline_executor_interface() and binding.io_type == "output" ): raise RuntimeError(f"Can not bind module output with another module output!") if ( - not self.is_global_interface() - and binding.is_global_interface() + not self.is_pipeline_executor_interface() + and binding.is_pipeline_executor_interface() and binding.io_type == "input" ): - raise RuntimeError(f"Can not bind module output with global input!") + raise RuntimeError(f"Can not bind module output with pipeline input!") - if self.is_global_interface() and self.io_type == "output": + if self.is_pipeline_executor_interface() and self.io_type == "output": raise RuntimeError(f"Global output can not be used as binding start point.") - if self.is_global_interface() and binding.io_type != "input": + if self.is_pipeline_executor_interface() and binding.io_type != "input": raise RuntimeError(f"Global input can only bind with module input.") self.bindings.append(binding) - if not self.is_global_interface(): + if not self.is_pipeline_executor_interface(): # Check whether the data types of the source and destination are the same. if ( isinstance(binding.io_owner, PipelineConfig.ModuleWrapper) diff --git a/src/runtime/pipeline/pipeline_executor.h b/src/runtime/pipeline/pipeline_executor.h index d4bbb3c8042d..ab55205a3917 100644 --- a/src/runtime/pipeline/pipeline_executor.h +++ b/src/runtime/pipeline/pipeline_executor.h @@ -107,9 +107,9 @@ class TVM_DLL PipelineExecutor : public ModuleNode { } } // Check if mod_idx is read successfully. - ICHECK(mod_idx >= 0); + ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx; // Check if the output is read successfully. - ICHECK(!output.Empty()); + ICHECK(!output.Empty()) << "Invalid output binding result."; pipeline_config_.Insert(mod_idx, output); // Check if there is lib, json and params information. if (!lib_name.empty() && !json_name.empty() && !params_name.empty()) { diff --git a/src/runtime/pipeline/pipeline_struct.h b/src/runtime/pipeline/pipeline_struct.h index d7860c963f38..8f9a7d45e0fb 100644 --- a/src/runtime/pipeline/pipeline_struct.h +++ b/src/runtime/pipeline/pipeline_struct.h @@ -26,7 +26,7 @@ #include #include #include -#define GLOBAL_MODULE_INDEX -1 +#define PIPELINE_EXECUTOR_INDEX -1 /*! * \brief All binding information of a output interface. */ @@ -37,16 +37,16 @@ struct OutputBindings { */ std::unordered_map bindings; /*! - * \brief If there is one global binding in bindings, then current output is - * global interface. - * \return Whether this output interface is global output interface. + * \brief If there is one PipelineExecutor binding in bindings, then current output is + * PipelineExecutor interface. + * \return Whether this output interface is PipelineExecutor output interface. */ bool IsGlobalOutput() const { int num_output = 0; for (auto binding : bindings) { - /* output is global output when value is GLOBAL_MODULE_INDEX. + /* output is PipelineExecutor output when value is PIPELINE_EXECUTOR_INDEX. */ - num_output += (binding.first == GLOBAL_MODULE_INDEX); + num_output += (binding.first == PIPELINE_EXECUTOR_INDEX); } /* If this output is a global output then there is only one such output in map.*/ ICHECK(num_output <= 1); diff --git a/tests/python/relay/test_pipeline_executor.py b/tests/python/relay/test_pipeline_executor.py index a0244195ac1c..9e52fd737f72 100644 --- a/tests/python/relay/test_pipeline_executor.py +++ b/tests/python/relay/test_pipeline_executor.py @@ -180,10 +180,12 @@ def test_pipeline(): pipe_config = pipeline_executor.PipelineConfig() - # The global input named "data_0" will be connected to a input named "data_0" of mod1. + # The pipeline input named "data_0" will be connected to a input named "data_0" + # of mod1. pipe_config["input"]["data_0"].connect(pipe_config[mod1]["input"]["data_0"]) - # The global Input named "data_1" will be connected to a input named "data_1" of mod2. + # The pipeline Input named "data_1" will be connected to a input named "data_1" + # of mod2. pipe_config["input"]["data_1"].connect(pipe_config[mod2]["input"]["data_1"]) # The mod1 output[0] will be connected to a input named "data_0" of mod2. @@ -195,10 +197,10 @@ def test_pipeline(): # The mod2 output[2] will be connected to a input named "data_1" of mod3. pipe_config[mod2]["output"][0].connect(pipe_config[mod3]["input"]["data_1"]) - # The mod1 output[2] will be connected to global output[1]. + # The mod1 output[2] will be connected to pipeline output[0]. pipe_config[mod1]["output"][2].connect(pipe_config["output"]["0"]) - # The mod3 output[0] will be connected to global output[2]. + # The mod3 output[0] will be connected to pipeline output[1]. pipe_config[mod3]["output"][0].connect(pipe_config["output"]["1"]) # Print configueration (print(pipe_config)), the result looks like following. # @@ -246,7 +248,7 @@ def test_pipeline(): # Use the import function to create and initialize PipelineModule. pipeline_module_test = pipeline_executor.PipelineModule() pipeline_module_test.load_library(config_file_name) - assert pipeline_module_test.get_num_outputs() == 2 + assert pipeline_module_test.num_outputs == 2 if __name__ == "__main__": From 8bb71c545224ee2961208c7aa4a741aac94c55cd Mon Sep 17 00:00:00 2001 From: huajsj Date: Tue, 5 Oct 2021 14:19:34 -0700 Subject: [PATCH 08/23] polish documents. --- python/tvm/contrib/pipeline_executor.py | 41 +++++++++++----------- src/runtime/pipeline/pipeline_executor.cc | 4 +-- src/runtime/pipeline/pipeline_executor.h | 4 +-- src/runtime/pipeline/pipeline_scheduler.cc | 10 +++--- src/runtime/pipeline/pipeline_scheduler.h | 2 +- src/runtime/pipeline/pipeline_struct.h | 26 +++++++------- 6 files changed, 43 insertions(+), 44 deletions(-) diff --git a/python/tvm/contrib/pipeline_executor.py b/python/tvm/contrib/pipeline_executor.py index ed29bba383f4..dc5098838d00 100644 --- a/python/tvm/contrib/pipeline_executor.py +++ b/python/tvm/contrib/pipeline_executor.py @@ -94,13 +94,13 @@ def __init__(self, module=None): self.load_functions() def load_library(self, config_file_name): - """Import files to create pipeline executor. + """Import files to create a pipeline executor. Parameters ---------- config_file_name : str - The configuration file path, the configuration file contains the - disk path of the parameter file, library file and JSON file。 + Path and name of the configuration file, the configuration file contains the + disk path of the parameter file, library file, and JSON file. """ # Create an empty PipelineExecutorFactoryModule. pipeline_factory = PipelineExecutorFactoryModule() @@ -123,7 +123,7 @@ def num_outputs(self): The number of outputs. """ if not self._get_num_outputs: - raise RuntimeError(f"The pipeline executor has not been initialized.") + raise RuntimeError(f"The pipeline executor might not have been initialized.") return self._get_num_outputs() @@ -179,7 +179,7 @@ def get_owner_idx(self): return -1 def is_pipeline_executor_interface(self): - """The pipeline interface is the interface visible to the caller which use a pipeline + """The pipeline interface is the interface visible to the caller uses a pipeline executor, the pipeline input interface is responsible for passing parameters to the internal module interface, and the pipeline output interface is responsible for outputting the results computed by the pipeline executor to a caller. @@ -219,9 +219,9 @@ def check_dag_acyclic(self, start, inputs): def connect(self, binding): """Connect the current interface to the destination interface. - Correct connections are as follows: 1. pipeline input connected to module input, - 2. module output connected to pipline output, 3. module output connected to - module input. + Correct connections are as follows: 1. the pipeline input connected to a module input, + 2. the module output connected to a pipeline output, 3. the module output connected to + a module input. Parameters ---------- @@ -545,8 +545,8 @@ def __init__(self, pipeline_libs=None, mods_config=None): "tvm.pipeline_executor.create", allow_missing=True ) self.module = None - # Only create pipeline executor when pipeline_libs, mods_config and - # self.pipeline_create are not None. + # Only create the pipeline executor when the value of pipeline_libs, mods_config, and + # self.pipeline_create is valid. if pipeline_libs and mods_config and self.pipeline_create: graph_executors, config = self.graph_executor_create(pipeline_libs, mods_config) self.module = self.pipeline_create(graph_executors, config) @@ -571,15 +571,14 @@ def graph_executor_create(self, pipeline_mods, mod_config): The Modudle configuration. """ - # Modules need to be stored in the list named 'mods' in index order. + # Should store modules in the list named 'mods' in index order. mods = [None for _ in range(len(pipeline_mods))] for lib_index in pipeline_mods: pipeline_lib = pipeline_mods[lib_index]["lib"] dev = pipeline_mods[lib_index]["dev"] lib = graph_executor.GraphModule(pipeline_lib["default"](dev)) - # Return a module list sorted by lib_index, because lib_index start from 1, use - # 'lib_index - 1' here to get the correct index value of module in list. - mods[lib_index - 1] = lib.module + # Return a module list sorted by lib_index. + mods[lib_index] = lib.module return mods, json.dumps(mod_config) @@ -589,7 +588,7 @@ def export_library(self, directory_path): Parameters ---------- directory_path : str - The directory to which these files are exported. + Export the files to this directory. """ if not self.pipeline_libs: raise RuntimeError(f"The pipeline executor has not been initialized.") @@ -599,10 +598,10 @@ def export_library(self, directory_path): raise RuntimeError(f"The directory {directory_path} does not exist.") # Create a configuration copy for export. export_conf = self.mods_config.copy() - # Export the library, JSON and parameter into files, then export these files path - # into a configuraton file. + # Export the library, JSON, and parameter into files, then export these files path + # into a configuration file. for lib_index in self.pipeline_libs: - mconf = export_conf[lib_index - 1] + mconf = export_conf[lib_index] mconf["lib_name"] = "{}/lib{}.so".format(directory_path, lib_index) mconf["json_name"] = "{}/json{}".format(directory_path, lib_index) mconf["params_name"] = "{}/params{}".format(directory_path, lib_index) @@ -611,9 +610,9 @@ def export_library(self, directory_path): self.pipeline_libs[lib_index]["dev"].device_id, ) - # Get the graph, lib and parameters from GraphExecutorFactoryModule. + # Get the graph, lib, and parameters from GraphExecutorFactoryModule. graph, lib, params = self.pipeline_libs[lib_index]["lib"] - # Export the lib, graph and parameters to disk. + # Export the lib, graph, and parameters to disk. lib.export_library(mconf["lib_name"]) with open(mconf["json_name"], "w") as file_handle: file_handle.write(graph) @@ -634,7 +633,7 @@ def load_library(self, config_file_name): ---------- config_file_name : str The configuration file path, the configuration file contains the - disk path of the parameter file, library file and JSON file。 + disk path of the parameter file, library file, and JSON file. """ if self.module: raise RuntimeError(f"The pipeline executor has already been initialized.") diff --git a/src/runtime/pipeline/pipeline_executor.cc b/src/runtime/pipeline/pipeline_executor.cc index a8ac6c2ec194..3da2fefe0a77 100644 --- a/src/runtime/pipeline/pipeline_executor.cc +++ b/src/runtime/pipeline/pipeline_executor.cc @@ -43,11 +43,11 @@ PackedFunc PipelineExecutor::GetFunction(const std::string& name, /*! * \brief Initialize the pipeline executor with a list of modules to be pipelined * and config in JSON format. - * \param modules The module list used for building pipeline. + * \param modules The module list used for building the pipeline. * \param pipeline_json The configuration of modules dependencies. */ void PipelineExecutor::Init(const Array& modules, const std::string& pipeline_json) { - // Use JSONReader to load pipeline configuration from file. + // Use JSONReader to load pipeline configuration. std::istringstream is(pipeline_json); dmlc::JSONReader reader(&is); this->Load(&reader); diff --git a/src/runtime/pipeline/pipeline_executor.h b/src/runtime/pipeline/pipeline_executor.h index ab55205a3917..e9019810fb79 100644 --- a/src/runtime/pipeline/pipeline_executor.h +++ b/src/runtime/pipeline/pipeline_executor.h @@ -50,7 +50,7 @@ class TVM_DLL PipelineExecutor : public ModuleNode { */ const char* type_key() const final { return "PipelineExecutor"; } /*! - * \brief Initialize the pipeline executor with module array and json text. + * \brief Initialize the pipeline executor with module array and JSON text. * \param modules The module list used for building pipeline. * \param pipeline_json The configuration of modules dependencies. */ @@ -73,7 +73,7 @@ class TVM_DLL PipelineExecutor : public ModuleNode { private: /*!\brief The class used to execute pipeline logic.*/ PipelineScheduler pipeline_function_; - /*!\brief The Dependency information of each graph runtime module of pipeline.*/ + /*!\brief The Dependency information of each graph runtime module of the pipeline.*/ PipelineConfig pipeline_config_; /*!\brief The Module information used to create graph runtime.*/ ModuleConfig mod_config_; diff --git a/src/runtime/pipeline/pipeline_scheduler.cc b/src/runtime/pipeline/pipeline_scheduler.cc index b20d9a9d4ecd..67ac46c796bd 100644 --- a/src/runtime/pipeline/pipeline_scheduler.cc +++ b/src/runtime/pipeline/pipeline_scheduler.cc @@ -24,9 +24,9 @@ namespace tvm { namespace runtime { /*! * \brief Initialize the pipeline. - * \param modules The list of graph executor module. + * \param modules The list of graph executor modules. * \param pipeline_conf The dependency information of each graph executor module. - * \param mod_config The config information generated by the export library function call. + * \param mod_config The config is generated by the export library function call. */ size_t PipelineScheduler::PipelineInit(Array modules, const PipelineConfig& pipeline_config, const ModuleConfig& mod_config) { @@ -37,14 +37,14 @@ size_t PipelineScheduler::PipelineInit(Array modules, const PipelineConf graph_modules_.push_back(mod); } } else { - // If 'modules' is empty, need to build the graph exectuor from mod_config. + // If the value of 'modules' is empty, need to build the graph exectuor from mod_config. graph_modules_ = PipelineCreateGraphModules(mod_config); } return num_output; } /*! * \brief Use the mod_config information to create a graph runtime list. - * \param mod_config The config information that generate by export library function call. + * \param mod_config The config information that generates by the export library function call. */ std::vector PipelineScheduler::PipelineCreateGraphModules(const ModuleConfig& mod_config) { const PackedFunc* graph_executor_create = Registry::Get("tvm.graph_executor.create"); @@ -62,7 +62,7 @@ std::vector PipelineScheduler::PipelineCreateGraphModules(const ModuleCo const std::string json((std::istreambuf_iterator(ifJson)), std::istreambuf_iterator()); - // Create graph executor. + // Create a graph executor. std::istringstream istr(config.second.dev); std::string str; int device_type = 1, device_id = 0; diff --git a/src/runtime/pipeline/pipeline_scheduler.h b/src/runtime/pipeline/pipeline_scheduler.h index b774da275ba1..fd88cb8e2afb 100644 --- a/src/runtime/pipeline/pipeline_scheduler.h +++ b/src/runtime/pipeline/pipeline_scheduler.h @@ -37,7 +37,7 @@ namespace runtime { class PipelineScheduler { public: /*! - * \brief Use the information of mod_config to create graph executor list. + * \brief Use the information of mod_config to create a graph executor list. * \param mod_config The configuration information generated by the library export library * function call. */ diff --git a/src/runtime/pipeline/pipeline_struct.h b/src/runtime/pipeline/pipeline_struct.h index 8f9a7d45e0fb..ff4b92a6fba3 100644 --- a/src/runtime/pipeline/pipeline_struct.h +++ b/src/runtime/pipeline/pipeline_struct.h @@ -32,19 +32,20 @@ */ struct OutputBindings { /*!\brief Output interface binding information, 'int' is the index of the module that - * use this output data as the input interface data, 'string' is the input interface name + * uses this output data as the input interface data, 'string' is the input interface name * of the module. */ std::unordered_map bindings; /*! - * \brief If there is one PipelineExecutor binding in bindings, then current output is + * \brief If there is one PipelineExecutor binding in bindings, then the current output is * PipelineExecutor interface. * \return Whether this output interface is PipelineExecutor output interface. */ bool IsGlobalOutput() const { int num_output = 0; for (auto binding : bindings) { - /* output is PipelineExecutor output when value is PIPELINE_EXECUTOR_INDEX. + /* The output is a PipelineExecutor output when the index value + * equal PIPELINE_EXECUTOR_INDEX. */ num_output += (binding.first == PIPELINE_EXECUTOR_INDEX); } @@ -53,8 +54,8 @@ struct OutputBindings { return num_output == 1; } /*! - * \brief Create module interface map from JSONReader. - * \param reader Json reader. + * \brief Create a module interface map from JSONReader. + * \param reader JSON reader. */ void Load(dmlc::JSONReader* reader) { reader->BeginArray(); @@ -71,9 +72,8 @@ struct OutputBindings { reader->Read(&input_name); } } - // mod_idx == -1 means this module is global module, then the input or output - // is the whole pipeline input or output. - ICHECK(mod_idx >= -1); + // The value of 'mod_idx' should larger than PIPELINE_EXECUTOR_INDEX. + ICHECK(mod_idx >= PIPELINE_EXECUTOR_INDEX); bindings[mod_idx] = input_name; } } @@ -93,9 +93,9 @@ struct OutputMap { * \return Return true to indicate that this class has not been successfully loaded. */ bool Empty() { return output_binding_map.empty(); } - /*! \brief Global output is the final outputs of pipeline, this function is used to - * get how many global outputs are in this Outputmap - * \return Number of global outputs. + /*! \brief The pipeline outputs is the final outputs of pipeline, this function is used to + * get how many pipeline outputs are in this Outputmap + * \return Number of pipeline outputs. */ size_t GetGlobalOutputNum(void) const { size_t num_output = 0; @@ -106,7 +106,7 @@ struct OutputMap { } /*! - * \brief Create output binding map from JSONReader. + * \brief Create a output binding map from JSONReader. * \param reader Json reader. */ void Load(dmlc::JSONReader* reader) { @@ -130,7 +130,7 @@ struct OutputMap { } }; /*! - * \brief Binding or dependency information of each module output interface. + * \brief The binding or dependency information of each module output interface. */ struct PipelineConfig { /*!\brief The module index is the key, this variable records all module pipeline configuration From d5e1f61a6c194d4bed9a7984ef4289b57c832bc9 Mon Sep 17 00:00:00 2001 From: huajsj Date: Tue, 5 Oct 2021 23:12:20 -0700 Subject: [PATCH 09/23] adress review comments --- python/tvm/contrib/pipeline_executor.py | 61 +++++++------------- src/runtime/pipeline/pipeline_executor.cc | 3 +- src/runtime/pipeline/pipeline_executor.h | 11 +++- tests/python/relay/test_pipeline_executor.py | 3 +- 4 files changed, 33 insertions(+), 45 deletions(-) diff --git a/python/tvm/contrib/pipeline_executor.py b/python/tvm/contrib/pipeline_executor.py index dc5098838d00..39cb54296d70 100644 --- a/python/tvm/contrib/pipeline_executor.py +++ b/python/tvm/contrib/pipeline_executor.py @@ -87,13 +87,13 @@ class PipelineModule(object): Common interface for pipeline executor factory modules. """ - def __init__(self, module=None): - self.module = module.module if module else None - self._get_num_outputs = None + def __init__(self, module): + self.module = module.module # Get the packed functions from the pipeline executor. - self.load_functions() + self._get_num_outputs = self.module["get_num_outputs"] - def load_library(self, config_file_name): + @staticmethod + def load_library(config_file_name): """Import files to create a pipeline executor. Parameters @@ -102,17 +102,9 @@ def load_library(self, config_file_name): Path and name of the configuration file, the configuration file contains the disk path of the parameter file, library file, and JSON file. """ - # Create an empty PipelineExecutorFactoryModule. - pipeline_factory = PipelineExecutorFactoryModule() # Load the configuration file to initialize a PipelineExecutorFactoryModule. - pipeline_factory.load_library(config_file_name) - self.module = pipeline_factory.module - # Get packed functions from the pipeline executor. - self.load_functions() - - def load_functions(self): - # Get functions from the pipeline executor. - self._get_num_outputs = self.module["get_num_outputs"] if self.module else None + pipeline_factory = PipelineExecutorFactoryModule.load_library(config_file_name) + return PipelineModule(pipeline_factory) @property def num_outputs(self): @@ -122,8 +114,6 @@ def num_outputs(self): count : int The number of outputs. """ - if not self._get_num_outputs: - raise RuntimeError(f"The pipeline executor might not have been initialized.") return self._get_num_outputs() @@ -538,18 +528,14 @@ class PipelineExecutorFactoryModule(object): """ - def __init__(self, pipeline_libs=None, mods_config=None): - self.pipeline_libs = pipeline_libs + def __init__(self, pipeline_mods, mods_config): + self.pipeline_mods = pipeline_mods self.mods_config = mods_config + graph_executors, config = self.graph_executor_create(pipeline_mods, mods_config) self.pipeline_create = tvm._ffi.get_global_func( - "tvm.pipeline_executor.create", allow_missing=True + "tvm.pipeline_executor.create", allow_missing=False ) - self.module = None - # Only create the pipeline executor when the value of pipeline_libs, mods_config, and - # self.pipeline_create is valid. - if pipeline_libs and mods_config and self.pipeline_create: - graph_executors, config = self.graph_executor_create(pipeline_libs, mods_config) - self.module = self.pipeline_create(graph_executors, config) + self.module = self.pipeline_create(graph_executors, config) def graph_executor_create(self, pipeline_mods, mod_config): """Create graph_executor list and return configuration as a json string. @@ -570,7 +556,6 @@ def graph_executor_create(self, pipeline_mods, mod_config): mod_config : str The Modudle configuration. """ - # Should store modules in the list named 'mods' in index order. mods = [None for _ in range(len(pipeline_mods))] for lib_index in pipeline_mods: @@ -590,7 +575,7 @@ def export_library(self, directory_path): directory_path : str Export the files to this directory. """ - if not self.pipeline_libs: + if not self.pipeline_mods: raise RuntimeError(f"The pipeline executor has not been initialized.") # Check if the directory_path exists. @@ -600,18 +585,18 @@ def export_library(self, directory_path): export_conf = self.mods_config.copy() # Export the library, JSON, and parameter into files, then export these files path # into a configuration file. - for lib_index in self.pipeline_libs: + for lib_index in self.pipeline_mods: mconf = export_conf[lib_index] mconf["lib_name"] = "{}/lib{}.so".format(directory_path, lib_index) mconf["json_name"] = "{}/json{}".format(directory_path, lib_index) mconf["params_name"] = "{}/params{}".format(directory_path, lib_index) mconf["dev"] = "{},{}".format( - self.pipeline_libs[lib_index]["dev"].device_type, - self.pipeline_libs[lib_index]["dev"].device_id, + self.pipeline_mods[lib_index]["dev"].device_type, + self.pipeline_mods[lib_index]["dev"].device_id, ) # Get the graph, lib, and parameters from GraphExecutorFactoryModule. - graph, lib, params = self.pipeline_libs[lib_index]["lib"] + graph, lib, params = self.pipeline_mods[lib_index]["lib"] # Export the lib, graph, and parameters to disk. lib.export_library(mconf["lib_name"]) with open(mconf["json_name"], "w") as file_handle: @@ -626,7 +611,8 @@ def export_library(self, directory_path): return conf_file_name - def load_library(self, config_file_name): + @staticmethod + def load_library(config_file_name): """Load configuration file to create and initialize pipeline executor. Parameters @@ -635,11 +621,8 @@ def load_library(self, config_file_name): The configuration file path, the configuration file contains the disk path of the parameter file, library file, and JSON file. """ - if self.module: - raise RuntimeError(f"The pipeline executor has already been initialized.") - - self.pipe_config = "" + load_config = "" with open(config_file_name, "r") as file_handle: - self.pipe_config = file_handle.read() + load_config = file_handle.read() - self.module = self.pipeline_create([], self.pipe_config) + return PipelineExecutorFactoryModule([], json.loads(load_config)) diff --git a/src/runtime/pipeline/pipeline_executor.cc b/src/runtime/pipeline/pipeline_executor.cc index 3da2fefe0a77..0a456f6fe332 100644 --- a/src/runtime/pipeline/pipeline_executor.cc +++ b/src/runtime/pipeline/pipeline_executor.cc @@ -50,7 +50,8 @@ void PipelineExecutor::Init(const Array& modules, const std::string& pip // Use JSONReader to load pipeline configuration. std::istringstream is(pipeline_json); dmlc::JSONReader reader(&is); - this->Load(&reader); + // When the value of 'modules' is empty, here need to load the modules from JSON. + this->Load(&reader, modules.empty()); // Initialize the pipeline function class used for pipeline thread pool management // and schedule etc. This function returns the number of output. num_outputs_ = pipeline_function_.PipelineInit(modules, pipeline_config_, mod_config_); diff --git a/src/runtime/pipeline/pipeline_executor.h b/src/runtime/pipeline/pipeline_executor.h index e9019810fb79..5327aef3d03f 100644 --- a/src/runtime/pipeline/pipeline_executor.h +++ b/src/runtime/pipeline/pipeline_executor.h @@ -80,7 +80,7 @@ class TVM_DLL PipelineExecutor : public ModuleNode { /*!\brief How many outputs are in this pipeline executor.*/ size_t num_outputs_ = 0; /*!\brief Json loader.*/ - void Load(dmlc::JSONReader* reader) { + void Load(dmlc::JSONReader* reader, bool load_module = false) { reader->BeginArray(); while (reader->NextArrayItem()) { std::string key; @@ -104,6 +104,8 @@ class TVM_DLL PipelineExecutor : public ModuleNode { reader->Read(&dev); } else if (key == "output") { reader->Read(&output); + } else { + LOG(FATAL) << "do not support key " << key; } } // Check if mod_idx is read successfully. @@ -111,8 +113,11 @@ class TVM_DLL PipelineExecutor : public ModuleNode { // Check if the output is read successfully. ICHECK(!output.Empty()) << "Invalid output binding result."; pipeline_config_.Insert(mod_idx, output); - // Check if there is lib, json and params information. - if (!lib_name.empty() && !json_name.empty() && !params_name.empty()) { + // Load the lib, json, and params information. + if (load_module) { + ICHECK(!lib_name.empty()) << "lib_name is empty."; + ICHECK(!json_name.empty()) << "json_name is empty."; + ICHECK(!params_name.empty()) << "params_name is empty."; mod_config_[mod_idx] = GraphModuleLoadInfo(lib_name, json_name, params_name, dev); } } diff --git a/tests/python/relay/test_pipeline_executor.py b/tests/python/relay/test_pipeline_executor.py index 9e52fd737f72..6471ade63844 100644 --- a/tests/python/relay/test_pipeline_executor.py +++ b/tests/python/relay/test_pipeline_executor.py @@ -246,8 +246,7 @@ def test_pipeline(): assert pipeline_module # Use the import function to create and initialize PipelineModule. - pipeline_module_test = pipeline_executor.PipelineModule() - pipeline_module_test.load_library(config_file_name) + pipeline_module_test = pipeline_executor.PipelineModule.load_library(config_file_name) assert pipeline_module_test.num_outputs == 2 From 575c07dbea3c236779ef37578e85ae04811ff6fe Mon Sep 17 00:00:00 2001 From: huajsj Date: Tue, 5 Oct 2021 23:41:19 -0700 Subject: [PATCH 10/23] address review comments. --- src/runtime/pipeline/pipeline_executor.h | 3 ++- src/runtime/pipeline/pipeline_struct.h | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/runtime/pipeline/pipeline_executor.h b/src/runtime/pipeline/pipeline_executor.h index 5327aef3d03f..637c2b63a7c3 100644 --- a/src/runtime/pipeline/pipeline_executor.h +++ b/src/runtime/pipeline/pipeline_executor.h @@ -108,7 +108,8 @@ class TVM_DLL PipelineExecutor : public ModuleNode { LOG(FATAL) << "do not support key " << key; } } - // Check if mod_idx is read successfully. + // Check if mod_idx is read successfully, in this level reading there is no any moudle + // is PipelineExecutor, hence the mod_idx should start from 0. ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx; // Check if the output is read successfully. ICHECK(!output.Empty()) << "Invalid output binding result."; diff --git a/src/runtime/pipeline/pipeline_struct.h b/src/runtime/pipeline/pipeline_struct.h index ff4b92a6fba3..12f2ad149c63 100644 --- a/src/runtime/pipeline/pipeline_struct.h +++ b/src/runtime/pipeline/pipeline_struct.h @@ -72,7 +72,9 @@ struct OutputBindings { reader->Read(&input_name); } } - // The value of 'mod_idx' should larger than PIPELINE_EXECUTOR_INDEX. + // In this level 'Load' that reading the output binding , the module can be + // a 'PipelineExecutor', hence the value of 'mod_idx' should start from + // PIPELINE_EXECUTOR_INDEX. ICHECK(mod_idx >= PIPELINE_EXECUTOR_INDEX); bindings[mod_idx] = input_name; } From ea58369c85356171d88c994d3f035ac193ee8303 Mon Sep 17 00:00:00 2001 From: huajsj Date: Thu, 7 Oct 2021 21:54:51 -0700 Subject: [PATCH 11/23] address review comments. --- python/tvm/contrib/pipeline_executor.py | 108 +++++++++++++-------- src/runtime/pipeline/pipeline_executor.cc | 87 ++++++++++++++++- src/runtime/pipeline/pipeline_executor.h | 69 +++++++++---- src/runtime/pipeline/pipeline_scheduler.cc | 69 +------------ src/runtime/pipeline/pipeline_scheduler.h | 10 +- src/runtime/pipeline/pipeline_struct.h | 6 ++ 6 files changed, 206 insertions(+), 143 deletions(-) diff --git a/python/tvm/contrib/pipeline_executor.py b/python/tvm/contrib/pipeline_executor.py index 39cb54296d70..67308e13efb0 100644 --- a/python/tvm/contrib/pipeline_executor.py +++ b/python/tvm/contrib/pipeline_executor.py @@ -83,15 +83,28 @@ class PipelineModule(object): Parameters ---------- - module : PipelineExecutorFactoryModule - Common interface for pipeline executor factory modules. + module : PipelineExecutorFactoryModule/Module + Common interface for pipeline executor factory modules or Module. """ def __init__(self, module): - self.module = module.module + if isinstance(module, PipelineExecutorFactoryModule): + self.module = module.module + else: + self.module = module # Get the packed functions from the pipeline executor. self._get_num_outputs = self.module["get_num_outputs"] + @property + def num_outputs(self): + """Get the number of outputs. + Returns + ------- + count : int + The number of outputs. + """ + return self._get_num_outputs() + @staticmethod def load_library(config_file_name): """Import files to create a pipeline executor. @@ -102,19 +115,28 @@ def load_library(config_file_name): Path and name of the configuration file, the configuration file contains the disk path of the parameter file, library file, and JSON file. """ - # Load the configuration file to initialize a PipelineExecutorFactoryModule. - pipeline_factory = PipelineExecutorFactoryModule.load_library(config_file_name) - return PipelineModule(pipeline_factory) + config = "" + with open(config_file_name, "r") as file_handle: + config = file_handle.read() + config = json.loads(config) + if "load_config" not in config or "pipeline_config" not in config: + raise RuntimeError(f"Config file format is wrong.") - @property - def num_outputs(self): - """Get the number of outputs. - Returns - ------- - count : int - The number of outputs. - """ - return self._get_num_outputs() + # The config file use to load library, prameters, and JSON files. + load_config = "" + with open(config["load_config"], "r") as file_handle: + load_config = file_handle.read() + + # The config file use to load pipeline compute config. + pipeline_config = "" + with open(config["pipeline_config"], "r") as file_handle: + pipeline_config = file_handle.read() + + # Load a PipelineExecutor from the disk files. + load_library = tvm._ffi.get_global_func("tvm.pipeline_executor.load", allow_missing=False) + module = load_library(load_config, pipeline_config) + + return PipelineModule(module) class PipelineConfig(object): @@ -581,16 +603,22 @@ def export_library(self, directory_path): # Check if the directory_path exists. if not os.path.exists(directory_path): raise RuntimeError(f"The directory {directory_path} does not exist.") - # Create a configuration copy for export. - export_conf = self.mods_config.copy() + # Create an load configuration. + load_config_file_name = "{}/load_config".format(directory_path) + pipeline_config_file_name = "{}/pipeline_config".format(directory_path) + config = {} + config["load_config"] = load_config_file_name + config["pipeline_config"] = pipeline_config_file_name + load_config = [] # Export the library, JSON, and parameter into files, then export these files path # into a configuration file. for lib_index in self.pipeline_mods: - mconf = export_conf[lib_index] - mconf["lib_name"] = "{}/lib{}.so".format(directory_path, lib_index) - mconf["json_name"] = "{}/json{}".format(directory_path, lib_index) - mconf["params_name"] = "{}/params{}".format(directory_path, lib_index) - mconf["dev"] = "{},{}".format( + mconfig = {} + mconfig["mod_idx"] = lib_index + mconfig["lib_name"] = "{}/lib{}.so".format(directory_path, lib_index) + mconfig["json_name"] = "{}/json{}".format(directory_path, lib_index) + mconfig["params_name"] = "{}/params{}".format(directory_path, lib_index) + mconfig["dev"] = "{},{}".format( self.pipeline_mods[lib_index]["dev"].device_type, self.pipeline_mods[lib_index]["dev"].device_id, ) @@ -598,31 +626,25 @@ def export_library(self, directory_path): # Get the graph, lib, and parameters from GraphExecutorFactoryModule. graph, lib, params = self.pipeline_mods[lib_index]["lib"] # Export the lib, graph, and parameters to disk. - lib.export_library(mconf["lib_name"]) - with open(mconf["json_name"], "w") as file_handle: + lib.export_library(mconfig["lib_name"]) + with open(mconfig["json_name"], "w") as file_handle: file_handle.write(graph) - with open(mconf["params_name"], "wb") as file_handle: + with open(mconfig["params_name"], "wb") as file_handle: file_handle.write(relay.save_param_dict(params)) - # Export the configuration file to disk. - conf_file_name = "{}/config".format(directory_path) - with open(conf_file_name, "w") as file_handle: - file_handle.write(json.dumps(export_conf)) + load_config.append(mconfig) - return conf_file_name + # Export the configuration file to disk. + with open(load_config_file_name, "w") as file_handle: + file_handle.write(json.dumps(load_config)) - @staticmethod - def load_library(config_file_name): - """Load configuration file to create and initialize pipeline executor. + # Export the pipeline configuration file to disk. + with open(pipeline_config_file_name, "w") as file_handle: + file_handle.write(json.dumps(self.mods_config)) - Parameters - ---------- - config_file_name : str - The configuration file path, the configuration file contains the - disk path of the parameter file, library file, and JSON file. - """ - load_config = "" - with open(config_file_name, "r") as file_handle: - load_config = file_handle.read() + # Export the configuration file to disk. + config_file_name = "{}/config".format(directory_path) + with open(config_file_name, "w") as file_handle: + file_handle.write(json.dumps(config)) - return PipelineExecutorFactoryModule([], json.loads(load_config)) + return config_file_name diff --git a/src/runtime/pipeline/pipeline_executor.cc b/src/runtime/pipeline/pipeline_executor.cc index 0a456f6fe332..29399d24623d 100644 --- a/src/runtime/pipeline/pipeline_executor.cc +++ b/src/runtime/pipeline/pipeline_executor.cc @@ -40,32 +40,109 @@ PackedFunc PipelineExecutor::GetFunction(const std::string& name, } return nullptr; } + +/*! + * \brief Use the mod_config information to create a graph runtime list. + * \param mod_config The config information that generates by the export library function call. + */ +std::vector PipelineExecutor::CreateGraphModules(const ModuleConfig& mod_config) { + const PackedFunc* graph_executor_create = Registry::Get("tvm.graph_executor.create"); + std::vector ret; + ret.resize(mod_config.size()); + for (auto config : mod_config) { + // Load library. + auto lib = Module::LoadFromFile(config.second.lib_name.c_str()); + + // Read json. + std::ifstream ifJson(config.second.json_name.c_str()); + if (ifJson.fail()) { + LOG(FATAL) << "json file not found!"; + } + const std::string json((std::istreambuf_iterator(ifJson)), + std::istreambuf_iterator()); + + // Create a graph executor. + std::istringstream istr(config.second.dev); + std::string str; + int device_type = 1, device_id = 0; + while (getline(istr, str, ';')) { + std::istringstream istr_dev(str); + std::string str_temp; + if (getline(istr_dev, str_temp)) { + device_type = stoi(str_temp); + } + if (getline(istr_dev, str_temp)) { + device_id = stoi(str_temp); + } + } + Module graph_module = (*graph_executor_create)(json, lib, device_type, device_id); + + // Load parameters. + TVMByteArray params_arr; + std::ifstream if_param(config.second.params_name.c_str()); + if (if_param.fail()) { + LOG(FATAL) << "params file not found!"; + } + const std::string params((std::istreambuf_iterator(if_param)), + std::istreambuf_iterator()); + params_arr.data = params.c_str(); + params_arr.size = params.length(); + auto load_params = graph_module.GetFunction("load_params"); + load_params(params_arr); + + // Put a graph executor module into the vector. + ret[config.first] = graph_module; + } + return ret; +} + /*! * \brief Initialize the pipeline executor with a list of modules to be pipelined * and config in JSON format. * \param modules The module list used for building the pipeline. * \param pipeline_json The configuration of modules dependencies. */ -void PipelineExecutor::Init(const Array& modules, const std::string& pipeline_json) { +void PipelineExecutor::Init(const std::vector& modules, const std::string& pipeline_json) { + ICHECK(!modules.empty()) << "The graph executor module list is empty."; // Use JSONReader to load pipeline configuration. std::istringstream is(pipeline_json); dmlc::JSONReader reader(&is); - // When the value of 'modules' is empty, here need to load the modules from JSON. - this->Load(&reader, modules.empty()); + PipelineConfig& pipeline_config = this->LoadPipelineConfig(&reader); + ICHECK(!pipeline_config.Empty()) << "The pipeline config information is empty."; // Initialize the pipeline function class used for pipeline thread pool management // and schedule etc. This function returns the number of output. - num_outputs_ = pipeline_function_.PipelineInit(modules, pipeline_config_, mod_config_); + num_outputs_ = pipeline_scheduler_.PipelineInit(modules, pipeline_config); return; } Module PipelineExecutorCreate(const Array& m, const std::string& pipeline_json) { + ICHECK(!m.empty()) << "The module list is empty."; auto exec = make_object(); - exec->Init(m, pipeline_json); + std::vector graph_modules; + for (auto mod : m) { + graph_modules.push_back(mod); + } + exec->Init(graph_modules, pipeline_json); + return Module(exec); +} + +Module PipelineExecutorLoad(const std::string& load_json, const std::string& pipeline_json) { + auto exec = make_object(); + std::istringstream is(load_json); + dmlc::JSONReader reader(&is); + ModuleConfig& mod_config = exec->LoadModuleConfig(&reader); + ICHECK(!mod_config.empty()) << "The module config is empty."; + std::vector modules = exec->CreateGraphModules(mod_config); + exec->Init(modules, pipeline_json); return Module(exec); } TVM_REGISTER_GLOBAL("tvm.pipeline_executor.create").set_body([](TVMArgs args, TVMRetValue* rv) { *rv = PipelineExecutorCreate(args[0], args[1]); }); + +TVM_REGISTER_GLOBAL("tvm.pipeline_executor.load").set_body([](TVMArgs args, TVMRetValue* rv) { + *rv = PipelineExecutorLoad(args[0], args[1]); +}); } // namespace runtime } // namespace tvm diff --git a/src/runtime/pipeline/pipeline_executor.h b/src/runtime/pipeline/pipeline_executor.h index 637c2b63a7c3..eb2acc40979a 100644 --- a/src/runtime/pipeline/pipeline_executor.h +++ b/src/runtime/pipeline/pipeline_executor.h @@ -54,7 +54,13 @@ class TVM_DLL PipelineExecutor : public ModuleNode { * \param modules The module list used for building pipeline. * \param pipeline_json The configuration of modules dependencies. */ - void Init(const Array& modules, const std::string& pipeline_json); + void Init(const std::vector& modules, const std::string& pipeline_json); + /*! + * \brief Use the information of mod_config to create a graph executor list. + * \param mod_config The configuration information generated by the library export library + * function call. + */ + std::vector CreateGraphModules(const ModuleConfig& mod_config); /*! * \brief Give frontends an access to packed functions. * \param name The name of the function. @@ -70,17 +76,8 @@ class TVM_DLL PipelineExecutor : public ModuleNode { */ int NumOutputs() const { return num_outputs_; } - private: - /*!\brief The class used to execute pipeline logic.*/ - PipelineScheduler pipeline_function_; - /*!\brief The Dependency information of each graph runtime module of the pipeline.*/ - PipelineConfig pipeline_config_; - /*!\brief The Module information used to create graph runtime.*/ - ModuleConfig mod_config_; - /*!\brief How many outputs are in this pipeline executor.*/ - size_t num_outputs_ = 0; - /*!\brief Json loader.*/ - void Load(dmlc::JSONReader* reader, bool load_module = false) { + /*!\brief Load the module files information.*/ + ModuleConfig& LoadModuleConfig(dmlc::JSONReader* reader) { reader->BeginArray(); while (reader->NextArrayItem()) { std::string key; @@ -90,7 +87,6 @@ class TVM_DLL PipelineExecutor : public ModuleNode { std::string json_name; std::string params_name; std::string dev; - OutputMap output; while (reader->NextObjectItem(&key)) { if (key == "mod_idx") { reader->Read(&mod_idx); @@ -102,6 +98,45 @@ class TVM_DLL PipelineExecutor : public ModuleNode { reader->Read(¶ms_name); } else if (key == "dev") { reader->Read(&dev); + } else { + LOG(FATAL) << "do not support key " << key; + } + } + // Check if mod_idx is read successfully, in this level reading there is no any moudle + // is PipelineExecutor, hence the mod_idx should start from 0. + ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx; + // Load the lib, json, and params information. + ICHECK(!lib_name.empty()) << "lib_name is empty."; + ICHECK(!json_name.empty()) << "json_name is empty."; + ICHECK(!params_name.empty()) << "params_name is empty."; + mod_config_[mod_idx] = GraphModuleLoadInfo(lib_name, json_name, params_name, dev); + } + return mod_config_; + } + + private: + /*!\brief The class used to execute and schedule the pipeline logic.*/ + PipelineScheduler pipeline_scheduler_; + /*!\brief The Dependency information of each graph runtime module of the pipeline.*/ + PipelineConfig pipeline_config_; + /*!\brief The Module information used to create the graph runtimes.*/ + ModuleConfig mod_config_; + /*!\brief How many outputs are in this pipeline executor.*/ + size_t num_outputs_ = 0; + /*!\brief Json loader.*/ + PipelineConfig& LoadPipelineConfig(dmlc::JSONReader* reader) { + reader->BeginArray(); + while (reader->NextArrayItem()) { + std::string key; + reader->BeginObject(); + int mod_idx = -1; + OutputMap output; + std::string dev; + while (reader->NextObjectItem(&key)) { + if (key == "mod_idx") { + reader->Read(&mod_idx); + } else if (key == "dev") { + reader->Read(&dev); } else if (key == "output") { reader->Read(&output); } else { @@ -114,14 +149,8 @@ class TVM_DLL PipelineExecutor : public ModuleNode { // Check if the output is read successfully. ICHECK(!output.Empty()) << "Invalid output binding result."; pipeline_config_.Insert(mod_idx, output); - // Load the lib, json, and params information. - if (load_module) { - ICHECK(!lib_name.empty()) << "lib_name is empty."; - ICHECK(!json_name.empty()) << "json_name is empty."; - ICHECK(!params_name.empty()) << "params_name is empty."; - mod_config_[mod_idx] = GraphModuleLoadInfo(lib_name, json_name, params_name, dev); - } } + return pipeline_config_; } }; } // namespace runtime diff --git a/src/runtime/pipeline/pipeline_scheduler.cc b/src/runtime/pipeline/pipeline_scheduler.cc index 67ac46c796bd..82caf855a479 100644 --- a/src/runtime/pipeline/pipeline_scheduler.cc +++ b/src/runtime/pipeline/pipeline_scheduler.cc @@ -26,75 +26,12 @@ namespace runtime { * \brief Initialize the pipeline. * \param modules The list of graph executor modules. * \param pipeline_conf The dependency information of each graph executor module. - * \param mod_config The config is generated by the export library function call. */ -size_t PipelineScheduler::PipelineInit(Array modules, const PipelineConfig& pipeline_config, - const ModuleConfig& mod_config) { +size_t PipelineScheduler::PipelineInit(const std::vector& modules, + const PipelineConfig& pipeline_config) { + graph_modules_ = modules; int num_output = pipeline_config.GetGlobalOutputNum(); - // If 'modules' is not empty just return in vector container - if (!modules.empty()) { - for (auto mod : modules) { - graph_modules_.push_back(mod); - } - } else { - // If the value of 'modules' is empty, need to build the graph exectuor from mod_config. - graph_modules_ = PipelineCreateGraphModules(mod_config); - } return num_output; } -/*! - * \brief Use the mod_config information to create a graph runtime list. - * \param mod_config The config information that generates by the export library function call. - */ -std::vector PipelineScheduler::PipelineCreateGraphModules(const ModuleConfig& mod_config) { - const PackedFunc* graph_executor_create = Registry::Get("tvm.graph_executor.create"); - std::vector ret; - ret.resize(mod_config.size()); - for (auto config : mod_config) { - // Load library. - auto lib = Module::LoadFromFile(config.second.lib_name.c_str()); - - // Read json. - std::ifstream ifJson(config.second.json_name.c_str()); - if (ifJson.fail()) { - LOG(FATAL) << "json file not found!"; - } - const std::string json((std::istreambuf_iterator(ifJson)), - std::istreambuf_iterator()); - - // Create a graph executor. - std::istringstream istr(config.second.dev); - std::string str; - int device_type = 1, device_id = 0; - while (getline(istr, str, ';')) { - std::istringstream istr_dev(str); - std::string str_temp; - if (getline(istr_dev, str_temp)) { - device_type = stoi(str_temp); - } - if (getline(istr_dev, str_temp)) { - device_id = stoi(str_temp); - } - } - Module graph_module = (*graph_executor_create)(json, lib, device_type, device_id); - - // Load parameters. - TVMByteArray params_arr; - std::ifstream if_param(config.second.params_name.c_str()); - if (if_param.fail()) { - LOG(FATAL) << "params file not found!"; - } - const std::string params((std::istreambuf_iterator(if_param)), - std::istreambuf_iterator()); - params_arr.data = params.c_str(); - params_arr.size = params.length(); - auto load_params = graph_module.GetFunction("load_params"); - load_params(params_arr); - - // Put a graph executor module into the vector. - ret[config.first] = graph_module; - } - return ret; -} } // namespace runtime } // namespace tvm diff --git a/src/runtime/pipeline/pipeline_scheduler.h b/src/runtime/pipeline/pipeline_scheduler.h index fd88cb8e2afb..5ee127edffa3 100644 --- a/src/runtime/pipeline/pipeline_scheduler.h +++ b/src/runtime/pipeline/pipeline_scheduler.h @@ -36,20 +36,12 @@ namespace runtime { */ class PipelineScheduler { public: - /*! - * \brief Use the information of mod_config to create a graph executor list. - * \param mod_config The configuration information generated by the library export library - * function call. - */ - std::vector PipelineCreateGraphModules(const ModuleConfig& mod_config); /*! * \brief Initialize the pipeline. * \param modules The list of graph executor module. * \param pipeline_config The dependency information of each graph executor module. - * \param mod_config The config information generated by the export library function call. */ - size_t PipelineInit(Array modules, const PipelineConfig& pipeline_config, - const ModuleConfig& mod_config); + size_t PipelineInit(const std::vector& modules, const PipelineConfig& pipeline_config); private: /*!\brief The list of graph executors.*/ diff --git a/src/runtime/pipeline/pipeline_struct.h b/src/runtime/pipeline/pipeline_struct.h index 12f2ad149c63..755d526da56f 100644 --- a/src/runtime/pipeline/pipeline_struct.h +++ b/src/runtime/pipeline/pipeline_struct.h @@ -145,6 +145,12 @@ struct PipelineConfig { } void Insert(int key, const OutputMap& map) { config[key] = map; } + + /*!\brief This function is used to verify whether config is loaded successfully. + * \return Return true to indicate that this class has not been successfully loaded. + */ + bool Empty() { return config.empty(); } + /*! * \brief Get the number of global outputs that is the outputs of entire pipeline. * \return How much output does the entire pipeline have. From da9bf18b1df3a65ca179d37320c6a80abef066ab Mon Sep 17 00:00:00 2001 From: huajsj Date: Fri, 8 Oct 2021 09:22:24 -0700 Subject: [PATCH 12/23] polish the document. --- src/runtime/pipeline/pipeline_executor.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtime/pipeline/pipeline_executor.h b/src/runtime/pipeline/pipeline_executor.h index eb2acc40979a..de7d34d9392c 100644 --- a/src/runtime/pipeline/pipeline_executor.h +++ b/src/runtime/pipeline/pipeline_executor.h @@ -102,8 +102,8 @@ class TVM_DLL PipelineExecutor : public ModuleNode { LOG(FATAL) << "do not support key " << key; } } - // Check if mod_idx is read successfully, in this level reading there is no any moudle - // is PipelineExecutor, hence the mod_idx should start from 0. + // Check if mod_idx is read successfully, in this level reading there all the moudles + // are graph executor modules, hence the mod_idx should start from 0. ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx; // Load the lib, json, and params information. ICHECK(!lib_name.empty()) << "lib_name is empty."; From c7a934464bb6a40909574f1c5cacd8a3146263f7 Mon Sep 17 00:00:00 2001 From: huajsj Date: Fri, 8 Oct 2021 10:29:06 -0700 Subject: [PATCH 13/23] address review comments. --- python/tvm/contrib/pipeline_executor.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/python/tvm/contrib/pipeline_executor.py b/python/tvm/contrib/pipeline_executor.py index 67308e13efb0..c8607d1036c1 100644 --- a/python/tvm/contrib/pipeline_executor.py +++ b/python/tvm/contrib/pipeline_executor.py @@ -83,7 +83,7 @@ class PipelineModule(object): Parameters ---------- - module : PipelineExecutorFactoryModule/Module + module : Union[PipelineExecutorFactoryModule, Module] Common interface for pipeline executor factory modules or Module. """ @@ -120,15 +120,15 @@ def load_library(config_file_name): config = file_handle.read() config = json.loads(config) if "load_config" not in config or "pipeline_config" not in config: - raise RuntimeError(f"Config file format is wrong.") + raise RuntimeError( + '"load_config" or "pipeline_config" is missing in %s' % config_file_name + ) # The config file use to load library, prameters, and JSON files. - load_config = "" with open(config["load_config"], "r") as file_handle: load_config = file_handle.read() # The config file use to load pipeline compute config. - pipeline_config = "" with open(config["pipeline_config"], "r") as file_handle: pipeline_config = file_handle.read() @@ -636,15 +636,15 @@ def export_library(self, directory_path): # Export the configuration file to disk. with open(load_config_file_name, "w") as file_handle: - file_handle.write(json.dumps(load_config)) + json.dump(load_config, file_handle) # Export the pipeline configuration file to disk. with open(pipeline_config_file_name, "w") as file_handle: - file_handle.write(json.dumps(self.mods_config)) + json.dump(self.mods_config, file_handle) # Export the configuration file to disk. config_file_name = "{}/config".format(directory_path) with open(config_file_name, "w") as file_handle: - file_handle.write(json.dumps(config)) + json.dump(config, file_handle) return config_file_name From e74c1adf979453ec74b00fdf2184bd7b1334437c Mon Sep 17 00:00:00 2001 From: huajsj Date: Fri, 8 Oct 2021 10:41:06 -0700 Subject: [PATCH 14/23] address review comments. --- python/tvm/contrib/pipeline_executor.py | 1 - src/runtime/pipeline/pipeline_executor.cc | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python/tvm/contrib/pipeline_executor.py b/python/tvm/contrib/pipeline_executor.py index c8607d1036c1..87dcaf3ed20f 100644 --- a/python/tvm/contrib/pipeline_executor.py +++ b/python/tvm/contrib/pipeline_executor.py @@ -115,7 +115,6 @@ def load_library(config_file_name): Path and name of the configuration file, the configuration file contains the disk path of the parameter file, library file, and JSON file. """ - config = "" with open(config_file_name, "r") as file_handle: config = file_handle.read() config = json.loads(config) diff --git a/src/runtime/pipeline/pipeline_executor.cc b/src/runtime/pipeline/pipeline_executor.cc index 29399d24623d..3820ce942af0 100644 --- a/src/runtime/pipeline/pipeline_executor.cc +++ b/src/runtime/pipeline/pipeline_executor.cc @@ -56,7 +56,7 @@ std::vector PipelineExecutor::CreateGraphModules(const ModuleConfig& mod // Read json. std::ifstream ifJson(config.second.json_name.c_str()); if (ifJson.fail()) { - LOG(FATAL) << "json file not found!"; + LOG(FATAL) << "json file not found: " << config.second.json_name; } const std::string json((std::istreambuf_iterator(ifJson)), std::istreambuf_iterator()); @@ -79,9 +79,10 @@ std::vector PipelineExecutor::CreateGraphModules(const ModuleConfig& mod // Load parameters. TVMByteArray params_arr; - std::ifstream if_param(config.second.params_name.c_str()); + const char* params_file_name = config.second.params_name.c_str(); + std::ifstream if_param(params_file_name); if (if_param.fail()) { - LOG(FATAL) << "params file not found!"; + LOG(FATAL) << "params file not found: " << params_file_name; } const std::string params((std::istreambuf_iterator(if_param)), std::istreambuf_iterator()); From de5643573f19a0156a8239370a2dcd1e0c2d6d17 Mon Sep 17 00:00:00 2001 From: huajsj Date: Fri, 8 Oct 2021 22:11:49 -0700 Subject: [PATCH 15/23] polish comments. --- tests/python/relay/test_pipeline_executor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/relay/test_pipeline_executor.py b/tests/python/relay/test_pipeline_executor.py index 6471ade63844..3b2eaa6f503f 100644 --- a/tests/python/relay/test_pipeline_executor.py +++ b/tests/python/relay/test_pipeline_executor.py @@ -129,7 +129,7 @@ def get_manual_conf(mods, target): def test_pipe_config_check(): # This function is used to trigger runtime error by applying wrong logic connection. - # Get the three pipeline modules here. + # Get these three pipeline modules here. (mod1, mod2, mod3), dshape = get_mannual_mod() # The input or output name is illegal and expects a runtime error. From 8221ca52170a3078514a00426af5d0cb433d523e Mon Sep 17 00:00:00 2001 From: huajsj Date: Sat, 9 Oct 2021 13:40:31 -0700 Subject: [PATCH 16/23] Triger build. --- tests/python/relay/test_pipeline_executor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/relay/test_pipeline_executor.py b/tests/python/relay/test_pipeline_executor.py index 3b2eaa6f503f..87f69d43ed45 100644 --- a/tests/python/relay/test_pipeline_executor.py +++ b/tests/python/relay/test_pipeline_executor.py @@ -129,7 +129,7 @@ def get_manual_conf(mods, target): def test_pipe_config_check(): # This function is used to trigger runtime error by applying wrong logic connection. - # Get these three pipeline modules here. + # Get three pipeline modules here. (mod1, mod2, mod3), dshape = get_mannual_mod() # The input or output name is illegal and expects a runtime error. From cdfa5c221acedbaef248d7bc350e7505fe5d98e1 Mon Sep 17 00:00:00 2001 From: huajsj Date: Mon, 11 Oct 2021 16:50:06 -0700 Subject: [PATCH 17/23] address review comments. --- python/tvm/contrib/pipeline_executor.py | 12 ++--- src/runtime/pipeline/pipeline_executor.h | 11 ++-- src/runtime/pipeline/pipeline_struct.h | 55 ++++++++++---------- tests/python/relay/test_pipeline_executor.py | 4 +- 4 files changed, 41 insertions(+), 41 deletions(-) diff --git a/python/tvm/contrib/pipeline_executor.py b/python/tvm/contrib/pipeline_executor.py index 87dcaf3ed20f..c47b3408ebbb 100644 --- a/python/tvm/contrib/pipeline_executor.py +++ b/python/tvm/contrib/pipeline_executor.py @@ -71,7 +71,7 @@ def build(pipe_configs): ) mconf["dev"] = "{},{}".format(dev.device_type, dev.device_id) - # Create a pipeline configuration, 'mod_idx' start from 0. + # Create a pipeline configuration. string_config[mod_idx] = mconf libs[mod_idx] = {"lib": lib, "dev": dev} @@ -479,8 +479,11 @@ def get_config(self): for dep in binding.bindings: dep_item = {} _, dname = dep.get_name() - dep_item["mod_idx"] = dep.get_owner_idx() - dep_item["input_name"] = dname + if dep.is_pipeline_executor_interface(): + dep_item["global_output_index"] = int(dname) + else: + dep_item["mod_idx"] = dep.get_owner_idx() + dep_item["input_name"] = dname dep_conf.append(dep_item) # The value of ouput_idx start from 0. @@ -633,15 +636,12 @@ def export_library(self, directory_path): load_config.append(mconfig) - # Export the configuration file to disk. with open(load_config_file_name, "w") as file_handle: json.dump(load_config, file_handle) - # Export the pipeline configuration file to disk. with open(pipeline_config_file_name, "w") as file_handle: json.dump(self.mods_config, file_handle) - # Export the configuration file to disk. config_file_name = "{}/config".format(directory_path) with open(config_file_name, "w") as file_handle: json.dump(config, file_handle) diff --git a/src/runtime/pipeline/pipeline_executor.h b/src/runtime/pipeline/pipeline_executor.h index de7d34d9392c..e5431b87450f 100644 --- a/src/runtime/pipeline/pipeline_executor.h +++ b/src/runtime/pipeline/pipeline_executor.h @@ -57,8 +57,7 @@ class TVM_DLL PipelineExecutor : public ModuleNode { void Init(const std::vector& modules, const std::string& pipeline_json); /*! * \brief Use the information of mod_config to create a graph executor list. - * \param mod_config The configuration information generated by the library export library - * function call. + * \param mod_config The configuration information generated by the library export function call. */ std::vector CreateGraphModules(const ModuleConfig& mod_config); /*! @@ -102,8 +101,8 @@ class TVM_DLL PipelineExecutor : public ModuleNode { LOG(FATAL) << "do not support key " << key; } } - // Check if mod_idx is read successfully, in this level reading there all the moudles - // are graph executor modules, hence the mod_idx should start from 0. + // Check if mod_idx is read successfully, All the moudles here are graph executor modules, + // hence the mod_idx should start from 0. ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx; // Load the lib, json, and params information. ICHECK(!lib_name.empty()) << "lib_name is empty."; @@ -143,8 +142,8 @@ class TVM_DLL PipelineExecutor : public ModuleNode { LOG(FATAL) << "do not support key " << key; } } - // Check if mod_idx is read successfully, in this level reading there is no any moudle - // is PipelineExecutor, hence the mod_idx should start from 0. + // Check if mod_idx is read successfully, All moudles here are graph executor modules, hence + // the mod_idx should start from 0. ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx; // Check if the output is read successfully. ICHECK(!output.Empty()) << "Invalid output binding result."; diff --git a/src/runtime/pipeline/pipeline_struct.h b/src/runtime/pipeline/pipeline_struct.h index 755d526da56f..568d8a532604 100644 --- a/src/runtime/pipeline/pipeline_struct.h +++ b/src/runtime/pipeline/pipeline_struct.h @@ -26,7 +26,6 @@ #include #include #include -#define PIPELINE_EXECUTOR_INDEX -1 /*! * \brief All binding information of a output interface. */ @@ -36,23 +35,12 @@ struct OutputBindings { * of the module. */ std::unordered_map bindings; - /*! - * \brief If there is one PipelineExecutor binding in bindings, then the current output is - * PipelineExecutor interface. - * \return Whether this output interface is PipelineExecutor output interface. - */ - bool IsGlobalOutput() const { - int num_output = 0; - for (auto binding : bindings) { - /* The output is a PipelineExecutor output when the index value - * equal PIPELINE_EXECUTOR_INDEX. - */ - num_output += (binding.first == PIPELINE_EXECUTOR_INDEX); - } - /* If this output is a global output then there is only one such output in map.*/ - ICHECK(num_output <= 1); - return num_output == 1; - } + /*! Whether this output binding with global output.*/ + bool global_binding = false; + /*! The index of global output that this output binding with.*/ + int global_output_index = std::numeric_limits::min(); + /*!\brief Whether this binding bind with a PipelineExecutor output interface.*/ + bool IsGlobalOutput() const { return global_binding; } /*! * \brief Create a module interface map from JSONReader. * \param reader JSON reader. @@ -67,19 +55,31 @@ struct OutputBindings { while (reader->NextObjectItem(&key)) { if (key == "mod_idx") { reader->Read(&mod_idx); - } - if (key == "input_name") { + } else if (key == "input_name") { reader->Read(&input_name); + } else if (key == "global_output_index") { + reader->Read(&global_output_index); + // When find key with value as 'global_output_index', this output should bind with + // a global output.*/ + global_binding = true; + } else { + LOG(FATAL) << "do not support key " << key; } } - // In this level 'Load' that reading the output binding , the module can be - // a 'PipelineExecutor', hence the value of 'mod_idx' should start from - // PIPELINE_EXECUTOR_INDEX. - ICHECK(mod_idx >= PIPELINE_EXECUTOR_INDEX); - bindings[mod_idx] = input_name; + // When this output bind with a global interface, check whether the interface index + // is correct. + if (global_binding) { + ICHECK(global_output_index >= 0); + } else { + // When this output does not bind with a global interface, check whether the binding + // module index is correct. + ICHECK(mod_idx >= 0); + bindings[mod_idx] = input_name; + } } } }; + /*! * \brief The binding information of all outputs of a module. */ @@ -121,9 +121,10 @@ struct OutputMap { while (reader->NextObjectItem(&key)) { if (key == "output_idx") { reader->Read(&output_idx); - } - if (key == "dependent") { + } else if (key == "dependent") { reader->Read(&binding); + } else { + LOG(FATAL) << "do not support key " << key; } } ICHECK(output_idx >= 0); diff --git a/tests/python/relay/test_pipeline_executor.py b/tests/python/relay/test_pipeline_executor.py index 87f69d43ed45..d8490ce9e671 100644 --- a/tests/python/relay/test_pipeline_executor.py +++ b/tests/python/relay/test_pipeline_executor.py @@ -81,7 +81,7 @@ def get_manual_conf(mods, target): "output": [ {"output_idx": 0, "dependent": [{"mod_idx": 1, "input_name": "data_0"}]}, {"output_idx": 1, "dependent": [{"mod_idx": 2, "input_name": "data_0"}]}, - {"output_idx": 2, "dependent": [{"mod_idx": -1, "input_name": "0"}]}, + {"output_idx": 2, "dependent": [{"global_output_index": 0}]}, ], } mod_config[mods[0]] = { @@ -112,7 +112,7 @@ def get_manual_conf(mods, target): pipe_config3 = { "mod_idx": 2, - "output": [{"output_idx": 0, "dependent": [{"mod_idx": -1, "input_name": "1"}]}], + "output": [{"output_idx": 0, "dependent": [{"global_output_index": 1}]}], } mod_config[mods[2]] = { "pipeline": pipe_config3, From 16e9abe10ae2bb30fb702cad556fa556b6a80607 Mon Sep 17 00:00:00 2001 From: huajsj Date: Mon, 11 Oct 2021 17:44:29 -0700 Subject: [PATCH 18/23] address review comments. --- python/tvm/contrib/pipeline_executor.py | 9 +++++---- src/runtime/pipeline/pipeline_executor.h | 10 +++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/python/tvm/contrib/pipeline_executor.py b/python/tvm/contrib/pipeline_executor.py index c47b3408ebbb..5889f1f0eb24 100644 --- a/python/tvm/contrib/pipeline_executor.py +++ b/python/tvm/contrib/pipeline_executor.py @@ -190,10 +190,11 @@ def get_owner_idx(self): return -1 def is_pipeline_executor_interface(self): - """The pipeline interface is the interface visible to the caller uses a pipeline - executor, the pipeline input interface is responsible for passing parameters to the - internal module interface, and the pipeline output interface is responsible for - outputting the results computed by the pipeline executor to a caller. + """The pipeline interface is used to interact with the caller, there are two types + such interfaces, one is 'input' another is 'output'. the pipeline input interface + is responsible for passing parameters to the internal module interface, and the + pipeline output interface is responsible for outputting the results computed by + the pipeline executor to a caller. """ return not isinstance(self.io_owner, PipelineConfig.ModuleWrapper) diff --git a/src/runtime/pipeline/pipeline_executor.h b/src/runtime/pipeline/pipeline_executor.h index e5431b87450f..febd6dd3a078 100644 --- a/src/runtime/pipeline/pipeline_executor.h +++ b/src/runtime/pipeline/pipeline_executor.h @@ -101,8 +101,8 @@ class TVM_DLL PipelineExecutor : public ModuleNode { LOG(FATAL) << "do not support key " << key; } } - // Check if mod_idx is read successfully, All the moudles here are graph executor modules, - // hence the mod_idx should start from 0. + // Check if the vairable 'mod_idx' is successfully read, All moudles here are graph executor + // modules, hence the value of 'mod_idx' should start from 0. ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx; // Load the lib, json, and params information. ICHECK(!lib_name.empty()) << "lib_name is empty."; @@ -142,10 +142,10 @@ class TVM_DLL PipelineExecutor : public ModuleNode { LOG(FATAL) << "do not support key " << key; } } - // Check if mod_idx is read successfully, All moudles here are graph executor modules, hence - // the mod_idx should start from 0. + // Check if the variable 'mod_idx' is successfully read, All moudles here are graph executor + // modules, hence the value of 'mod_idx' should start from 0. ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx; - // Check if the output is read successfully. + // Check if the output is successfully read. ICHECK(!output.Empty()) << "Invalid output binding result."; pipeline_config_.Insert(mod_idx, output); } From 15a8f9f85872fb1b747d4b6ca7348dabca9cdc57 Mon Sep 17 00:00:00 2001 From: huajsj Date: Mon, 11 Oct 2021 18:13:31 -0700 Subject: [PATCH 19/23] fix grammar issue. --- src/runtime/pipeline/pipeline_struct.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/runtime/pipeline/pipeline_struct.h b/src/runtime/pipeline/pipeline_struct.h index 568d8a532604..f4d9b374d027 100644 --- a/src/runtime/pipeline/pipeline_struct.h +++ b/src/runtime/pipeline/pipeline_struct.h @@ -35,11 +35,11 @@ struct OutputBindings { * of the module. */ std::unordered_map bindings; - /*! Whether this output binding with global output.*/ + /*! Whether the output binding is global.*/ bool global_binding = false; - /*! The index of global output that this output binding with.*/ + /*! The index value of the global interface to which the current output are bound.*/ int global_output_index = std::numeric_limits::min(); - /*!\brief Whether this binding bind with a PipelineExecutor output interface.*/ + /*!\brief Whether this binding is bound to the PipelineExecutor output interface.*/ bool IsGlobalOutput() const { return global_binding; } /*! * \brief Create a module interface map from JSONReader. @@ -59,20 +59,20 @@ struct OutputBindings { reader->Read(&input_name); } else if (key == "global_output_index") { reader->Read(&global_output_index); - // When find key with value as 'global_output_index', this output should bind with - // a global output.*/ + // When the key value is 'global_output_index', it means that this output is bound to + // a global interface. global_binding = true; } else { LOG(FATAL) << "do not support key " << key; } } - // When this output bind with a global interface, check whether the interface index + // When this output is bound to a global interface, check if the global interface index // is correct. if (global_binding) { ICHECK(global_output_index >= 0); } else { - // When this output does not bind with a global interface, check whether the binding - // module index is correct. + // When this output is bound to a graph executor module interface, check if the module + // index is correct. ICHECK(mod_idx >= 0); bindings[mod_idx] = input_name; } From 5e55c8e58e8d7c789d58e807a088992c4efd8e5d Mon Sep 17 00:00:00 2001 From: huajsj Date: Mon, 11 Oct 2021 22:43:14 -0700 Subject: [PATCH 20/23] polish documents. --- src/runtime/pipeline/pipeline_struct.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/pipeline/pipeline_struct.h b/src/runtime/pipeline/pipeline_struct.h index f4d9b374d027..08fd37630a3b 100644 --- a/src/runtime/pipeline/pipeline_struct.h +++ b/src/runtime/pipeline/pipeline_struct.h @@ -91,7 +91,7 @@ struct OutputMap { return *this; } - /*!\brief This function is used to verify whether OutputMap is loaded successfully. + /*!\brief This function is used to verify whether OutputMap is successfully loaded. * \return Return true to indicate that this class has not been successfully loaded. */ bool Empty() { return output_binding_map.empty(); } From b6cf7ab8b207d8f58bc33eafdd7029e98f02b2c0 Mon Sep 17 00:00:00 2001 From: huajsj Date: Tue, 12 Oct 2021 12:40:21 -0700 Subject: [PATCH 21/23] add single global binding check. --- src/runtime/pipeline/pipeline_struct.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/runtime/pipeline/pipeline_struct.h b/src/runtime/pipeline/pipeline_struct.h index 08fd37630a3b..a21432709ff4 100644 --- a/src/runtime/pipeline/pipeline_struct.h +++ b/src/runtime/pipeline/pipeline_struct.h @@ -35,12 +35,10 @@ struct OutputBindings { * of the module. */ std::unordered_map bindings; - /*! Whether the output binding is global.*/ - bool global_binding = false; /*! The index value of the global interface to which the current output are bound.*/ int global_output_index = std::numeric_limits::min(); /*!\brief Whether this binding is bound to the PipelineExecutor output interface.*/ - bool IsGlobalOutput() const { return global_binding; } + bool IsGlobalOutput() const { return global_output_index >= 0; } /*! * \brief Create a module interface map from JSONReader. * \param reader JSON reader. @@ -52,12 +50,16 @@ struct OutputBindings { reader->BeginObject(); std::string input_name; int mod_idx = std::numeric_limits::min(); + // Whether the output binding is global. + bool global_binding = false; while (reader->NextObjectItem(&key)) { if (key == "mod_idx") { reader->Read(&mod_idx); } else if (key == "input_name") { reader->Read(&input_name); } else if (key == "global_output_index") { + // There should be only one global binding. + ICHECK(global_output_index < 0); reader->Read(&global_output_index); // When the key value is 'global_output_index', it means that this output is bound to // a global interface. From e081de3d7426003b18ced0d9d92f0bd9b3d469da Mon Sep 17 00:00:00 2001 From: huajsj Date: Tue, 12 Oct 2021 18:25:15 -0700 Subject: [PATCH 22/23] address review comments. --- python/tvm/contrib/pipeline_executor.py | 12 ++++++------ src/runtime/pipeline/pipeline_executor.h | 10 +++------- src/runtime/pipeline/pipeline_struct.h | 14 +++++++------- tests/python/relay/test_pipeline_executor.py | 10 +++++----- 4 files changed, 21 insertions(+), 25 deletions(-) diff --git a/python/tvm/contrib/pipeline_executor.py b/python/tvm/contrib/pipeline_executor.py index 5889f1f0eb24..37b9fed8eb91 100644 --- a/python/tvm/contrib/pipeline_executor.py +++ b/python/tvm/contrib/pipeline_executor.py @@ -123,11 +123,11 @@ def load_library(config_file_name): '"load_config" or "pipeline_config" is missing in %s' % config_file_name ) - # The config file use to load library, prameters, and JSON files. + # The config file used to load library, prameters, and JSON files. with open(config["load_config"], "r") as file_handle: load_config = file_handle.read() - # The config file use to load pipeline compute config. + # The config file used to load pipeline compute config. with open(config["pipeline_config"], "r") as file_handle: pipeline_config = file_handle.read() @@ -190,11 +190,11 @@ def get_owner_idx(self): return -1 def is_pipeline_executor_interface(self): - """The pipeline interface is used to interact with the caller, there are two types - such interfaces, one is 'input' another is 'output'. the pipeline input interface + """The pipeline interface is used to interact with the caller. There are two types + of interfaces, one is 'input' another is 'output'. The pipeline input interface is responsible for passing parameters to the internal module interface, and the pipeline output interface is responsible for outputting the results computed by - the pipeline executor to a caller. + the pipeline executor to the caller. """ return not isinstance(self.io_owner, PipelineConfig.ModuleWrapper) @@ -489,7 +489,7 @@ def get_config(self): # The value of ouput_idx start from 0. output["output_idx"] = int(binding.name) - output["dependent"] = dep_conf + output["dependencies"] = dep_conf output_conf.append(output) mconf["mod_idx"] = module.idx diff --git a/src/runtime/pipeline/pipeline_executor.h b/src/runtime/pipeline/pipeline_executor.h index febd6dd3a078..a883ba25ec08 100644 --- a/src/runtime/pipeline/pipeline_executor.h +++ b/src/runtime/pipeline/pipeline_executor.h @@ -56,7 +56,7 @@ class TVM_DLL PipelineExecutor : public ModuleNode { */ void Init(const std::vector& modules, const std::string& pipeline_json); /*! - * \brief Use the information of mod_config to create a graph executor list. + * \brief Use the information of mod_config to create a list of graph executor. * \param mod_config The configuration information generated by the library export function call. */ std::vector CreateGraphModules(const ModuleConfig& mod_config); @@ -101,8 +101,6 @@ class TVM_DLL PipelineExecutor : public ModuleNode { LOG(FATAL) << "do not support key " << key; } } - // Check if the vairable 'mod_idx' is successfully read, All moudles here are graph executor - // modules, hence the value of 'mod_idx' should start from 0. ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx; // Load the lib, json, and params information. ICHECK(!lib_name.empty()) << "lib_name is empty."; @@ -116,9 +114,9 @@ class TVM_DLL PipelineExecutor : public ModuleNode { private: /*!\brief The class used to execute and schedule the pipeline logic.*/ PipelineScheduler pipeline_scheduler_; - /*!\brief The Dependency information of each graph runtime module of the pipeline.*/ + /*!\brief The dependency information of each graph runtime module of the pipeline.*/ PipelineConfig pipeline_config_; - /*!\brief The Module information used to create the graph runtimes.*/ + /*!\brief The module information used to create the graph runtimes.*/ ModuleConfig mod_config_; /*!\brief How many outputs are in this pipeline executor.*/ size_t num_outputs_ = 0; @@ -142,8 +140,6 @@ class TVM_DLL PipelineExecutor : public ModuleNode { LOG(FATAL) << "do not support key " << key; } } - // Check if the variable 'mod_idx' is successfully read, All moudles here are graph executor - // modules, hence the value of 'mod_idx' should start from 0. ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx; // Check if the output is successfully read. ICHECK(!output.Empty()) << "Invalid output binding result."; diff --git a/src/runtime/pipeline/pipeline_struct.h b/src/runtime/pipeline/pipeline_struct.h index a21432709ff4..3cc9621702c1 100644 --- a/src/runtime/pipeline/pipeline_struct.h +++ b/src/runtime/pipeline/pipeline_struct.h @@ -69,12 +69,12 @@ struct OutputBindings { } } // When this output is bound to a global interface, check if the global interface index - // is correct. + // start from 0. if (global_binding) { ICHECK(global_output_index >= 0); } else { // When this output is bound to a graph executor module interface, check if the module - // index is correct. + // index start from 0. ICHECK(mod_idx >= 0); bindings[mod_idx] = input_name; } @@ -123,7 +123,7 @@ struct OutputMap { while (reader->NextObjectItem(&key)) { if (key == "output_idx") { reader->Read(&output_idx); - } else if (key == "dependent") { + } else if (key == "dependencies") { reader->Read(&binding); } else { LOG(FATAL) << "do not support key " << key; @@ -138,7 +138,7 @@ struct OutputMap { * \brief The binding or dependency information of each module output interface. */ struct PipelineConfig { - /*!\brief The module index is the key, this variable records all module pipeline configuration + /*!\brief The key is the module index, this variable records all module pipeline configuration * information. */ std::unordered_map config; @@ -155,8 +155,8 @@ struct PipelineConfig { bool Empty() { return config.empty(); } /*! - * \brief Get the number of global outputs that is the outputs of entire pipeline. - * \return How much output does the entire pipeline have. + * \brief Get the number of global outputs. + * \return The number of outputs the entire pipeline has. */ size_t GetGlobalOutputNum() const { size_t num_output = 0; @@ -167,7 +167,7 @@ struct PipelineConfig { } }; /*! - * \brief The informations used to initialize the graph executor module, the information + * \brief The information used to initialize the graph executor module, the information * come from the export library function call. */ struct GraphModuleLoadInfo { diff --git a/tests/python/relay/test_pipeline_executor.py b/tests/python/relay/test_pipeline_executor.py index d8490ce9e671..07bf7e6e2c83 100644 --- a/tests/python/relay/test_pipeline_executor.py +++ b/tests/python/relay/test_pipeline_executor.py @@ -79,9 +79,9 @@ def get_manual_conf(mods, target): pipe_config1 = { "mod_idx": 0, "output": [ - {"output_idx": 0, "dependent": [{"mod_idx": 1, "input_name": "data_0"}]}, - {"output_idx": 1, "dependent": [{"mod_idx": 2, "input_name": "data_0"}]}, - {"output_idx": 2, "dependent": [{"global_output_index": 0}]}, + {"output_idx": 0, "dependencies": [{"mod_idx": 1, "input_name": "data_0"}]}, + {"output_idx": 1, "dependencies": [{"mod_idx": 2, "input_name": "data_0"}]}, + {"output_idx": 2, "dependencies": [{"global_output_index": 0}]}, ], } mod_config[mods[0]] = { @@ -97,7 +97,7 @@ def get_manual_conf(mods, target): pipe_config2 = { "mod_idx": 1, "output": [ - {"output_idx": 0, "dependent": [{"mod_idx": 2, "input_name": "data_1"}]}, + {"output_idx": 0, "dependencies": [{"mod_idx": 2, "input_name": "data_1"}]}, ], } mod_config[mods[1]] = { @@ -112,7 +112,7 @@ def get_manual_conf(mods, target): pipe_config3 = { "mod_idx": 2, - "output": [{"output_idx": 0, "dependent": [{"global_output_index": 1}]}], + "output": [{"output_idx": 0, "dependencies": [{"global_output_index": 1}]}], } mod_config[mods[2]] = { "pipeline": pipe_config3, From ea0311d93df2410dc6a9bff74598c3a0c420078b Mon Sep 17 00:00:00 2001 From: huajsj Date: Tue, 12 Oct 2021 21:24:09 -0700 Subject: [PATCH 23/23] trigger build. --- tests/python/relay/test_pipeline_executor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/relay/test_pipeline_executor.py b/tests/python/relay/test_pipeline_executor.py index 07bf7e6e2c83..4a9b7eacdf65 100644 --- a/tests/python/relay/test_pipeline_executor.py +++ b/tests/python/relay/test_pipeline_executor.py @@ -236,7 +236,7 @@ def test_pipeline(): # Export the parameter configuration to a file. directory_path = tvm.contrib.utils.tempdir().temp_dir - # If the directory does not exist, create the directory. + # If the directory does not exist, create it. if not os.path.exists(directory_path): os.makedirs(directory_path) config_file_name = pipeline_mod_factory.export_library(directory_path)