A bit of a rough draft which I started writing before last week’s µTVM discussion on including TVM C runtime inside Model Library Format tarballs and finished up this weekend. Comments welcomed!
cc @jroesch @tqchen @mousius @manupa-arm @ashutosh-arm @ramana-arm @comaniac @kparzysz @csullivan @mbs-octoml @tkonolige
Summary
This RFC proposes we add an external_dependencies
key to Model Library Format metadata.json
. Adding this key affords both users and downstream Project API servers a convenient way to know that the model must be linked against some third-party library in compilation. As we begin introducing support for third-party libraries such as CMSIS-NN in microTVM, having such a facility will become more useful.
Motivation
microTVM’s build flow departs significantly from the typical TVM build flow because it delegates all aspects of model compilation to the user. The microTVM build flow is roughly:
- TVM exports the model of interest in Model Library Format, a TVM export format intended to include all of the relevant pieces needed to integrate a model with an e.g. firmware project.
- The user integrates the pieces from the Model Library Format archive with their firmware project (either manually, or via Project API or some other automation).
This has currently been shown to work with models that target the CPU only as long as TVM selects a schedule that doesn’t depend on external libraries such as CMSIS-NN. In [RFC]Use CMSIS-NN with TVM, ARM is proposing to add CMSIS-NN schedules for many common operators. Some additional mechanism is needed in Model Library Format so TVM can indicate to downstream Project API and users that external libraries such as CMSIS-NN are required to compile the generated model code.
Guide-level explanation
A rough sketch of the design is as follows:
-
TVM can generate code dependent on third-party libraries in at least two cases:
- When a TVM or BYOC schedule introduces a
tir.call_extern
node which refers to a function implemented in an external library. - When the code-loading process for a BYOC backend requires the use of an external library. For example, the CUDA codegen merely generates CUDA source. In the C++ runtime,
CUDAModule
is responsible for passing the source onward to the CUDA library for compilation. Though a parallel process has not yet been specified for the C runtime, we should consider this case when designing this RFC.
- When a TVM or BYOC schedule introduces a
-
A path needs to be defined by which the BYOC or schedule can communicate the external dependency to the compiler. At present, the BYOC flow is a
PackedFunc
which accepts a Relay subgraph and returns aruntime::Module
. However, given ongoing work in Additional Target Hooks RFC, it will soon be possible for BYOC to return TIR as well. Therefore, here again we have two cases to consider:-
When the schedule for an operator (e.g. from TVM-internal scheduling or from the new
relay_to_tir
pass) includes TIR that depends on an external library.- In this case, the schedule or BYOC should generate TIR containing an attribute
kExternalDependency
containingArray<ExternalDependency>
.
- In this case, the schedule or BYOC should generate TIR containing an attribute
-
When the
runtime::Module
created in TIR codegen (e.g. due to use of a non-DSO-exportable backend with third-party library dependencies; or due to BYOC) requires a third-party library.- In this case, the
runtime::Module
should include a functionget_external_dependencies
which returnsArray<ExternalDependency>
. This function is included in the spirit of other such metadata-passing functions such asget_const_vars
, which should likely be split into a separate metadata dict. Splitting metadata functions apart from operator implementations inruntime::Module
is out of scope of this RFC.
- In this case, the
-
-
The compiler flow then assembles a list of
ExternalDependency
structs:class ExternalDependencyNode : public ObjectNode { /*! \brief Short unique name for this external dependency. * All ExternalDependencyNode generated for a given Relay module with the same short_name * must match in all other attributes. The compiler validates this at the end of the flow. */ std::string short_name; /*! \brief URL to the dependency. */ std::string url; /*! \brief Type of URL. One of "path" "url" "git". */ std::string url_type; /*! \brief Version specifier. Required for "git" url_type. * When url_type is "git," the tag or branch to checkout. */ std::string version_spec; };
As mentioned in the commentary, if two
ExternalDependencyNode
with the sameshort_name
are generated within the sametvm.relay.build
call, the other attributes must match. The compiler verifies this and removes exact duplicates. -
The compiler returns
external_dependencies
as part of its output. Model Library Format surfaces this as a new keyexternal_dependencies
inmetadata.json
:{ // ... "external_dependencies": [ { "short_name": "cmsis-nn", "url": "http://github.com/...", "url_type": "git", "version_spec": "5.8.0" } ] }
Reference-level explanation
This section is brief as this pre-RFC seeks feedback on the Guide-level part. The main pieces needed:
- A pass which finds all
kExternalDependency
attributes and assembles a list of them. - A collection function which invokes the pass and any
get_external_dependencies
functions, validates and uniquifies them, and creates the compiler output. - Model Library Format changes.
Drawbacks
This increases the maintenance burden on BYOC and internal non-DSO-exportable flows by requiring us to provide explicit versioning information about the libraries linked against.
Rationale and alternatives
- We could require that the dependency always be bundled in with the compiler output.
- This seems worse as it increases compiler output size, and in many cases, the runtime already has taken care of this.
- We could specify less information e.g. only the name of the external dependency. This removes the burden of validating the uniquified list of external dependencies and removes one target-specific compilation failure mode.
- While this is probably sufficient for people within the TVM community, downstream consumers of TVM output will probably be left with significant research needed to actually use the TVM-generated code.
Prior art
Don’t have any yet for this pre-RFC
Unresolved questions
- Is this level of specificity the right level to use when linking to external dependencies?
- How might we version or future-proof this metadata?
- When generating artifacts for
--runtime=c
, thestandalone_crt
directory is in some sense an “external dependency” (e.g. it currently lives outside the Model Library Format tarball). Should we list it here as one? - (perhaps the subject of another RFC, but related yet here) Should we provide a pathway towards optionally including external dependencies inside Model Library Format tarballs? If we do, what is the C++ runtime equivalent, if any?
- How should
libtvm_runtime.so
compile-time dependencies be represented here?
Future possibilities
Not yet considered.