comaniac commented on a change in pull request #9494:
URL: https://github.com/apache/tvm/pull/9494#discussion_r747719539
##########
File path: python/tvm/contrib/pipeline_executor.py
##########
@@ -93,8 +108,71 @@ def __init__(self, module):
else:
self.module = module
# Get the packed functions from the pipeline executor.
+ self._run = self.module["run"]
+ self._stop = self.module["stop"]
+ self._set_input = self.module["set_input"]
+ self._set_param = self.module["set_param"]
+ self._get_input = self.module["get_input"]
+ self._get_output = self.module["get_output"]
+ self._get_num_inputs = self.module["get_num_inputs"]
self._get_num_outputs = self.module["get_num_outputs"]
+ def run(self, sync=False):
+ """Run the pipeline executor."""
+ self._run(sync)
+
+ def stop(self):
+ """Stop the pipeline executor."""
+ self._stop()
+
+ def set_input(self, key, value):
+ """Set inputs to the module via "value".
+ Parameters
+ ----------
+ key : str
+ The input key
+
+ value : array_like.
+ The input value
+ """
+ self._set_input(key, tvm.nd.array(value, tvm.cpu()))
+
+ def set_params(self, params_name, params_data):
+ """Set params to the module via param name and params data.
+ Parameters
+ ----------
+ params_name : str
+ The params name
+
+ params_data : dict of str to NDArray
+ A list of params data and params key name.
+ """
+ if params_data:
Review comment:
Why `param_data` could be empty?
##########
File path: python/tvm/contrib/pipeline_executor.py
##########
@@ -70,11 +76,20 @@ def build(pipe_configs):
mod_name=mod_config["mod_name"],
)
- mconf["dev"] = "{},{}".format(dev.device_type, dev.device_id)
+ pipe_config["dev"] = "{},{}".format(dev.device_type, dev.device_id)
# Create a pipeline configuration.
- string_config[mod_idx] = mconf
+ module_string_config[mod_idx] = pipe_config
libs[mod_idx] = {"lib": lib, "dev": dev}
+ # Merge the "input_connection", the "param_connection" and the
"module_connection" into one
+ # configuration.
+ string_config = {}
+ if "input_connection" not in config:
+ raise RuntimeError('"input_connection" is missing')
Review comment:
Move this checker to the top of this function with the
`module_connection` checking. If this is missing, it's not necessary to run
anything in between.
##########
File path: python/tvm/contrib/pipeline_executor.py
##########
@@ -505,6 +643,36 @@ def get_config(self):
"dev": module.dev,
}
+ # Create a mapping of global input and module input.
+ input_connection = []
+ for input_name in self.input_bindings.bindings:
+ _, input_dict = self.input_bindings.bindings[input_name].format()
+ # Check the correctness of "input_dict".
+ check_data_param(input_dict)
+ if "interface_name" not in input_dict["connection"][0]:
+ raise RuntimeError(f'"interface_name is missing in connection
config"!')
+ # Establish the mapping of global interface and the mapping of
module interfaces.
+ input_map = {
+ "global_interface_name": input_dict["interface_name"],
+ "mod_idx": input_dict["connection"][0]["mod_idx"],
+ "module_interface_name":
input_dict["connection"][0]["interface_name"],
+ }
+ input_connection.append(input_map)
+
+ # Create a mapping of global param and module param.
+ param_connection = []
+ for param_name in self.param_bindings.bindings:
+ _, param_dict = self.param_bindings.bindings[param_name].format()
+ check_data_param(param_dict)
Review comment:
After reviewing all use cases, I feel you should separate `format` for
two purposes:
1. For `__repr__`. This case can directly be inlined without a new function.
2. For dict with check.
```python
def __repr__(self):
str_format = " |{}: ".format(self.name)
...
return str_format
def to_dict(self, validate=True):
dict_format = {"interface_name": self.name, "connection": []}
...
if validate:
# check data param
return dict_format
```
##########
File path: src/runtime/pipeline/pipeline_executor.cc
##########
@@ -34,13 +36,134 @@ PackedFunc PipelineExecutor::GetFunction(const
std::string& name,
if (name == "get_num_outputs") {
return PackedFunc(
[sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { *rv =
this->NumOutputs(); });
+ } else if (name == "get_num_inputs") {
+ return PackedFunc(
+ [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { *rv =
this->NumInputs(); });
+ } else if (name == "set_input") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
+ if (String::CanConvertFrom(args[0])) {
+ this->SetInput(args[0].operator String(), args[1]);
+ } else {
+ LOG(FATAL) << "Function only support the input name value in the form
of string";
+ }
+ });
+ } else if (name == "set_param") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
+ if (String::CanConvertFrom(args[0]) && String::CanConvertFrom(args[1])) {
+ this->SetParam(args[0].operator String(), args[1].operator String(),
args[2]);
+ } else {
+ LOG(FATAL) << "Function only support the params name and keyin the
form of string";
+ }
+ });
+ } else if (name == "get_output") {
+ return PackedFunc(
+ [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { *rv =
this->GetOutput(); });
+ } else if (name == "get_input") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
+ if (String::CanConvertFrom(args[0])) {
+ *rv = this->GetInput(args[0].operator String());
+ } else {
+ LOG(FATAL) << "Function only support the input name value in the form
of string";
+ }
+ });
+ } else if (name == "run") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
this->Run(args[0]); });
+ } else if (name == "stop") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
this->Stop(); });
} else {
LOG(FATAL) << "Unknown packed function: " << name;
return PackedFunc();
}
return nullptr;
}
+/*!
+ * \brief There are some input called pipeline global input that user need to
use function
+ "set_input" to set the data for it, this function return the number of
such global input.
+ \return Return the number of pipeline global input.
+ */
+
+int PipelineExecutor::NumInputs() const {
+ // The number of inputs obtained from the input configuration.
+ size_t config_inputs_num = input_connection_config.size(), ret = 0;
+ // The number of inputs obtained from the graph runtime and pipeline
configuration.
+ size_t internal_inputs_num = pipeline_config_.GetInputOutputBindingNum();
+ for (auto runtime : runtimes_) {
+ ret += runtime->NumInputs();
+ }
+ // Use the summary of all backend runtime module input number to minus the
internal inputs
+ // number, then we will get the pipeline global input number
Review comment:
```suggestion
// Remove module inputs that are only used internally.
```
##########
File path: src/runtime/pipeline/pipeline_struct.h
##########
@@ -81,25 +131,39 @@ struct OutputBindings {
}
}
};
-
/*!
* \brief The binding information of all outputs of a module.
*/
-struct OutputMap {
+class ConfigOutputBindings {
+ private:
/*! \brief Output binding map, 'int' is output interface index.*/
- std::unordered_map<int, OutputBindings> output_binding_map;
- OutputMap& operator=(const OutputMap& output) {
+ std::unordered_map<int, ConfigBindings> output_binding_map;
Review comment:
```suggestion
std::unordered_map<int, ConfigBindings> output_binding_map_;
```
##########
File path: src/runtime/pipeline/pipeline_struct.h
##########
@@ -21,24 +21,74 @@
#include <assert.h>
#include <dlpack/dlpack.h>
#include <dmlc/json.h>
+#include <tvm/runtime/ndarray.h>
+#include <tvm/runtime/packed_func.h>
#include <limits>
#include <string>
#include <unordered_map>
+#include <utility>
#include <vector>
+namespace tvm {
+namespace runtime {
+#define GLOBAL_MODULE_INDEX -1
+/*!
+ *\brief The mapping of a module output and a global output in the graph
module.
+ */
+struct GlobalOutputPair {
+ int mod_output_idx;
+ int global_output_idx;
+ GlobalOutputPair(const int idx, const int gidx) : mod_output_idx(idx),
global_output_idx(gidx) {}
+ GlobalOutputPair() {}
+};
+
+/*!
+ *\brief Use the module index and the output index to specify a module output.
+ */
+struct ModuleOutputPair {
+ int mod_idx;
+ int output_idx;
+ ModuleOutputPair(const int midx, const int idx) : mod_idx(midx),
output_idx(idx) {}
+ ModuleOutputPair() {}
+};
/*!
* \brief All binding information of a output interface.
*/
-struct OutputBindings {
+class ConfigBindings {
+ private:
/*!\brief Output interface binding information, 'int' is the index of the
module that
* uses this output data as the input interface data, 'string' is the input
interface name
* of the module.
*/
std::unordered_map<int, std::string> bindings;
/*! The index value of the global interface to which the current output are
bound.*/
int global_output_index = std::numeric_limits<int>::min();
+
+ public:
+ /*!
+ *\brief Return the memeber variable "bindings".
+ */
+ std::unordered_map<int, std::string>& Get() { return bindings; }
+ /*!\brief Get the value of global outpu index.*/
+ int GetGlobalOutputIndex() const { return global_output_index; }
/*!\brief Whether this binding is bound to the PipelineExecutor output
interface.*/
bool IsGlobalOutput() const { return global_output_index >= 0; }
Review comment:
Should be the following?
```suggestion
bool IsGlobalOutput() const { return global_output_index >
GLOBAL_MODULE_INDEX; }
```
##########
File path: python/tvm/contrib/pipeline_executor.py
##########
@@ -93,8 +108,71 @@ def __init__(self, module):
else:
self.module = module
# Get the packed functions from the pipeline executor.
+ self._run = self.module["run"]
+ self._stop = self.module["stop"]
+ self._set_input = self.module["set_input"]
+ self._set_param = self.module["set_param"]
+ self._get_input = self.module["get_input"]
+ self._get_output = self.module["get_output"]
+ self._get_num_inputs = self.module["get_num_inputs"]
self._get_num_outputs = self.module["get_num_outputs"]
+ def run(self, sync=False):
+ """Run the pipeline executor."""
+ self._run(sync)
+
+ def stop(self):
+ """Stop the pipeline executor."""
+ self._stop()
+
+ def set_input(self, key, value):
+ """Set inputs to the module via "value".
+ Parameters
+ ----------
+ key : str
+ The input key
+
+ value : array_like.
+ The input value
+ """
+ self._set_input(key, tvm.nd.array(value, tvm.cpu()))
+
+ def set_params(self, params_name, params_data):
+ """Set params to the module via param name and params data.
+ Parameters
+ ----------
+ params_name : str
+ The params name
+
+ params_data : dict of str to NDArray
+ A list of params data and params key name.
+ """
+ if params_data:
+ keys = list(params_data.keys())
+ keys.sort(key=lambda x: -np.prod(params_data[x].shape))
+ for k in keys:
+ self._set_param(params_name, k, tvm.nd.array(params_data[k],
tvm.cpu()))
Review comment:
Cannot understand the logic here. Why you need to set the parameters in
this specific order?
##########
File path: src/runtime/pipeline/pipeline_struct.h
##########
@@ -134,37 +220,316 @@ struct OutputMap {
}
}
};
+
+/*!
+ * \brief A map of the global module input interfaces and the graph modudles
input interfaces.
+ */
+struct InputConnectionConfig {
+ /*!\brief The key is the name of global module input interfaces. the value
is the pair of
+ * the index of a graph module and the name of a graph module input
interface.
+ */
+ std::unordered_map<std::string, std::pair<int, std::string>>
input_connection;
+ bool Empty() { return input_connection.empty(); }
+ std::pair<int, std::string> operator[](const std::string key) {
+ if (input_connection.find(key) == input_connection.end()) {
+ LOG(FATAL) << "Not find the key " << key;
+ }
+ return input_connection[key];
+ }
+
+ size_t size() const { return input_connection.size(); }
+ /*!
+ * \brief Create a input connection config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ reader->BeginObject();
+ std::string key;
+ std::string global_interface_name;
+ std::string module_interface_name;
+ int mod_idx = -1;
+ while (reader->NextObjectItem(&key)) {
+ if (key == "global_interface_name") {
+ reader->Read(&global_interface_name);
+ } else if (key == "mod_idx") {
+ reader->Read(&mod_idx);
+ } else if (key == "module_interface_name") {
+ reader->Read(&module_interface_name);
+ } else {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ ICHECK(!global_interface_name.empty()) << "Invalid global interface name
value";
+ ICHECK(!module_interface_name.empty()) << "Invalid module interface name
value";
+ input_connection[global_interface_name] = make_pair(mod_idx,
module_interface_name);
+ }
+ }
+};
+/*!
+ * \brief A map of the global module param interfaces and the graph modudles
param.
+ */
+struct ParamConnectionConfig {
+ /*!\brief The key is the name of global module param interfaces. the value
is the
+ * index of a graph module.
+ */
+ std::unordered_map<std::string, int> param_connection;
+ bool Empty() { return param_connection.empty(); }
+ int operator[](const std::string key) {
+ if (param_connection.find(key) == param_connection.end()) {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ return param_connection[key];
+ }
+ /*!
+ * \brief Create a param connection config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ reader->BeginObject();
+ std::string key;
+ std::string global_param_name;
+ int mod_idx = -1;
+ while (reader->NextObjectItem(&key)) {
+ if (key == "global_param_name") {
+ reader->Read(&global_param_name);
+ } else if (key == "mod_idx") {
+ reader->Read(&mod_idx);
+ } else {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ ICHECK(!global_param_name.empty()) << "Invalid global param name value";
+ param_connection[global_param_name] = mod_idx;
+ }
+ }
+};
/*!
* \brief The binding or dependency information of each module output
interface.
*/
-struct PipelineConfig {
- /*!\brief The key is the module index, this variable records all module
pipeline configuration
+class ConfigPipelineExecution {
+ private:
+ /*
+ *!\brief The key is the module index, this variable records all module
pipeline configuration
* information.
*/
- std::unordered_map<int, OutputMap> config;
- OutputMap& operator[](int key) {
+ std::unordered_map<int, ConfigOutputBindings> config;
+ /*
+ *\brief The key is the global output index, this variable records the
mapping of global output
+ * and the module output.
+ */
+ std::unordered_map<int, ModuleOutputPair> global_output_map;
+ /*
+ *\brief The number of binding of module outputs and inputs.
+ */
+ size_t module_input_output_binding_total_num;
+
+ public:
+ ConfigOutputBindings& operator[](int key) {
ICHECK(config.find(key) != config.end());
return config[key];
}
+ /*!
+ *\brief Check if the module index existing in the "config".
+ */
+ bool FindModuleInConfig(int mod_idx) { return config.find(mod_idx) !=
config.end(); }
+ /*!
+ *\brief Build the mapping of key and "ConfigOutputBindings", key is module
index.
+ */
+ void Insert(int key, const ConfigOutputBindings& map) { config[key] = map; }
- void Insert(int key, const OutputMap& map) { config[key] = map; }
-
- /*!\brief This function is used to verify whether config is loaded
successfully.
+ /*
+ *!\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.
* \return The number of outputs the entire pipeline has.
*/
size_t GetGlobalOutputNum() const {
- size_t num_output = 0;
+ // The number of pipeline outputs is the size of "global_output_map";
+ return global_output_map.size();
+ }
+ /*
+ *!\brief Get the map of global outputs and module outputs.
+ */
+ std::unordered_map<int, ModuleOutputPair>&
GetGlobalConfigOutputBindings(void) {
+ return global_output_map;
+ }
+ /*
+ *!\brief Get the number of module output and module input bindings.
+ */
+ size_t GetInputOutputBindingNum() const { return
module_input_output_binding_total_num; }
+ /*
+ *!\brief Parse the config to construct data struct using in pipeline
execution.
+ */
+ void ParseConfiguration(const std::unordered_map<int, ConfigOutputBindings>&
config) {
+ if (config.empty()) {
+ LOG(FATAL) << "The Configuration loading not finish yet.";
+ }
+ module_input_output_binding_total_num = 0;
for (auto mod_output : config) {
- num_output += mod_output.second.GetGlobalOutputNum();
+ // Get the numbers of binding of input and output.
+ module_input_output_binding_total_num +=
mod_output.second.GetInputOutputBindingNum();
+ // Use global output index as key to create a mapping of global index
and module output.
+ const std::vector<GlobalOutputPair>& global_output =
+ mod_output.second.GetGlobalConfigOutputBindings();
+
+ for (auto output : global_output) {
+ global_output_map[output.global_output_idx] =
+ ModuleOutputPair(mod_output.first, output.mod_output_idx);
+ }
}
- return num_output;
+ return;
+ }
+ /*!
+ * \brief Create a pipeline config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ std::string key;
+ reader->BeginObject();
+ int mod_idx = -1;
+ ConfigOutputBindings 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 {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ // Check if the output is successfully read.
+ ICHECK(!output.Empty()) << "Invalid output binding result.";
+ Insert(mod_idx, output);
+ }
+ // Call this function after "config" loading finished.
+ ParseConfiguration(config);
+ }
+};
+/*
+ *\brief Runtime of backend.
+ */
+class BackendRuntime {
+ private:
+ /*\brief The index of runtime indicate the position in the pipeline.*/
+ int runtime_idx;
+ /*\brief The Runtime module of a backedn graph executor.*/
+ Module module;
+ /*!
+ *\brief To transfer data between two different backends, we need a local
+ * tensor variable as a medium. This variable is a mapping of input data and
local
+ * data.
+ */
+ std::unordered_map<DLTensor*, DLTensor*> input_tensor_local_copy;
+ /*!\brief The packed functions.*/
+ tvm::runtime::PackedFunc run;
+ tvm::runtime::PackedFunc set_input;
+ tvm::runtime::PackedFunc get_input;
+ tvm::runtime::PackedFunc get_output;
+ tvm::runtime::PackedFunc get_num_output;
+ tvm::runtime::PackedFunc get_num_inputs;
+ tvm::runtime::PackedFunc get_input_index;
Review comment:
Why you need to keep them?
##########
File path: src/runtime/pipeline/pipeline_struct.h
##########
@@ -134,37 +220,316 @@ struct OutputMap {
}
}
};
+
+/*!
+ * \brief A map of the global module input interfaces and the graph modudles
input interfaces.
+ */
+struct InputConnectionConfig {
+ /*!\brief The key is the name of global module input interfaces. the value
is the pair of
+ * the index of a graph module and the name of a graph module input
interface.
+ */
+ std::unordered_map<std::string, std::pair<int, std::string>>
input_connection;
+ bool Empty() { return input_connection.empty(); }
+ std::pair<int, std::string> operator[](const std::string key) {
+ if (input_connection.find(key) == input_connection.end()) {
+ LOG(FATAL) << "Not find the key " << key;
+ }
+ return input_connection[key];
+ }
+
+ size_t size() const { return input_connection.size(); }
+ /*!
+ * \brief Create a input connection config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ reader->BeginObject();
+ std::string key;
+ std::string global_interface_name;
+ std::string module_interface_name;
+ int mod_idx = -1;
+ while (reader->NextObjectItem(&key)) {
+ if (key == "global_interface_name") {
+ reader->Read(&global_interface_name);
+ } else if (key == "mod_idx") {
+ reader->Read(&mod_idx);
+ } else if (key == "module_interface_name") {
+ reader->Read(&module_interface_name);
+ } else {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ ICHECK(!global_interface_name.empty()) << "Invalid global interface name
value";
+ ICHECK(!module_interface_name.empty()) << "Invalid module interface name
value";
+ input_connection[global_interface_name] = make_pair(mod_idx,
module_interface_name);
+ }
+ }
+};
+/*!
+ * \brief A map of the global module param interfaces and the graph modudles
param.
+ */
+struct ParamConnectionConfig {
+ /*!\brief The key is the name of global module param interfaces. the value
is the
+ * index of a graph module.
+ */
+ std::unordered_map<std::string, int> param_connection;
+ bool Empty() { return param_connection.empty(); }
+ int operator[](const std::string key) {
+ if (param_connection.find(key) == param_connection.end()) {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ return param_connection[key];
+ }
+ /*!
+ * \brief Create a param connection config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ reader->BeginObject();
+ std::string key;
+ std::string global_param_name;
+ int mod_idx = -1;
+ while (reader->NextObjectItem(&key)) {
+ if (key == "global_param_name") {
+ reader->Read(&global_param_name);
+ } else if (key == "mod_idx") {
+ reader->Read(&mod_idx);
+ } else {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ ICHECK(!global_param_name.empty()) << "Invalid global param name value";
+ param_connection[global_param_name] = mod_idx;
+ }
+ }
+};
/*!
* \brief The binding or dependency information of each module output
interface.
*/
-struct PipelineConfig {
- /*!\brief The key is the module index, this variable records all module
pipeline configuration
+class ConfigPipelineExecution {
+ private:
+ /*
+ *!\brief The key is the module index, this variable records all module
pipeline configuration
* information.
*/
- std::unordered_map<int, OutputMap> config;
- OutputMap& operator[](int key) {
+ std::unordered_map<int, ConfigOutputBindings> config;
+ /*
+ *\brief The key is the global output index, this variable records the
mapping of global output
+ * and the module output.
+ */
+ std::unordered_map<int, ModuleOutputPair> global_output_map;
+ /*
+ *\brief The number of binding of module outputs and inputs.
+ */
+ size_t module_input_output_binding_total_num;
+
+ public:
+ ConfigOutputBindings& operator[](int key) {
ICHECK(config.find(key) != config.end());
return config[key];
}
+ /*!
+ *\brief Check if the module index existing in the "config".
+ */
+ bool FindModuleInConfig(int mod_idx) { return config.find(mod_idx) !=
config.end(); }
+ /*!
+ *\brief Build the mapping of key and "ConfigOutputBindings", key is module
index.
+ */
+ void Insert(int key, const ConfigOutputBindings& map) { config[key] = map; }
- void Insert(int key, const OutputMap& map) { config[key] = map; }
-
- /*!\brief This function is used to verify whether config is loaded
successfully.
+ /*
+ *!\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.
* \return The number of outputs the entire pipeline has.
*/
size_t GetGlobalOutputNum() const {
- size_t num_output = 0;
+ // The number of pipeline outputs is the size of "global_output_map";
+ return global_output_map.size();
+ }
+ /*
+ *!\brief Get the map of global outputs and module outputs.
+ */
+ std::unordered_map<int, ModuleOutputPair>&
GetGlobalConfigOutputBindings(void) {
+ return global_output_map;
+ }
+ /*
+ *!\brief Get the number of module output and module input bindings.
+ */
+ size_t GetInputOutputBindingNum() const { return
module_input_output_binding_total_num; }
+ /*
+ *!\brief Parse the config to construct data struct using in pipeline
execution.
+ */
+ void ParseConfiguration(const std::unordered_map<int, ConfigOutputBindings>&
config) {
+ if (config.empty()) {
+ LOG(FATAL) << "The Configuration loading not finish yet.";
+ }
+ module_input_output_binding_total_num = 0;
for (auto mod_output : config) {
- num_output += mod_output.second.GetGlobalOutputNum();
+ // Get the numbers of binding of input and output.
+ module_input_output_binding_total_num +=
mod_output.second.GetInputOutputBindingNum();
+ // Use global output index as key to create a mapping of global index
and module output.
+ const std::vector<GlobalOutputPair>& global_output =
+ mod_output.second.GetGlobalConfigOutputBindings();
+
+ for (auto output : global_output) {
+ global_output_map[output.global_output_idx] =
+ ModuleOutputPair(mod_output.first, output.mod_output_idx);
+ }
}
- return num_output;
+ return;
+ }
+ /*!
+ * \brief Create a pipeline config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ std::string key;
+ reader->BeginObject();
+ int mod_idx = -1;
+ ConfigOutputBindings 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 {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ // Check if the output is successfully read.
+ ICHECK(!output.Empty()) << "Invalid output binding result.";
+ Insert(mod_idx, output);
+ }
+ // Call this function after "config" loading finished.
+ ParseConfiguration(config);
+ }
+};
+/*
+ *\brief Runtime of backend.
+ */
+class BackendRuntime {
+ private:
+ /*\brief The index of runtime indicate the position in the pipeline.*/
+ int runtime_idx;
+ /*\brief The Runtime module of a backedn graph executor.*/
+ Module module;
+ /*!
+ *\brief To transfer data between two different backends, we need a local
+ * tensor variable as a medium. This variable is a mapping of input data and
local
+ * data.
+ */
+ std::unordered_map<DLTensor*, DLTensor*> input_tensor_local_copy;
Review comment:
```suggestion
std::unordered_map<DLTensor*, DLTensor*> input_tensor_local_copy_;
```
##########
File path: src/runtime/pipeline/pipeline_struct.h
##########
@@ -134,37 +220,316 @@ struct OutputMap {
}
}
};
+
+/*!
+ * \brief A map of the global module input interfaces and the graph modudles
input interfaces.
+ */
+struct InputConnectionConfig {
+ /*!\brief The key is the name of global module input interfaces. the value
is the pair of
+ * the index of a graph module and the name of a graph module input
interface.
+ */
+ std::unordered_map<std::string, std::pair<int, std::string>>
input_connection;
+ bool Empty() { return input_connection.empty(); }
+ std::pair<int, std::string> operator[](const std::string key) {
+ if (input_connection.find(key) == input_connection.end()) {
+ LOG(FATAL) << "Not find the key " << key;
+ }
+ return input_connection[key];
+ }
+
+ size_t size() const { return input_connection.size(); }
+ /*!
+ * \brief Create a input connection config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ reader->BeginObject();
+ std::string key;
+ std::string global_interface_name;
+ std::string module_interface_name;
+ int mod_idx = -1;
+ while (reader->NextObjectItem(&key)) {
+ if (key == "global_interface_name") {
+ reader->Read(&global_interface_name);
+ } else if (key == "mod_idx") {
+ reader->Read(&mod_idx);
+ } else if (key == "module_interface_name") {
+ reader->Read(&module_interface_name);
+ } else {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ ICHECK(!global_interface_name.empty()) << "Invalid global interface name
value";
+ ICHECK(!module_interface_name.empty()) << "Invalid module interface name
value";
+ input_connection[global_interface_name] = make_pair(mod_idx,
module_interface_name);
+ }
+ }
+};
+/*!
+ * \brief A map of the global module param interfaces and the graph modudles
param.
+ */
+struct ParamConnectionConfig {
+ /*!\brief The key is the name of global module param interfaces. the value
is the
+ * index of a graph module.
+ */
+ std::unordered_map<std::string, int> param_connection;
+ bool Empty() { return param_connection.empty(); }
+ int operator[](const std::string key) {
+ if (param_connection.find(key) == param_connection.end()) {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ return param_connection[key];
+ }
+ /*!
+ * \brief Create a param connection config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ reader->BeginObject();
+ std::string key;
+ std::string global_param_name;
+ int mod_idx = -1;
+ while (reader->NextObjectItem(&key)) {
+ if (key == "global_param_name") {
+ reader->Read(&global_param_name);
+ } else if (key == "mod_idx") {
+ reader->Read(&mod_idx);
+ } else {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ ICHECK(!global_param_name.empty()) << "Invalid global param name value";
+ param_connection[global_param_name] = mod_idx;
+ }
+ }
+};
/*!
* \brief The binding or dependency information of each module output
interface.
*/
-struct PipelineConfig {
- /*!\brief The key is the module index, this variable records all module
pipeline configuration
+class ConfigPipelineExecution {
+ private:
+ /*
+ *!\brief The key is the module index, this variable records all module
pipeline configuration
* information.
*/
- std::unordered_map<int, OutputMap> config;
- OutputMap& operator[](int key) {
+ std::unordered_map<int, ConfigOutputBindings> config;
+ /*
+ *\brief The key is the global output index, this variable records the
mapping of global output
+ * and the module output.
+ */
+ std::unordered_map<int, ModuleOutputPair> global_output_map;
+ /*
+ *\brief The number of binding of module outputs and inputs.
+ */
+ size_t module_input_output_binding_total_num;
+
+ public:
+ ConfigOutputBindings& operator[](int key) {
ICHECK(config.find(key) != config.end());
return config[key];
}
+ /*!
+ *\brief Check if the module index existing in the "config".
+ */
+ bool FindModuleInConfig(int mod_idx) { return config.find(mod_idx) !=
config.end(); }
+ /*!
+ *\brief Build the mapping of key and "ConfigOutputBindings", key is module
index.
+ */
+ void Insert(int key, const ConfigOutputBindings& map) { config[key] = map; }
- void Insert(int key, const OutputMap& map) { config[key] = map; }
-
- /*!\brief This function is used to verify whether config is loaded
successfully.
+ /*
+ *!\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.
* \return The number of outputs the entire pipeline has.
*/
size_t GetGlobalOutputNum() const {
- size_t num_output = 0;
+ // The number of pipeline outputs is the size of "global_output_map";
+ return global_output_map.size();
+ }
+ /*
+ *!\brief Get the map of global outputs and module outputs.
+ */
+ std::unordered_map<int, ModuleOutputPair>&
GetGlobalConfigOutputBindings(void) {
+ return global_output_map;
+ }
+ /*
+ *!\brief Get the number of module output and module input bindings.
+ */
+ size_t GetInputOutputBindingNum() const { return
module_input_output_binding_total_num; }
+ /*
+ *!\brief Parse the config to construct data struct using in pipeline
execution.
+ */
+ void ParseConfiguration(const std::unordered_map<int, ConfigOutputBindings>&
config) {
+ if (config.empty()) {
+ LOG(FATAL) << "The Configuration loading not finish yet.";
+ }
+ module_input_output_binding_total_num = 0;
for (auto mod_output : config) {
- num_output += mod_output.second.GetGlobalOutputNum();
+ // Get the numbers of binding of input and output.
+ module_input_output_binding_total_num +=
mod_output.second.GetInputOutputBindingNum();
+ // Use global output index as key to create a mapping of global index
and module output.
+ const std::vector<GlobalOutputPair>& global_output =
+ mod_output.second.GetGlobalConfigOutputBindings();
+
+ for (auto output : global_output) {
+ global_output_map[output.global_output_idx] =
+ ModuleOutputPair(mod_output.first, output.mod_output_idx);
+ }
}
- return num_output;
+ return;
+ }
+ /*!
+ * \brief Create a pipeline config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ std::string key;
+ reader->BeginObject();
+ int mod_idx = -1;
+ ConfigOutputBindings 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 {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ // Check if the output is successfully read.
+ ICHECK(!output.Empty()) << "Invalid output binding result.";
+ Insert(mod_idx, output);
+ }
+ // Call this function after "config" loading finished.
+ ParseConfiguration(config);
+ }
+};
+/*
+ *\brief Runtime of backend.
+ */
+class BackendRuntime {
+ private:
+ /*\brief The index of runtime indicate the position in the pipeline.*/
+ int runtime_idx;
+ /*\brief The Runtime module of a backedn graph executor.*/
+ Module module;
+ /*!
+ *\brief To transfer data between two different backends, we need a local
+ * tensor variable as a medium. This variable is a mapping of input data and
local
+ * data.
+ */
+ std::unordered_map<DLTensor*, DLTensor*> input_tensor_local_copy;
+ /*!\brief The packed functions.*/
+ tvm::runtime::PackedFunc run;
+ tvm::runtime::PackedFunc set_input;
+ tvm::runtime::PackedFunc get_input;
+ tvm::runtime::PackedFunc get_output;
+ tvm::runtime::PackedFunc get_num_output;
+ tvm::runtime::PackedFunc get_num_inputs;
+ tvm::runtime::PackedFunc get_input_index;
+ /*!\brief The new DLTensor have same shape, data type with a existing
DLTensor.*/
+ DLTensor* CreateFromDLTensor(const DLTensor* from) {
+ DLTensor* ret = NULL;
+ TVMArrayAlloc(from->shape, from->ndim, from->dtype.code, from->dtype.bits,
from->dtype.lanes,
+ kDLCPU, 0, &ret);
Review comment:
Why CPU?
##########
File path: python/tvm/contrib/pipeline_executor.py
##########
@@ -465,9 +588,24 @@ def get_config(self):
will be used to create pipeline executor.
"""
+ def check_data_param(data_dict):
+ if "interface_name" not in data_dict:
+ raise RuntimeError(f'"inteface_name" is missing in global
config!"')
+ if "connection" not in data_dict:
+ raise RuntimeError(f'"connection" is missing!"')
+ # The global interface mapping should be one-to-one.
+ if len(data_dict["connection"]) == 0:
Review comment:
```suggestion
if not data_dict["connection"]:
```
##########
File path: python/tvm/contrib/pipeline_executor.py
##########
@@ -93,8 +108,71 @@ def __init__(self, module):
else:
self.module = module
# Get the packed functions from the pipeline executor.
+ self._run = self.module["run"]
+ self._stop = self.module["stop"]
+ self._set_input = self.module["set_input"]
+ self._set_param = self.module["set_param"]
+ self._get_input = self.module["get_input"]
+ self._get_output = self.module["get_output"]
+ self._get_num_inputs = self.module["get_num_inputs"]
self._get_num_outputs = self.module["get_num_outputs"]
+ def run(self, sync=False):
+ """Run the pipeline executor."""
+ self._run(sync)
+
+ def stop(self):
+ """Stop the pipeline executor."""
+ self._stop()
+
+ def set_input(self, key, value):
+ """Set inputs to the module via "value".
+ Parameters
+ ----------
+ key : str
+ The input key
+
+ value : array_like.
+ The input value
+ """
+ self._set_input(key, tvm.nd.array(value, tvm.cpu()))
+
+ def set_params(self, params_name, params_data):
+ """Set params to the module via param name and params data.
+ Parameters
+ ----------
+ params_name : str
+ The params name
+
+ params_data : dict of str to NDArray
+ A list of params data and params key name.
+ """
+ if params_data:
+ keys = list(params_data.keys())
+ keys.sort(key=lambda x: -np.prod(params_data[x].shape))
+ for k in keys:
+ self._set_param(params_name, k, tvm.nd.array(params_data[k],
tvm.cpu()))
+
+ def get_input(self, key):
+ """Get the input via a input name.
+ Parameters
+ ----------
+ key : str
+ The input key
+ """
+ self._get_input(key)
+
+ def get_output(self):
+ """Get the output.
+
+ Parameters:
+ -----------
+ out : Array[NDArray]
+ A list of output data.
Review comment:
This should be "Returns"?
##########
File path: python/tvm/contrib/pipeline_executor.py
##########
@@ -93,8 +108,71 @@ def __init__(self, module):
else:
self.module = module
# Get the packed functions from the pipeline executor.
+ self._run = self.module["run"]
+ self._stop = self.module["stop"]
+ self._set_input = self.module["set_input"]
+ self._set_param = self.module["set_param"]
+ self._get_input = self.module["get_input"]
+ self._get_output = self.module["get_output"]
+ self._get_num_inputs = self.module["get_num_inputs"]
self._get_num_outputs = self.module["get_num_outputs"]
+ def run(self, sync=False):
+ """Run the pipeline executor."""
+ self._run(sync)
+
+ def stop(self):
+ """Stop the pipeline executor."""
+ self._stop()
+
+ def set_input(self, key, value):
+ """Set inputs to the module via "value".
+ Parameters
+ ----------
+ key : str
+ The input key
+
+ value : array_like.
+ The input value
+ """
+ self._set_input(key, tvm.nd.array(value, tvm.cpu()))
+
+ def set_params(self, params_name, params_data):
+ """Set params to the module via param name and params data.
+ Parameters
+ ----------
+ params_name : str
+ The params name
+
+ params_data : dict of str to NDArray
+ A list of params data and params key name.
+ """
+ if params_data:
+ keys = list(params_data.keys())
+ keys.sort(key=lambda x: -np.prod(params_data[x].shape))
+ for k in keys:
+ self._set_param(params_name, k, tvm.nd.array(params_data[k],
tvm.cpu()))
+
+ def get_input(self, key):
+ """Get the input via a input name.
+ Parameters
+ ----------
+ key : str
+ The input key
Review comment:
Return?
##########
File path: python/tvm/contrib/pipeline_executor.py
##########
@@ -199,12 +287,21 @@ def is_pipeline_executor_interface(self):
return not isinstance(self.io_owner, PipelineConfig.ModuleWrapper)
def __repr__(self):
- # Get all binding information.
- ret = " |{}: ".format(self.name)
+ # Get all binding information in the form of string.
+ ret, _ = self.format()
+ return ret
+
+ def format(self):
+ """Obtain binding information in the form of string and
dictionary."""
Review comment:
1. Write a complete docstring with return type.
2. The function name and docstring are confusing.
3. Based on your use cases, it is not necessary to return a tuple. Instead,
you could just have two modes: `format(self, ret_dict=False)`.
##########
File path: python/tvm/contrib/pipeline_executor.py
##########
@@ -505,6 +643,36 @@ def get_config(self):
"dev": module.dev,
}
+ # Create a mapping of global input and module input.
+ input_connection = []
+ for input_name in self.input_bindings.bindings:
+ _, input_dict = self.input_bindings.bindings[input_name].format()
+ # Check the correctness of "input_dict".
+ check_data_param(input_dict)
+ if "interface_name" not in input_dict["connection"][0]:
+ raise RuntimeError(f'"interface_name is missing in connection
config"!')
Review comment:
```suggestion
raise RuntimeError(f"interface_name is missing in connection
config!")
```
##########
File path: python/tvm/contrib/pipeline_executor.py
##########
@@ -93,8 +108,71 @@ def __init__(self, module):
else:
self.module = module
# Get the packed functions from the pipeline executor.
+ self._run = self.module["run"]
+ self._stop = self.module["stop"]
+ self._set_input = self.module["set_input"]
+ self._set_param = self.module["set_param"]
+ self._get_input = self.module["get_input"]
+ self._get_output = self.module["get_output"]
+ self._get_num_inputs = self.module["get_num_inputs"]
self._get_num_outputs = self.module["get_num_outputs"]
+ def run(self, sync=False):
+ """Run the pipeline executor."""
+ self._run(sync)
+
+ def stop(self):
+ """Stop the pipeline executor."""
+ self._stop()
+
+ def set_input(self, key, value):
+ """Set inputs to the module via "value".
+ Parameters
+ ----------
+ key : str
+ The input key
+
+ value : array_like.
+ The input value
+ """
+ self._set_input(key, tvm.nd.array(value, tvm.cpu()))
+
+ def set_params(self, params_name, params_data):
+ """Set params to the module via param name and params data.
+ Parameters
+ ----------
+ params_name : str
+ The params name
+
+ params_data : dict of str to NDArray
+ A list of params data and params key name.
+ """
+ if params_data:
+ keys = list(params_data.keys())
+ keys.sort(key=lambda x: -np.prod(params_data[x].shape))
+ for k in keys:
+ self._set_param(params_name, k, tvm.nd.array(params_data[k],
tvm.cpu()))
+
+ def get_input(self, key):
+ """Get the input via a input name.
+ Parameters
+ ----------
+ key : str
+ The input key
+ """
+ self._get_input(key)
+
+ def get_output(self):
+ """Get the output.
+
+ Parameters:
+ -----------
+ out : Array[NDArray]
+ A list of output data.
+ """
+
Review comment:
Remove this empty line
##########
File path: src/runtime/pipeline/pipeline_executor.cc
##########
@@ -34,13 +36,134 @@ PackedFunc PipelineExecutor::GetFunction(const
std::string& name,
if (name == "get_num_outputs") {
return PackedFunc(
[sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { *rv =
this->NumOutputs(); });
+ } else if (name == "get_num_inputs") {
+ return PackedFunc(
+ [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { *rv =
this->NumInputs(); });
+ } else if (name == "set_input") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
+ if (String::CanConvertFrom(args[0])) {
+ this->SetInput(args[0].operator String(), args[1]);
+ } else {
+ LOG(FATAL) << "Function only support the input name value in the form
of string";
+ }
+ });
+ } else if (name == "set_param") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
+ if (String::CanConvertFrom(args[0]) && String::CanConvertFrom(args[1])) {
+ this->SetParam(args[0].operator String(), args[1].operator String(),
args[2]);
+ } else {
+ LOG(FATAL) << "Function only support the params name and keyin the
form of string";
+ }
+ });
+ } else if (name == "get_output") {
+ return PackedFunc(
+ [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { *rv =
this->GetOutput(); });
+ } else if (name == "get_input") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
+ if (String::CanConvertFrom(args[0])) {
+ *rv = this->GetInput(args[0].operator String());
+ } else {
+ LOG(FATAL) << "Function only support the input name value in the form
of string";
+ }
+ });
+ } else if (name == "run") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
this->Run(args[0]); });
+ } else if (name == "stop") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
this->Stop(); });
} else {
LOG(FATAL) << "Unknown packed function: " << name;
return PackedFunc();
}
return nullptr;
}
+/*!
+ * \brief There are some input called pipeline global input that user need to
use function
+ "set_input" to set the data for it, this function return the number of
such global input.
+ \return Return the number of pipeline global input.
+ */
+
+int PipelineExecutor::NumInputs() const {
+ // The number of inputs obtained from the input configuration.
+ size_t config_inputs_num = input_connection_config.size(), ret = 0;
+ // The number of inputs obtained from the graph runtime and pipeline
configuration.
+ size_t internal_inputs_num = pipeline_config_.GetInputOutputBindingNum();
+ for (auto runtime : runtimes_) {
+ ret += runtime->NumInputs();
+ }
+ // Use the summary of all backend runtime module input number to minus the
internal inputs
+ // number, then we will get the pipeline global input number
+ ret -= internal_inputs_num;
+ // Check whether these two numbers are equal.
+ if (config_inputs_num != ret) {
+ LOG(FATAL) << "The number of inputs from the configuration file is
inconsistent!";
Review comment:
```suggestion
LOG(FATAL) << "Incorrect input number in configuration: Expected " <<
ret << " but got " << config_inputs_num;
```
##########
File path: src/runtime/pipeline/pipeline_executor.cc
##########
@@ -34,13 +36,134 @@ PackedFunc PipelineExecutor::GetFunction(const
std::string& name,
if (name == "get_num_outputs") {
return PackedFunc(
[sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { *rv =
this->NumOutputs(); });
+ } else if (name == "get_num_inputs") {
+ return PackedFunc(
+ [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { *rv =
this->NumInputs(); });
+ } else if (name == "set_input") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
+ if (String::CanConvertFrom(args[0])) {
+ this->SetInput(args[0].operator String(), args[1]);
+ } else {
+ LOG(FATAL) << "Function only support the input name value in the form
of string";
+ }
+ });
+ } else if (name == "set_param") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
+ if (String::CanConvertFrom(args[0]) && String::CanConvertFrom(args[1])) {
+ this->SetParam(args[0].operator String(), args[1].operator String(),
args[2]);
+ } else {
+ LOG(FATAL) << "Function only support the params name and keyin the
form of string";
+ }
+ });
+ } else if (name == "get_output") {
+ return PackedFunc(
+ [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { *rv =
this->GetOutput(); });
+ } else if (name == "get_input") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
+ if (String::CanConvertFrom(args[0])) {
+ *rv = this->GetInput(args[0].operator String());
+ } else {
+ LOG(FATAL) << "Function only support the input name value in the form
of string";
+ }
+ });
+ } else if (name == "run") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
this->Run(args[0]); });
+ } else if (name == "stop") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
this->Stop(); });
} else {
LOG(FATAL) << "Unknown packed function: " << name;
return PackedFunc();
}
return nullptr;
}
+/*!
+ * \brief There are some input called pipeline global input that user need to
use function
+ "set_input" to set the data for it, this function return the number of
such global input.
+ \return Return the number of pipeline global input.
+ */
+
Review comment:
```suggestion
/*!
* \brief Pipeline global inputs are the data inputs that have to be set by
users with "set_input". This function returns the number of pipeline global
inputs.
\return Return the number of pipeline global inputs.
*/
```
##########
File path: python/tvm/contrib/pipeline_executor.py
##########
@@ -93,8 +108,71 @@ def __init__(self, module):
else:
self.module = module
# Get the packed functions from the pipeline executor.
+ self._run = self.module["run"]
+ self._stop = self.module["stop"]
+ self._set_input = self.module["set_input"]
+ self._set_param = self.module["set_param"]
+ self._get_input = self.module["get_input"]
+ self._get_output = self.module["get_output"]
+ self._get_num_inputs = self.module["get_num_inputs"]
self._get_num_outputs = self.module["get_num_outputs"]
+ def run(self, sync=False):
+ """Run the pipeline executor."""
+ self._run(sync)
+
+ def stop(self):
+ """Stop the pipeline executor."""
+ self._stop()
+
+ def set_input(self, key, value):
+ """Set inputs to the module via "value".
+ Parameters
+ ----------
+ key : str
+ The input key
+
+ value : array_like.
+ The input value
+ """
+ self._set_input(key, tvm.nd.array(value, tvm.cpu()))
Review comment:
`tvm.nd.array(value, tvm.cpu())` is too strict. For example, the input
could already be an ND array. Also why fixing the target to CPU?
##########
File path: src/runtime/pipeline/pipeline_executor.cc
##########
@@ -34,13 +36,134 @@ PackedFunc PipelineExecutor::GetFunction(const
std::string& name,
if (name == "get_num_outputs") {
return PackedFunc(
[sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { *rv =
this->NumOutputs(); });
+ } else if (name == "get_num_inputs") {
+ return PackedFunc(
+ [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { *rv =
this->NumInputs(); });
+ } else if (name == "set_input") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
+ if (String::CanConvertFrom(args[0])) {
+ this->SetInput(args[0].operator String(), args[1]);
+ } else {
+ LOG(FATAL) << "Function only support the input name value in the form
of string";
+ }
+ });
+ } else if (name == "set_param") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
+ if (String::CanConvertFrom(args[0]) && String::CanConvertFrom(args[1])) {
+ this->SetParam(args[0].operator String(), args[1].operator String(),
args[2]);
+ } else {
+ LOG(FATAL) << "Function only support the params name and keyin the
form of string";
+ }
+ });
+ } else if (name == "get_output") {
+ return PackedFunc(
+ [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { *rv =
this->GetOutput(); });
+ } else if (name == "get_input") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
+ if (String::CanConvertFrom(args[0])) {
+ *rv = this->GetInput(args[0].operator String());
+ } else {
+ LOG(FATAL) << "Function only support the input name value in the form
of string";
+ }
+ });
+ } else if (name == "run") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
this->Run(args[0]); });
+ } else if (name == "stop") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
this->Stop(); });
} else {
LOG(FATAL) << "Unknown packed function: " << name;
return PackedFunc();
}
return nullptr;
}
+/*!
+ * \brief There are some input called pipeline global input that user need to
use function
+ "set_input" to set the data for it, this function return the number of
such global input.
+ \return Return the number of pipeline global input.
+ */
+
+int PipelineExecutor::NumInputs() const {
+ // The number of inputs obtained from the input configuration.
+ size_t config_inputs_num = input_connection_config.size(), ret = 0;
+ // The number of inputs obtained from the graph runtime and pipeline
configuration.
+ size_t internal_inputs_num = pipeline_config_.GetInputOutputBindingNum();
+ for (auto runtime : runtimes_) {
+ ret += runtime->NumInputs();
+ }
+ // Use the summary of all backend runtime module input number to minus the
internal inputs
+ // number, then we will get the pipeline global input number
+ ret -= internal_inputs_num;
+ // Check whether these two numbers are equal.
+ if (config_inputs_num != ret) {
+ LOG(FATAL) << "The number of inputs from the configuration file is
inconsistent!";
+ }
+ return ret;
+}
+/*!
+ * \brief Return the input index and module index for a given input name.
+ * \param name The input name.
+ * \return std::pair<int, int> The module index and the input index.
Review comment:
```suggestion
* \return std::pair<int, int> A pair of module index and the input index.
```
##########
File path: src/runtime/pipeline/pipeline_scheduler.cc
##########
@@ -26,12 +27,97 @@ 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.
+ * \return Return a list of backend runtime module.
*/
-size_t PipelineScheduler::PipelineInit(const std::vector<Module>& modules,
- const PipelineConfig& pipeline_config) {
+std::vector<std::shared_ptr<BackendRuntime>> PipelineScheduler::PipelineInit(
+ const std::vector<Module>& modules, ConfigPipelineExecution
pipeline_config) {
+ std::vector<std::shared_ptr<BackendRuntime>> runtimes;
graph_modules_ = modules;
- int num_output = pipeline_config.GetGlobalOutputNum();
- return num_output;
+ for (size_t i = 0; i < graph_modules_.size(); i++) {
+ auto runItem = std::make_shared<BackendRuntime>(graph_modules_[i], i);
+ runtimes.push_back(runItem);
+ }
+ // Initialize the outputs array.
+ auto& global_output_map = pipeline_config.GetGlobalConfigOutputBindings();
+ for (size_t i = 0; i < global_output_map.size(); i++) {
+ if (global_output_map.find(i) == global_output_map.end()) {
+ LOG(FATAL) << "Not find global output index " << i;
+ }
+ ModuleOutputPair& output_pair = global_output_map[i];
+ NDArray output =
runtimes[output_pair.mod_idx]->CreateFromOutput(output_pair.output_idx);
+ output_array.push_back(output);
+ }
+ return runtimes;
}
+
+/*!
+ * \brief Exeute in the serialized mode.
+ * \param runtimes A list of backend runtimes module.
+ * \param pipeline_config The dependency information of each graph executor
module.
+ */
+void PipelineScheduler::PipelineRunSerial(
Review comment:
Cannot understand `PipelineRunSerial`. Did you mean "sequential"?
##########
File path: tests/python/relay/test_pipeline_executor.py
##########
@@ -71,6 +71,19 @@ def get_mannual_mod():
return mods, dshape
+def recreate_parameters(mod):
+ # Get the binding parameters from a module, then create a same parameters
which have different
+ # data value. This function can be used to test "set_param" function.
Review comment:
```suggestion
# Get the binding parameters from a module, then create the same
parameters with different data
# This function can be used to test "set_param" function.
```
##########
File path: src/runtime/pipeline/pipeline_struct.h
##########
@@ -21,24 +21,74 @@
#include <assert.h>
#include <dlpack/dlpack.h>
#include <dmlc/json.h>
+#include <tvm/runtime/ndarray.h>
+#include <tvm/runtime/packed_func.h>
#include <limits>
#include <string>
#include <unordered_map>
+#include <utility>
#include <vector>
+namespace tvm {
+namespace runtime {
+#define GLOBAL_MODULE_INDEX -1
+/*!
+ *\brief The mapping of a module output and a global output in the graph
module.
+ */
+struct GlobalOutputPair {
+ int mod_output_idx;
+ int global_output_idx;
+ GlobalOutputPair(const int idx, const int gidx) : mod_output_idx(idx),
global_output_idx(gidx) {}
+ GlobalOutputPair() {}
+};
Review comment:
Seems like you can simply use
```
using GlobalOutputPair = std::pair<int, int>;
```
##########
File path: src/runtime/pipeline/pipeline_scheduler.cc
##########
@@ -26,12 +27,97 @@ 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.
+ * \return Return a list of backend runtime module.
*/
-size_t PipelineScheduler::PipelineInit(const std::vector<Module>& modules,
- const PipelineConfig& pipeline_config) {
+std::vector<std::shared_ptr<BackendRuntime>> PipelineScheduler::PipelineInit(
+ const std::vector<Module>& modules, ConfigPipelineExecution
pipeline_config) {
+ std::vector<std::shared_ptr<BackendRuntime>> runtimes;
graph_modules_ = modules;
- int num_output = pipeline_config.GetGlobalOutputNum();
- return num_output;
+ for (size_t i = 0; i < graph_modules_.size(); i++) {
+ auto runItem = std::make_shared<BackendRuntime>(graph_modules_[i], i);
+ runtimes.push_back(runItem);
+ }
+ // Initialize the outputs array.
+ auto& global_output_map = pipeline_config.GetGlobalConfigOutputBindings();
+ for (size_t i = 0; i < global_output_map.size(); i++) {
+ if (global_output_map.find(i) == global_output_map.end()) {
+ LOG(FATAL) << "Not find global output index " << i;
+ }
+ ModuleOutputPair& output_pair = global_output_map[i];
+ NDArray output =
runtimes[output_pair.mod_idx]->CreateFromOutput(output_pair.output_idx);
+ output_array.push_back(output);
+ }
+ return runtimes;
}
+
+/*!
+ * \brief Exeute in the serialized mode.
+ * \param runtimes A list of backend runtimes module.
+ * \param pipeline_config The dependency information of each graph executor
module.
+ */
+void PipelineScheduler::PipelineRunSerial(
+ const std::vector<std::shared_ptr<BackendRuntime>>& runtimes,
+ ConfigPipelineExecution pipeline_config) {
+ for (size_t i = 0; i < runtimes.size(); i++) {
+ // The offset in vector is the runtime execution order, this offset value
should
+ // be same with the the value of "runtime_idx" in runtime.
+ if (static_cast<int>(i) != runtimes[i]->GetModuleIndex()) {
+ LOG(FATAL) << "runtime index " << runtimes[i]->GetModuleIndex()
+ << " is not same as vector offset value " << i;
Review comment:
Will the pipeline stop properly (e.g., kill running threads, etc) when
encountering an error like this?
##########
File path: src/runtime/pipeline/pipeline_struct.h
##########
@@ -21,24 +21,74 @@
#include <assert.h>
#include <dlpack/dlpack.h>
#include <dmlc/json.h>
+#include <tvm/runtime/ndarray.h>
+#include <tvm/runtime/packed_func.h>
#include <limits>
#include <string>
#include <unordered_map>
+#include <utility>
#include <vector>
+namespace tvm {
+namespace runtime {
+#define GLOBAL_MODULE_INDEX -1
+/*!
+ *\brief The mapping of a module output and a global output in the graph
module.
+ */
+struct GlobalOutputPair {
+ int mod_output_idx;
+ int global_output_idx;
+ GlobalOutputPair(const int idx, const int gidx) : mod_output_idx(idx),
global_output_idx(gidx) {}
+ GlobalOutputPair() {}
+};
+
+/*!
+ *\brief Use the module index and the output index to specify a module output.
+ */
+struct ModuleOutputPair {
+ int mod_idx;
+ int output_idx;
+ ModuleOutputPair(const int midx, const int idx) : mod_idx(midx),
output_idx(idx) {}
+ ModuleOutputPair() {}
+};
/*!
* \brief All binding information of a output interface.
*/
-struct OutputBindings {
+class ConfigBindings {
+ private:
/*!\brief Output interface binding information, 'int' is the index of the
module that
* uses this output data as the input interface data, 'string' is the input
interface name
* of the module.
*/
std::unordered_map<int, std::string> bindings;
Review comment:
Private variables need `_` as the suffix.
##########
File path: src/runtime/pipeline/pipeline_executor.cc
##########
@@ -34,13 +36,134 @@ PackedFunc PipelineExecutor::GetFunction(const
std::string& name,
if (name == "get_num_outputs") {
return PackedFunc(
[sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { *rv =
this->NumOutputs(); });
+ } else if (name == "get_num_inputs") {
+ return PackedFunc(
+ [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { *rv =
this->NumInputs(); });
+ } else if (name == "set_input") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
+ if (String::CanConvertFrom(args[0])) {
+ this->SetInput(args[0].operator String(), args[1]);
+ } else {
+ LOG(FATAL) << "Function only support the input name value in the form
of string";
+ }
+ });
+ } else if (name == "set_param") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
+ if (String::CanConvertFrom(args[0]) && String::CanConvertFrom(args[1])) {
+ this->SetParam(args[0].operator String(), args[1].operator String(),
args[2]);
+ } else {
+ LOG(FATAL) << "Function only support the params name and keyin the
form of string";
+ }
+ });
+ } else if (name == "get_output") {
+ return PackedFunc(
+ [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { *rv =
this->GetOutput(); });
+ } else if (name == "get_input") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
+ if (String::CanConvertFrom(args[0])) {
+ *rv = this->GetInput(args[0].operator String());
+ } else {
+ LOG(FATAL) << "Function only support the input name value in the form
of string";
+ }
+ });
+ } else if (name == "run") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
this->Run(args[0]); });
+ } else if (name == "stop") {
+ return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
this->Stop(); });
} else {
LOG(FATAL) << "Unknown packed function: " << name;
return PackedFunc();
}
return nullptr;
}
+/*!
+ * \brief There are some input called pipeline global input that user need to
use function
+ "set_input" to set the data for it, this function return the number of
such global input.
+ \return Return the number of pipeline global input.
+ */
+
+int PipelineExecutor::NumInputs() const {
+ // The number of inputs obtained from the input configuration.
+ size_t config_inputs_num = input_connection_config.size(), ret = 0;
+ // The number of inputs obtained from the graph runtime and pipeline
configuration.
+ size_t internal_inputs_num = pipeline_config_.GetInputOutputBindingNum();
+ for (auto runtime : runtimes_) {
+ ret += runtime->NumInputs();
+ }
+ // Use the summary of all backend runtime module input number to minus the
internal inputs
+ // number, then we will get the pipeline global input number
+ ret -= internal_inputs_num;
+ // Check whether these two numbers are equal.
+ if (config_inputs_num != ret) {
+ LOG(FATAL) << "The number of inputs from the configuration file is
inconsistent!";
+ }
+ return ret;
+}
+/*!
+ * \brief Return the input index and module index for a given input name.
+ * \param name The input name.
+ * \return std::pair<int, int> The module index and the input index.
+ */
+std::pair<int, int> PipelineExecutor::GetInputIndex(const std::string& name) {
+ std::pair<int, std::string> index = input_connection_config[name];
+ auto gruntime = runtimes_[index.first];
+ return std::make_pair(index.first, gruntime->GetInputIndex(index.second));
+}
+/*!
+ * \brief Return the module index for a given input param name.
+ * \param name The params name.
+ * \return int The module index.
+ */
+int PipelineExecutor::GetParamModuleIndex(const std::string& name) {
+ return param_connection_config[name];
+}
+/*!
+ * \brief set input to the graph module.
+ * \param input_name The input name.
+ * \param data_in The input data.
+ */
+void PipelineExecutor::SetInput(std::string input_name, DLTensor* data_in) {
+ std::pair<int, int> indexs = this->GetInputIndex(input_name);
+ runtimes_[indexs.first]->SetInput(indexs.second, data_in);
Review comment:
Need error checking.
##########
File path: src/runtime/pipeline/pipeline_scheduler.cc
##########
@@ -26,12 +27,97 @@ 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.
+ * \return Return a list of backend runtime module.
*/
-size_t PipelineScheduler::PipelineInit(const std::vector<Module>& modules,
- const PipelineConfig& pipeline_config) {
+std::vector<std::shared_ptr<BackendRuntime>> PipelineScheduler::PipelineInit(
+ const std::vector<Module>& modules, ConfigPipelineExecution
pipeline_config) {
+ std::vector<std::shared_ptr<BackendRuntime>> runtimes;
graph_modules_ = modules;
- int num_output = pipeline_config.GetGlobalOutputNum();
- return num_output;
+ for (size_t i = 0; i < graph_modules_.size(); i++) {
+ auto runItem = std::make_shared<BackendRuntime>(graph_modules_[i], i);
+ runtimes.push_back(runItem);
+ }
+ // Initialize the outputs array.
+ auto& global_output_map = pipeline_config.GetGlobalConfigOutputBindings();
+ for (size_t i = 0; i < global_output_map.size(); i++) {
+ if (global_output_map.find(i) == global_output_map.end()) {
+ LOG(FATAL) << "Not find global output index " << i;
+ }
+ ModuleOutputPair& output_pair = global_output_map[i];
+ NDArray output =
runtimes[output_pair.mod_idx]->CreateFromOutput(output_pair.output_idx);
+ output_array.push_back(output);
+ }
+ return runtimes;
}
+
+/*!
+ * \brief Exeute in the serialized mode.
+ * \param runtimes A list of backend runtimes module.
+ * \param pipeline_config The dependency information of each graph executor
module.
+ */
+void PipelineScheduler::PipelineRunSerial(
+ const std::vector<std::shared_ptr<BackendRuntime>>& runtimes,
+ ConfigPipelineExecution pipeline_config) {
+ for (size_t i = 0; i < runtimes.size(); i++) {
+ // The offset in vector is the runtime execution order, this offset value
should
+ // be same with the the value of "runtime_idx" in runtime.
+ if (static_cast<int>(i) != runtimes[i]->GetModuleIndex()) {
+ LOG(FATAL) << "runtime index " << runtimes[i]->GetModuleIndex()
+ << " is not same as vector offset value " << i;
+ }
+
+ if (!pipeline_config.FindModuleInConfig(i)) {
+ LOG(FATAL) << "Not find the configuration for the module " << i;
+ }
+
+ runtimes[i]->Run();
+ // Check if there is any output need to be forward to other graph module
or to be as
+ // global output.
+ int outputs_num = runtimes[i]->NumOutputs();
+ for (int j = 0; j < outputs_num; j++) {
+ ConfigBindings& out_binding = pipeline_config[i][j];
+ std::unordered_map<int, std::string>& input_connections =
out_binding.Get();
+ NDArray output = runtimes[i]->GetOutput(j);
+ for (auto bind : input_connections) {
+ // If the value of "bind.first" less then 0 then this is not a graph
module binding.
+ if (bind.first < 0) continue;
+ // Set input data for the graph module.
+ runtimes[bind.first]->SetInput(bind.second,
const_cast<DLTensor*>(output.operator->()));
+ }
+ // Store the output.
+ if (out_binding.IsGlobalOutput()) {
+ int global_idx = out_binding.GetGlobalOutputIndex();
+ TVMArrayCopyFromTo(const_cast<DLTensor*>(output.operator->()),
+
const_cast<DLTensor*>(output_array[global_idx].operator->()), nullptr);
+ }
+ }
+ }
+}
+/*!
+ * \brief Execute pipeline.
+ * \param runtimes A list of backend runtimes module.
+ * \param pipeline_config The dependency information of each graph executor
module.
+ * \param serialize_mode If the execution is serialized.
+ */
+void PipelineScheduler::PipelineRun(const
std::vector<std::shared_ptr<BackendRuntime>>& runtimes,
+ ConfigPipelineExecution pipeline_config,
bool serialize_mode) {
+ if (!serialize_mode) {
+ // TODO(huajsj) remove this check after all of pipeline features in.
+ LOG(FATAL) << "Currently Only supports serialized mode.";
+ } else {
+ PipelineRunSerial(runtimes, pipeline_config);
+ }
+}
+/*!
+ * \brief Stop the pipeline exection.
+ */
+void PipelineScheduler::PipelineStop() {
+ // TODO(huajsj) Remove this.
+ std::cout << __FUNCTION__ << std::endl;
Review comment:
Do not use `std::cout` in this way.
##########
File path: src/runtime/pipeline/pipeline_struct.h
##########
@@ -134,37 +220,316 @@ struct OutputMap {
}
}
};
+
+/*!
+ * \brief A map of the global module input interfaces and the graph modudles
input interfaces.
+ */
+struct InputConnectionConfig {
+ /*!\brief The key is the name of global module input interfaces. the value
is the pair of
+ * the index of a graph module and the name of a graph module input
interface.
+ */
+ std::unordered_map<std::string, std::pair<int, std::string>>
input_connection;
+ bool Empty() { return input_connection.empty(); }
+ std::pair<int, std::string> operator[](const std::string key) {
+ if (input_connection.find(key) == input_connection.end()) {
+ LOG(FATAL) << "Not find the key " << key;
+ }
+ return input_connection[key];
+ }
+
+ size_t size() const { return input_connection.size(); }
+ /*!
+ * \brief Create a input connection config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ reader->BeginObject();
+ std::string key;
+ std::string global_interface_name;
+ std::string module_interface_name;
+ int mod_idx = -1;
+ while (reader->NextObjectItem(&key)) {
+ if (key == "global_interface_name") {
+ reader->Read(&global_interface_name);
+ } else if (key == "mod_idx") {
+ reader->Read(&mod_idx);
+ } else if (key == "module_interface_name") {
+ reader->Read(&module_interface_name);
+ } else {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ ICHECK(!global_interface_name.empty()) << "Invalid global interface name
value";
+ ICHECK(!module_interface_name.empty()) << "Invalid module interface name
value";
+ input_connection[global_interface_name] = make_pair(mod_idx,
module_interface_name);
+ }
+ }
+};
+/*!
+ * \brief A map of the global module param interfaces and the graph modudles
param.
+ */
+struct ParamConnectionConfig {
+ /*!\brief The key is the name of global module param interfaces. the value
is the
+ * index of a graph module.
+ */
+ std::unordered_map<std::string, int> param_connection;
+ bool Empty() { return param_connection.empty(); }
+ int operator[](const std::string key) {
+ if (param_connection.find(key) == param_connection.end()) {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ return param_connection[key];
+ }
+ /*!
+ * \brief Create a param connection config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ reader->BeginObject();
+ std::string key;
+ std::string global_param_name;
+ int mod_idx = -1;
+ while (reader->NextObjectItem(&key)) {
+ if (key == "global_param_name") {
+ reader->Read(&global_param_name);
+ } else if (key == "mod_idx") {
+ reader->Read(&mod_idx);
+ } else {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ ICHECK(!global_param_name.empty()) << "Invalid global param name value";
+ param_connection[global_param_name] = mod_idx;
+ }
+ }
+};
/*!
* \brief The binding or dependency information of each module output
interface.
*/
-struct PipelineConfig {
- /*!\brief The key is the module index, this variable records all module
pipeline configuration
+class ConfigPipelineExecution {
+ private:
+ /*
+ *!\brief The key is the module index, this variable records all module
pipeline configuration
* information.
*/
- std::unordered_map<int, OutputMap> config;
- OutputMap& operator[](int key) {
+ std::unordered_map<int, ConfigOutputBindings> config;
+ /*
+ *\brief The key is the global output index, this variable records the
mapping of global output
+ * and the module output.
+ */
+ std::unordered_map<int, ModuleOutputPair> global_output_map;
+ /*
+ *\brief The number of binding of module outputs and inputs.
+ */
+ size_t module_input_output_binding_total_num;
Review comment:
```suggestion
size_t module_input_output_binding_total_num_;
```
##########
File path: src/runtime/pipeline/pipeline_struct.h
##########
@@ -21,24 +21,74 @@
#include <assert.h>
#include <dlpack/dlpack.h>
#include <dmlc/json.h>
+#include <tvm/runtime/ndarray.h>
+#include <tvm/runtime/packed_func.h>
#include <limits>
#include <string>
#include <unordered_map>
+#include <utility>
#include <vector>
+namespace tvm {
+namespace runtime {
+#define GLOBAL_MODULE_INDEX -1
+/*!
+ *\brief The mapping of a module output and a global output in the graph
module.
+ */
+struct GlobalOutputPair {
+ int mod_output_idx;
+ int global_output_idx;
+ GlobalOutputPair(const int idx, const int gidx) : mod_output_idx(idx),
global_output_idx(gidx) {}
+ GlobalOutputPair() {}
+};
+
+/*!
+ *\brief Use the module index and the output index to specify a module output.
+ */
+struct ModuleOutputPair {
+ int mod_idx;
+ int output_idx;
+ ModuleOutputPair(const int midx, const int idx) : mod_idx(midx),
output_idx(idx) {}
+ ModuleOutputPair() {}
+};
Review comment:
Ditto
##########
File path: src/runtime/pipeline/pipeline_struct.h
##########
@@ -134,37 +220,316 @@ struct OutputMap {
}
}
};
+
+/*!
+ * \brief A map of the global module input interfaces and the graph modudles
input interfaces.
+ */
+struct InputConnectionConfig {
+ /*!\brief The key is the name of global module input interfaces. the value
is the pair of
+ * the index of a graph module and the name of a graph module input
interface.
+ */
+ std::unordered_map<std::string, std::pair<int, std::string>>
input_connection;
+ bool Empty() { return input_connection.empty(); }
+ std::pair<int, std::string> operator[](const std::string key) {
+ if (input_connection.find(key) == input_connection.end()) {
+ LOG(FATAL) << "Not find the key " << key;
+ }
+ return input_connection[key];
+ }
+
+ size_t size() const { return input_connection.size(); }
+ /*!
+ * \brief Create a input connection config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ reader->BeginObject();
+ std::string key;
+ std::string global_interface_name;
+ std::string module_interface_name;
+ int mod_idx = -1;
+ while (reader->NextObjectItem(&key)) {
+ if (key == "global_interface_name") {
+ reader->Read(&global_interface_name);
+ } else if (key == "mod_idx") {
+ reader->Read(&mod_idx);
+ } else if (key == "module_interface_name") {
+ reader->Read(&module_interface_name);
+ } else {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ ICHECK(!global_interface_name.empty()) << "Invalid global interface name
value";
+ ICHECK(!module_interface_name.empty()) << "Invalid module interface name
value";
+ input_connection[global_interface_name] = make_pair(mod_idx,
module_interface_name);
+ }
+ }
+};
+/*!
+ * \brief A map of the global module param interfaces and the graph modudles
param.
+ */
+struct ParamConnectionConfig {
+ /*!\brief The key is the name of global module param interfaces. the value
is the
+ * index of a graph module.
+ */
+ std::unordered_map<std::string, int> param_connection;
+ bool Empty() { return param_connection.empty(); }
+ int operator[](const std::string key) {
+ if (param_connection.find(key) == param_connection.end()) {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ return param_connection[key];
+ }
+ /*!
+ * \brief Create a param connection config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ reader->BeginObject();
+ std::string key;
+ std::string global_param_name;
+ int mod_idx = -1;
+ while (reader->NextObjectItem(&key)) {
+ if (key == "global_param_name") {
+ reader->Read(&global_param_name);
+ } else if (key == "mod_idx") {
+ reader->Read(&mod_idx);
+ } else {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ ICHECK(!global_param_name.empty()) << "Invalid global param name value";
+ param_connection[global_param_name] = mod_idx;
+ }
+ }
+};
/*!
* \brief The binding or dependency information of each module output
interface.
*/
-struct PipelineConfig {
- /*!\brief The key is the module index, this variable records all module
pipeline configuration
+class ConfigPipelineExecution {
+ private:
+ /*
+ *!\brief The key is the module index, this variable records all module
pipeline configuration
* information.
*/
- std::unordered_map<int, OutputMap> config;
- OutputMap& operator[](int key) {
+ std::unordered_map<int, ConfigOutputBindings> config;
Review comment:
```suggestion
std::unordered_map<int, ConfigOutputBindings> config_;
```
##########
File path: src/runtime/pipeline/pipeline_struct.h
##########
@@ -134,37 +220,316 @@ struct OutputMap {
}
}
};
+
+/*!
+ * \brief A map of the global module input interfaces and the graph modudles
input interfaces.
+ */
+struct InputConnectionConfig {
+ /*!\brief The key is the name of global module input interfaces. the value
is the pair of
+ * the index of a graph module and the name of a graph module input
interface.
+ */
+ std::unordered_map<std::string, std::pair<int, std::string>>
input_connection;
+ bool Empty() { return input_connection.empty(); }
+ std::pair<int, std::string> operator[](const std::string key) {
+ if (input_connection.find(key) == input_connection.end()) {
+ LOG(FATAL) << "Not find the key " << key;
+ }
+ return input_connection[key];
+ }
+
+ size_t size() const { return input_connection.size(); }
+ /*!
+ * \brief Create a input connection config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ reader->BeginObject();
+ std::string key;
+ std::string global_interface_name;
+ std::string module_interface_name;
+ int mod_idx = -1;
+ while (reader->NextObjectItem(&key)) {
+ if (key == "global_interface_name") {
+ reader->Read(&global_interface_name);
+ } else if (key == "mod_idx") {
+ reader->Read(&mod_idx);
+ } else if (key == "module_interface_name") {
+ reader->Read(&module_interface_name);
+ } else {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ ICHECK(!global_interface_name.empty()) << "Invalid global interface name
value";
+ ICHECK(!module_interface_name.empty()) << "Invalid module interface name
value";
+ input_connection[global_interface_name] = make_pair(mod_idx,
module_interface_name);
+ }
+ }
+};
+/*!
+ * \brief A map of the global module param interfaces and the graph modudles
param.
+ */
+struct ParamConnectionConfig {
+ /*!\brief The key is the name of global module param interfaces. the value
is the
+ * index of a graph module.
+ */
+ std::unordered_map<std::string, int> param_connection;
+ bool Empty() { return param_connection.empty(); }
+ int operator[](const std::string key) {
+ if (param_connection.find(key) == param_connection.end()) {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ return param_connection[key];
+ }
+ /*!
+ * \brief Create a param connection config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ reader->BeginObject();
+ std::string key;
+ std::string global_param_name;
+ int mod_idx = -1;
+ while (reader->NextObjectItem(&key)) {
+ if (key == "global_param_name") {
+ reader->Read(&global_param_name);
+ } else if (key == "mod_idx") {
+ reader->Read(&mod_idx);
+ } else {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ ICHECK(!global_param_name.empty()) << "Invalid global param name value";
+ param_connection[global_param_name] = mod_idx;
+ }
+ }
+};
/*!
* \brief The binding or dependency information of each module output
interface.
*/
-struct PipelineConfig {
- /*!\brief The key is the module index, this variable records all module
pipeline configuration
+class ConfigPipelineExecution {
+ private:
+ /*
+ *!\brief The key is the module index, this variable records all module
pipeline configuration
* information.
*/
- std::unordered_map<int, OutputMap> config;
- OutputMap& operator[](int key) {
+ std::unordered_map<int, ConfigOutputBindings> config;
+ /*
+ *\brief The key is the global output index, this variable records the
mapping of global output
+ * and the module output.
+ */
+ std::unordered_map<int, ModuleOutputPair> global_output_map;
+ /*
+ *\brief The number of binding of module outputs and inputs.
+ */
+ size_t module_input_output_binding_total_num;
+
+ public:
+ ConfigOutputBindings& operator[](int key) {
ICHECK(config.find(key) != config.end());
return config[key];
}
+ /*!
+ *\brief Check if the module index existing in the "config".
+ */
+ bool FindModuleInConfig(int mod_idx) { return config.find(mod_idx) !=
config.end(); }
+ /*!
+ *\brief Build the mapping of key and "ConfigOutputBindings", key is module
index.
+ */
+ void Insert(int key, const ConfigOutputBindings& map) { config[key] = map; }
- void Insert(int key, const OutputMap& map) { config[key] = map; }
-
- /*!\brief This function is used to verify whether config is loaded
successfully.
+ /*
+ *!\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.
* \return The number of outputs the entire pipeline has.
*/
size_t GetGlobalOutputNum() const {
- size_t num_output = 0;
+ // The number of pipeline outputs is the size of "global_output_map";
+ return global_output_map.size();
+ }
+ /*
+ *!\brief Get the map of global outputs and module outputs.
+ */
+ std::unordered_map<int, ModuleOutputPair>&
GetGlobalConfigOutputBindings(void) {
+ return global_output_map;
+ }
+ /*
+ *!\brief Get the number of module output and module input bindings.
+ */
+ size_t GetInputOutputBindingNum() const { return
module_input_output_binding_total_num; }
+ /*
+ *!\brief Parse the config to construct data struct using in pipeline
execution.
+ */
+ void ParseConfiguration(const std::unordered_map<int, ConfigOutputBindings>&
config) {
+ if (config.empty()) {
+ LOG(FATAL) << "The Configuration loading not finish yet.";
+ }
+ module_input_output_binding_total_num = 0;
for (auto mod_output : config) {
- num_output += mod_output.second.GetGlobalOutputNum();
+ // Get the numbers of binding of input and output.
+ module_input_output_binding_total_num +=
mod_output.second.GetInputOutputBindingNum();
+ // Use global output index as key to create a mapping of global index
and module output.
+ const std::vector<GlobalOutputPair>& global_output =
+ mod_output.second.GetGlobalConfigOutputBindings();
+
+ for (auto output : global_output) {
+ global_output_map[output.global_output_idx] =
+ ModuleOutputPair(mod_output.first, output.mod_output_idx);
+ }
}
- return num_output;
+ return;
+ }
+ /*!
+ * \brief Create a pipeline config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ std::string key;
+ reader->BeginObject();
+ int mod_idx = -1;
+ ConfigOutputBindings 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 {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ // Check if the output is successfully read.
+ ICHECK(!output.Empty()) << "Invalid output binding result.";
+ Insert(mod_idx, output);
+ }
+ // Call this function after "config" loading finished.
+ ParseConfiguration(config);
+ }
+};
+/*
+ *\brief Runtime of backend.
+ */
+class BackendRuntime {
+ private:
+ /*\brief The index of runtime indicate the position in the pipeline.*/
+ int runtime_idx;
+ /*\brief The Runtime module of a backedn graph executor.*/
+ Module module;
Review comment:
```suggestion
Module module_;
```
##########
File path: src/runtime/pipeline/pipeline_struct.h
##########
@@ -134,37 +220,316 @@ struct OutputMap {
}
}
};
+
+/*!
+ * \brief A map of the global module input interfaces and the graph modudles
input interfaces.
+ */
+struct InputConnectionConfig {
+ /*!\brief The key is the name of global module input interfaces. the value
is the pair of
+ * the index of a graph module and the name of a graph module input
interface.
+ */
+ std::unordered_map<std::string, std::pair<int, std::string>>
input_connection;
+ bool Empty() { return input_connection.empty(); }
+ std::pair<int, std::string> operator[](const std::string key) {
+ if (input_connection.find(key) == input_connection.end()) {
+ LOG(FATAL) << "Not find the key " << key;
+ }
+ return input_connection[key];
+ }
+
+ size_t size() const { return input_connection.size(); }
+ /*!
+ * \brief Create a input connection config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ reader->BeginObject();
+ std::string key;
+ std::string global_interface_name;
+ std::string module_interface_name;
+ int mod_idx = -1;
+ while (reader->NextObjectItem(&key)) {
+ if (key == "global_interface_name") {
+ reader->Read(&global_interface_name);
+ } else if (key == "mod_idx") {
+ reader->Read(&mod_idx);
+ } else if (key == "module_interface_name") {
+ reader->Read(&module_interface_name);
+ } else {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ ICHECK(!global_interface_name.empty()) << "Invalid global interface name
value";
+ ICHECK(!module_interface_name.empty()) << "Invalid module interface name
value";
+ input_connection[global_interface_name] = make_pair(mod_idx,
module_interface_name);
+ }
+ }
+};
+/*!
+ * \brief A map of the global module param interfaces and the graph modudles
param.
+ */
+struct ParamConnectionConfig {
+ /*!\brief The key is the name of global module param interfaces. the value
is the
+ * index of a graph module.
+ */
+ std::unordered_map<std::string, int> param_connection;
+ bool Empty() { return param_connection.empty(); }
+ int operator[](const std::string key) {
+ if (param_connection.find(key) == param_connection.end()) {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ return param_connection[key];
+ }
+ /*!
+ * \brief Create a param connection config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ reader->BeginObject();
+ std::string key;
+ std::string global_param_name;
+ int mod_idx = -1;
+ while (reader->NextObjectItem(&key)) {
+ if (key == "global_param_name") {
+ reader->Read(&global_param_name);
+ } else if (key == "mod_idx") {
+ reader->Read(&mod_idx);
+ } else {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ ICHECK(!global_param_name.empty()) << "Invalid global param name value";
+ param_connection[global_param_name] = mod_idx;
+ }
+ }
+};
/*!
* \brief The binding or dependency information of each module output
interface.
*/
-struct PipelineConfig {
- /*!\brief The key is the module index, this variable records all module
pipeline configuration
+class ConfigPipelineExecution {
+ private:
+ /*
+ *!\brief The key is the module index, this variable records all module
pipeline configuration
* information.
*/
- std::unordered_map<int, OutputMap> config;
- OutputMap& operator[](int key) {
+ std::unordered_map<int, ConfigOutputBindings> config;
+ /*
+ *\brief The key is the global output index, this variable records the
mapping of global output
+ * and the module output.
+ */
+ std::unordered_map<int, ModuleOutputPair> global_output_map;
Review comment:
```suggestion
std::unordered_map<int, ModuleOutputPair> global_output_map_;
```
##########
File path: src/runtime/pipeline/pipeline_struct.h
##########
@@ -134,37 +220,316 @@ struct OutputMap {
}
}
};
+
+/*!
+ * \brief A map of the global module input interfaces and the graph modudles
input interfaces.
+ */
+struct InputConnectionConfig {
+ /*!\brief The key is the name of global module input interfaces. the value
is the pair of
+ * the index of a graph module and the name of a graph module input
interface.
+ */
+ std::unordered_map<std::string, std::pair<int, std::string>>
input_connection;
+ bool Empty() { return input_connection.empty(); }
+ std::pair<int, std::string> operator[](const std::string key) {
+ if (input_connection.find(key) == input_connection.end()) {
+ LOG(FATAL) << "Not find the key " << key;
+ }
+ return input_connection[key];
+ }
+
+ size_t size() const { return input_connection.size(); }
+ /*!
+ * \brief Create a input connection config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ reader->BeginObject();
+ std::string key;
+ std::string global_interface_name;
+ std::string module_interface_name;
+ int mod_idx = -1;
+ while (reader->NextObjectItem(&key)) {
+ if (key == "global_interface_name") {
+ reader->Read(&global_interface_name);
+ } else if (key == "mod_idx") {
+ reader->Read(&mod_idx);
+ } else if (key == "module_interface_name") {
+ reader->Read(&module_interface_name);
+ } else {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ ICHECK(!global_interface_name.empty()) << "Invalid global interface name
value";
+ ICHECK(!module_interface_name.empty()) << "Invalid module interface name
value";
+ input_connection[global_interface_name] = make_pair(mod_idx,
module_interface_name);
+ }
+ }
+};
+/*!
+ * \brief A map of the global module param interfaces and the graph modudles
param.
+ */
+struct ParamConnectionConfig {
+ /*!\brief The key is the name of global module param interfaces. the value
is the
+ * index of a graph module.
+ */
+ std::unordered_map<std::string, int> param_connection;
+ bool Empty() { return param_connection.empty(); }
+ int operator[](const std::string key) {
+ if (param_connection.find(key) == param_connection.end()) {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ return param_connection[key];
+ }
+ /*!
+ * \brief Create a param connection config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ reader->BeginObject();
+ std::string key;
+ std::string global_param_name;
+ int mod_idx = -1;
+ while (reader->NextObjectItem(&key)) {
+ if (key == "global_param_name") {
+ reader->Read(&global_param_name);
+ } else if (key == "mod_idx") {
+ reader->Read(&mod_idx);
+ } else {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ ICHECK(!global_param_name.empty()) << "Invalid global param name value";
+ param_connection[global_param_name] = mod_idx;
+ }
+ }
+};
/*!
* \brief The binding or dependency information of each module output
interface.
*/
-struct PipelineConfig {
- /*!\brief The key is the module index, this variable records all module
pipeline configuration
+class ConfigPipelineExecution {
+ private:
+ /*
+ *!\brief The key is the module index, this variable records all module
pipeline configuration
* information.
*/
- std::unordered_map<int, OutputMap> config;
- OutputMap& operator[](int key) {
+ std::unordered_map<int, ConfigOutputBindings> config;
+ /*
+ *\brief The key is the global output index, this variable records the
mapping of global output
+ * and the module output.
+ */
+ std::unordered_map<int, ModuleOutputPair> global_output_map;
+ /*
+ *\brief The number of binding of module outputs and inputs.
+ */
+ size_t module_input_output_binding_total_num;
+
+ public:
+ ConfigOutputBindings& operator[](int key) {
ICHECK(config.find(key) != config.end());
return config[key];
}
+ /*!
+ *\brief Check if the module index existing in the "config".
+ */
+ bool FindModuleInConfig(int mod_idx) { return config.find(mod_idx) !=
config.end(); }
+ /*!
+ *\brief Build the mapping of key and "ConfigOutputBindings", key is module
index.
+ */
+ void Insert(int key, const ConfigOutputBindings& map) { config[key] = map; }
- void Insert(int key, const OutputMap& map) { config[key] = map; }
-
- /*!\brief This function is used to verify whether config is loaded
successfully.
+ /*
+ *!\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.
* \return The number of outputs the entire pipeline has.
*/
size_t GetGlobalOutputNum() const {
- size_t num_output = 0;
+ // The number of pipeline outputs is the size of "global_output_map";
+ return global_output_map.size();
+ }
+ /*
+ *!\brief Get the map of global outputs and module outputs.
+ */
+ std::unordered_map<int, ModuleOutputPair>&
GetGlobalConfigOutputBindings(void) {
+ return global_output_map;
+ }
+ /*
+ *!\brief Get the number of module output and module input bindings.
+ */
+ size_t GetInputOutputBindingNum() const { return
module_input_output_binding_total_num; }
+ /*
+ *!\brief Parse the config to construct data struct using in pipeline
execution.
+ */
+ void ParseConfiguration(const std::unordered_map<int, ConfigOutputBindings>&
config) {
+ if (config.empty()) {
+ LOG(FATAL) << "The Configuration loading not finish yet.";
+ }
+ module_input_output_binding_total_num = 0;
for (auto mod_output : config) {
- num_output += mod_output.second.GetGlobalOutputNum();
+ // Get the numbers of binding of input and output.
+ module_input_output_binding_total_num +=
mod_output.second.GetInputOutputBindingNum();
+ // Use global output index as key to create a mapping of global index
and module output.
+ const std::vector<GlobalOutputPair>& global_output =
+ mod_output.second.GetGlobalConfigOutputBindings();
+
+ for (auto output : global_output) {
+ global_output_map[output.global_output_idx] =
+ ModuleOutputPair(mod_output.first, output.mod_output_idx);
+ }
}
- return num_output;
+ return;
+ }
+ /*!
+ * \brief Create a pipeline config from JSONReader.
+ * \param reader Json reader.
+ */
+ void Load(dmlc::JSONReader* reader) {
+ reader->BeginArray();
+ while (reader->NextArrayItem()) {
+ std::string key;
+ reader->BeginObject();
+ int mod_idx = -1;
+ ConfigOutputBindings 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 {
+ LOG(FATAL) << "do not support key " << key;
+ }
+ }
+ ICHECK(mod_idx >= 0) << "Invalid mod_idx value " << mod_idx;
+ // Check if the output is successfully read.
+ ICHECK(!output.Empty()) << "Invalid output binding result.";
+ Insert(mod_idx, output);
+ }
+ // Call this function after "config" loading finished.
+ ParseConfiguration(config);
+ }
+};
+/*
+ *\brief Runtime of backend.
+ */
+class BackendRuntime {
+ private:
+ /*\brief The index of runtime indicate the position in the pipeline.*/
+ int runtime_idx;
Review comment:
```suggestion
int runtime_idx_;
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]