blob: 56b2fa3fbad91ecd8fd5ba8e43c304b4aad0f51e [file]
/*
* 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 graph_executor_factory.cc
* \brief Graph executor factory implementations
*/
#include "./graph_executor_factory.h"
#include <tvm/runtime/container/map.h>
#include <tvm/runtime/container/string.h>
#include <tvm/runtime/device_api.h>
#include <tvm/runtime/registry.h>
#include <iterator>
#include <vector>
namespace tvm {
namespace runtime {
GraphExecutorFactory::GraphExecutorFactory(
const std::string& graph_json,
const std::unordered_map<std::string, tvm::runtime::NDArray>& params,
const std::string& module_name) {
graph_json_ = graph_json;
params_ = params;
module_name_ = module_name;
}
PackedFunc GraphExecutorFactory::GetFunction(
const std::string& name, const tvm::runtime::ObjectPtr<tvm::runtime::Object>& sptr_to_self) {
if (name == module_name_) {
return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
std::vector<Device> devices;
for (int i = 0; i < args.num_args; ++i) {
devices.emplace_back(args[i].operator Device());
}
*rv = this->ExecutorCreate(devices);
});
} else if (name == "get_graph_json") {
return PackedFunc(
[sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { *rv = this->graph_json_; });
} else if (name == "get_graph_params") {
return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
Map<String, tvm::runtime::NDArray> params;
for (const auto& kv : params_) {
params.Set(kv.first, kv.second);
}
*rv = params;
});
} else if (name == "debug_create") {
return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
ICHECK_GE(args.size(), 2);
std::string module_name = args[0].operator String();
ICHECK(module_name == module_name_) << "Currently we only support single model for now.";
std::vector<Device> devices;
for (int i = 1; i < args.num_args; ++i) {
devices.emplace_back(args[i].operator Device());
}
*rv = this->DebugExecutorCreate(devices);
});
} else if (name == "remove_params") {
return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
std::unordered_map<std::string, tvm::runtime::NDArray> empty_params{};
auto exec =
make_object<GraphExecutorFactory>(this->graph_json_, empty_params, this->module_name_);
exec->Import(this->imports_[0]);
*rv = Module(exec);
});
} else if (name == "cuda_graph_create") {
return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
std::vector<Device> devices;
for (int i = 0; i < args.num_args; ++i) {
devices.emplace_back(args[i].operator Device());
}
*rv = this->CudaGraphExecutorCreate(devices);
});
} else {
return PackedFunc();
}
}
void GraphExecutorFactory::SaveToBinary(dmlc::Stream* stream) {
stream->Write(graph_json_);
std::vector<std::string> names;
std::vector<DLTensor*> arrays;
for (const auto& v : params_) {
names.emplace_back(v.first);
arrays.emplace_back(const_cast<DLTensor*>(v.second.operator->()));
}
uint64_t sz = arrays.size();
ICHECK(sz == names.size());
stream->Write(sz);
stream->Write(names);
for (size_t i = 0; i < sz; ++i) {
tvm::runtime::SaveDLTensor(stream, arrays[i]);
}
stream->Write(module_name_);
}
Module GraphExecutorFactory::ExecutorCreate(const std::vector<Device>& devs) {
auto exec = make_object<GraphExecutor>();
exec->Init(this->graph_json_, this->imports_[0], devs, PackedFunc());
// set params
SetParams(exec.get(), this->params_);
return Module(exec);
}
Module GraphExecutorFactory::DebugExecutorCreate(const std::vector<Device>& devs) {
const PackedFunc* pf = tvm::runtime::Registry::Get("tvm.graph_executor_debug.create");
ICHECK(pf != nullptr) << "Cannot find function tvm.graph_executor_debug.create in registry. "
"Do you enable debug graph executor build?";
// Debug executor create packed function will call GetAllContexs, so we unpack the devs.
std::vector<int> unpacked_devs;
for (const auto& dev : devs) {
unpacked_devs.emplace_back(dev.device_type);
unpacked_devs.emplace_back(dev.device_id);
}
size_t args_size = unpacked_devs.size() + 2;
std::vector<TVMValue> values(args_size);
std::vector<int> codes(args_size);
runtime::TVMArgsSetter setter(values.data(), codes.data());
setter(0, this->graph_json_);
setter(1, this->imports_[0]);
for (size_t i = 0; i < unpacked_devs.size(); ++i) {
setter(i + 2, unpacked_devs[i]);
}
TVMRetValue rv;
pf->CallPacked(TVMArgs(values.data(), codes.data(), args_size), &rv);
Module mod = rv.operator Module();
// debug graph executor is one child class of graph executor.
SetParams(const_cast<GraphExecutor*>(mod.as<GraphExecutor>()), this->params_);
return mod;
}
Module GraphExecutorFactory::CudaGraphExecutorCreate(const std::vector<Device>& devs) {
const PackedFunc* pf = tvm::runtime::Registry::Get("tvm.graph_executor_cuda_graph.create");
ICHECK(pf != nullptr) << "Cannot find function tvm.graph_executor_cuda_graph.create in registry. "
"Did you set(USE_GRAPH_EXECUTOR_CUGRAPH=ON)?";
std::vector<int> unpacked_devs;
for (const auto& dev : devs) {
unpacked_devs.emplace_back(dev.device_type);
unpacked_devs.emplace_back(dev.device_id);
}
size_t args_size = unpacked_devs.size() + 2;
std::vector<TVMValue> values(args_size);
std::vector<int> codes(args_size);
runtime::TVMArgsSetter setter(values.data(), codes.data());
setter(0, this->graph_json_);
setter(1, this->imports_[0]);
for (size_t i = 0; i < unpacked_devs.size(); ++i) {
setter(i + 2, unpacked_devs[i]);
}
TVMRetValue rv;
pf->CallPacked(TVMArgs(values.data(), codes.data(), args_size), &rv);
Module mod = rv.operator Module();
SetParams(const_cast<GraphExecutor*>(mod.as<GraphExecutor>()), this->params_);
return mod;
}
Module GraphExecutorFactoryModuleLoadBinary(void* strm) {
dmlc::Stream* stream = static_cast<dmlc::Stream*>(strm);
std::string graph_json;
std::unordered_map<std::string, tvm::runtime::NDArray> params;
std::string module_name;
ICHECK(stream->Read(&graph_json));
uint64_t sz;
ICHECK(stream->Read(&sz));
std::vector<std::string> names;
ICHECK(stream->Read(&names));
ICHECK(sz == names.size());
for (size_t i = 0; i < sz; ++i) {
tvm::runtime::NDArray temp;
temp.Load(stream);
params[names[i]] = temp;
}
ICHECK(stream->Read(&module_name));
auto exec = make_object<GraphExecutorFactory>(graph_json, params, module_name);
return Module(exec);
}
TVM_REGISTER_GLOBAL("tvm.graph_executor_factory.create")
.set_body([](TVMArgs args, TVMRetValue* rv) {
ICHECK_GE(args.num_args, 3) << "The expected number of arguments for "
"graph_executor_factory.create needs at least 3, "
"but it has "
<< args.num_args;
// The argument order is graph_json, module, module_name, param0_name, param0_tensor,
// [param1_name, param1_tensor], ...
ICHECK_EQ((args.size() - 3) % 2, 0);
std::unordered_map<std::string, tvm::runtime::NDArray> params;
for (size_t i = 3; i < static_cast<size_t>(args.size()); i += 2) {
std::string name = args[i].operator String();
params[name] = args[i + 1].operator tvm::runtime::NDArray();
}
auto exec = make_object<GraphExecutorFactory>(args[0], params, args[2]);
exec->Import(args[1]);
*rv = Module(exec);
});
TVM_REGISTER_GLOBAL("runtime.module.loadbinary_GraphExecutorFactory")
.set_body_typed(GraphExecutorFactoryModuleLoadBinary);
Module GraphRuntimeFactoryModuleLoadBinary(void* strm) {
LOG(WARNING) << "You are loading a module which was built with GraphRuntimeFactory. "
<< "GraphRuntime has been renamed to GraphExecutor, and support for loading "
<< "GraphRuntimeFactory modules will be removed after the next TVM release. "
<< "Please rebuild the module before then to avoid breakage.";
return GraphExecutorFactoryModuleLoadBinary(strm);
}
TVM_REGISTER_GLOBAL("runtime.module.loadbinary_GraphRuntimeFactory")
.set_body_typed(GraphRuntimeFactoryModuleLoadBinary);
} // namespace runtime
} // namespace tvm