Skip to content

Commit 94f28b2

Browse files
Mousiusshamao01
andauthored
[TVMC] Add --config argument for config files (apache#11012)
* [TVMC] Add `--config` argument for config files Collecting common configurations for users of TVM and exposing them gracefully in tvmc using a `--config` option as defined in https://github.com/apache/tvm-rfcs/blob/main/rfcs/0030-tvmc-comand-line-configuration-files.md Co-authored-by: Shai Maor <[email protected]> * Add correct test guards Co-authored-by: Shai Maor <[email protected]>
1 parent e2dd0f8 commit 94f28b2

12 files changed

+401
-7
lines changed

configs/host/default.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"targets": [
3+
{
4+
"kind": "llvm"
5+
}
6+
]
7+
}

configs/test/compile_config_test.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"targets": [
3+
{"kind": "cmsis-nn", "from_device": "1"},
4+
{"kind": "c", "mcpu": "cortex-m55"}
5+
],
6+
"executor": { "kind": "aot"},
7+
"runtime": { "kind": "crt"},
8+
"pass-config": { "tir.disable_vectorize": "1"}
9+
}

configs/test/tune_config_test.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"targets": [
3+
{ "kind": "llvm" }
4+
],
5+
"trials": "2"
6+
}

python/tvm/driver/tvmc/autotuner.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747

4848

4949
@register_parser
50-
def add_tune_parser(subparsers, _):
50+
def add_tune_parser(subparsers, _, json_params):
5151
"""Include parser for 'tune' subcommand"""
5252

5353
parser = subparsers.add_parser("tune", help="auto-tune a model")
@@ -224,6 +224,9 @@ def add_tune_parser(subparsers, _):
224224
type=parse_shape_string,
225225
)
226226

227+
for one_entry in json_params:
228+
parser.set_defaults(**one_entry)
229+
227230

228231
def drive_tune(args):
229232
"""Invoke auto-tuning with command line arguments

python/tvm/driver/tvmc/compiler.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343

4444

4545
@register_parser
46-
def add_compile_parser(subparsers, _):
46+
def add_compile_parser(subparsers, _, json_params):
4747
"""Include parser for 'compile' subcommand"""
4848

4949
parser = subparsers.add_parser("compile", help="compile a model.")
@@ -143,6 +143,9 @@ def add_compile_parser(subparsers, _):
143143
help="The output module name. Defaults to 'default'.",
144144
)
145145

146+
for one_entry in json_params:
147+
parser.set_defaults(**one_entry)
148+
146149

147150
def drive_compile(args):
148151
"""Invoke tvmc.compiler module with command line arguments
+159
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#!/usr/bin/env python
2+
3+
# Licensed to the Apache Software Foundation (ASF) under one
4+
# or more contributor license agreements. See the NOTICE file
5+
# distributed with this work for additional information
6+
# regarding copyright ownership. The ASF licenses this file
7+
# to you under the Apache License, Version 2.0 (the
8+
# "License"); you may not use this file except in compliance
9+
# with the License. You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing,
14+
# software distributed under the License is distributed on an
15+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
# KIND, either express or implied. See the License for the
17+
# specific language governing permissions and limitations
18+
# under the License.
19+
"""
20+
manipulate json config file to work with TVMC
21+
"""
22+
import os
23+
import json
24+
from tvm.driver.tvmc import TVMCException
25+
26+
27+
def find_json_file(name, path):
28+
"""search for json file given file name a path
29+
30+
Parameters
31+
----------
32+
name: string
33+
the file name need to be searched
34+
path: string
35+
path to search at
36+
37+
Returns
38+
-------
39+
string
40+
the full path to that file
41+
42+
"""
43+
match = ""
44+
for root, _dirs, files in os.walk(path):
45+
if name in files:
46+
match = os.path.join(root, name)
47+
break
48+
49+
return match
50+
51+
52+
def read_and_convert_json_into_dict(config_args):
53+
"""Read json configuration file and return a dictionary with all parameters
54+
55+
Parameters
56+
----------
57+
args: argparse.Namespace
58+
Arguments from command line parser holding the json file path.
59+
60+
Returns
61+
-------
62+
dictionary
63+
dictionary with all the json arguments keys and values
64+
65+
"""
66+
try:
67+
if ".json" not in config_args.config:
68+
config_args.config = config_args.config.strip() + ".json"
69+
if os.path.isfile(config_args.config):
70+
json_config_file = config_args.config
71+
else:
72+
config_dir = os.path.abspath(
73+
os.path.join(os.path.realpath(__file__), "..", "..", "..", "..", "..", "configs")
74+
)
75+
json_config_file = find_json_file(config_args.config, config_dir)
76+
return json.load(open(json_config_file, "rb"))
77+
78+
except FileNotFoundError:
79+
raise TVMCException(
80+
f"File {config_args.config} does not exist at {config_dir} or is wrong format."
81+
)
82+
83+
84+
def parse_target_from_json(one_target, command_line_list):
85+
"""parse the targets out of the json file struct
86+
87+
Parameters
88+
----------
89+
one_target: dict
90+
dictionary with all target's details
91+
command_line_list: list
92+
list to update with target parameters
93+
"""
94+
target_kind, *sub_type = [
95+
one_target[key] if key == "kind" else (key, one_target[key]) for key in one_target
96+
]
97+
98+
internal_dict = {}
99+
if sub_type:
100+
sub_target_type = sub_type[0][0]
101+
target_value = sub_type[0][1]
102+
internal_dict[f"target_{target_kind}_{sub_target_type}"] = target_value
103+
command_line_list.append(internal_dict)
104+
105+
return target_kind
106+
107+
108+
def convert_config_json_to_cli(json_params):
109+
"""convert all configuration keys & values from dictionary to cli format
110+
111+
Parameters
112+
----------
113+
args: dictionary
114+
dictionary with all configuration keys & values.
115+
116+
Returns
117+
-------
118+
int
119+
list of configuration values in cli format
120+
121+
"""
122+
command_line_list = []
123+
for param_key in json_params:
124+
if param_key == "targets":
125+
target_list = [
126+
parse_target_from_json(one_target, command_line_list)
127+
for one_target in json_params[param_key]
128+
]
129+
130+
internal_dict = {}
131+
internal_dict["target"] = ", ".join(map(str, target_list))
132+
command_line_list.append(internal_dict)
133+
134+
elif param_key in ("executor", "runtime"):
135+
for key, value in json_params[param_key].items():
136+
if key == "kind":
137+
kind = f"{value}_"
138+
new_dict_key = param_key
139+
else:
140+
new_dict_key = f"{param_key}_{kind}{key}"
141+
142+
internal_dict = {}
143+
internal_dict[new_dict_key.replace("-", "_")] = value
144+
command_line_list.append(internal_dict)
145+
146+
elif isinstance(json_params[param_key], dict):
147+
internal_dict = {}
148+
modify_param_key = param_key.replace("-", "_")
149+
internal_dict[modify_param_key] = []
150+
for key, value in json_params[param_key].items():
151+
internal_dict[modify_param_key].append(f"{key}={value}")
152+
command_line_list.append(internal_dict)
153+
154+
else:
155+
internal_dict = {}
156+
internal_dict[param_key.replace("-", "_")] = json_params[param_key]
157+
command_line_list.append(internal_dict)
158+
159+
return command_line_list

python/tvm/driver/tvmc/main.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@
2626
import tvm
2727

2828
from tvm.driver.tvmc import TVMCException, TVMCImportError
29-
29+
from tvm.driver.tvmc.config_options import (
30+
read_and_convert_json_into_dict,
31+
convert_config_json_to_cli,
32+
)
3033

3134
REGISTERED_PARSER = []
3235

@@ -64,12 +67,19 @@ def _main(argv):
6467
# so it doesn't interfere with the creation of the dynamic subparsers.
6568
add_help=False,
6669
)
70+
71+
parser.add_argument("--config", default="default", help="configuration json file")
72+
config_arg, argv = parser.parse_known_args(argv)
73+
74+
json_param_dict = read_and_convert_json_into_dict(config_arg)
75+
json_config_values = convert_config_json_to_cli(json_param_dict)
76+
6777
parser.add_argument("-v", "--verbose", action="count", default=0, help="increase verbosity")
6878
parser.add_argument("--version", action="store_true", help="print the version and exit")
6979

7080
subparser = parser.add_subparsers(title="commands")
7181
for make_subparser in REGISTERED_PARSER:
72-
make_subparser(subparser, parser)
82+
make_subparser(subparser, parser, json_config_values)
7383

7484
# Finally, add help for the main parser.
7585
parser.add_argument("-h", "--help", action="help", help="show this help message and exit.")

python/tvm/driver/tvmc/micro.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545

4646

4747
@register_parser
48-
def add_micro_parser(subparsers, main_parser):
48+
def add_micro_parser(subparsers, main_parser, json_params):
4949
"""Includes parser for 'micro' context and associated subcommands:
5050
create-project (create), build, and flash.
5151
"""
@@ -231,6 +231,9 @@ def _add_parser(parser, platform):
231231
help="show this help message which includes platform-specific options and exit.",
232232
)
233233

234+
for one_entry in json_params:
235+
micro.set_defaults(**one_entry)
236+
234237

235238
def drive_micro(args):
236239
# Call proper handler based on subcommand parsed.

python/tvm/driver/tvmc/runner.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060

6161

6262
@register_parser
63-
def add_run_parser(subparsers, main_parser):
63+
def add_run_parser(subparsers, main_parser, json_params):
6464
"""Include parser for 'run' subcommand"""
6565

6666
# Use conflict_handler='resolve' to allow '--list-options' option to be properly overriden when
@@ -191,6 +191,9 @@ def add_run_parser(subparsers, main_parser):
191191
help="show this help message with platform-specific options and exit.",
192192
)
193193

194+
for one_entry in json_params:
195+
parser.set_defaults(**one_entry)
196+
194197

195198
def drive_run(args):
196199
"""Invoke runner module with command line arguments

python/tvm/driver/tvmc/target.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def generate_target_args(parser):
8181
parser.add_argument(
8282
"--target",
8383
help="compilation target as plain string, inline JSON or path to a JSON file",
84-
required=True,
84+
required=False,
8585
)
8686
for target_kind in _valid_target_kinds():
8787
_generate_target_kind_args(parser, target_kind)

tests/python/driver/tvmc/test_command_line.py

+36
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,39 @@ def test_tvmc_cl_workflow(keras_simple, tmpdir_factory):
5656
run_args = run_str.split(" ")[1:]
5757
_main(run_args)
5858
assert os.path.exists(output_path)
59+
60+
61+
@pytest.mark.skipif(
62+
platform.machine() == "aarch64",
63+
reason="Currently failing on AArch64 - see https://github.com/apache/tvm/issues/10673",
64+
)
65+
def test_tvmc_cl_workflow_json_config(keras_simple, tmpdir_factory):
66+
pytest.importorskip("tensorflow")
67+
tune_config_file = "tune_config_test"
68+
tmpdir = tmpdir_factory.mktemp("data")
69+
70+
# Test model tuning
71+
log_path = os.path.join(tmpdir, "keras-autotuner_records.json")
72+
tuning_str = (
73+
f"tvmc tune --config {tune_config_file} --output {log_path} "
74+
f"--enable-autoscheduler {keras_simple}"
75+
)
76+
tuning_args = tuning_str.split(" ")[1:]
77+
_main(tuning_args)
78+
assert os.path.exists(log_path)
79+
80+
# Test model compilation
81+
package_path = os.path.join(tmpdir, "keras-tvm.tar")
82+
compile_str = (
83+
f"tvmc compile --tuning-records {log_path} " f"--output {package_path} {keras_simple}"
84+
)
85+
compile_args = compile_str.split(" ")[1:]
86+
_main(compile_args)
87+
assert os.path.exists(package_path)
88+
89+
# Test running the model
90+
output_path = os.path.join(tmpdir, "predictions.npz")
91+
run_str = f"tvmc run --outputs {output_path} {package_path}"
92+
run_args = run_str.split(" ")[1:]
93+
_main(run_args)
94+
assert os.path.exists(output_path)

0 commit comments

Comments
 (0)