Skip to content

Commit c88ed14

Browse files
Merge pull request #558 from vlad-perevezentsev/aspect_selector
Add aspect selector
2 parents 33031a2 + 5c57730 commit c88ed14

File tree

3 files changed

+143
-0
lines changed

3 files changed

+143
-0
lines changed

dpctl/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
set_global_queue,
6565
)
6666

67+
from ._device_selection import select_device_with_aspects
6768
from ._sycl_timer import SyclTimer
6869
from ._version import get_versions
6970
from .enum_types import backend_type, device_type, event_status_type
@@ -81,6 +82,7 @@
8182
"select_default_device",
8283
"select_gpu_device",
8384
"select_host_device",
85+
"select_device_with_aspects",
8486
"get_num_devices",
8587
"has_cpu_devices",
8688
"has_gpu_devices",

dpctl/_device_selection.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import collections.abc
2+
from itertools import chain
3+
4+
from . import SyclDevice, get_devices
5+
6+
7+
def select_device_with_aspects(required_aspects, excluded_aspects=[]):
8+
"""Selects the root :class:`dpctl.SyclDevice` that has the highest
9+
default selector score among devices that have all aspects in the
10+
`required_aspects` list, and do not have any aspects in `excluded_aspects`
11+
list.
12+
13+
The list of SYCL device aspects can be found in SYCL 2020 specs:
14+
15+
https://www.khronos.org/registry/SYCL/specs/sycl-2020/html/sycl-2020.html#sec:device-aspects
16+
17+
:Example:
18+
.. code-block:: python
19+
20+
import dpctl
21+
# select a GPU that supports double precision
22+
dpctl.select_device_with_aspects(['fp64', 'gpu'])
23+
# select non-custom device with USM shared allocations
24+
dpctl.select_device_with_aspects(
25+
['usm_shared_allocations'], excluded_aspects=['custom'])
26+
"""
27+
if isinstance(required_aspects, str):
28+
required_aspects = [required_aspects]
29+
if isinstance(excluded_aspects, str):
30+
excluded_aspects = [excluded_aspects]
31+
seq = collections.abc.Sequence
32+
input_types_ok = isinstance(required_aspects, seq) and isinstance(
33+
excluded_aspects, seq
34+
)
35+
if not input_types_ok:
36+
raise TypeError(
37+
"Aspects are expected to be Python sequences, "
38+
"e.g. lists, of strings"
39+
)
40+
for asp in chain(required_aspects, excluded_aspects):
41+
if type(asp) != str:
42+
raise TypeError("The list objects must be of a string type")
43+
if not hasattr(SyclDevice, "has_aspect_" + asp):
44+
raise AttributeError(f"The {asp} aspect is not supported in dpctl")
45+
devs = get_devices()
46+
max_score = 0
47+
selected_dev = None
48+
49+
for dev in devs:
50+
aspect_status = all(
51+
(
52+
getattr(dev, "has_aspect_" + asp) is True
53+
for asp in required_aspects
54+
)
55+
)
56+
aspect_status = aspect_status and not (
57+
any(
58+
(
59+
getattr(dev, "has_aspect_" + asp) is True
60+
for asp in excluded_aspects
61+
)
62+
)
63+
)
64+
if aspect_status and dev.default_selector_score > max_score:
65+
max_score = dev.default_selector_score
66+
selected_dev = dev
67+
68+
if selected_dev is None:
69+
raise ValueError(
70+
f"Requested device is unavailable: "
71+
f"required_aspects={required_aspects}, "
72+
f"excluded_aspects={excluded_aspects}"
73+
)
74+
75+
return selected_dev

dpctl/tests/test_sycl_device.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,3 +645,69 @@ def test_hashing_of_device():
645645
"""
646646
device_dict = {dpctl.SyclDevice(): "default_device"}
647647
assert device_dict
648+
649+
650+
list_of_supported_aspects = [
651+
"cpu",
652+
"gpu",
653+
"accelerator",
654+
"custom",
655+
"fp16",
656+
"fp64",
657+
"image",
658+
"online_compiler",
659+
"online_linker",
660+
"queue_profiling",
661+
"usm_device_allocations",
662+
"usm_host_allocations",
663+
"usm_shared_allocations",
664+
"usm_system_allocator",
665+
]
666+
667+
# SYCL 2020 spec aspects not presently
668+
# supported in DPC++, and dpctl
669+
list_of_unsupported_aspects = [
670+
"emulated",
671+
"host_debuggable",
672+
"atomic64",
673+
"usm_atomic_host_allocations",
674+
"usm_atomic_shared_allocations",
675+
]
676+
677+
678+
@pytest.fixture(params=list_of_supported_aspects)
679+
def supported_aspect(request):
680+
return request.param
681+
682+
683+
@pytest.fixture(params=list_of_unsupported_aspects)
684+
def unsupported_aspect(request):
685+
return request.param
686+
687+
688+
def test_supported_aspect(supported_aspect):
689+
try:
690+
dpctl.select_device_with_aspects(supported_aspect)
691+
except ValueError:
692+
# ValueError may be raised if no device with
693+
# requested aspect charateristics is available
694+
pass
695+
696+
697+
def test_unsupported_aspect(unsupported_aspect):
698+
try:
699+
dpctl.select_device_with_aspects(unsupported_aspect)
700+
raise AttributeError(
701+
f"The {unsupported_aspect} aspect is now supported in dpctl"
702+
)
703+
except AttributeError:
704+
pytest.skip(
705+
f"The {unsupported_aspect} aspect is not supported in dpctl"
706+
)
707+
708+
709+
def test_handle_no_device():
710+
with pytest.raises(ValueError):
711+
dpctl.select_device_with_aspects(["gpu", "cpu"])
712+
with pytest.raises(ValueError):
713+
dpctl.select_device_with_aspects("cpu", excluded_aspects="cpu")

0 commit comments

Comments
 (0)