From 5e83f079c8f1ce564e8c172ee852b7fb7f99c7cb Mon Sep 17 00:00:00 2001 From: Josh Fromm Date: Thu, 20 Aug 2020 11:38:18 -0700 Subject: [PATCH 1/5] Add python binding to new JSON target construction. --- python/tvm/target/target.py | 46 +++++++++++++++++---- src/target/target.cc | 4 +- tests/python/unittest/test_target_target.py | 24 +++++++++++ 3 files changed, 66 insertions(+), 8 deletions(-) diff --git a/python/tvm/target/target.py b/python/tvm/target/target.py index 0ea19f81f765..cfce0e3414ba 100644 --- a/python/tvm/target/target.py +++ b/python/tvm/target/target.py @@ -347,13 +347,43 @@ def create_llvm(llvm_args): return _ffi_api.TargetCreate('hexagon', *args_list) -def create(target_str): +def create(target): """Get a target given target string. Parameters ---------- - target_str : str - The target string. + target : str or dict + The target string or configuration dictionary. + When using a dictionary to configure target, the + possible values are: + { + kind : str (required) + Which codegen path to use, for example 'llvm' or 'cuda'. + keys : List of str (optional) + A set of strategies that can be dispatched to. When using + "kind=opencl" for example, one could set keys to ["mali", "opencl", "gpu"]. + device : str (optional) + A single key that corresponds to the actual device being run on. + This will be effectively appended to the keys. + libs : List of str (optional) + The set of external libraries to use. For example ['cblas', 'mkl']. + system-lib : bool (optional) + If True, build a module that contains self registered functions. + Useful for environments where dynamic loading like dlopen is banned. + mcpu : str (optional) + The specific cpu being run on. Serves only as an annotation. + model : str (optional) + An annotation indicating what model a workload came from. + runtime : str (optional) + An annotation indicating which runtime to use with a workload. + mtriple : str (optional) + The llvm triplet describing the target, for example "arm64-linux-android". + mattr : List of str (optional) + The llvm features to compile with, for example ["+avx512f", "+mmx"]. + mfloat-abi : str (optional) + An llvm setting that is one of 'hard' or 'soft' indicating whether to use + hardware or software floating-point operations. + } Returns ------- @@ -364,9 +394,11 @@ def create(target_str): ---- See the note on :py:mod:`tvm.target` on target string format. """ - if isinstance(target_str, Target): + if isinstance(target, Target): return target_str - if not isinstance(target_str, str): - raise ValueError("target_str has to be string type") + if isinstance(target, dict): + return _ffi_api.TargetFromConfig(target) + if isinstance(target, str): + return _ffi_api.TargetFromString(target) - return _ffi_api.TargetFromString(target_str) + raise ValueError("target has to be a string or dictionary.") diff --git a/src/target/target.cc b/src/target/target.cc index 6a245973315e..47b405430ead 100644 --- a/src/target/target.cc +++ b/src/target/target.cc @@ -410,7 +410,7 @@ Target Target::FromConfig(const Map& config_dict) { const auto* cfg_keys = config[kKeys].as(); CHECK(cfg_keys != nullptr) << "AttributeError: Expect type of field 'keys' is an Array, but get: " - << config[kTag]->GetTypeKey(); + << config[kKeys]->GetTypeKey(); for (const ObjectRef& e : *cfg_keys) { const auto* key = e.as(); CHECK(key != nullptr) << "AttributeError: Expect 'keys' to be an array of strings, but it " @@ -525,6 +525,8 @@ TVM_REGISTER_GLOBAL("target.GetCurrentTarget").set_body_typed(Target::Current); TVM_REGISTER_GLOBAL("target.TargetFromString").set_body_typed(Target::Create); +TVM_REGISTER_GLOBAL("target.TargetFromConfig").set_body_typed(Target::FromConfig); + TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable) .set_dispatch([](const ObjectRef& node, ReprPrinter* p) { auto* op = static_cast(node.get()); diff --git a/tests/python/unittest/test_target_target.py b/tests/python/unittest/test_target_target.py index 4258da9f576e..ed587611604d 100644 --- a/tests/python/unittest/test_target_target.py +++ b/tests/python/unittest/test_target_target.py @@ -80,7 +80,31 @@ def test_target_create(): assert tgt is not None +def test_target_config(): + """ + Test that constructing a target from a dictionary works. + """ + target_config = { + 'kind': 'llvm', + 'keys': ['arm_cpu', 'cpu'], + 'device': 'arm_cpu', + 'libs': ['cblas'], + 'system-lib': True, + 'mfloat-abi': 'hard', + 'mattr': ['+neon', '-avx512f'], + } + target = tvm.target.create(target_config) + assert target.kind.name == 'llvm' + assert all([key in target.keys for key in ['arm_cpu', 'cpu']]) + assert target.device_name == 'arm_cpu' + assert target.libs == ['cblas'] + assert 'system-lib' in str(target) + assert target.attrs['mfloat-abi'] == 'hard' + assert all([attr in target.attrs['mattr'] for attr in ['+neon', '-avx512f']]) + + if __name__ == "__main__": test_target_dispatch() test_target_string_parse() test_target_create() + test_target_config() From 9dd7fe5d6f3bf901f5aa69d41318c1c5c72b8848 Mon Sep 17 00:00:00 2001 From: Josh Fromm Date: Thu, 20 Aug 2020 15:32:22 -0700 Subject: [PATCH 2/5] Added json string parsing and new test. --- python/tvm/target/target.py | 15 +++++--- tests/python/unittest/test_target_target.py | 39 ++++++++++++++++----- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/python/tvm/target/target.py b/python/tvm/target/target.py index cfce0e3414ba..da492dcc58f2 100644 --- a/python/tvm/target/target.py +++ b/python/tvm/target/target.py @@ -17,6 +17,7 @@ """Target data structure.""" import os import re +import json import warnings import tvm._ffi @@ -353,8 +354,9 @@ def create(target): Parameters ---------- target : str or dict - The target string or configuration dictionary. - When using a dictionary to configure target, the + Can be one of a literal target string, a json string describing + a configuration, or a dictionary of configuration options. + When using a dictionary or json string to configure target, the possible values are: { kind : str (required) @@ -395,10 +397,15 @@ def create(target): See the note on :py:mod:`tvm.target` on target string format. """ if isinstance(target, Target): - return target_str + return target if isinstance(target, dict): return _ffi_api.TargetFromConfig(target) if isinstance(target, str): - return _ffi_api.TargetFromString(target) + # Check if target is a valid json string by trying to load it. + # If we cant, then assume it is a non-json target string. + try: + return _ffi_api.TargetFromConfig(json.loads(target)) + except: + return _ffi_api.TargetFromString(target) raise ValueError("target has to be a string or dictionary.") diff --git a/tests/python/unittest/test_target_target.py b/tests/python/unittest/test_target_target.py index ed587611604d..121f0a923749 100644 --- a/tests/python/unittest/test_target_target.py +++ b/tests/python/unittest/test_target_target.py @@ -14,6 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +import json import tvm from tvm import te from tvm.target import cuda, rocm, mali, intel_graphics, arm_cpu, vta, bifrost, hexagon @@ -93,14 +94,35 @@ def test_target_config(): 'mfloat-abi': 'hard', 'mattr': ['+neon', '-avx512f'], } - target = tvm.target.create(target_config) - assert target.kind.name == 'llvm' - assert all([key in target.keys for key in ['arm_cpu', 'cpu']]) - assert target.device_name == 'arm_cpu' - assert target.libs == ['cblas'] - assert 'system-lib' in str(target) - assert target.attrs['mfloat-abi'] == 'hard' - assert all([attr in target.attrs['mattr'] for attr in ['+neon', '-avx512f']]) + # Convert config dictionary to json string. + target_config_str = json.dumps(target_config) + # Test both dictionary input and json string. + for config in [target_config, target_config_str]: + target = tvm.target.create(config) + assert target.kind.name == 'llvm' + assert all([key in target.keys for key in ['arm_cpu', 'cpu']]) + assert target.device_name == 'arm_cpu' + assert target.libs == ['cblas'] + assert 'system-lib' in str(target) + assert target.attrs['mfloat-abi'] == 'hard' + assert all([attr in target.attrs['mattr'] for attr in ['+neon', '-avx512f']]) + + +def test_config_map(): + """ + Confirm that constructing a target with invalid + attributes fails as expected. + """ + target_config = { + 'kind': 'llvm', + 'libs': {'a': 'b', 'c': 'd'} + } + failed = False + try: + target = tvm.target.create(target_config) + except: + failed = True + assert failed == True if __name__ == "__main__": @@ -108,3 +130,4 @@ def test_target_config(): test_target_string_parse() test_target_create() test_target_config() + test_config_map() From f1da53f62192fc0c1e56eaeca6c2b1c128d11151 Mon Sep 17 00:00:00 2001 From: Josh Fromm Date: Thu, 20 Aug 2020 15:36:04 -0700 Subject: [PATCH 3/5] Add error type. --- tests/python/unittest/test_target_target.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/unittest/test_target_target.py b/tests/python/unittest/test_target_target.py index 121f0a923749..d19f122d08f7 100644 --- a/tests/python/unittest/test_target_target.py +++ b/tests/python/unittest/test_target_target.py @@ -120,7 +120,7 @@ def test_config_map(): failed = False try: target = tvm.target.create(target_config) - except: + except AttributeError: failed = True assert failed == True From 0b17562debaa97ae7e659274bde0eba8ff0b03da Mon Sep 17 00:00:00 2001 From: Josh Fromm Date: Thu, 20 Aug 2020 15:37:47 -0700 Subject: [PATCH 4/5] Add error type in json decoding check. --- python/tvm/target/target.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/tvm/target/target.py b/python/tvm/target/target.py index da492dcc58f2..d589fc7d46f7 100644 --- a/python/tvm/target/target.py +++ b/python/tvm/target/target.py @@ -405,7 +405,7 @@ def create(target): # If we cant, then assume it is a non-json target string. try: return _ffi_api.TargetFromConfig(json.loads(target)) - except: + except json.decoder.JSONDecodeError: return _ffi_api.TargetFromString(target) raise ValueError("target has to be a string or dictionary.") From 8c786a00beb54debcd946fb09f3848c89ae3d1db Mon Sep 17 00:00:00 2001 From: Josh Fromm Date: Thu, 20 Aug 2020 17:43:10 -0700 Subject: [PATCH 5/5] Fix sphinx formatting. --- python/tvm/target/target.py | 55 ++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/python/tvm/target/target.py b/python/tvm/target/target.py index d589fc7d46f7..9dcc164be78a 100644 --- a/python/tvm/target/target.py +++ b/python/tvm/target/target.py @@ -358,34 +358,33 @@ def create(target): a configuration, or a dictionary of configuration options. When using a dictionary or json string to configure target, the possible values are: - { - kind : str (required) - Which codegen path to use, for example 'llvm' or 'cuda'. - keys : List of str (optional) - A set of strategies that can be dispatched to. When using - "kind=opencl" for example, one could set keys to ["mali", "opencl", "gpu"]. - device : str (optional) - A single key that corresponds to the actual device being run on. - This will be effectively appended to the keys. - libs : List of str (optional) - The set of external libraries to use. For example ['cblas', 'mkl']. - system-lib : bool (optional) - If True, build a module that contains self registered functions. - Useful for environments where dynamic loading like dlopen is banned. - mcpu : str (optional) - The specific cpu being run on. Serves only as an annotation. - model : str (optional) - An annotation indicating what model a workload came from. - runtime : str (optional) - An annotation indicating which runtime to use with a workload. - mtriple : str (optional) - The llvm triplet describing the target, for example "arm64-linux-android". - mattr : List of str (optional) - The llvm features to compile with, for example ["+avx512f", "+mmx"]. - mfloat-abi : str (optional) - An llvm setting that is one of 'hard' or 'soft' indicating whether to use - hardware or software floating-point operations. - } + + kind : str (required) + Which codegen path to use, for example 'llvm' or 'cuda'. + keys : List of str (optional) + A set of strategies that can be dispatched to. When using + "kind=opencl" for example, one could set keys to ["mali", "opencl", "gpu"]. + device : str (optional) + A single key that corresponds to the actual device being run on. + This will be effectively appended to the keys. + libs : List of str (optional) + The set of external libraries to use. For example ['cblas', 'mkl']. + system-lib : bool (optional) + If True, build a module that contains self registered functions. + Useful for environments where dynamic loading like dlopen is banned. + mcpu : str (optional) + The specific cpu being run on. Serves only as an annotation. + model : str (optional) + An annotation indicating what model a workload came from. + runtime : str (optional) + An annotation indicating which runtime to use with a workload. + mtriple : str (optional) + The llvm triplet describing the target, for example "arm64-linux-android". + mattr : List of str (optional) + The llvm features to compile with, for example ["+avx512f", "+mmx"]. + mfloat-abi : str (optional) + An llvm setting that is one of 'hard' or 'soft' indicating whether to use + hardware or software floating-point operations. Returns -------