From 4c4b34fad69c1e095ee88e885a178638acf1ed89 Mon Sep 17 00:00:00 2001 From: vlad-perevezentsev Date: Mon, 30 Aug 2021 16:08:14 -0500 Subject: [PATCH 1/5] Add select_device_with_aspects func --- dpctl/__init__.py | 2 ++ dpctl/_device_selection.py | 29 +++++++++++++++++++ dpctl/tests/test_sycl_device.py | 50 +++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 dpctl/_device_selection.py diff --git a/dpctl/__init__.py b/dpctl/__init__.py index c033a75de2..9870786547 100644 --- a/dpctl/__init__.py +++ b/dpctl/__init__.py @@ -64,6 +64,7 @@ set_global_queue, ) +from ._device_selection import select_device_with_aspects from ._version import get_versions from .enum_types import backend_type, device_type, event_status_type @@ -80,6 +81,7 @@ "select_default_device", "select_gpu_device", "select_host_device", + "select_device_with_aspects", "get_num_devices", "has_cpu_devices", "has_gpu_devices", diff --git a/dpctl/_device_selection.py b/dpctl/_device_selection.py new file mode 100644 index 0000000000..64a891b080 --- /dev/null +++ b/dpctl/_device_selection.py @@ -0,0 +1,29 @@ +from . import SyclDevice, get_devices + + +def select_device_with_aspects(aspect_list, deny_list=[]): + check_list = aspect_list + deny_list + for asp in check_list: + if type(asp) != str: + raise TypeError("The list objects must be of a string type") + if not hasattr(SyclDevice, "has_aspect_" + asp): + raise ValueError(f"The {asp} aspect is not supported in dpctl") + devs = get_devices() + max_score = 0 + selected_dev = None + + for dev in devs: + aspect_status = True + for asp in aspect_list: + has_aspect = "dev.has_aspect_" + asp + if not eval(has_aspect): + aspect_status = False + for deny in deny_list: + has_aspect = "dev.has_aspect_" + deny + if eval(has_aspect): + aspect_status = False + if aspect_status and dev.default_selector_score > max_score: + max_score = dev.default_selector_score + selected_dev = dev + + return selected_dev diff --git a/dpctl/tests/test_sycl_device.py b/dpctl/tests/test_sycl_device.py index ddf72268c8..69cc76d747 100644 --- a/dpctl/tests/test_sycl_device.py +++ b/dpctl/tests/test_sycl_device.py @@ -645,3 +645,53 @@ def test_hashing_of_device(): """ device_dict = {dpctl.SyclDevice(): "default_device"} assert device_dict + + +list_of_valid_aspects = [ + "cpu", + "gpu", + "accelerator", + "custom", + "fp16", + "fp64", + "image", + "online_compiler", + "online_linker", + "queue_profiling", + "usm_device_allocations", + "usm_host_allocations", + "usm_shared_allocations", + "usm_system_allocator", +] + +list_of_invalid_aspects = [ + "emulated", + "host_debuggable", + "atomic64", + "usm_atomic_host_allocations", + "usm_atomic_shared_allocations", +] + + +@pytest.fixture(params=list_of_valid_aspects) +def valid_aspects(request): + return request.param + + +@pytest.fixture(params=list_of_invalid_aspects) +def invalid_aspects(request): + return request.param + + +def test_valid_aspects(valid_aspects): + dpctl.select_device_with_aspects([valid_aspects]) + + +def test_invalid_aspects(invalid_aspects): + try: + dpctl.select_device_with_aspects([invalid_aspects]) + raise AttributeError( + f"The {invalid_aspects} aspect is supported in dpctl" + ) + except ValueError: + pytest.skip(f"The {invalid_aspects} aspect is not supported in dpctl") From 6a06099b9ca47069c3d629a335ccc7827089eb08 Mon Sep 17 00:00:00 2001 From: vlad-perevezentsev Date: Tue, 31 Aug 2021 08:16:20 -0500 Subject: [PATCH 2/5] Fix remark and add docstrings --- dpctl/_device_selection.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/dpctl/_device_selection.py b/dpctl/_device_selection.py index 64a891b080..e831087ca5 100644 --- a/dpctl/_device_selection.py +++ b/dpctl/_device_selection.py @@ -13,15 +13,23 @@ def select_device_with_aspects(aspect_list, deny_list=[]): selected_dev = None for dev in devs: - aspect_status = True - for asp in aspect_list: - has_aspect = "dev.has_aspect_" + asp - if not eval(has_aspect): - aspect_status = False - for deny in deny_list: - has_aspect = "dev.has_aspect_" + deny - if eval(has_aspect): - aspect_status = False + # aspect_status = True + # for asp in aspect_list: + # has_aspect = "dev.has_aspect_" + asp + # if not eval(has_aspect): + # aspect_status = False + # for deny in deny_list: + # has_aspect = "dev.has_aspect_" + deny + # if eval(has_aspect): + # aspect_status = False + aspect_status = all( + (getattr(dev, "has_aspect_" + asp) is True for asp in aspect_list) + ) + aspect_status = aspect_status and not ( + any( + (getattr(dev, "has_aspect_" + asp) is True for asp in deny_list) + ) + ) if aspect_status and dev.default_selector_score > max_score: max_score = dev.default_selector_score selected_dev = dev From 26ab5ce1245513f475db68d3a67654fcfc8cc366 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Tue, 31 Aug 2021 09:48:28 -0500 Subject: [PATCH 3/5] Changes to select_device_with_aspects 1. Added docstring 2. Rename arguments from "aspect_list", "deny_list" to "required_aspects", "excluded_aspects" 3. Permit aspects to be gives as Python sequences 4. If unsupported aspect is specified, AttributeError is raised as opposed to ValueError 5. ValueError is raised of the requested device is not available. 6. Tests were modified to account for possibility that function invocation may raise ValueError 7. Test was added to test that ValueError error is raised when impossible configuration is requested, like when required_aspects and excluded_aspects sets intersect. --- dpctl/_device_selection.py | 66 +++++++++++++++++++++++++-------- dpctl/tests/test_sycl_device.py | 42 ++++++++++++++------- 2 files changed, 80 insertions(+), 28 deletions(-) diff --git a/dpctl/_device_selection.py b/dpctl/_device_selection.py index e831087ca5..15a082d401 100644 --- a/dpctl/_device_selection.py +++ b/dpctl/_device_selection.py @@ -1,37 +1,73 @@ +import collections.abc +from itertools import chain + from . import SyclDevice, get_devices -def select_device_with_aspects(aspect_list, deny_list=[]): - check_list = aspect_list + deny_list - for asp in check_list: +def select_device_with_aspects(required_aspects, excluded_aspects=[]): + """Selects the root :class:`dpctl.SyclDevice` that has the highest + default selector score among devices that have all aspects in the + `required_aspects` list, and do not have any aspects in `excluded_aspects` + list. + + Supported + + :Example: + .. code-block:: python + + import dpctl + # select a GPU that supports double precision + dpctl.select_device_with_aspects(['fp64', 'gpu']) + # select non-custom device with USM shared allocations + dpctl.select_device_with_aspects( + ['usm_shared_allocations'], excluded_aspects=['custom']) + """ + if isinstance(required_aspects, str): + required_aspects = [required_aspects] + if isinstance(excluded_aspects, str): + excluded_aspects = [excluded_aspects] + seq = collections.abc.Sequence + input_types_ok = isinstance(required_aspects, seq) and isinstance( + excluded_aspects, seq + ) + if not input_types_ok: + raise TypeError( + "Aspects are expected to be Python sequences, " + "e.g. lists, of strings" + ) + for asp in chain(required_aspects, excluded_aspects): if type(asp) != str: raise TypeError("The list objects must be of a string type") if not hasattr(SyclDevice, "has_aspect_" + asp): - raise ValueError(f"The {asp} aspect is not supported in dpctl") + raise AttributeError(f"The {asp} aspect is not supported in dpctl") devs = get_devices() max_score = 0 selected_dev = None for dev in devs: - # aspect_status = True - # for asp in aspect_list: - # has_aspect = "dev.has_aspect_" + asp - # if not eval(has_aspect): - # aspect_status = False - # for deny in deny_list: - # has_aspect = "dev.has_aspect_" + deny - # if eval(has_aspect): - # aspect_status = False aspect_status = all( - (getattr(dev, "has_aspect_" + asp) is True for asp in aspect_list) + ( + getattr(dev, "has_aspect_" + asp) is True + for asp in required_aspects + ) ) aspect_status = aspect_status and not ( any( - (getattr(dev, "has_aspect_" + asp) is True for asp in deny_list) + ( + getattr(dev, "has_aspect_" + asp) is True + for asp in excluded_aspects + ) ) ) if aspect_status and dev.default_selector_score > max_score: max_score = dev.default_selector_score selected_dev = dev + if selected_dev is None: + raise ValueError( + f"Requested device is unavailable: " + f"required_aspects={required_aspects}, " + f"excluded_aspects={excluded_aspects}" + ) + return selected_dev diff --git a/dpctl/tests/test_sycl_device.py b/dpctl/tests/test_sycl_device.py index 69cc76d747..a944613d3e 100644 --- a/dpctl/tests/test_sycl_device.py +++ b/dpctl/tests/test_sycl_device.py @@ -647,7 +647,7 @@ def test_hashing_of_device(): assert device_dict -list_of_valid_aspects = [ +list_of_supported_aspects = [ "cpu", "gpu", "accelerator", @@ -664,7 +664,9 @@ def test_hashing_of_device(): "usm_system_allocator", ] -list_of_invalid_aspects = [ +# SYCL 2020 spec aspects not presently +# supported in DPC++, and dpctl +list_of_unsupported_aspects = [ "emulated", "host_debuggable", "atomic64", @@ -673,25 +675,39 @@ def test_hashing_of_device(): ] -@pytest.fixture(params=list_of_valid_aspects) -def valid_aspects(request): +@pytest.fixture(params=list_of_supported_aspects) +def supported_aspect(request): return request.param -@pytest.fixture(params=list_of_invalid_aspects) -def invalid_aspects(request): +@pytest.fixture(params=list_of_unsupported_aspects) +def unsupported_aspect(request): return request.param -def test_valid_aspects(valid_aspects): - dpctl.select_device_with_aspects([valid_aspects]) +def test_supported_aspect(supported_aspect): + try: + dpctl.select_device_with_aspects(supported_aspect) + except ValueError: + # ValueError may be raised if no device with + # requested aspect charateristics is available + pass -def test_invalid_aspects(invalid_aspects): +def test_unsupported_aspect(unsupported_aspect): try: - dpctl.select_device_with_aspects([invalid_aspects]) + dpctl.select_device_with_aspects(unsupported_aspect) raise AttributeError( - f"The {invalid_aspects} aspect is supported in dpctl" + f"The {unsupported_aspect} aspect is now supported in dpctl" ) - except ValueError: - pytest.skip(f"The {invalid_aspects} aspect is not supported in dpctl") + except AttributeError: + pytest.skip( + f"The {unsupported_aspect} aspect is not supported in dpctl" + ) + + +def test_handle_no_device(): + with pytest.raises(ValueError): + dpctl.select_device_with_aspects(["gpu", "cpu"]) + with pytest.raises(ValueError): + dpctl.select_device_with_aspects("cpu", excluded_aspects="cpu") From bb68cb504edd1ee16a2e61aa3c4c7b32446af3ca Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Wed, 1 Sep 2021 07:21:45 -0500 Subject: [PATCH 4/5] Update _device_selection.py Added a reference to where to find the list of all aspects --- dpctl/_device_selection.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dpctl/_device_selection.py b/dpctl/_device_selection.py index 15a082d401..ff681c99d5 100644 --- a/dpctl/_device_selection.py +++ b/dpctl/_device_selection.py @@ -10,7 +10,9 @@ def select_device_with_aspects(required_aspects, excluded_aspects=[]): `required_aspects` list, and do not have any aspects in `excluded_aspects` list. - Supported + The list of SYCL device aspects can be found in SYCL 2020 specs: + + https://www.khronos.org/registry/SYCL/specs/sycl-2020/html/sycl-2020.html#sec:device-aspects :Example: .. code-block:: python From 8b099668b8fed3f9735197d28c81ab60823d9ef1 Mon Sep 17 00:00:00 2001 From: vlad-perevezentsev Date: Wed, 1 Sep 2021 08:19:27 -0500 Subject: [PATCH 5/5] Fix pre-commit --- dpctl/_device_selection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpctl/_device_selection.py b/dpctl/_device_selection.py index ff681c99d5..7013552f0a 100644 --- a/dpctl/_device_selection.py +++ b/dpctl/_device_selection.py @@ -12,7 +12,7 @@ def select_device_with_aspects(required_aspects, excluded_aspects=[]): The list of SYCL device aspects can be found in SYCL 2020 specs: - https://www.khronos.org/registry/SYCL/specs/sycl-2020/html/sycl-2020.html#sec:device-aspects + https://www.khronos.org/registry/SYCL/specs/sycl-2020/html/sycl-2020.html#sec:device-aspects :Example: .. code-block:: python