From ceea583f28bb0dc82ec7a77e9701c969afc4c526 Mon Sep 17 00:00:00 2001 From: Haichen Shen Date: Tue, 5 Nov 2019 02:08:16 +0000 Subject: [PATCH 1/8] [AutoTVM] Use vm compile in extracting task from relay --- python/tvm/autotvm/task/relay_integration.py | 17 +++++++---------- python/tvm/relay/backend/vm.py | 4 +++- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/python/tvm/autotvm/task/relay_integration.py b/python/tvm/autotvm/task/relay_integration.py index 4a407714b414..f261b22f04e9 100644 --- a/python/tvm/autotvm/task/relay_integration.py +++ b/python/tvm/autotvm/task/relay_integration.py @@ -32,7 +32,7 @@ # TODO(moreau89) find a more elegant way to lower for VTAs -def _lower(func, +def _lower(mod, target, params): """ Helper to lower VTA properly. @@ -49,12 +49,10 @@ def _lower(func, grc = graph_runtime_codegen.GraphRuntimeCodegen(None, target) return grc.codegen(mod["main"]) # default case - mod, _ = relay.optimize(func, target, params) - grc = graph_runtime_codegen.GraphRuntimeCodegen(None, target) - return grc.codegen(mod["main"]) + return relay.vm.compile(mod, target, params) -def extract_from_program(func, params, ops, target, target_host=None, +def extract_from_program(mod, params, ops, target, target_host=None, template_keys=None): """ Extract tuning tasks from a relay program. @@ -81,11 +79,11 @@ def extract_from_program(func, params, ops, target, target_host=None, task: Array of autotvm.task.Task collected tasks """ - return extract_from_multiple_program([func], [params], ops, target, target_host, - template_keys=template_keys) + return extract_from_multiple_program([mod], [params], ops, target, target_host, + template_keys) -def extract_from_multiple_program(funcs, params, ops, target, target_host=None, +def extract_from_multiple_program(mods, params, ops, target, target_host=None, template_keys=None): """ Extract tuning tasks from multiple relay programs. @@ -145,10 +143,9 @@ def extract_from_multiple_program(funcs, params, ops, target, target_host=None, old_state = logger.disabled logger.disabled = True - for func, param in zip(funcs, params): + for mod, param in zip(mods, params): relay.backend.compile_engine.get().clear() # wrap build call in thread to avoid multiprocessing problems - mod = relay.Module.from_expr(func) build_thread = threading.Thread(target=_lower, args=(mod, target, param)) build_thread.start() diff --git a/python/tvm/relay/backend/vm.py b/python/tvm/relay/backend/vm.py index a523722def61..c7141b0d8d0f 100644 --- a/python/tvm/relay/backend/vm.py +++ b/python/tvm/relay/backend/vm.py @@ -449,7 +449,9 @@ def update_target_host(self, target, target_host): break if not target_host: target_host = "llvm" if tvm.module.enabled("llvm") else "stackvm" - return tvm.target.create(target_host) + if isinstance(target_host, str): + target_host = tvm.target.create(target_host) + return target_host def tophub_context(self, target): # If current dispatch context is fallback context (the default root context), From f5499b13874c4cb627f2ef603e2b82ac0e90afdc Mon Sep 17 00:00:00 2001 From: Haichen Shen Date: Wed, 13 Nov 2019 20:08:30 +0000 Subject: [PATCH 2/8] update --- python/tvm/autotvm/task/relay_integration.py | 16 ++++++++++------ .../relay/test_autotvm_task_extraction.py | 18 +++++++++++++++--- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/python/tvm/autotvm/task/relay_integration.py b/python/tvm/autotvm/task/relay_integration.py index f261b22f04e9..bbaaf0524b14 100644 --- a/python/tvm/autotvm/task/relay_integration.py +++ b/python/tvm/autotvm/task/relay_integration.py @@ -45,11 +45,11 @@ def _lower(mod, with relay.build_config(opt_level=3, disabled_pass={"AlterOpLayout"}): import vta with vta.build_config(): - mod, _ = relay.optimize(func, target, params) + mod, _ = relay.optimize(mod, target, params) grc = graph_runtime_codegen.GraphRuntimeCodegen(None, target) return grc.codegen(mod["main"]) # default case - return relay.vm.compile(mod, target, params) + return relay.vm.compile(mod, target=target, params=params) def extract_from_program(mod, params, ops, target, target_host=None, @@ -60,8 +60,8 @@ def extract_from_program(mod, params, ops, target, target_host=None, Parameters ---------- - func: relay.expr.Function - The func to tune + mod: relay.module.Module or relay.expr.Function + The module or function to tune params: dict of str to numpy array The associated parameters of the program ops: List of relay op @@ -92,8 +92,8 @@ def extract_from_multiple_program(mods, params, ops, target, target_host=None, Parameters ---------- - funcs: List of relay.expr.Function - The list of functions to tune + mods: List[relay.module.Module] or List[relay.expr.Function] + The list of modules or functions to tune params: List of dict of str to numpy array The associated parameters of the programs ops: List of relay op @@ -144,6 +144,10 @@ def extract_from_multiple_program(mods, params, ops, target, target_host=None, logger.disabled = True for mod, param in zip(mods, params): + if isinstance(mod, relay.expr.Function): + mod = relay.Module.from_expr(mod) + assert isinstance(mod, relay.module.Module), \ + "only support relay Module or Function to be tuned" relay.backend.compile_engine.get().clear() # wrap build call in thread to avoid multiprocessing problems build_thread = threading.Thread(target=_lower, diff --git a/tests/python/relay/test_autotvm_task_extraction.py b/tests/python/relay/test_autotvm_task_extraction.py index d29d74322f6f..8f550d82c4f6 100644 --- a/tests/python/relay/test_autotvm_task_extraction.py +++ b/tests/python/relay/test_autotvm_task_extraction.py @@ -45,12 +45,20 @@ def test_task_extraction(): params=params, ops=(relay.op.nn.conv2d,)) assert len(tasks) == 12 + tasks = autotvm.task.extract_from_program(mod, target=target, + params=params, + ops=(relay.op.nn.conv2d,)) + assert len(tasks) == 12 mod, params, _ = get_network('resnet-18', batch_size=1) tasks = autotvm.task.extract_from_program(mod["main"], target=target, params=params, ops=(relay.op.nn.dense,)) assert len(tasks) == 1 + tasks = autotvm.task.extract_from_program(mod, target=target, + params=params, + ops=(relay.op.nn.dense,)) + assert len(tasks) == 1 mod, params, _ = get_network('resnet-18', batch_size=1) mod_list.append(mod) @@ -59,22 +67,26 @@ def test_task_extraction(): params=params, ops=(relay.op.nn.conv2d, relay.op.nn.dense)) assert len(tasks) == 13 + tasks = autotvm.task.extract_from_program(mod, target=target, + params=params, + ops=(relay.op.nn.conv2d, relay.op.nn.dense)) + assert len(tasks) == 13 mod, params, _ = get_network('mobilenet', batch_size=1) mod_list.append(mod) params_list.append(params) - tasks = autotvm.task.extract_from_program(mod["main"], target=target, + tasks = autotvm.task.extract_from_program(mod, target=target, params=params, ops=(relay.op.nn.conv2d, relay.op.nn.dense)) assert len(tasks) == 20 mod, params, _ = get_network('dcgan', batch_size=1) - tasks = autotvm.task.extract_from_program(mod["main"], target=target, + tasks = autotvm.task.extract_from_program(mod, target=target, params=params, ops=(relay.op.nn.conv2d_transpose,)) assert len(tasks) == 4 - tasks = autotvm.task.extract_from_multiple_program([m['main'] for m in mod_list], params_list, + tasks = autotvm.task.extract_from_multiple_program(mod_list, params_list, target=target, ops=(relay.op.nn.conv2d,)) assert len(tasks) == 31 From ed1dd3d0128e5413de52220c3bb25533ba794dea Mon Sep 17 00:00:00 2001 From: Haichen Shen Date: Wed, 8 Jan 2020 00:16:04 +0000 Subject: [PATCH 3/8] restructure vm compiler to reduce task extraction time --- python/tvm/autotvm/task/relay_integration.py | 4 +- python/tvm/relay/backend/vm.py | 43 ++++++++++++------ src/relay/backend/vm/compiler.cc | 46 ++++++++++++-------- src/relay/backend/vm/compiler.h | 15 +++---- tests/python/relay/test_vm.py | 2 +- 5 files changed, 67 insertions(+), 43 deletions(-) diff --git a/python/tvm/autotvm/task/relay_integration.py b/python/tvm/autotvm/task/relay_integration.py index bbaaf0524b14..4a25f01a4c2b 100644 --- a/python/tvm/autotvm/task/relay_integration.py +++ b/python/tvm/autotvm/task/relay_integration.py @@ -49,7 +49,9 @@ def _lower(mod, grc = graph_runtime_codegen.GraphRuntimeCodegen(None, target) return grc.codegen(mod["main"]) # default case - return relay.vm.compile(mod, target=target, params=params) + compiler = relay.vm.VMCompiler() + compiler.set_params(params) + compiler.lower(mod, target=target) def extract_from_program(mod, params, ops, target, target_host=None, diff --git a/python/tvm/relay/backend/vm.py b/python/tvm/relay/backend/vm.py index c7141b0d8d0f..14c3763f77ca 100644 --- a/python/tvm/relay/backend/vm.py +++ b/python/tvm/relay/backend/vm.py @@ -363,7 +363,8 @@ def run(self, *args, **kwargs): def compile(mod, target=None, target_host=None, params=None): - """ + """Compile the module to VM executable. + Parameters ---------- mod : relay.Module @@ -393,21 +394,19 @@ def compile(mod, target=None, target_host=None, params=None): The VM executable that contains both library code and bytecode. """ compiler = VMCompiler() - - target = compiler.update_target(target) - target_host = compiler.update_target_host(target, target_host) if params: compiler.set_params(params) - tophub_context = compiler.tophub_context(target) - with tophub_context: - compiler._compile(mod, target, target_host) - return Executable(compiler._get_exec()) + compiler.lower(mod, target, target_host) + compiler.codegen() + return compiler.get_exec() + class VMCompiler(object): """Build Relay module to run on VM runtime.""" def __init__(self): self.mod = _vm._VMCompiler() - self._compile = self.mod["compile"] + self._lower = self.mod["lower"] + self._codegen = self.mod["codegen"] self._get_exec = self.mod["get_executable"] self._set_params_func = self.mod["set_params"] @@ -420,8 +419,24 @@ def set_params(self, params): inputs[name] = _expr.const(param) self._set_params_func(inputs) - def update_target(self, target): - """Update target""" + def lower(self, mod, target=None, target_host=None): + """Lower the module to VM bytecode.""" + target = self._update_target(target) + target_host = self._update_target_host(target, target_host) + tophub_context = self._tophub_context(target) + with tophub_context: + self._lower(mod, target, target_host) + + def codegen(self): + """Generate the machine code.""" + self._codegen() + + def get_exec(self): + """Return the executable.""" + return Executable(self._get_exec()) + + def _update_target(self, target): + """Update target.""" target = target if target else tvm.target.current_target() if target is None: raise ValueError("Target is not set in env or passed as argument.") @@ -439,8 +454,8 @@ def update_target(self, target): "{}".format(type(target))) return tgts - def update_target_host(self, target, target_host): - """Update target host""" + def _update_target_host(self, target, target_host): + """Update target host.""" target_host = None if target_host == "" else target_host if not target_host: for device_type, tgt in target.items(): @@ -453,7 +468,7 @@ def update_target_host(self, target, target_host): target_host = tvm.target.create(target_host) return target_host - def tophub_context(self, target): + def _tophub_context(self, target): # If current dispatch context is fallback context (the default root context), # then load pre-tuned parameters from TopHub if isinstance(autotvm.DispatchContext.current, autotvm.FallbackContext): diff --git a/src/relay/backend/vm/compiler.cc b/src/relay/backend/vm/compiler.cc index 6a3c580aa56e..891862341301 100644 --- a/src/relay/backend/vm/compiler.cc +++ b/src/relay/backend/vm/compiler.cc @@ -743,11 +743,16 @@ class VMFunctionCompiler : ExprFunctor { PackedFunc VMCompiler::GetFunction(const std::string& name, const ObjectPtr& sptr_to_self) { - if (name == "compile") { + if (name == "lower") { return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { CHECK_EQ(args.num_args, 3); Module mod = args[0]; - this->Compile(mod, args[1], args[2]); + this->Lower(mod, args[1], args[2]); + }); + } if (name == "codegen") { + return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { + CHECK_EQ(args.num_args, 0); + this->Codegen(); }); } else if (name == "get_executable") { return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { @@ -802,9 +807,9 @@ relay::Function VMCompiler::BindParamsByName( return ret; } -void VMCompiler::Compile(Module mod, - const TargetsMap& targets, - const tvm::Target& target_host) { +void VMCompiler::Lower(Module mod, + const TargetsMap& targets, + const tvm::Target& target_host) { CHECK_EQ(targets.size(), 1) << "Currently VM compiler doesn't support heterogeneous compilation"; if (params_.size()) { @@ -813,7 +818,7 @@ void VMCompiler::Compile(Module mod, mod->Add(gvar, f); } - InitVM(); + exec_ = make_object(); targets_ = targets; target_host_ = target_host; @@ -852,11 +857,20 @@ void VMCompiler::Compile(Module mod, exec_->constants.push_back(vm::Tensor(data)); } - LibraryCodegen(); - + // update global function map for (auto gv : context_.global_map) { exec_->global_map.insert({gv.first->name_hint, gv.second}); } + + // update primitive function map + size_t primitive_index = 0; + for (const auto& cfunc : context_.cached_funcs) { + if (cfunc->target->str() == "ext_dev") { + exec_->primitive_map.insert({cfunc->func_name, primitive_index++}); + } else { + exec_->primitive_map.insert({cfunc->funcs[0]->name, primitive_index++}); + } + } } Module VMCompiler::OptimizeModule(const Module& mod, const TargetsMap& targets) { @@ -942,7 +956,11 @@ void VMCompiler::PopulateGlobalMap() { } } -void VMCompiler::LibraryCodegen() { +void VMCompiler::Codegen() { + if (!context_.module.defined()) { + LOG(WARNING) << "Did you forget to call VMCompiler::Lower?"; + return; + } auto const &cached_funcs = context_.cached_funcs; if (cached_funcs.size() == 0) { return; @@ -979,15 +997,7 @@ void VMCompiler::LibraryCodegen() { } } } - exec_->lib = mod; - size_t primitive_index = 0; - for (auto cfunc : cached_funcs) { - if (cfunc->target->str() == "ext_dev") { - exec_->primitive_map.insert({cfunc->func_name, primitive_index++}); - } else { - exec_->primitive_map.insert({cfunc->funcs[0]->name, primitive_index++}); - } - } + exec_->lib = mod; } runtime::Module CreateVMCompiler() { diff --git a/src/relay/backend/vm/compiler.h b/src/relay/backend/vm/compiler.h index 2beab1536a18..225bdcd80855 100644 --- a/src/relay/backend/vm/compiler.h +++ b/src/relay/backend/vm/compiler.h @@ -91,10 +91,6 @@ class VMCompiler : public runtime::ModuleNode { return "VMCompiler"; } - void InitVM() { - exec_ = make_object(); - } - /*! * \brief Set the parameters * @@ -111,9 +107,12 @@ class VMCompiler : public runtime::ModuleNode { to target mapping. For homogeneous compilation, it is a build target. * \param target_host Host compilation target, if target is device. */ - void Compile(Module mod, - const TargetsMap& targets, - const tvm::Target& target_host); + void Lower(Module mod, + const TargetsMap& targets, + const tvm::Target& target_host); + + /*! \brief Generate the machine code for lowered functions. */ + void Codegen(); protected: /*! @@ -130,8 +129,6 @@ class VMCompiler : public runtime::ModuleNode { void PopulateGlobalMap(); - void LibraryCodegen(); - protected: /*! \brief Target devices. */ TargetsMap targets_; diff --git a/tests/python/relay/test_vm.py b/tests/python/relay/test_vm.py index a4c5b7d2a3c3..8a160b11ee65 100644 --- a/tests/python/relay/test_vm.py +++ b/tests/python/relay/test_vm.py @@ -572,4 +572,4 @@ def test_add_op_broadcast(): if __name__ == "__main__": - pytest.main() + pytest.main([__file__]) From c1a40be599bbfae13ec52dcfd8aee80b943197df Mon Sep 17 00:00:00 2001 From: Haichen Shen Date: Wed, 8 Jan 2020 00:26:47 +0000 Subject: [PATCH 4/8] x --- src/relay/backend/vm/compiler.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/relay/backend/vm/compiler.cc b/src/relay/backend/vm/compiler.cc index 891862341301..7946ee66007c 100644 --- a/src/relay/backend/vm/compiler.cc +++ b/src/relay/backend/vm/compiler.cc @@ -749,7 +749,7 @@ PackedFunc VMCompiler::GetFunction(const std::string& name, Module mod = args[0]; this->Lower(mod, args[1], args[2]); }); - } if (name == "codegen") { + } else if (name == "codegen") { return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { CHECK_EQ(args.num_args, 0); this->Codegen(); @@ -997,7 +997,7 @@ void VMCompiler::Codegen() { } } } - exec_->lib = mod; + exec_->lib = mod; } runtime::Module CreateVMCompiler() { From a48f2c7b4c3e3b0a62c38aefee7d560590022142 Mon Sep 17 00:00:00 2001 From: Haichen Shen Date: Wed, 8 Jan 2020 00:28:36 +0000 Subject: [PATCH 5/8] fix --- python/tvm/autotvm/task/relay_integration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/tvm/autotvm/task/relay_integration.py b/python/tvm/autotvm/task/relay_integration.py index 4a25f01a4c2b..55763afcf7bc 100644 --- a/python/tvm/autotvm/task/relay_integration.py +++ b/python/tvm/autotvm/task/relay_integration.py @@ -47,7 +47,7 @@ def _lower(mod, with vta.build_config(): mod, _ = relay.optimize(mod, target, params) grc = graph_runtime_codegen.GraphRuntimeCodegen(None, target) - return grc.codegen(mod["main"]) + grc.codegen(mod["main"]) # default case compiler = relay.vm.VMCompiler() compiler.set_params(params) From 8f48d0692ca232958a1adc18786ef56e9cf6c697 Mon Sep 17 00:00:00 2001 From: Haichen Shen Date: Wed, 8 Jan 2020 00:30:24 +0000 Subject: [PATCH 6/8] update doc --- src/relay/backend/vm/compiler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/relay/backend/vm/compiler.h b/src/relay/backend/vm/compiler.h index 225bdcd80855..7efcb4ba8d81 100644 --- a/src/relay/backend/vm/compiler.h +++ b/src/relay/backend/vm/compiler.h @@ -100,7 +100,7 @@ class VMCompiler : public runtime::ModuleNode { void SetParam(const std::string& name, runtime::NDArray data_in); /*! - * \brief Compile functions in a Module + * \brief Lower the functions in a Module * * \param mod Relay Module * \param targets For heterogeneous compilation, it is a dictionary indicating context From 23aeda083f8bff65804692c64a7a676676456103 Mon Sep 17 00:00:00 2001 From: Haichen Shen Date: Wed, 8 Jan 2020 20:16:32 +0000 Subject: [PATCH 7/8] udpate doc --- docs/api/python/relay/backend.rst | 3 ++ python/tvm/relay/backend/vm.py | 46 +++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/docs/api/python/relay/backend.rst b/docs/api/python/relay/backend.rst index fa6ab883c20d..c30f226e8437 100644 --- a/docs/api/python/relay/backend.rst +++ b/docs/api/python/relay/backend.rst @@ -28,3 +28,6 @@ tvm.relay.backend .. automodule:: tvm.relay.backend.graph_runtime_codegen :members: + +.. automodule:: tvm.relay.backend.vm + :members: diff --git a/python/tvm/relay/backend/vm.py b/python/tvm/relay/backend/vm.py index 14c3763f77ca..8650eeb18ef1 100644 --- a/python/tvm/relay/backend/vm.py +++ b/python/tvm/relay/backend/vm.py @@ -363,7 +363,7 @@ def run(self, *args, **kwargs): def compile(mod, target=None, target_host=None, params=None): - """Compile the module to VM executable. + """Compile the module to VM executable. A helper function for VMCompiler. Parameters ---------- @@ -402,7 +402,7 @@ def compile(mod, target=None, target_host=None, params=None): class VMCompiler(object): - """Build Relay module to run on VM runtime.""" + """Compiler that compiles Relay module to VM executable.""" def __init__(self): self.mod = _vm._VMCompiler() self._lower = self.mod["lower"] @@ -411,7 +411,14 @@ def __init__(self): self._set_params_func = self.mod["set_params"] def set_params(self, params): - """Set constant parameters for the model""" + """Set constant parameters for the model. + + Parameters + ---------- + params : dict of str to NDArray + Input parameters to the graph that do not change + during inference time. Used for constant folding. + """ inputs = {} for name, param in params.items(): if isinstance(param, np.ndarray): @@ -420,7 +427,27 @@ def set_params(self, params): self._set_params_func(inputs) def lower(self, mod, target=None, target_host=None): - """Lower the module to VM bytecode.""" + """Lower the module to VM bytecode. + + Parameters + ---------- + mod : relay.Module + The Relay module to build. + + target : str, :any:`tvm.target.Target`, or dict of str(i.e. + device/context name) to str/tvm.target.Target, optional + For heterogeneous compilation, it is a dictionary indicating context + to target mapping. For homogeneous compilation, it is a build target. + + target_host : str or :any:`tvm.target.Target`, optional + Host compilation target, if target is device. + When TVM compiles device specific program such as CUDA, + we also need host(CPU) side code to interact with the driver + to setup the dimensions and parameters correctly. + target_host is used to specify the host side codegen target. + By default, llvm is used if it is enabled, + otherwise a stackvm intepreter is used. + """ target = self._update_target(target) target_host = self._update_target_host(target, target_host) tophub_context = self._tophub_context(target) @@ -428,11 +455,17 @@ def lower(self, mod, target=None, target_host=None): self._lower(mod, target, target_host) def codegen(self): - """Generate the machine code.""" + """Generate the kernel library.""" self._codegen() def get_exec(self): - """Return the executable.""" + """Get the VM executable. + + Returns + ------- + exec : Executable + The VM executable that contains both library code and bytecode. + """ return Executable(self._get_exec()) def _update_target(self, target): @@ -469,6 +502,7 @@ def _update_target_host(self, target, target_host): return target_host def _tophub_context(self, target): + """Get the autotvm context.""" # If current dispatch context is fallback context (the default root context), # then load pre-tuned parameters from TopHub if isinstance(autotvm.DispatchContext.current, autotvm.FallbackContext): From 7192fe233abae1cbf9e1844c4796ffe33ae7d403 Mon Sep 17 00:00:00 2001 From: Haichen Shen Date: Wed, 8 Jan 2020 22:41:55 +0000 Subject: [PATCH 8/8] lint --- python/tvm/relay/backend/vm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/tvm/relay/backend/vm.py b/python/tvm/relay/backend/vm.py index 8650eeb18ef1..bad4ac227d09 100644 --- a/python/tvm/relay/backend/vm.py +++ b/python/tvm/relay/backend/vm.py @@ -428,7 +428,7 @@ def set_params(self, params): def lower(self, mod, target=None, target_host=None): """Lower the module to VM bytecode. - + Parameters ---------- mod : relay.Module