Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TVMC] Support compiling and running with VM #10722

Merged
merged 6 commits into from
Mar 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 64 additions & 13 deletions python/tvm/driver/tvmc/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ def compile_model(
disabled_pass: Optional[str] = None,
pass_context_configs: Optional[List[str]] = None,
additional_target_options: Optional[Dict[str, Dict[str, Any]]] = None,
use_vm: bool = False,
):
"""Compile a model from a supported framework into a TVM module.

Expand Down Expand Up @@ -248,7 +249,8 @@ def compile_model(
PassContext.
additional_target_options: Optional[Dict[str, Dict[str, Any]]]
Additional target options in a dictionary to combine with initial Target arguments

use_vm: bool
margaretqian marked this conversation as resolved.
Show resolved Hide resolved
Whether to use the VM to compile the model as opposed to the graph executor

Returns
-------
Expand Down Expand Up @@ -291,25 +293,40 @@ def compile_model(
opt_level=opt_level, config=config, disabled_pass=disabled_pass
):
logger.debug("building relay graph with autoscheduler")
graph_module = relay.build(
mod, target=tvm_target, executor=executor, runtime=runtime, params=params
graph_module = build(
mod,
tvm_target=tvm_target,
executor=executor,
runtime=runtime,
params=params,
use_vm=use_vm,
)
else:
with autotvm.apply_history_best(tuning_records):
with tvm.transform.PassContext(
opt_level=opt_level, config=config, disabled_pass=disabled_pass
):
logger.debug("building relay graph with tuning records")
graph_module = relay.build(
mod, target=tvm_target, executor=executor, runtime=runtime, params=params
graph_module = build(
mod,
tvm_target=tvm_target,
executor=executor,
runtime=runtime,
params=params,
use_vm=use_vm,
)
else:
with tvm.transform.PassContext(
opt_level=opt_level, config=config, disabled_pass=disabled_pass
):
logger.debug("building relay graph (no tuning records provided)")
graph_module = relay.build(
mod, target=tvm_target, executor=executor, runtime=runtime, params=params
graph_module = build(
mod,
tvm_target=tvm_target,
executor=executor,
runtime=runtime,
params=params,
use_vm=use_vm,
)

# Generate output dump files with sources
Expand All @@ -319,19 +336,18 @@ def compile_model(
dump_code = [dump_code]
dumps = {}
for source_type in dump_code:
lib = graph_module.get_lib()
if use_vm:
lib = graph_module.lib
else:
lib = graph_module.get_lib()
# TODO lib.get_source call have inconsistent behavior for unsupported
# formats (@leandron).
source = str(mod) if source_type == "relay" else lib.get_source(source_type)
dumps[source_type] = source

# Create a new tvmc model package object from the graph definition.
package_path = tvmc_model.export_package(
graph_module,
package_path,
cross,
cross_options,
output_format,
graph_module, package_path, cross, cross_options, output_format
)

# Write dumps to file.
Expand All @@ -341,6 +357,41 @@ def compile_model(
return TVMCPackage(package_path)


def build(
mod: tvm.IRModule,
tvm_target: str,
executor: Executor,
runtime: Runtime,
params: Dict[str, tvm.nd.NDArray],
use_vm: bool,
):
"""
Builds the model with the provided executor.

Parameters
----------
mod : tvm.IRModule
The relay module corresponding to this model.
tvm_target : str
The target for which to compile. Can be a plain string or
a path.
executor : Executor
The graph executor to build the model if use_vm is not True
runtime : Runtime
The runtime configuration.
params : dict
A parameter dictionary for the model.
use_vm: bool
Whether to use the VM to compile the model as opposed to the graph executor

"""
if use_vm:
logger.debug("building with vm compile")
return relay.vm.compile(mod, target=tvm_target, params=params)
logger.debug("building with relay build")
return relay.build(mod, target=tvm_target, executor=executor, runtime=runtime, params=params)


def save_dumps(module_name: str, dumps: Dict[str, str], dump_root: str = "."):
"""
Serialize dump files to the disk.
Expand Down
94 changes: 79 additions & 15 deletions python/tvm/driver/tvmc/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
from tvm.driver.tvmc import TVMCException
from tvm.relay.backend.executor_factory import GraphExecutorFactoryModule
from tvm.runtime.module import BenchmarkResult
from tvm.runtime.vm import Executable


try:
from tvm.micro import export_model_library_format
Expand Down Expand Up @@ -182,6 +184,42 @@ def default_package_path(self):
"""
return self._tmp_dir.relpath("model_package.tar")

def export_vm_format(
self,
vm_exec: Executable,
package_path: Optional[str] = None,
lib_format: str = "so",
):
"""Save this TVMCModel compiled via vm to file.
Parameters
----------
vm_exec : vm.Executable
The VM Executable containing compiled the compiled artifacts needed to run this model.
package_path : str, None
Where the model should be saved. Note that it will be packaged as a .tar file.
If not provided, the package will be saved to a generically named file in tmp.
lib_format : str
How to export the modules function library. Must be one of "so" or "tar".

Returns
-------
package_path : str
The path that the package was saved to.
"""
lib_name = "lib." + lib_format
temp = self._tmp_dir
if package_path is None:
package_path = self.default_package_path()

path_lib = temp.relpath(lib_name)
vm_exec.mod.export_library(path_lib)
self.lib_path = path_lib
# Package up all the temp files into a tar file.
with tarfile.open(package_path, "w") as tar:
tar.add(path_lib, lib_name)

return package_path

def export_classic_format(
self,
executor_factory: GraphExecutorFactoryModule,
Expand Down Expand Up @@ -248,7 +286,7 @@ def export_classic_format(

def export_package(
self,
executor_factory: GraphExecutorFactoryModule,
executor_factory: Union[GraphExecutorFactoryModule, Executable],
package_path: Optional[str] = None,
cross: Optional[Union[str, Callable]] = None,
cross_options: Optional[str] = None,
Expand Down Expand Up @@ -281,7 +319,9 @@ def export_package(
if output_format == "mlf" and cross:
raise TVMCException("Specifying the MLF output and a cross compiler is not supported.")

if output_format in ["so", "tar"]:
if isinstance(executor_factory, Executable):
package_path = self.export_vm_format(executor_factory, package_path, output_format)
elif output_format in ["so", "tar"]:
package_path = self.export_classic_format(
executor_factory, package_path, cross, cross_options, output_format
)
Expand Down Expand Up @@ -314,9 +354,16 @@ class TVMCPackage(object):

project_dir : Path, str
If given and loading a MLF file, the path to the project directory that contains the file.

use_vm : bool
Whether the graph module was compiled with vm or not.
"""

def __init__(self, package_path: str, project_dir: Optional[Union[Path, str]] = None):
def __init__(
self,
package_path: str,
project_dir: Optional[Union[Path, str]] = None,
):
self._tmp_dir = utils.tempdir()
self.package_path = package_path
self.import_package(self.package_path)
Expand Down Expand Up @@ -351,23 +398,40 @@ def import_package(self, package_path: str):
self.type = "mlf"
else:
# Classic format
lib_name_so = "mod.so"
lib_name_tar = "mod.tar"
if os.path.exists(temp.relpath(lib_name_so)):
self.lib_name = lib_name_so
elif os.path.exists(temp.relpath(lib_name_tar)):
self.lib_name = lib_name_tar
classic_lib_name_so = "mod.so"
classic_lib_name_tar = "mod.tar"

# VM format
vm_lib_name_so = "lib.so"
vm_lib_name_tar = "lib.tar"

if os.path.exists(temp.relpath(classic_lib_name_so)):
self.lib_name = classic_lib_name_so
self.type = "classic"
elif os.path.exists(temp.relpath(classic_lib_name_tar)):
self.lib_name = classic_lib_name_tar
self.type = "classic"
elif os.path.exists(temp.relpath(vm_lib_name_so)):
self.lib_name = vm_lib_name_so
self.type = "vm"
elif os.path.exists(temp.relpath(vm_lib_name_tar)):
self.lib_name = vm_lib_name_tar
self.type = "vm"
else:
raise TVMCException("Couldn't find exported library in the package.")
self.lib_path = temp.relpath(self.lib_name)

graph = temp.relpath("mod.json")
params = temp.relpath("mod.params")
self.lib_path = temp.relpath(self.lib_name)

self.type = "classic"
graph, params = None, None
if self.type == "classic":
graph = temp.relpath("mod.json")
params = temp.relpath("mod.params")

with open(params, "rb") as param_file:
self.params = bytearray(param_file.read())
if params is not None:
with open(params, "rb") as param_file:
self.params = bytearray(param_file.read())
else:
self.params = None

if graph is not None:
with open(graph) as graph_file:
Expand Down
Loading