From 1b705f8f6f372126c2282777d688fa3e810ad208 Mon Sep 17 00:00:00 2001 From: Valery Chernov Date: Thu, 17 Feb 2022 16:39:28 +0300 Subject: [PATCH 01/15] set_input_with_index was implemented for VM --- include/tvm/runtime/vm/vm.h | 9 ++++++++ src/runtime/vm/vm.cc | 43 +++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/include/tvm/runtime/vm/vm.h b/include/tvm/runtime/vm/vm.h index d7311951b702..cd519f09d6e8 100644 --- a/include/tvm/runtime/vm/vm.h +++ b/include/tvm/runtime/vm/vm.h @@ -270,6 +270,15 @@ class VirtualMachine : public runtime::ModuleNode { */ void SetInput(std::string name, TVMArgs args, int offset); + /*! + * \brief Set input tensor with index to a function. + * \param name The function name + * \param args args[1:] are two arguments (index, tensor) to the + * function. If the tensor is not of the correct device for the function, + * they will be copied to the device. + */ + void SetInputWithIndex(std::string name, TVMArgs args); + /*! * \brief Internal hook for profiling the start of an op. * diff --git a/src/runtime/vm/vm.cc b/src/runtime/vm/vm.cc index f0d0a44908ae..c1d2c6bb5c91 100644 --- a/src/runtime/vm/vm.cc +++ b/src/runtime/vm/vm.cc @@ -221,6 +221,9 @@ PackedFunc VirtualMachine::GetFunction(const std::string& name, } else if (name == "set_input") { return PackedFunc( [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { SetInput(args[0], args, 1); }); + } else if (name == "set_input_with_index") { + return PackedFunc( + [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { SetInputWithIndex(args[0], args); }); } else if (name == "load_late_bound_consts") { return PackedFunc([this](TVMArgs args, TVMRetValue* rv) { CHECK_EQ(args.size(), 1); @@ -267,6 +270,46 @@ void VirtualMachine::SetInput(std::string func_name, TVMArgs args, int offset) { inputs_.emplace(func_name, func_args); } +void VirtualMachine::SetInputWithIndex(std::string func_name, TVMArgs args) { + ICHECK(exec_) << "The executable is not created yet."; + auto gvit = exec_->global_map.find(func_name); + ICHECK(gvit != exec_->global_map.end()) << "Cannot find function " << func_name; + auto func_index = gvit->second; + const auto& vm_func = exec_->functions[func_index]; + const auto& param_names = vm_func.params; + ICHECK_EQ(args.size(), 3) << "The expected number of arguments is 3 (func_name, index, tensor)"; + // TODO(vvchernov): Looks like it should be checked earlier and in other place + ICHECK_EQ(param_names.size(), vm_func.param_device_indexes.size()) + << "The number of provided parameters doesn't match the number of assigned devices"; + if (inputs_.count(func_name)) { + ICHECK_EQ(inputs_[func_name].size(), param_names.size()) + << "The size of function" << func_name + << " doesn't match the number of provided parameters"; + } else { + std::vector func_args(param_names.size()); + inputs_.emplace(func_name, func_args); + } + ICHECK_EQ(args[1].type_code(), kTVMArgInt) << "The second argument doesn't match integer index"; + int inp_index = args[1]; + auto& input_tensors = inputs_[func_name]; + Device dev = GetDevice(vm_func.param_device_indexes[inp_index]); + + if (args[2].type_code() == kTVMDLTensorHandle) { + // Automatically convert input DLTensors to NDArray + DLTensor* tensor = args[2]; + std::vector shape; + for (int64_t i = 0; i < tensor->ndim; i++) { + shape.push_back(tensor->shape[i]); + } + NDArray ary = NDArray::Empty(shape, tensor->dtype, dev); + ary.CopyFrom(tensor); + input_tensors[inp_index] = ary; + } else { + ObjectRef obj = CopyTo(args[2], dev); + input_tensors[inp_index] = obj; + } +} + inline Device VirtualMachine::GetDevice(Index device_index) const { ICHECK_GE(devices_.size(), device_index) << "invalid device index: " << device_index; return devices_[device_index]; From e9eb68628c6459aa47623effe3c0e1e056936522 Mon Sep 17 00:00:00 2001 From: Valery Chernov Date: Mon, 21 Feb 2022 10:10:59 +0300 Subject: [PATCH 02/15] clean code --- include/tvm/runtime/vm/vm.h | 7 +++++ src/runtime/vm/vm.cc | 60 +++++++++++++++++-------------------- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/include/tvm/runtime/vm/vm.h b/include/tvm/runtime/vm/vm.h index cd519f09d6e8..01e7e6f2aa2d 100644 --- a/include/tvm/runtime/vm/vm.h +++ b/include/tvm/runtime/vm/vm.h @@ -295,6 +295,13 @@ class VirtualMachine : public runtime::ModuleNode { */ virtual void OpStopHook(); + private: + const VMFunction& checkAndGetVMFunction(const std::string& func_name) const; + void SetInputTensorWithIndex(std::vector& tensors, + const TVMArgValue& tensor, + int index, + Device dev); + protected: /*! \brief The virtual machine's packed function table. */ std::vector packed_funcs_; diff --git a/src/runtime/vm/vm.cc b/src/runtime/vm/vm.cc index c1d2c6bb5c91..480f28928fb1 100644 --- a/src/runtime/vm/vm.cc +++ b/src/runtime/vm/vm.cc @@ -237,45 +237,25 @@ PackedFunc VirtualMachine::GetFunction(const std::string& name, } void VirtualMachine::SetInput(std::string func_name, TVMArgs args, int offset) { - ICHECK(exec_) << "The executable is not created yet."; - auto gvit = exec_->global_map.find(func_name); - ICHECK(gvit != exec_->global_map.end()) << "Cannot find function " << func_name; - auto func_index = gvit->second; - const auto& vm_func = exec_->functions[func_index]; + const auto& vm_func = checkAndGetVMFunction(func_name); const auto& param_names = vm_func.params; ICHECK_EQ(args.size() - offset, param_names.size()) << "The number of provided parameters doesn't match the number of arguments"; + // TODO(vvchernov): Looks like it should be checked earlier and in other place ICHECK_EQ(param_names.size(), vm_func.param_device_indexes.size()) << "The number of provided parameters doesn't match the number of assigned devices"; std::vector func_args(param_names.size()); for (int i = offset; i < args.size(); ++i) { - Device dev = GetDevice(vm_func.param_device_indexes[i - offset]); - - if (args[i].type_code() == kTVMDLTensorHandle) { - // Automatically convert input DLTensors to NDArray - DLTensor* tensor = args[i]; - std::vector shape; - for (int64_t i = 0; i < tensor->ndim; i++) { - shape.push_back(tensor->shape[i]); - } - NDArray ary = NDArray::Empty(shape, tensor->dtype, dev); - ary.CopyFrom(tensor); - func_args[i - offset] = ary; - } else { - ObjectRef obj = CopyTo(args[i], dev); - func_args[i - offset] = obj; - } + int index = i - offset; + Device dev = GetDevice(vm_func.param_device_indexes[index]); + SetInputTensorWithIndex(func_args, args[i], index, dev); } inputs_.erase(func_name); inputs_.emplace(func_name, func_args); } void VirtualMachine::SetInputWithIndex(std::string func_name, TVMArgs args) { - ICHECK(exec_) << "The executable is not created yet."; - auto gvit = exec_->global_map.find(func_name); - ICHECK(gvit != exec_->global_map.end()) << "Cannot find function " << func_name; - auto func_index = gvit->second; - const auto& vm_func = exec_->functions[func_index]; + const auto& vm_func = checkAndGetVMFunction(func_name); const auto& param_names = vm_func.params; ICHECK_EQ(args.size(), 3) << "The expected number of arguments is 3 (func_name, index, tensor)"; // TODO(vvchernov): Looks like it should be checked earlier and in other place @@ -289,24 +269,38 @@ void VirtualMachine::SetInputWithIndex(std::string func_name, TVMArgs args) { std::vector func_args(param_names.size()); inputs_.emplace(func_name, func_args); } - ICHECK_EQ(args[1].type_code(), kTVMArgInt) << "The second argument doesn't match integer index"; + ICHECK_EQ(args[1].type_code(), kTVMArgInt) << "The second argument doesn't match integer"; int inp_index = args[1]; - auto& input_tensors = inputs_[func_name]; + ICHECK_LT(inp_index, param_names.size()); Device dev = GetDevice(vm_func.param_device_indexes[inp_index]); - if (args[2].type_code() == kTVMDLTensorHandle) { + SetInputTensorWithIndex(inputs_[func_name], args[2], inp_index, dev); +} + +const VMFunction& VirtualMachine::checkAndGetVMFunction(const std::string& func_name) const { + ICHECK(exec_) << "The executable is not created yet."; + auto gvit = exec_->global_map.find(func_name); + ICHECK(gvit != exec_->global_map.end()) << "Cannot find function " << func_name; + auto func_index = gvit->second; + return exec_->functions[func_index]; +} + +void VirtualMachine::SetInputTensorWithIndex(std::vector& tensors, + const TVMArgValue& inp_tensor, + int index, + Device dev) { + if (inp_tensor.type_code() == kTVMDLTensorHandle) { // Automatically convert input DLTensors to NDArray - DLTensor* tensor = args[2]; + DLTensor* tensor = inp_tensor; std::vector shape; for (int64_t i = 0; i < tensor->ndim; i++) { shape.push_back(tensor->shape[i]); } NDArray ary = NDArray::Empty(shape, tensor->dtype, dev); ary.CopyFrom(tensor); - input_tensors[inp_index] = ary; + tensors[index] = ary; } else { - ObjectRef obj = CopyTo(args[2], dev); - input_tensors[inp_index] = obj; + tensors[index] = CopyTo(inp_tensor, dev); } } From 289bd82e58820d9ca4d0dd6f168d50a855e6fb19 Mon Sep 17 00:00:00 2001 From: Valery Chernov Date: Mon, 21 Feb 2022 11:13:06 +0300 Subject: [PATCH 03/15] add getInputIndexFromName. add function descriptions. lint fix --- include/tvm/runtime/vm/vm.h | 22 +++++++++++++++++++++- src/runtime/vm/vm.cc | 24 +++++++++++++----------- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/include/tvm/runtime/vm/vm.h b/include/tvm/runtime/vm/vm.h index 01e7e6f2aa2d..525c1004d044 100644 --- a/include/tvm/runtime/vm/vm.h +++ b/include/tvm/runtime/vm/vm.h @@ -296,8 +296,28 @@ class VirtualMachine : public runtime::ModuleNode { virtual void OpStopHook(); private: + /*! + * \brief Get index of input tensor from its name. + * \param input_name The input tensor name + * \param func_name The function's name. + * \return The input tensor index. + */ + int64_t getInputIndexFromName(const std::string& input_name, + const std::string& func_name) const; + /*! + * \brief Check executable exists and function name is in global map, get VM function. + * \param func_name The function's name. + * \return VM function. + */ const VMFunction& checkAndGetVMFunction(const std::string& func_name) const; - void SetInputTensorWithIndex(std::vector& tensors, + /*! + * \brief Set one input tensor with given index to set of input tensors if need copy to given device. + * \param tensors the input tensors set (destination) + * \param tensor some tensor (not neccessary DLTensor) + * \param index The input tensor index. + * \param dev device to copy if need. + */ + void SetInputTensorWithIndex(std::vector& tensors, // NOLINT(*) const TVMArgValue& tensor, int index, Device dev); diff --git a/src/runtime/vm/vm.cc b/src/runtime/vm/vm.cc index 480f28928fb1..8cb94aeecde0 100644 --- a/src/runtime/vm/vm.cc +++ b/src/runtime/vm/vm.cc @@ -190,17 +190,7 @@ PackedFunc VirtualMachine::GetFunction(const std::string& name, } else if (name == "get_input_index") { return TypedPackedFunc( [this](std::string input_name, std::string func_name) { - auto gvit = exec_->global_map.find(func_name); - ICHECK(gvit != exec_->global_map.end()) << "Cannot find function " << func_name; - auto func_index = gvit->second; - const auto& vm_func = exec_->functions[func_index]; - const auto& param_names = vm_func.params; - for (uint64_t i = 0; i < param_names.size(); i++) { - if (input_name == param_names[i]) { - return static_cast(i); - } - } - return static_cast(-1); + return getInputIndexFromName(input_name, func_name); }); } else if (name == "init") { return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { @@ -277,6 +267,18 @@ void VirtualMachine::SetInputWithIndex(std::string func_name, TVMArgs args) { SetInputTensorWithIndex(inputs_[func_name], args[2], inp_index, dev); } +int64_t VirtualMachine::getInputIndexFromName(const std::string& input_name, + const std::string& func_name) const { + const auto& vm_func = checkAndGetVMFunction(func_name); + const auto& param_names = vm_func.params; + for (uint64_t i = 0; i < param_names.size(); i++) { + if (input_name == param_names[i]) { + return static_cast(i); + } + } + return static_cast(-1); +} + const VMFunction& VirtualMachine::checkAndGetVMFunction(const std::string& func_name) const { ICHECK(exec_) << "The executable is not created yet."; auto gvit = exec_->global_map.find(func_name); From 4e01e40a5df6f51eca7e017fff699b2c7d221ae3 Mon Sep 17 00:00:00 2001 From: Valery Chernov Date: Mon, 21 Feb 2022 11:45:34 +0300 Subject: [PATCH 04/15] fix lint --- include/tvm/runtime/vm/vm.h | 19 +++++++------------ src/runtime/vm/vm.cc | 4 +--- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/include/tvm/runtime/vm/vm.h b/include/tvm/runtime/vm/vm.h index 525c1004d044..290025e01fb9 100644 --- a/include/tvm/runtime/vm/vm.h +++ b/include/tvm/runtime/vm/vm.h @@ -302,25 +302,20 @@ class VirtualMachine : public runtime::ModuleNode { * \param func_name The function's name. * \return The input tensor index. */ - int64_t getInputIndexFromName(const std::string& input_name, - const std::string& func_name) const; + int64_t getInputIndexFromName(const std::string& input_name, const std::string& func_name) const; /*! * \brief Check executable exists and function name is in global map, get VM function. * \param func_name The function's name. * \return VM function. */ const VMFunction& checkAndGetVMFunction(const std::string& func_name) const; - /*! - * \brief Set one input tensor with given index to set of input tensors if need copy to given device. - * \param tensors the input tensors set (destination) - * \param tensor some tensor (not neccessary DLTensor) - * \param index The input tensor index. - * \param dev device to copy if need. + /*! + * \brief Set one input tensor with given index to set of input tensors if need copy to given + * device. \param tensors the input tensors set (destination) \param tensor some tensor (not + * neccessary DLTensor). \param index The input tensor index. \param dev device to copy if need. */ - void SetInputTensorWithIndex(std::vector& tensors, // NOLINT(*) - const TVMArgValue& tensor, - int index, - Device dev); + void SetInputTensorWithIndex(std::vector& tensors, // NOLINT(*) + const TVMArgValue& tensor, int index, Device dev); protected: /*! \brief The virtual machine's packed function table. */ diff --git a/src/runtime/vm/vm.cc b/src/runtime/vm/vm.cc index 8cb94aeecde0..23176d93ddc5 100644 --- a/src/runtime/vm/vm.cc +++ b/src/runtime/vm/vm.cc @@ -288,9 +288,7 @@ const VMFunction& VirtualMachine::checkAndGetVMFunction(const std::string& func_ } void VirtualMachine::SetInputTensorWithIndex(std::vector& tensors, - const TVMArgValue& inp_tensor, - int index, - Device dev) { + const TVMArgValue& inp_tensor, int index, Device dev) { if (inp_tensor.type_code() == kTVMDLTensorHandle) { // Automatically convert input DLTensors to NDArray DLTensor* tensor = inp_tensor; From 3fb51236e2430a4f432439a874a0ad9efe788461 Mon Sep 17 00:00:00 2001 From: Valery Chernov Date: Mon, 21 Feb 2022 12:43:02 +0300 Subject: [PATCH 05/15] transfer comparison of parameter names number and assigned devices number to VMFunction constructor --- include/tvm/runtime/vm/vm.h | 5 ++++- src/runtime/vm/vm.cc | 6 ------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/include/tvm/runtime/vm/vm.h b/include/tvm/runtime/vm/vm.h index 290025e01fb9..20eff03a17a5 100644 --- a/include/tvm/runtime/vm/vm.h +++ b/include/tvm/runtime/vm/vm.h @@ -93,7 +93,10 @@ struct VMFunction { params(std::move(params)), instructions(std::move(instructions)), register_file_size(register_file_size), - param_device_indexes(std::move(param_device_indexes)) {} + param_device_indexes(std::move(param_device_indexes)) { + ICHECK_EQ(params.size(), param_device_indexes.size()) + << "The number of provided parameters doesn't match the number of assigned devices"; + } VMFunction() = default; diff --git a/src/runtime/vm/vm.cc b/src/runtime/vm/vm.cc index 23176d93ddc5..03f934fd1e61 100644 --- a/src/runtime/vm/vm.cc +++ b/src/runtime/vm/vm.cc @@ -231,9 +231,6 @@ void VirtualMachine::SetInput(std::string func_name, TVMArgs args, int offset) { const auto& param_names = vm_func.params; ICHECK_EQ(args.size() - offset, param_names.size()) << "The number of provided parameters doesn't match the number of arguments"; - // TODO(vvchernov): Looks like it should be checked earlier and in other place - ICHECK_EQ(param_names.size(), vm_func.param_device_indexes.size()) - << "The number of provided parameters doesn't match the number of assigned devices"; std::vector func_args(param_names.size()); for (int i = offset; i < args.size(); ++i) { int index = i - offset; @@ -248,9 +245,6 @@ void VirtualMachine::SetInputWithIndex(std::string func_name, TVMArgs args) { const auto& vm_func = checkAndGetVMFunction(func_name); const auto& param_names = vm_func.params; ICHECK_EQ(args.size(), 3) << "The expected number of arguments is 3 (func_name, index, tensor)"; - // TODO(vvchernov): Looks like it should be checked earlier and in other place - ICHECK_EQ(param_names.size(), vm_func.param_device_indexes.size()) - << "The number of provided parameters doesn't match the number of assigned devices"; if (inputs_.count(func_name)) { ICHECK_EQ(inputs_[func_name].size(), param_names.size()) << "The size of function" << func_name From cc479b2ba5cb5a5655916b1ddaf3a886403a4bf2 Mon Sep 17 00:00:00 2001 From: Valery Chernov Date: Mon, 21 Feb 2022 13:48:20 +0300 Subject: [PATCH 06/15] add GetVMFunctionWithName to Executable API --- include/tvm/runtime/vm/executable.h | 7 +++++++ include/tvm/runtime/vm/vm.h | 2 +- src/runtime/vm/executable.cc | 25 +++++++++---------------- src/runtime/vm/vm.cc | 5 +---- 4 files changed, 18 insertions(+), 21 deletions(-) diff --git a/include/tvm/runtime/vm/executable.h b/include/tvm/runtime/vm/executable.h index 6359da0a5375..774bca1e2d28 100644 --- a/include/tvm/runtime/vm/executable.h +++ b/include/tvm/runtime/vm/executable.h @@ -218,6 +218,13 @@ class Executable : public ModuleNode { */ void SetLib(const runtime::Module& lib); + /*! + * \brief Get VMFunction. + * \param func_name The function's name. + * \return VMFunction. + */ + const VMFunction& GetVMFunctionWithName(const std::string& func_name) const; + /*! * \brief Get the arity of the VMFunction. * \param func Function name. diff --git a/include/tvm/runtime/vm/vm.h b/include/tvm/runtime/vm/vm.h index 20eff03a17a5..496837a3a15d 100644 --- a/include/tvm/runtime/vm/vm.h +++ b/include/tvm/runtime/vm/vm.h @@ -307,7 +307,7 @@ class VirtualMachine : public runtime::ModuleNode { */ int64_t getInputIndexFromName(const std::string& input_name, const std::string& func_name) const; /*! - * \brief Check executable exists and function name is in global map, get VM function. + * \brief Check executable exists and get VM function from it. * \param func_name The function's name. * \return VM function. */ diff --git a/src/runtime/vm/executable.cc b/src/runtime/vm/executable.cc index dc8f572a13dc..a003367c3724 100644 --- a/src/runtime/vm/executable.cc +++ b/src/runtime/vm/executable.cc @@ -109,27 +109,20 @@ PackedFunc Executable::GetFunction(const std::string& name, const ObjectPtrsecond]; + ICHECK(it != global_map.end()) << "Cannot find function " << func_name << " in executable"; + return functions[it->second]; +} + +int Executable::GetFunctionArity(std::string func_name) const { + const auto& func = GetVMFunctionWithName(func_name); return func.params.size(); } std::string Executable::GetFunctionParameterName(std::string func_name, uint32_t index) const { - auto it = global_map.find(func_name); - if (it == global_map.end()) { - LOG(ERROR) << "Cannot find function " << func_name << " in executable"; - return ""; - } - const auto& func = functions[it->second]; - if (index > func.params.size()) { - LOG(ERROR) << "Invalid parameter index"; - return ""; - } + const auto& func = GetVMFunctionWithName(func_name); + ICHECK_LT(index, func.params.size()) << "Invalid parameter index"; return func.params[index]; } diff --git a/src/runtime/vm/vm.cc b/src/runtime/vm/vm.cc index 03f934fd1e61..14f0969b08b6 100644 --- a/src/runtime/vm/vm.cc +++ b/src/runtime/vm/vm.cc @@ -275,10 +275,7 @@ int64_t VirtualMachine::getInputIndexFromName(const std::string& input_name, const VMFunction& VirtualMachine::checkAndGetVMFunction(const std::string& func_name) const { ICHECK(exec_) << "The executable is not created yet."; - auto gvit = exec_->global_map.find(func_name); - ICHECK(gvit != exec_->global_map.end()) << "Cannot find function " << func_name; - auto func_index = gvit->second; - return exec_->functions[func_index]; + return exec_->GetVMFunctionWithName(func_name); } void VirtualMachine::SetInputTensorWithIndex(std::vector& tensors, From 0adc5d169581ad97f0b1ca3020a178cce5fccbaf Mon Sep 17 00:00:00 2001 From: Valery Chernov Date: Mon, 21 Feb 2022 13:56:43 +0300 Subject: [PATCH 07/15] clean code --- include/tvm/runtime/vm/vm.h | 7 +++++++ src/runtime/vm/vm.cc | 32 ++++++++++++++++++-------------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/include/tvm/runtime/vm/vm.h b/include/tvm/runtime/vm/vm.h index 496837a3a15d..fad01ef9986f 100644 --- a/include/tvm/runtime/vm/vm.h +++ b/include/tvm/runtime/vm/vm.h @@ -312,6 +312,13 @@ class VirtualMachine : public runtime::ModuleNode { * \return VM function. */ const VMFunction& checkAndGetVMFunction(const std::string& func_name) const; + /*! + * \brief Creats inputs_ field, if it exists check its size. + * \param func_name The function's name. + * \param size inputs_ field size. + * \return VM function. + */ + void createInputsOrCheckSize(const std::string& func_name, size_t size); /*! * \brief Set one input tensor with given index to set of input tensors if need copy to given * device. \param tensors the input tensors set (destination) \param tensor some tensor (not diff --git a/src/runtime/vm/vm.cc b/src/runtime/vm/vm.cc index 14f0969b08b6..a76b8e5493e4 100644 --- a/src/runtime/vm/vm.cc +++ b/src/runtime/vm/vm.cc @@ -228,10 +228,10 @@ PackedFunc VirtualMachine::GetFunction(const std::string& name, void VirtualMachine::SetInput(std::string func_name, TVMArgs args, int offset) { const auto& vm_func = checkAndGetVMFunction(func_name); - const auto& param_names = vm_func.params; - ICHECK_EQ(args.size() - offset, param_names.size()) + size_t params_num = vm_func.params.size(); + ICHECK_EQ(args.size() - offset, params_num) << "The number of provided parameters doesn't match the number of arguments"; - std::vector func_args(param_names.size()); + std::vector func_args(params_num); for (int i = offset; i < args.size(); ++i) { int index = i - offset; Device dev = GetDevice(vm_func.param_device_indexes[index]); @@ -243,21 +243,14 @@ void VirtualMachine::SetInput(std::string func_name, TVMArgs args, int offset) { void VirtualMachine::SetInputWithIndex(std::string func_name, TVMArgs args) { const auto& vm_func = checkAndGetVMFunction(func_name); - const auto& param_names = vm_func.params; + size_t params_num = vm_func.params.size(); ICHECK_EQ(args.size(), 3) << "The expected number of arguments is 3 (func_name, index, tensor)"; - if (inputs_.count(func_name)) { - ICHECK_EQ(inputs_[func_name].size(), param_names.size()) - << "The size of function" << func_name - << " doesn't match the number of provided parameters"; - } else { - std::vector func_args(param_names.size()); - inputs_.emplace(func_name, func_args); - } ICHECK_EQ(args[1].type_code(), kTVMArgInt) << "The second argument doesn't match integer"; int inp_index = args[1]; - ICHECK_LT(inp_index, param_names.size()); - Device dev = GetDevice(vm_func.param_device_indexes[inp_index]); + ICHECK_LT(inp_index, params_num); + createInputsOrCheckSize(func_name, params_num); + Device dev = GetDevice(vm_func.param_device_indexes[inp_index]); SetInputTensorWithIndex(inputs_[func_name], args[2], inp_index, dev); } @@ -278,6 +271,17 @@ const VMFunction& VirtualMachine::checkAndGetVMFunction(const std::string& func_ return exec_->GetVMFunctionWithName(func_name); } +void VirtualMachine::createInputsOrCheckSize(const std::string& func_name, size_t size) { + if (inputs_.count(func_name)) { + ICHECK_EQ(inputs_[func_name].size(), size) + << "The size of function" << func_name + << " doesn't match the number of provided parameters"; + } else { + std::vector func_args(size); + inputs_.emplace(func_name, func_args); + } +} + void VirtualMachine::SetInputTensorWithIndex(std::vector& tensors, const TVMArgValue& inp_tensor, int index, Device dev) { if (inp_tensor.type_code() == kTVMDLTensorHandle) { From 602a192c4edceed4a5760348f48d689eff39058e Mon Sep 17 00:00:00 2001 From: Valery Chernov Date: Mon, 21 Feb 2022 14:34:05 +0300 Subject: [PATCH 08/15] add SetInputWithName (set_input_with_name) to VM API --- include/tvm/runtime/vm/vm.h | 24 ++++++++++++++++++++++-- src/runtime/vm/vm.cc | 35 ++++++++++++++++++++++++++++------- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/include/tvm/runtime/vm/vm.h b/include/tvm/runtime/vm/vm.h index fad01ef9986f..8072345046a8 100644 --- a/include/tvm/runtime/vm/vm.h +++ b/include/tvm/runtime/vm/vm.h @@ -282,6 +282,15 @@ class VirtualMachine : public runtime::ModuleNode { */ void SetInputWithIndex(std::string name, TVMArgs args); + /*! + * \brief Set input tensor with name to a function. + * \param name The function name + * \param args args[1:] are two arguments (name, tensor) to the + * function. If the tensor is not of the correct device for the function, + * they will be copied to the device. + */ + void SetInputWithName(std::string name, TVMArgs args); + /*! * \brief Internal hook for profiling the start of an op. * @@ -301,17 +310,27 @@ class VirtualMachine : public runtime::ModuleNode { private: /*! * \brief Get index of input tensor from its name. - * \param input_name The input tensor name * \param func_name The function's name. + * \param input_name The input tensor name. + * \return The input tensor index. + */ + int64_t getInputIndexFromVMFunction(const std::string& func_name, const std::string& input_name) const; + + /*! + * \brief Get index of input tensor from its name. + * \param params parameter names. + * \param input_name The input tensor name. * \return The input tensor index. */ - int64_t getInputIndexFromName(const std::string& input_name, const std::string& func_name) const; + int64_t getInputIndexFromName(const std::vector& params, const std::string& input_name) const; + /*! * \brief Check executable exists and get VM function from it. * \param func_name The function's name. * \return VM function. */ const VMFunction& checkAndGetVMFunction(const std::string& func_name) const; + /*! * \brief Creats inputs_ field, if it exists check its size. * \param func_name The function's name. @@ -319,6 +338,7 @@ class VirtualMachine : public runtime::ModuleNode { * \return VM function. */ void createInputsOrCheckSize(const std::string& func_name, size_t size); + /*! * \brief Set one input tensor with given index to set of input tensors if need copy to given * device. \param tensors the input tensors set (destination) \param tensor some tensor (not diff --git a/src/runtime/vm/vm.cc b/src/runtime/vm/vm.cc index a76b8e5493e4..39cf419f175c 100644 --- a/src/runtime/vm/vm.cc +++ b/src/runtime/vm/vm.cc @@ -190,7 +190,7 @@ PackedFunc VirtualMachine::GetFunction(const std::string& name, } else if (name == "get_input_index") { return TypedPackedFunc( [this](std::string input_name, std::string func_name) { - return getInputIndexFromName(input_name, func_name); + return getInputIndexFromVMFunction(func_name, input_name); }); } else if (name == "init") { return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { @@ -214,6 +214,9 @@ PackedFunc VirtualMachine::GetFunction(const std::string& name, } else if (name == "set_input_with_index") { return PackedFunc( [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { SetInputWithIndex(args[0], args); }); + } else if (name == "set_input_with_name") { + return PackedFunc( + [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { SetInputWithName(args[0], args); }); } else if (name == "load_late_bound_consts") { return PackedFunc([this](TVMArgs args, TVMRetValue* rv) { CHECK_EQ(args.size(), 1); @@ -245,7 +248,7 @@ void VirtualMachine::SetInputWithIndex(std::string func_name, TVMArgs args) { const auto& vm_func = checkAndGetVMFunction(func_name); size_t params_num = vm_func.params.size(); ICHECK_EQ(args.size(), 3) << "The expected number of arguments is 3 (func_name, index, tensor)"; - ICHECK_EQ(args[1].type_code(), kTVMArgInt) << "The second argument doesn't match integer"; + ICHECK_EQ(args[1].type_code(), kTVMArgInt) << "The second argument type doesn't match integer"; int inp_index = args[1]; ICHECK_LT(inp_index, params_num); @@ -254,12 +257,30 @@ void VirtualMachine::SetInputWithIndex(std::string func_name, TVMArgs args) { SetInputTensorWithIndex(inputs_[func_name], args[2], inp_index, dev); } -int64_t VirtualMachine::getInputIndexFromName(const std::string& input_name, - const std::string& func_name) const { +void VirtualMachine::SetInputWithName(std::string func_name, TVMArgs args) { + const auto& vm_func = checkAndGetVMFunction(func_name); + size_t params_num = vm_func.params.size(); + ICHECK_EQ(args.size(), 3) << "The expected number of arguments is 3 (func_name, name, tensor)"; + ICHECK_EQ(args[1].type_code(), kTVMStr) << "The second argument type doesn't match string"; + int inp_index = int(getInputIndexFromName(vm_func.params, args[1])); + ICHECK_LT(inp_index, params_num); + + createInputsOrCheckSize(func_name, params_num); + Device dev = GetDevice(vm_func.param_device_indexes[inp_index]); + SetInputTensorWithIndex(inputs_[func_name], args[2], inp_index, dev); +} + +int64_t VirtualMachine::getInputIndexFromVMFunction(const std::string& func_name, + const std::string& input_name) const { const auto& vm_func = checkAndGetVMFunction(func_name); - const auto& param_names = vm_func.params; - for (uint64_t i = 0; i < param_names.size(); i++) { - if (input_name == param_names[i]) { + return getInputIndexFromName(vm_func.params, input_name); +} + +int64_t VirtualMachine::getInputIndexFromName(const std::vector& params, + const std::string& input_name) const { + // TODO(vvchernov): excess integer type? + for (uint64_t i = 0; i < params.size(); i++) { + if (input_name == params[i]) { return static_cast(i); } } From 18b68a21d5b1beb11ee7b3ba5fc8f14c79c353ea Mon Sep 17 00:00:00 2001 From: Valery Chernov Date: Mon, 21 Feb 2022 14:48:42 +0300 Subject: [PATCH 09/15] join SetInputWithIndex and SetInputWithName to SetOneInputTensor (set_one_input) to VM API, the joined methods were removed --- include/tvm/runtime/vm/vm.h | 15 +++------------ src/runtime/vm/vm.cc | 33 ++++++++++++--------------------- 2 files changed, 15 insertions(+), 33 deletions(-) diff --git a/include/tvm/runtime/vm/vm.h b/include/tvm/runtime/vm/vm.h index 8072345046a8..7fa04f2429dc 100644 --- a/include/tvm/runtime/vm/vm.h +++ b/include/tvm/runtime/vm/vm.h @@ -274,22 +274,13 @@ class VirtualMachine : public runtime::ModuleNode { void SetInput(std::string name, TVMArgs args, int offset); /*! - * \brief Set input tensor with index to a function. + * \brief Set one input tensor with index or name to a function. * \param name The function name - * \param args args[1:] are two arguments (index, tensor) to the + * \param args args[1:] are two arguments (index or name, tensor) to the * function. If the tensor is not of the correct device for the function, * they will be copied to the device. */ - void SetInputWithIndex(std::string name, TVMArgs args); - - /*! - * \brief Set input tensor with name to a function. - * \param name The function name - * \param args args[1:] are two arguments (name, tensor) to the - * function. If the tensor is not of the correct device for the function, - * they will be copied to the device. - */ - void SetInputWithName(std::string name, TVMArgs args); + void SetOneInputTensor(std::string func_name, TVMArgs args); /*! * \brief Internal hook for profiling the start of an op. diff --git a/src/runtime/vm/vm.cc b/src/runtime/vm/vm.cc index 39cf419f175c..5197a3a897f5 100644 --- a/src/runtime/vm/vm.cc +++ b/src/runtime/vm/vm.cc @@ -211,12 +211,9 @@ PackedFunc VirtualMachine::GetFunction(const std::string& name, } else if (name == "set_input") { return PackedFunc( [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { SetInput(args[0], args, 1); }); - } else if (name == "set_input_with_index") { + } else if (name == "set_one_input") { return PackedFunc( - [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { SetInputWithIndex(args[0], args); }); - } else if (name == "set_input_with_name") { - return PackedFunc( - [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { SetInputWithName(args[0], args); }); + [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { SetOneInputTensor(args[0], args); }); } else if (name == "load_late_bound_consts") { return PackedFunc([this](TVMArgs args, TVMRetValue* rv) { CHECK_EQ(args.size(), 1); @@ -244,25 +241,19 @@ void VirtualMachine::SetInput(std::string func_name, TVMArgs args, int offset) { inputs_.emplace(func_name, func_args); } -void VirtualMachine::SetInputWithIndex(std::string func_name, TVMArgs args) { +void VirtualMachine::SetOneInputTensor(std::string func_name, TVMArgs args) { + ICHECK_EQ(args.size(), 3) << "The expected number of arguments is 3 (func_name, index or name, tensor)"; const auto& vm_func = checkAndGetVMFunction(func_name); size_t params_num = vm_func.params.size(); - ICHECK_EQ(args.size(), 3) << "The expected number of arguments is 3 (func_name, index, tensor)"; - ICHECK_EQ(args[1].type_code(), kTVMArgInt) << "The second argument type doesn't match integer"; - int inp_index = args[1]; - ICHECK_LT(inp_index, params_num); - createInputsOrCheckSize(func_name, params_num); - Device dev = GetDevice(vm_func.param_device_indexes[inp_index]); - SetInputTensorWithIndex(inputs_[func_name], args[2], inp_index, dev); -} - -void VirtualMachine::SetInputWithName(std::string func_name, TVMArgs args) { - const auto& vm_func = checkAndGetVMFunction(func_name); - size_t params_num = vm_func.params.size(); - ICHECK_EQ(args.size(), 3) << "The expected number of arguments is 3 (func_name, name, tensor)"; - ICHECK_EQ(args[1].type_code(), kTVMStr) << "The second argument type doesn't match string"; - int inp_index = int(getInputIndexFromName(vm_func.params, args[1])); + int inp_index; + if (args[1].type_code() == kTVMArgInt) { + inp_index = args[1]; + } else if (args[1].type_code() == kTVMStr) { + inp_index = int(getInputIndexFromName(vm_func.params, args[1])); + } else { + LOG(FATAL) << "The second argument type (" << args[1].type_code() << ") doesn't match integer or string"; + } ICHECK_LT(inp_index, params_num); createInputsOrCheckSize(func_name, params_num); From e66814704a80bf66ea8c85b95881d88a2ca7a958 Mon Sep 17 00:00:00 2001 From: Valery Chernov Date: Mon, 21 Feb 2022 14:59:39 +0300 Subject: [PATCH 10/15] fix lint --- include/tvm/runtime/vm/vm.h | 14 ++++++++------ src/runtime/vm/vm.cc | 8 +++++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/include/tvm/runtime/vm/vm.h b/include/tvm/runtime/vm/vm.h index 7fa04f2429dc..5e63302e4f41 100644 --- a/include/tvm/runtime/vm/vm.h +++ b/include/tvm/runtime/vm/vm.h @@ -94,9 +94,9 @@ struct VMFunction { instructions(std::move(instructions)), register_file_size(register_file_size), param_device_indexes(std::move(param_device_indexes)) { - ICHECK_EQ(params.size(), param_device_indexes.size()) - << "The number of provided parameters doesn't match the number of assigned devices"; - } + ICHECK_EQ(params.size(), param_device_indexes.size()) + << "The number of provided parameters doesn't match the number of assigned devices"; + } VMFunction() = default; @@ -280,7 +280,7 @@ class VirtualMachine : public runtime::ModuleNode { * function. If the tensor is not of the correct device for the function, * they will be copied to the device. */ - void SetOneInputTensor(std::string func_name, TVMArgs args); + void SetOneInputTensor(std::string name, TVMArgs args); /*! * \brief Internal hook for profiling the start of an op. @@ -305,7 +305,8 @@ class VirtualMachine : public runtime::ModuleNode { * \param input_name The input tensor name. * \return The input tensor index. */ - int64_t getInputIndexFromVMFunction(const std::string& func_name, const std::string& input_name) const; + int64_t getInputIndexFromVMFunction(const std::string& func_name, + const std::string& input_name) const; /*! * \brief Get index of input tensor from its name. @@ -313,7 +314,8 @@ class VirtualMachine : public runtime::ModuleNode { * \param input_name The input tensor name. * \return The input tensor index. */ - int64_t getInputIndexFromName(const std::vector& params, const std::string& input_name) const; + int64_t getInputIndexFromName(const std::vector& params, + const std::string& input_name) const; /*! * \brief Check executable exists and get VM function from it. diff --git a/src/runtime/vm/vm.cc b/src/runtime/vm/vm.cc index 5197a3a897f5..05277cfc157d 100644 --- a/src/runtime/vm/vm.cc +++ b/src/runtime/vm/vm.cc @@ -242,7 +242,8 @@ void VirtualMachine::SetInput(std::string func_name, TVMArgs args, int offset) { } void VirtualMachine::SetOneInputTensor(std::string func_name, TVMArgs args) { - ICHECK_EQ(args.size(), 3) << "The expected number of arguments is 3 (func_name, index or name, tensor)"; + ICHECK_EQ(args.size(), 3) << "The expected number of arguments is 3 " + << "(func_name, index or name, tensor)"; const auto& vm_func = checkAndGetVMFunction(func_name); size_t params_num = vm_func.params.size(); @@ -250,9 +251,10 @@ void VirtualMachine::SetOneInputTensor(std::string func_name, TVMArgs args) { if (args[1].type_code() == kTVMArgInt) { inp_index = args[1]; } else if (args[1].type_code() == kTVMStr) { - inp_index = int(getInputIndexFromName(vm_func.params, args[1])); + inp_index = static_cast(getInputIndexFromName(vm_func.params, args[1])); } else { - LOG(FATAL) << "The second argument type (" << args[1].type_code() << ") doesn't match integer or string"; + LOG(FATAL) << "The second argument type (" << args[1].type_code() + << ") doesn't match integer or string"; } ICHECK_LT(inp_index, params_num); From 5056a7df8f81e81ba8dc45f1a895497c2899f36b Mon Sep 17 00:00:00 2001 From: Valery Chernov Date: Thu, 24 Feb 2022 11:24:42 +0300 Subject: [PATCH 11/15] some fixes after review --- include/tvm/runtime/vm/vm.h | 19 ++++++++-------- src/runtime/vm/vm.cc | 43 +++++++++++++++++++------------------ 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/include/tvm/runtime/vm/vm.h b/include/tvm/runtime/vm/vm.h index 5e63302e4f41..139c8ba5fcc8 100644 --- a/include/tvm/runtime/vm/vm.h +++ b/include/tvm/runtime/vm/vm.h @@ -94,8 +94,7 @@ struct VMFunction { instructions(std::move(instructions)), register_file_size(register_file_size), param_device_indexes(std::move(param_device_indexes)) { - ICHECK_EQ(params.size(), param_device_indexes.size()) - << "The number of provided parameters doesn't match the number of assigned devices"; + ICHECK_EQ(params.size(), param_device_indexes.size()); } VMFunction() = default; @@ -275,12 +274,12 @@ class VirtualMachine : public runtime::ModuleNode { /*! * \brief Set one input tensor with index or name to a function. - * \param name The function name - * \param args args[1:] are two arguments (index or name, tensor) to the - * function. If the tensor is not of the correct device for the function, + * \param name The function name. + * \param tag index or name of the input tensor . + * \param tensor the input tensor. If the tensor is not of the correct device for the function, * they will be copied to the device. */ - void SetOneInputTensor(std::string name, TVMArgs args); + void SetOneInput(std::string name, const TVMArgValue& tag, const TVMArgValue& tensor); /*! * \brief Internal hook for profiling the start of an op. @@ -305,7 +304,7 @@ class VirtualMachine : public runtime::ModuleNode { * \param input_name The input tensor name. * \return The input tensor index. */ - int64_t getInputIndexFromVMFunction(const std::string& func_name, + int64_t GetInputIndexFromVMFunction(const std::string& func_name, const std::string& input_name) const; /*! @@ -314,7 +313,7 @@ class VirtualMachine : public runtime::ModuleNode { * \param input_name The input tensor name. * \return The input tensor index. */ - int64_t getInputIndexFromName(const std::vector& params, + int64_t GetInputIndexFromName(const std::vector& params, const std::string& input_name) const; /*! @@ -322,7 +321,7 @@ class VirtualMachine : public runtime::ModuleNode { * \param func_name The function's name. * \return VM function. */ - const VMFunction& checkAndGetVMFunction(const std::string& func_name) const; + const VMFunction& CheckAndGetVMFunction(const std::string& func_name) const; /*! * \brief Creats inputs_ field, if it exists check its size. @@ -330,7 +329,7 @@ class VirtualMachine : public runtime::ModuleNode { * \param size inputs_ field size. * \return VM function. */ - void createInputsOrCheckSize(const std::string& func_name, size_t size); + void CreateInputsOrCheckSize(const std::string& func_name, size_t size); /*! * \brief Set one input tensor with given index to set of input tensors if need copy to given diff --git a/src/runtime/vm/vm.cc b/src/runtime/vm/vm.cc index 05277cfc157d..cf75eadd7b7b 100644 --- a/src/runtime/vm/vm.cc +++ b/src/runtime/vm/vm.cc @@ -190,7 +190,7 @@ PackedFunc VirtualMachine::GetFunction(const std::string& name, } else if (name == "get_input_index") { return TypedPackedFunc( [this](std::string input_name, std::string func_name) { - return getInputIndexFromVMFunction(func_name, input_name); + return GetInputIndexFromVMFunction(func_name, input_name); }); } else if (name == "init") { return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { @@ -212,8 +212,11 @@ PackedFunc VirtualMachine::GetFunction(const std::string& name, return PackedFunc( [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { SetInput(args[0], args, 1); }); } else if (name == "set_one_input") { - return PackedFunc( - [sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { SetOneInputTensor(args[0], args); }); + return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { + ICHECK_EQ(args.size(), 3) << "The expected number of arguments is 3 " + << "(func_name, index or name, tensor)"; + SetOneInput(args[0], args[1], args[2]); + }); } else if (name == "load_late_bound_consts") { return PackedFunc([this](TVMArgs args, TVMRetValue* rv) { CHECK_EQ(args.size(), 1); @@ -227,7 +230,7 @@ PackedFunc VirtualMachine::GetFunction(const std::string& name, } void VirtualMachine::SetInput(std::string func_name, TVMArgs args, int offset) { - const auto& vm_func = checkAndGetVMFunction(func_name); + const auto& vm_func = CheckAndGetVMFunction(func_name); size_t params_num = vm_func.params.size(); ICHECK_EQ(args.size() - offset, params_num) << "The number of provided parameters doesn't match the number of arguments"; @@ -241,35 +244,33 @@ void VirtualMachine::SetInput(std::string func_name, TVMArgs args, int offset) { inputs_.emplace(func_name, func_args); } -void VirtualMachine::SetOneInputTensor(std::string func_name, TVMArgs args) { - ICHECK_EQ(args.size(), 3) << "The expected number of arguments is 3 " - << "(func_name, index or name, tensor)"; - const auto& vm_func = checkAndGetVMFunction(func_name); +void VirtualMachine::SetOneInput(std::string func_name, const TVMArgValue& tag, const TVMArgValue& tensor) { + const auto& vm_func = CheckAndGetVMFunction(func_name); size_t params_num = vm_func.params.size(); int inp_index; - if (args[1].type_code() == kTVMArgInt) { - inp_index = args[1]; - } else if (args[1].type_code() == kTVMStr) { - inp_index = static_cast(getInputIndexFromName(vm_func.params, args[1])); + if (tag.type_code() == kTVMArgInt) { + inp_index = tag; + } else if (tag.type_code() == kTVMStr) { + inp_index = static_cast(GetInputIndexFromName(vm_func.params, tag)); } else { - LOG(FATAL) << "The second argument type (" << args[1].type_code() + LOG(FATAL) << "The type of input tensor tag (" << tag.type_code() << ") doesn't match integer or string"; } ICHECK_LT(inp_index, params_num); - createInputsOrCheckSize(func_name, params_num); + CreateInputsOrCheckSize(func_name, params_num); Device dev = GetDevice(vm_func.param_device_indexes[inp_index]); - SetInputTensorWithIndex(inputs_[func_name], args[2], inp_index, dev); + SetInputTensorWithIndex(inputs_[func_name], tensor, inp_index, dev); } -int64_t VirtualMachine::getInputIndexFromVMFunction(const std::string& func_name, +int64_t VirtualMachine::GetInputIndexFromVMFunction(const std::string& func_name, const std::string& input_name) const { - const auto& vm_func = checkAndGetVMFunction(func_name); - return getInputIndexFromName(vm_func.params, input_name); + const auto& vm_func = CheckAndGetVMFunction(func_name); + return GetInputIndexFromName(vm_func.params, input_name); } -int64_t VirtualMachine::getInputIndexFromName(const std::vector& params, +int64_t VirtualMachine::GetInputIndexFromName(const std::vector& params, const std::string& input_name) const { // TODO(vvchernov): excess integer type? for (uint64_t i = 0; i < params.size(); i++) { @@ -280,12 +281,12 @@ int64_t VirtualMachine::getInputIndexFromName(const std::vector& pa return static_cast(-1); } -const VMFunction& VirtualMachine::checkAndGetVMFunction(const std::string& func_name) const { +const VMFunction& VirtualMachine::CheckAndGetVMFunction(const std::string& func_name) const { ICHECK(exec_) << "The executable is not created yet."; return exec_->GetVMFunctionWithName(func_name); } -void VirtualMachine::createInputsOrCheckSize(const std::string& func_name, size_t size) { +void VirtualMachine::CreateInputsOrCheckSize(const std::string& func_name, size_t size) { if (inputs_.count(func_name)) { ICHECK_EQ(inputs_[func_name].size(), size) << "The size of function" << func_name From ba6533d1edae41bfa609f8979b94b61d7a610250 Mon Sep 17 00:00:00 2001 From: Valery Chernov Date: Thu, 24 Feb 2022 11:59:30 +0300 Subject: [PATCH 12/15] add set_one_input method to python API of VirtualMachine --- python/tvm/runtime/vm.py | 19 +++++++++++++++++++ src/runtime/vm/vm.cc | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/python/tvm/runtime/vm.py b/python/tvm/runtime/vm.py index 5395326805f0..8a87c0853a5a 100644 --- a/python/tvm/runtime/vm.py +++ b/python/tvm/runtime/vm.py @@ -380,6 +380,7 @@ def __init__(self, exe, device, memory_cfg=None): self._get_num_outputs = self.module["get_num_outputs"] self._get_input_index = self.module["get_input_index"] self._set_input = self.module["set_input"] + self._set_one_input = self.module["set_one_input"] self._setup_device(device, memory_cfg) def _setup_device(self, dev, memory_cfg): @@ -450,6 +451,24 @@ def set_input(self, func_name, *args, **kwargs): cargs = convert(args) self._set_input(func_name, *cargs) + def set_one_input(self, func_name, **kwargs): + """Set the one input tensor with tag to a function. + + Parameters + ---------- + func_name : str + The name of the function. + + kwargs: dict of str or int to tvm.runtime.NDArray or np.ndarray + Named arguments to the function. + """ + assert len(kwargs) == 1 + tag = kwargs.keys()[0] + if isinstance(tag, str): + func_params = self._exec.get_function_params(func_name) + assert tag in func_params + self._set_one_input(func_name, tag, kwargs[tag]) + def invoke(self, func_name, *args, **kwargs): """Invoke a function. diff --git a/src/runtime/vm/vm.cc b/src/runtime/vm/vm.cc index cf75eadd7b7b..51114681dd57 100644 --- a/src/runtime/vm/vm.cc +++ b/src/runtime/vm/vm.cc @@ -244,7 +244,8 @@ void VirtualMachine::SetInput(std::string func_name, TVMArgs args, int offset) { inputs_.emplace(func_name, func_args); } -void VirtualMachine::SetOneInput(std::string func_name, const TVMArgValue& tag, const TVMArgValue& tensor) { +void VirtualMachine::SetOneInput(std::string func_name, const TVMArgValue& tag, + const TVMArgValue& tensor) { const auto& vm_func = CheckAndGetVMFunction(func_name); size_t params_num = vm_func.params.size(); From 4441a6ec4bd8582b7d1235acecf757507c41e6dc Mon Sep 17 00:00:00 2001 From: Valery Chernov Date: Thu, 24 Feb 2022 17:37:00 +0300 Subject: [PATCH 13/15] pytests for set_input and set_one_input methods of VirtualMachine were implemented and checked --- python/tvm/runtime/vm.py | 26 +++-- tests/python/relay/test_vm.py | 212 ++++++++++++++++++++++++++++++++++ 2 files changed, 228 insertions(+), 10 deletions(-) diff --git a/python/tvm/runtime/vm.py b/python/tvm/runtime/vm.py index 8a87c0853a5a..27fd5af51a27 100644 --- a/python/tvm/runtime/vm.py +++ b/python/tvm/runtime/vm.py @@ -451,23 +451,29 @@ def set_input(self, func_name, *args, **kwargs): cargs = convert(args) self._set_input(func_name, *cargs) - def set_one_input(self, func_name, **kwargs): + def set_one_input(self, func_name, *args, **kwargs): """Set the one input tensor with tag to a function. Parameters ---------- func_name : str The name of the function. - - kwargs: dict of str or int to tvm.runtime.NDArray or np.ndarray - Named arguments to the function. + args : [str or int, tvm.runtime.NDArray] + name or index of tensor and input tensor, optional + kwargs: dict of str or int to tvm.runtime.NDArray, optional + taged arguments to the function. + Only args or kwargs should exist """ - assert len(kwargs) == 1 - tag = kwargs.keys()[0] - if isinstance(tag, str): - func_params = self._exec.get_function_params(func_name) - assert tag in func_params - self._set_one_input(func_name, tag, kwargs[tag]) + if kwargs: + assert len(kwargs) == 1 + tag = next(iter(kwargs)) + if isinstance(tag, str): + func_params = self._exec.get_function_params(func_name) + assert tag in func_params + self._set_one_input(func_name, tag, kwargs[tag]) + else: + assert len(args) == 2 + self._set_one_input(func_name, args[0], args[1]) def invoke(self, func_name, *args, **kwargs): """Invoke a function. diff --git a/tests/python/relay/test_vm.py b/tests/python/relay/test_vm.py index 0f73e0c04def..d528586d8520 100644 --- a/tests/python/relay/test_vm.py +++ b/tests/python/relay/test_vm.py @@ -35,6 +35,8 @@ from tvm.relay.dataflow_pattern import wildcard, is_op from tvm.relay.backend.vm import VMCompiler +from onnx import helper, checker, mapping + def check_result(target, dev, args, expected_result, mod): """ @@ -922,6 +924,216 @@ def test_get_input_index(target, dev): assert vm_factory.get_input_index(data_0) == 0 assert vm_factory.get_input_index("invalid") == -1 +def get_one_input_onnx_model(weight_data, tensor_type, shape, data_name): + constant = helper.make_node( + "Constant", + inputs=[], + outputs=["constant"], + value=helper.make_tensor( + name="const_tensor", + data_type=tensor_type, + dims=shape, + vals=weight_data.flatten(), + ), + ) + add_layer = helper.make_node("Add", [data_name, "constant"], ["out"]) + + graph = helper.make_graph( + [constant, add_layer], + "one_input_test", + inputs=[ + helper.make_tensor_value_info(data_name, tensor_type, shape), + ], + outputs=[ + helper.make_tensor_value_info( + "out", tensor_type, shape + ) + ], + ) + onnx_model = helper.make_model(graph, producer_name="one_input_test") + checker.check_model(onnx_model, full_check=True) + return onnx_model + +@tvm.testing.parametrize_targets("llvm") +def test_one_set_input(target, dev): + dtype = "float32" + tensor_type = mapping.NP_TYPE_TO_TENSOR_TYPE[np.dtype(dtype)] + in_shape = [1, 2, 3, 3] + in_data_name_0 = "d0" + + weight_data = np.random.uniform(size=in_shape).astype(dtype) + onnx_model = get_one_input_onnx_model(weight_data, tensor_type, in_shape, in_data_name_0) + + # Compile to VMExecutable. + shape_dict = {in_data_name_0: in_shape} + mod, _ = relay.frontend.from_onnx(onnx_model, shape_dict, freeze_params=True) + vm_exec = vm.compile(mod, target=target) + exe = runtime.vm.VirtualMachine(vm_exec, dev) + + data0_core = np.random.uniform(size=in_shape).astype(dtype) + data0 = tvm.nd.array(data0_core) + ref_res_core = data0_core + weight_data + ref_res = tvm.nd.array(ref_res_core) + + exe.set_input("main", data0) + output = exe.invoke("main") + assert output.dtype == ref_res.dtype + tvm.testing.assert_allclose(ref_res_core, output.numpy()) + + data_dict = {in_data_name_0: data0} + exe.set_input("main", **data_dict) + output = exe.invoke("main") + assert output.dtype == ref_res.dtype + tvm.testing.assert_allclose(ref_res_core, output.numpy()) + +def get_multiple_input_onnx_model(weight_data, tensor_type, shape, data_name0, data_name1): + constant = helper.make_node( + "Constant", + inputs=[], + outputs=["constant"], + value=helper.make_tensor( + name="const_tensor", + data_type=tensor_type, + dims=shape, + vals=weight_data.flatten(), + ), + ) + add_layer_0 = helper.make_node("Add", [data_name0, "constant"], ["out0"]) + add_layer_1 = helper.make_node("Add", [data_name1, "constant"], ["out1"]) + add_layer_2 = helper.make_node("Add", ["out0", "out1"], ["out"]) + + graph = helper.make_graph( + [constant, add_layer_0, add_layer_1, add_layer_2], + "multiple_input_test", + inputs=[ + helper.make_tensor_value_info(data_name0, tensor_type, shape), + helper.make_tensor_value_info(data_name1, tensor_type, shape), + ], + outputs=[ + helper.make_tensor_value_info( + "out", tensor_type, shape + ) + ], + ) + onnx_model = helper.make_model(graph, producer_name="multiple_input_test") + checker.check_model(onnx_model, full_check=True) + return onnx_model + +@tvm.testing.parametrize_targets("llvm") +def test_multiple_set_input(target, dev): + dtype = "float32" + tensor_type = mapping.NP_TYPE_TO_TENSOR_TYPE[np.dtype(dtype)] + in_shape = [1, 2, 3, 3] + in_data_name_0 = "d0" + in_data_name_1 = "d1" + + weight_data = np.random.uniform(size=in_shape).astype(dtype) + onnx_model = get_multiple_input_onnx_model(weight_data, tensor_type, in_shape, in_data_name_0, in_data_name_1) + + # Compile to VMExecutable. + shape_dict = {in_data_name_0: in_shape, in_data_name_1: in_shape} + mod, _ = relay.frontend.from_onnx(onnx_model, shape_dict, freeze_params=True) + vm_exec = vm.compile(mod, target=target) + exe = runtime.vm.VirtualMachine(vm_exec, dev) + + data0_core = np.random.uniform(size=in_shape).astype(dtype) + data0 = tvm.nd.array(data0_core) + data1_core = np.random.uniform(size=in_shape).astype(dtype) + data1 = tvm.nd.array(data1_core) + ref_res_core = (data0_core + weight_data) + (data1_core + weight_data) + ref_res = tvm.nd.array(ref_res_core) + + exe.set_input("main", data0, data1) + output = exe.invoke("main") + assert output.dtype == ref_res.dtype + tvm.testing.assert_allclose(ref_res_core, output.numpy()) + + data_dict = {in_data_name_1: data1, in_data_name_0: data0} + exe.set_input("main", **data_dict) + output = exe.invoke("main") + assert output.dtype == ref_res.dtype + tvm.testing.assert_allclose(ref_res_core, output.numpy()) + +@tvm.testing.parametrize_targets("llvm") +def test_one_set_one_input(target, dev): + dtype = "float32" + tensor_type = mapping.NP_TYPE_TO_TENSOR_TYPE[np.dtype(dtype)] + in_shape = [1, 2, 3, 3] + in_data_name_0 = "d0" + + weight_data = np.random.uniform(size=in_shape).astype(dtype) + onnx_model = get_one_input_onnx_model(weight_data, tensor_type, in_shape, in_data_name_0) + + # Compile to VMExecutable. + shape_dict = {in_data_name_0: in_shape} + mod, _ = relay.frontend.from_onnx(onnx_model, shape_dict, freeze_params=True) + vm_exec = vm.compile(mod, target=target) + exe = runtime.vm.VirtualMachine(vm_exec, dev) + + data0_core = np.random.uniform(size=in_shape).astype(dtype) + data0 = tvm.nd.array(data0_core) + ref_res_core = data0_core + weight_data + ref_res = tvm.nd.array(ref_res_core) + + exe.set_one_input("main", 0, data0) + output = exe.invoke("main") + assert output.dtype == ref_res.dtype + tvm.testing.assert_allclose(ref_res_core, output.numpy()) + + exe.set_one_input("main", in_data_name_0, data0) + output = exe.invoke("main") + assert output.dtype == ref_res.dtype + tvm.testing.assert_allclose(ref_res_core, output.numpy()) + + data_dict = {in_data_name_0: data0} + exe.set_one_input("main", **data_dict) + output = exe.invoke("main") + assert output.dtype == ref_res.dtype + tvm.testing.assert_allclose(ref_res_core, output.numpy()) + +@tvm.testing.parametrize_targets("llvm") +def test_multiple_set_one_input(target, dev): + dtype = "float32" + tensor_type = mapping.NP_TYPE_TO_TENSOR_TYPE[np.dtype(dtype)] + in_shape = [1, 2, 3, 3] + in_data_name_0 = "d0" + in_data_name_1 = "d1" + + weight_data = np.random.uniform(size=in_shape).astype(dtype) + onnx_model = get_multiple_input_onnx_model(weight_data, tensor_type, in_shape, in_data_name_0, in_data_name_1) + + # Compile to VMExecutable. + shape_dict = {in_data_name_0: in_shape, in_data_name_1: in_shape} + mod, _ = relay.frontend.from_onnx(onnx_model, shape_dict, freeze_params=True) + vm_exec = vm.compile(mod, target=target) + exe = runtime.vm.VirtualMachine(vm_exec, dev) + + data0_core = np.random.uniform(size=in_shape).astype(dtype) + data0 = tvm.nd.array(data0_core) + data1_core = np.random.uniform(size=in_shape).astype(dtype) + data1 = tvm.nd.array(data1_core) + ref_res_core = (data0_core + weight_data) + (data1_core + weight_data) + ref_res = tvm.nd.array(ref_res_core) + + exe.set_one_input("main", 1, data1) + exe.set_one_input("main", 0, data0) + output = exe.invoke("main") + assert output.dtype == ref_res.dtype + tvm.testing.assert_allclose(ref_res_core, output.numpy()) + + exe.set_one_input("main", in_data_name_1, data1) + exe.set_one_input("main", in_data_name_0, data0) + output = exe.invoke("main") + assert output.dtype == ref_res.dtype + tvm.testing.assert_allclose(ref_res_core, output.numpy()) + + data_dict = {in_data_name_1: data1} + exe.set_one_input("main", **data_dict) + data_dict = {in_data_name_0: data0} + exe.set_one_input("main", **data_dict) + output = exe.invoke("main") + assert output.dtype == ref_res.dtype + tvm.testing.assert_allclose(ref_res_core, output.numpy()) @tvm.testing.parametrize_targets("llvm") def test_benchmark(target, dev): From f2362bfa4a4aa70be2d9c5d6bdaa51dff2822ea9 Mon Sep 17 00:00:00 2001 From: Valery Chernov Date: Thu, 24 Feb 2022 20:34:53 +0300 Subject: [PATCH 14/15] CI restart From e2b4dea42abade48ad37f39dbc83b08ea4126dd2 Mon Sep 17 00:00:00 2001 From: Valery Chernov Date: Fri, 25 Feb 2022 09:34:21 +0300 Subject: [PATCH 15/15] construct simple model for pytests by relay instead of onnx tools (need for correct CI) --- tests/python/relay/test_vm.py | 104 ++++++---------------------------- 1 file changed, 17 insertions(+), 87 deletions(-) diff --git a/tests/python/relay/test_vm.py b/tests/python/relay/test_vm.py index d528586d8520..ebfec0fa23a0 100644 --- a/tests/python/relay/test_vm.py +++ b/tests/python/relay/test_vm.py @@ -35,8 +35,6 @@ from tvm.relay.dataflow_pattern import wildcard, is_op from tvm.relay.backend.vm import VMCompiler -from onnx import helper, checker, mapping - def check_result(target, dev, args, expected_result, mod): """ @@ -924,55 +922,27 @@ def test_get_input_index(target, dev): assert vm_factory.get_input_index(data_0) == 0 assert vm_factory.get_input_index("invalid") == -1 -def get_one_input_onnx_model(weight_data, tensor_type, shape, data_name): - constant = helper.make_node( - "Constant", - inputs=[], - outputs=["constant"], - value=helper.make_tensor( - name="const_tensor", - data_type=tensor_type, - dims=shape, - vals=weight_data.flatten(), - ), - ) - add_layer = helper.make_node("Add", [data_name, "constant"], ["out"]) - - graph = helper.make_graph( - [constant, add_layer], - "one_input_test", - inputs=[ - helper.make_tensor_value_info(data_name, tensor_type, shape), - ], - outputs=[ - helper.make_tensor_value_info( - "out", tensor_type, shape - ) - ], - ) - onnx_model = helper.make_model(graph, producer_name="one_input_test") - checker.check_model(onnx_model, full_check=True) - return onnx_model +def get_one_input_relay_mod(tensor_type, shape, data_name): + x = relay.var(data_name, shape = shape, dtype = tensor_type) + y = relay.exp(x) + f = relay.Function([x], y) + return IRModule.from_expr(f) @tvm.testing.parametrize_targets("llvm") def test_one_set_input(target, dev): dtype = "float32" - tensor_type = mapping.NP_TYPE_TO_TENSOR_TYPE[np.dtype(dtype)] in_shape = [1, 2, 3, 3] in_data_name_0 = "d0" - weight_data = np.random.uniform(size=in_shape).astype(dtype) - onnx_model = get_one_input_onnx_model(weight_data, tensor_type, in_shape, in_data_name_0) + mod = get_one_input_relay_mod(dtype, in_shape, in_data_name_0) # Compile to VMExecutable. - shape_dict = {in_data_name_0: in_shape} - mod, _ = relay.frontend.from_onnx(onnx_model, shape_dict, freeze_params=True) vm_exec = vm.compile(mod, target=target) exe = runtime.vm.VirtualMachine(vm_exec, dev) data0_core = np.random.uniform(size=in_shape).astype(dtype) data0 = tvm.nd.array(data0_core) - ref_res_core = data0_core + weight_data + ref_res_core = np.exp(data0_core) ref_res = tvm.nd.array(ref_res_core) exe.set_input("main", data0) @@ -986,53 +956,21 @@ def test_one_set_input(target, dev): assert output.dtype == ref_res.dtype tvm.testing.assert_allclose(ref_res_core, output.numpy()) -def get_multiple_input_onnx_model(weight_data, tensor_type, shape, data_name0, data_name1): - constant = helper.make_node( - "Constant", - inputs=[], - outputs=["constant"], - value=helper.make_tensor( - name="const_tensor", - data_type=tensor_type, - dims=shape, - vals=weight_data.flatten(), - ), - ) - add_layer_0 = helper.make_node("Add", [data_name0, "constant"], ["out0"]) - add_layer_1 = helper.make_node("Add", [data_name1, "constant"], ["out1"]) - add_layer_2 = helper.make_node("Add", ["out0", "out1"], ["out"]) - - graph = helper.make_graph( - [constant, add_layer_0, add_layer_1, add_layer_2], - "multiple_input_test", - inputs=[ - helper.make_tensor_value_info(data_name0, tensor_type, shape), - helper.make_tensor_value_info(data_name1, tensor_type, shape), - ], - outputs=[ - helper.make_tensor_value_info( - "out", tensor_type, shape - ) - ], - ) - onnx_model = helper.make_model(graph, producer_name="multiple_input_test") - checker.check_model(onnx_model, full_check=True) - return onnx_model +def get_multiple_input_relay_mod(tensor_type, shape, data_name0, data_name1): + x, y = [relay.var(c, shape=shape, dtype = tensor_type) for c in [data_name0, data_name1]] + f = relay.Function([x, y], x + y) + return IRModule.from_expr(f) @tvm.testing.parametrize_targets("llvm") def test_multiple_set_input(target, dev): dtype = "float32" - tensor_type = mapping.NP_TYPE_TO_TENSOR_TYPE[np.dtype(dtype)] in_shape = [1, 2, 3, 3] in_data_name_0 = "d0" in_data_name_1 = "d1" - weight_data = np.random.uniform(size=in_shape).astype(dtype) - onnx_model = get_multiple_input_onnx_model(weight_data, tensor_type, in_shape, in_data_name_0, in_data_name_1) + mod = get_multiple_input_relay_mod(dtype, in_shape, in_data_name_0, in_data_name_1) # Compile to VMExecutable. - shape_dict = {in_data_name_0: in_shape, in_data_name_1: in_shape} - mod, _ = relay.frontend.from_onnx(onnx_model, shape_dict, freeze_params=True) vm_exec = vm.compile(mod, target=target) exe = runtime.vm.VirtualMachine(vm_exec, dev) @@ -1040,7 +978,7 @@ def test_multiple_set_input(target, dev): data0 = tvm.nd.array(data0_core) data1_core = np.random.uniform(size=in_shape).astype(dtype) data1 = tvm.nd.array(data1_core) - ref_res_core = (data0_core + weight_data) + (data1_core + weight_data) + ref_res_core = data0_core + data1_core ref_res = tvm.nd.array(ref_res_core) exe.set_input("main", data0, data1) @@ -1057,22 +995,18 @@ def test_multiple_set_input(target, dev): @tvm.testing.parametrize_targets("llvm") def test_one_set_one_input(target, dev): dtype = "float32" - tensor_type = mapping.NP_TYPE_TO_TENSOR_TYPE[np.dtype(dtype)] in_shape = [1, 2, 3, 3] in_data_name_0 = "d0" - weight_data = np.random.uniform(size=in_shape).astype(dtype) - onnx_model = get_one_input_onnx_model(weight_data, tensor_type, in_shape, in_data_name_0) + mod = get_one_input_relay_mod(dtype, in_shape, in_data_name_0) # Compile to VMExecutable. - shape_dict = {in_data_name_0: in_shape} - mod, _ = relay.frontend.from_onnx(onnx_model, shape_dict, freeze_params=True) vm_exec = vm.compile(mod, target=target) exe = runtime.vm.VirtualMachine(vm_exec, dev) data0_core = np.random.uniform(size=in_shape).astype(dtype) data0 = tvm.nd.array(data0_core) - ref_res_core = data0_core + weight_data + ref_res_core = np.exp(data0_core) ref_res = tvm.nd.array(ref_res_core) exe.set_one_input("main", 0, data0) @@ -1094,17 +1028,13 @@ def test_one_set_one_input(target, dev): @tvm.testing.parametrize_targets("llvm") def test_multiple_set_one_input(target, dev): dtype = "float32" - tensor_type = mapping.NP_TYPE_TO_TENSOR_TYPE[np.dtype(dtype)] in_shape = [1, 2, 3, 3] in_data_name_0 = "d0" in_data_name_1 = "d1" - weight_data = np.random.uniform(size=in_shape).astype(dtype) - onnx_model = get_multiple_input_onnx_model(weight_data, tensor_type, in_shape, in_data_name_0, in_data_name_1) + mod = get_multiple_input_relay_mod(dtype, in_shape, in_data_name_0, in_data_name_1) # Compile to VMExecutable. - shape_dict = {in_data_name_0: in_shape, in_data_name_1: in_shape} - mod, _ = relay.frontend.from_onnx(onnx_model, shape_dict, freeze_params=True) vm_exec = vm.compile(mod, target=target) exe = runtime.vm.VirtualMachine(vm_exec, dev) @@ -1112,7 +1042,7 @@ def test_multiple_set_one_input(target, dev): data0 = tvm.nd.array(data0_core) data1_core = np.random.uniform(size=in_shape).astype(dtype) data1 = tvm.nd.array(data1_core) - ref_res_core = (data0_core + weight_data) + (data1_core + weight_data) + ref_res_core = data0_core + data1_core ref_res = tvm.nd.array(ref_res_core) exe.set_one_input("main", 1, data1)