Skip to content

Commit

Permalink
[TVMC] Support compiling and running with VM (#10722)
Browse files Browse the repository at this point in the history
* introduce vm compile path

* support vm in tvmc

* cleanup + lint

* add profiler + simplify vm case in tvmcpackage

* address comments + parametrize tests

Co-authored-by: Margaret Qian <[email protected]>
  • Loading branch information
margaretqian and Margaret Qian authored Mar 31, 2022
1 parent 8226bd0 commit 8775a80
Show file tree
Hide file tree
Showing 6 changed files with 274 additions and 103 deletions.
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
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

0 comments on commit 8775a80

Please sign in to comment.