From 8d41893113aa598090621e15b691640e0f0b96e9 Mon Sep 17 00:00:00 2001 From: huajsj Date: Mon, 9 Aug 2021 22:13:16 -0700 Subject: [PATCH] [Runtime] Pipeline Executor Initial patch. This patch is one of serial patch for PR 7892 splitting.this is the initial part of the pipeline executor, this patch include the cmake change and python and C++ interface for pipeline executor. --- CMakeLists.txt | 6 + cmake/config.cmake | 3 + python/tvm/contrib/pipeline_executor.py | 176 +++++++++++++++++++ src/runtime/pipeline/pipeline_executor.cc | 49 ++++++ src/runtime/pipeline/pipeline_executor.h | 72 ++++++++ tests/python/relay/test_pipeline_executor.py | 136 ++++++++++++++ 6 files changed, 442 insertions(+) create mode 100644 python/tvm/contrib/pipeline_executor.py create mode 100644 src/runtime/pipeline/pipeline_executor.cc create mode 100644 src/runtime/pipeline/pipeline_executor.h create mode 100644 tests/python/relay/test_pipeline_executor.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 59786af38a9f6..517467f3d983e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -377,6 +377,12 @@ if(USE_PROFILER) list(APPEND RUNTIME_SRCS ${RUNTIME_VM_PROFILER_SRCS}) endif(USE_PROFILER) +if(USE_PIPELINE_EXECUTOR) + message(STATUS "Build with Pipeline Executor support...") + file(GLOB RUNTIME_PIPELINE_SRCS src/runtime/pipeline/*.cc) + list(APPEND RUNTIME_SRCS ${RUNTIME_PIPELINE_SRCS}) +endif(USE_PIPELINE_EXECUTOR) + # Module rules include(cmake/modules/VTA.cmake) include(cmake/modules/StandaloneCrt.cmake) diff --git a/cmake/config.cmake b/cmake/config.cmake index daa0f1e84315b..91746c77f0327 100644 --- a/cmake/config.cmake +++ b/cmake/config.cmake @@ -105,6 +105,9 @@ set(USE_GRAPH_EXECUTOR ON) # Whether enable tiny graph executor with CUDA Graph set(USE_GRAPH_EXECUTOR_CUDA_GRAPH OFF) +# Whether enable subgraph runtime. +set(USE_PIPELINE_EXECUTOR ON) + # Whether to enable the profiler for the graph executor and vm set(USE_PROFILER ON) diff --git a/python/tvm/contrib/pipeline_executor.py b/python/tvm/contrib/pipeline_executor.py new file mode 100644 index 0000000000000..671d14c19f555 --- /dev/null +++ b/python/tvm/contrib/pipeline_executor.py @@ -0,0 +1,176 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +"""Pipeline executor that executes pipeline containing TVM PackedFunc.""" +import json +import tvm._ffi +from tvm import relay +from tvm.contrib import graph_executor + + +def pipeline_executor_enabled(): + """check if pipeline executor enabled. + Return + ------ + enable: bool + return pipeline executor get enabled or not + """ + pipeline_enabled = False + try: + pipelinecreate = tvm._ffi.get_global_func("tvm.pipeline_executor.create") + assert pipelinecreate + pipeline_enabled = True + except ValueError: + print("pipeline executor not enabled!") + + return pipeline_enabled + + +def build_pipeline(mod_n_configs): + """build module list that can use for pipeline execution. + + Parameters + ---------- + mod_n_configs: Dict[IRModule, Dict[str, Any]] + build configuration informaton, structure like following. + {IRModule: {"target":target, + "target_host":target_host, + "params":params, + "mod_name"mod_name, + "build":build}} + + Returns + ------- + ret: List[IRModule] + list of IRModule + string_config: Dict[int, Dict[str, any]] + pipeline configuration + """ + mods = {} + config_len = len(mod_n_configs) + string_config = [{} for _ in range(config_len)] + for _, (ir_mod, mod_config) in enumerate(mod_n_configs.items()): + # init lib_name and json_name params with empty + lib_name = "" + json_name = "" + params_name = "" + # Get module configuration + assert "pipeline" in mod_config and "mod_indx" in mod_config["pipeline"] + # Get module index in pipeline configuration + mconf = mod_config["pipeline"].copy() + # Get mod device config + dev = mod_config["dev"] + mod_indx = mconf["mod_indx"] - 1 + target = mod_config["target"] + assert mod_indx < config_len + build_func = relay.build + # if there is a self defined build function then use it. + if "build" in mod_config and mod_config["build"]: + build_func = mod_config["build"] + + # build IRModule + mod = build_func( + ir_mod, + target, + params=mod_config["params"], + target_host=mod_config["target_host"], + mod_name=mod_config["mod_name"], + ) + + mconf["lib_name"] = lib_name + mconf["json_name"] = json_name + mconf["params_name"] = params_name + mconf["dev"] = "{},{}".format(dev.device_type, dev.device_id) + # Create pipeline configuration + string_config[mod_indx] = mconf + # associate mod with device + mods[mod] = {"dev": dev} + + # return IRModule list and pipeline configuration + return mods, string_config + + +def create(pipeline_mods, mod_config): + """Create a pipeline runtime executor. + + Parameters + ---------- + pipeline_mods : List[IRModule] + list of IRModule + + mod_config : Dict[int, Dict[str, Any]] + modules and modules dependency configuration informaiton. + + Returns + ------- + submodule : PipelineModule + Runtime pipeline module. + """ + + submodule = PipelineModule(pipeline_mods, mod_config) + return submodule + + +class PipelineModule(object): + """Wrapper runtime module. This is a thin wrapper of the underlying TVM module. + you can also directly call set_input, run, and get_output of underlying module functions. + + Parameters + ---------- + graph_module : List[GraphModule] + The internal tvm module that holds the actual graph functions. + + pipeline_config : Dict[IRModule, Dict[str, Any]] + modules and modules dependency configuration informaiton. + + """ + + def graph_executor_create(self, pipeline_mods, mod_config): + """Create a pipeline runtime executor. + + Parameters + ---------- + pipeline_mods : List[IRModule] + list of IRModule + + mod_config : Dict[int, Dict[str, Any]] + modules and modules dependency configuration informaiton. + + Returns + ------- + mods : GreaphModule + Runtime graph module. + """ + + mods = [] + for pipeline_mod in pipeline_mods: + mod = graph_executor.GraphModule( + pipeline_mod["default"](pipeline_mods[pipeline_mod]["dev"]) + ) + mods.append(mod.module) + + return mods, json.dumps(mod_config) + + def __init__(self, pipeline_mods, mod_config): + self.pipeline_mods = pipeline_mods + self.mod_config = mod_config + mods, config = self.graph_executor_create(pipeline_mods, mod_config) + + pipelinecreate = tvm._ffi.get_global_func("tvm.pipeline_executor.create") + assert pipelinecreate + module = pipelinecreate(mods, config) + + self.module_ = module diff --git a/src/runtime/pipeline/pipeline_executor.cc b/src/runtime/pipeline/pipeline_executor.cc new file mode 100644 index 0000000000000..44440f3418056 --- /dev/null +++ b/src/runtime/pipeline/pipeline_executor.cc @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/*! + * \file pipeline_executor.cc + */ +#include "pipeline_executor.h" + +namespace tvm { +namespace runtime { + +void SubGraphRuntime::Init(const Array& modules, + const std::string& pipeline_json) { + return; +} + +PackedFunc SubGraphRuntime::GetFunction(const std::string& name, + const ObjectPtr& sptr_to_self) { + return PackedFunc(); +} + +Module PipelineRuntimeCreate(const Array& m, + const std::string& pipeline_json) { + auto exec = make_object(); + exec->Init(m, pipeline_json); + return Module(exec); +} + +TVM_REGISTER_GLOBAL("tvm.pipeline_executor.create").set_body([](TVMArgs args, TVMRetValue* rv) { + *rv = PipelineRuntimeCreate(args[0], args[1]); +}); +} // namespace runtime +} // namespace tvm diff --git a/src/runtime/pipeline/pipeline_executor.h b/src/runtime/pipeline/pipeline_executor.h new file mode 100644 index 0000000000000..fe92874975e93 --- /dev/null +++ b/src/runtime/pipeline/pipeline_executor.h @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/*! + * \brief pipeline executor + * \file pipeline_executor.h + */ +#ifndef TVM_RUNTIME_PIPELINE_PIPELINE_EXECUTOR_H_ +#define TVM_RUNTIME_PIPELINE_PIPELINE_EXECUTOR_H_ +#include + +#include +#include +#include +#include + +#include "../file_utils.h" +using namespace std; +namespace tvm { +namespace runtime { + +/*! + * \brief pipeline runtime. + * + * This runtime can be acccesibly in various language via + * TVM runtime PackedFunc API. + */ +class TVM_DLL SubGraphRuntime : public ModuleNode { + public: + /*! + * \return The type key of the executor. + */ + const char* type_key() const final { return "SubGraphRuntime"; } + /*! + * \brief Initialize the graph executor with graph and context. + * \param graph_json The execution graph. + * \param module The module containing the compiled functions for the host + * processor. + * \param ctxs The context of the host and devices where graph nodes will be + * executed on. + * \param lookup_linked_param_func If given, a PackedFunc invoked to lookup linked parameters + * by storage_id. If not given, linked parameters are looked-up using an internal implementation, + * which is not compatible with RPCModules. + */ + void Init(const Array& modules, const std::string& pipeline_json); + /*! + * \brief Get member function to front-end + * \param name The name of the function. + * \param sptr_to_self The pointer to the module node. + * \return The corresponding member function. + */ + virtual PackedFunc GetFunction(const std::string& name, const ObjectPtr& sptr_to_self); +}; +} // namespace runtime +} // namespace tvm +#endif // TVM_RUNTIME_PIPELINE_PIPELINE_EXECUTOR_H_ diff --git a/tests/python/relay/test_pipeline_executor.py b/tests/python/relay/test_pipeline_executor.py new file mode 100644 index 0000000000000..2a043918542f9 --- /dev/null +++ b/tests/python/relay/test_pipeline_executor.py @@ -0,0 +1,136 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +import numpy as np +import tvm +import tvm.testing +from tvm import relay +from tvm.relay import transform +from tvm.contrib import graph_executor, pipeline_executor + + +def get_mannual_mod(): + mods = [] + dshape = (3, 3) + data = relay.var("data_0", relay.TensorType(dshape, "float32")) + data21 = relay.var("data_1", relay.TensorType(dshape, "float32")) + data_net1_output_1 = relay.var("data_0", relay.TensorType(dshape, "float32")) + data_net1_output_2 = relay.var("data_1", relay.TensorType(dshape, "float32")) + data_net2_output_1 = relay.var("data_0", relay.TensorType(dshape, "float32")) + mvalue1 = np.full((1), 1).astype("float32") + mvalue2 = np.full((1), 2).astype("float32") + mvalue3 = np.full((1), 3).astype("float32") + mv1 = relay.Constant(tvm.nd.array(mvalue1)) + mv2 = relay.Constant(tvm.nd.array(mvalue2)) + mv3 = relay.Constant(tvm.nd.array(mvalue3)) + + # net1 have three output, output3 is final output + net_output1 = relay.add(data, mv1) + net_output2 = relay.subtract(data, mv2) + net_output3 = relay.multiply(data, mv3) + + # net2 use net1 output1 as input + net2 = relay.add(data_net1_output_1, mv2) + net2 = relay.add(net2, data21) + net2 = relay.add(net2, mv3) + + # net3 use net2 output1 and net1 outpu2 as input + net3 = relay.multiply(data_net2_output_1, mv3) + net3 = relay.add(net3, data_net1_output_2) + + mods.append( + tvm.IRModule.from_expr( + relay.Function([data], relay.Tuple([net_output1, net_output2, net_output3])) + ) + ) + mods.append(tvm.IRModule.from_expr(relay.Function([data_net1_output_1, data21], net2))) + mods.append( + tvm.IRModule.from_expr(relay.Function([data_net1_output_2, data_net2_output_1], net3)) + ) + + return mods, dshape + + +def pipeline(target): + """ + #Get 4 pipeline module. + """ + mods, dshape = get_mannual_mod() + """ + #Prepare batch data for pipeline feeding + """ + datas = [] + for i in range(len(mods) + 1): + datas.append(np.full(dshape, 3 + i).astype("float32")) + + # set configure + indx = 0 + mod_config = {} + mconfig = {"target_host": None, "mod_name": "default", "build": None, "params": None} + mconfig1 = mconfig.copy() + mconfig1["target"] = target[0] + mconfig1["dev"] = target[1] + # third output is final output, second output for mod3, first for mod2 + # input + mconfig1["pipeline"] = { + "mod_indx": 1, + "output": [ + {"output_indx": 1, "dependent": [{"mod_indx": 2, "input_name": "data_0"}]}, + {"output_indx": 2, "dependent": [{"mod_indx": 3, "input_name": "data_0"}]}, + {"output_indx": 3, "dependent": [{"mod_indx": 0, "input_name": "1"}]}, + ], + } + mod_config[mods[0]] = mconfig1 + + mconfig2 = mconfig.copy() + mconfig2["target"] = "llvm" + mconfig2["dev"] = tvm.cpu(0) + mconfig2["pipeline"] = { + "mod_indx": 2, + "output": [ + {"output_indx": 1, "dependent": [{"mod_indx": 3, "input_name": "data_1"}]}, + ], + } + mod_config[mods[1]] = mconfig2 + + mconfig3 = mconfig.copy() + mconfig3["target"] = "llvm" + mconfig3["dev"] = tvm.cpu(0) + + mconfig3["pipeline"] = { + "mod_indx": 3, + "output": [{"output_indx": 1, "dependent": [{"mod_indx": 0, "input_name": "2"}]}], + } + mod_config[mods[2]] = mconfig3 + """ + #build and create pipeline module + """ + with relay.build_config(opt_level=3): + pipeline_mods, string_config = pipeline_executor.build_pipeline(mod_config) + + pipeline_module = pipeline_executor.create(pipeline_mods, string_config) + + +def test_pipeline(): + if pipeline_executor.pipeline_executor_enabled(): + target_list = tvm.testing.enabled_targets() + for target in target_list: + pipeline(target) + + +if __name__ == "__main__": + test_pipeline()