From 9954fa7b79e01bd07df03d12e5b91c2a3f07cbf3 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Thu, 20 May 2021 08:16:28 -0500 Subject: [PATCH 1/2] DPCTLDeviceMgr_GetPositionInDevices(DRef, device_mask) added This function is more general that DPCTL_GetRelativeId(DRef). The latter corresponds to DPCTLDeviceMgr_GetPositionInDevices(DRef, DPCTLDevice_GetBackend(DRef) | DPCTLDevice_GetDeviceType(DRef)); The function can be used to generate filter selector based on a pattern, expressed by device_mask (any backend, given type), or (any backend, any type), or (given backend, given type). --- .../include/dpctl_sycl_device_manager.h | 20 +++++++++++++ .../source/dpctl_sycl_device_manager.cpp | 29 +++++++++++++++++++ dpctl-capi/tests/test_sycl_device_manager.cpp | 11 +++++-- 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/dpctl-capi/include/dpctl_sycl_device_manager.h b/dpctl-capi/include/dpctl_sycl_device_manager.h index fefa8e7af4..6b81035dd9 100644 --- a/dpctl-capi/include/dpctl_sycl_device_manager.h +++ b/dpctl-capi/include/dpctl_sycl_device_manager.h @@ -76,6 +76,26 @@ DPCTL_API __dpctl_give DPCTLDeviceVectorRef DPCTLDeviceMgr_GetDevices(int device_identifier); +/*! + * @brief Returns an index on the given device in the vector returned by + * #DPCTLDeviceMgr_GetDevices if found, -1 otherwise. + * + * The device_identifier can be a combination of #DPCTLSyclBackendType and + * #DPCTLSyclDeviceType bit flags. The function returns all devices that + * match the specified bit flags. + * + * @param DRef A #DPCTLSyclDeviceRef opaque pointer. + * @param device_identifier A bitflag that can be any combination of + * #DPCTLSyclBackendType and #DPCTLSyclDeviceType + * enum values. + * @return If found, returns the position of the given device in the + * vector that would be returned by #DPCTLDeviceMgr_GetDevices if called + * with the same device_identifier argument. + */ +DPCTL_API +int DPCTLDeviceMgr_GetPositionInDevices(__dpctl_keep DPCTLSyclDeviceRef DRef, + int device_identifier); + /*! * @brief If the DPCTLSyclDeviceRef argument is a root device, then this * function returns a cached default SYCL context for that device. diff --git a/dpctl-capi/source/dpctl_sycl_device_manager.cpp b/dpctl-capi/source/dpctl_sycl_device_manager.cpp index 1904d0d3ff..124bf01bf2 100644 --- a/dpctl-capi/source/dpctl_sycl_device_manager.cpp +++ b/dpctl-capi/source/dpctl_sycl_device_manager.cpp @@ -164,6 +164,35 @@ DPCTLDeviceMgr_GetDevices(int device_identifier) return wrap(Devices); } +int DPCTLDeviceMgr_GetPositionInDevices(__dpctl_keep DPCTLSyclDeviceRef DRef, + int device_identifier) +{ + constexpr int not_found = -1; + if (!DRef) { + return not_found; + } + + const auto &root_devices = device::get_devices(); + default_selector mRanker; + int index = not_found; + auto reference_device = *(unwrap(DRef)); + + for (const auto &root_device : root_devices) { + if (mRanker(root_device) < 0) + continue; + auto Bty(DPCTL_SyclBackendToDPCTLBackendType( + root_device.get_platform().get_backend())); + auto Dty(DPCTL_SyclDeviceTypeToDPCTLDeviceType( + root_device.get_info())); + if ((device_identifier & Bty) && (device_identifier & Dty)) { + ++index; + if (root_device == reference_device) + return index; + } + } + return not_found; +} + /*! * Returns the number of available devices for a specific backend and device * type combination. diff --git a/dpctl-capi/tests/test_sycl_device_manager.cpp b/dpctl-capi/tests/test_sycl_device_manager.cpp index 52811f7fea..05141009a4 100644 --- a/dpctl-capi/tests/test_sycl_device_manager.cpp +++ b/dpctl-capi/tests/test_sycl_device_manager.cpp @@ -146,12 +146,12 @@ struct TestDPCTLGetDevicesOrdering : public ::testing::TestWithParam { DPCTLDeviceVectorRef DV = nullptr; size_t nDevices = 0; + int device_type_mask; TestDPCTLGetDevicesOrdering() { - const int device_type_mask = - (GetParam() & DPCTLSyclDeviceType::DPCTL_ALL) | - DPCTLSyclBackendType::DPCTL_ALL_BACKENDS; + device_type_mask = (GetParam() & DPCTLSyclDeviceType::DPCTL_ALL) | + DPCTLSyclBackendType::DPCTL_ALL_BACKENDS; EXPECT_NO_FATAL_FAILURE( DV = DPCTLDeviceMgr_GetDevices(device_type_mask)); EXPECT_TRUE(DV != nullptr); @@ -179,6 +179,7 @@ TEST_P(TestDPCTLGetDevicesOrdering, ChkConsistencyWithFilterSelector) std::string fs_device_type, fs; DPCTLSyclDeviceRef DRef = nullptr, D0Ref = nullptr; DPCTLSyclDeviceSelectorRef DSRef = nullptr; + int j = -1; EXPECT_NO_FATAL_FAILURE(DRef = DPCTLDeviceVector_GetAt(DV, i)); EXPECT_NO_FATAL_FAILURE(Dty = DPCTLDevice_GetDeviceType(DRef)); EXPECT_NO_FATAL_FAILURE( @@ -189,6 +190,10 @@ TEST_P(TestDPCTLGetDevicesOrdering, ChkConsistencyWithFilterSelector) EXPECT_NO_FATAL_FAILURE(D0Ref = DPCTLDevice_CreateFromSelector(DSRef)); EXPECT_NO_FATAL_FAILURE(DPCTLDeviceSelector_Delete(DSRef)); EXPECT_TRUE(DPCTLDevice_AreEq(DRef, D0Ref)); + EXPECT_NO_FATAL_FAILURE( + j = DPCTLDeviceMgr_GetPositionInDevices(DRef, device_type_mask)); + EXPECT_TRUE(j >= 0); + EXPECT_TRUE(i == static_cast(j)); EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(D0Ref)); EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(DRef)); } From c31be2777a52fb72c79131fb97e54f1e97c694ae Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Thu, 20 May 2021 10:07:31 -0500 Subject: [PATCH 2/2] dpctl.SyclDevice.get_filter_string method added dpctl.SyclDevice.filter_string property gives fully specified filter selector, which includes backend, device_type and relative id. get_filter_string(include_backend, include_device_type) allows one to construct filter strings which would omit either backend or device_type or both, guaranteeing that dpctl.SyclDevice(obtained_selector_string) will give back the same device. ``` In [1]: import dpctl In [2]: default_dev = dpctl.SyclDevice() In [3]: default_dev.get_filter_string(include_backend=False) Out[3]: 'gpu:1' In [4]: default_dev.get_filter_string(include_device_type=False) Out[4]: 'level_zero:0' In [5]: default_dev.get_filter_string(include_device_type=False, include_backend=False) Out[5]: '4' ``` --- dpctl/_backend.pxd | 3 + dpctl/_sycl_device.pxd | 4 + dpctl/_sycl_device.pyx | 152 +++++++++++++++++++++++++++++++- dpctl/tests/test_sycl_device.py | 20 ++++- 4 files changed, 175 insertions(+), 4 deletions(-) diff --git a/dpctl/_backend.pxd b/dpctl/_backend.pxd index f78aca60ef..4e640ea835 100644 --- a/dpctl/_backend.pxd +++ b/dpctl/_backend.pxd @@ -208,6 +208,9 @@ cdef extern from "dpctl_sycl_device_manager.h": DPCTLDeviceVectorRef DVRef, size_t index) cdef DPCTLDeviceVectorRef DPCTLDeviceMgr_GetDevices(int device_identifier) + cdef int DPCTLDeviceMgr_GetPositionInDevices( + const DPCTLSyclDeviceRef DRef, + int device_identifier) cdef size_t DPCTLDeviceMgr_GetNumDevices(int device_identifier) cdef void DPCTLDeviceMgr_PrintDeviceInfo(const DPCTLSyclDeviceRef DRef) cdef DPCTLSyclContextRef DPCTLDeviceMgr_GetCachedContext( diff --git a/dpctl/_sycl_device.pxd b/dpctl/_sycl_device.pxd index b3923f50e3..88b23b12b3 100644 --- a/dpctl/_sycl_device.pxd +++ b/dpctl/_sycl_device.pxd @@ -51,3 +51,7 @@ cdef public class SyclDevice(_SyclDevice) [object PySyclDeviceObject, type PySyc cdef list create_sub_devices_by_counts(self, object counts) cdef list create_sub_devices_by_affinity(self, _partition_affinity_domain_type domain) cdef cpp_bool equals(self, SyclDevice q) + cdef int get_device_type_ordinal(self) + cdef int get_overall_ordinal(self) + cdef int get_backend_ordinal(self) + cdef int get_backend_and_device_type_ordinal(self) diff --git a/dpctl/_sycl_device.pyx b/dpctl/_sycl_device.pyx index 34ba8eae19..67319f4a20 100644 --- a/dpctl/_sycl_device.pyx +++ b/dpctl/_sycl_device.pyx @@ -61,6 +61,8 @@ from ._backend cimport ( # noqa: E211 DPCTLDevice_IsCPU, DPCTLDevice_IsGPU, DPCTLDevice_IsHost, + DPCTLDeviceMgr_GetDevices, + DPCTLDeviceMgr_GetPositionInDevices, DPCTLDeviceMgr_GetRelativeId, DPCTLDeviceMgr_PrintDeviceInfo, DPCTLDeviceSelector_Delete, @@ -825,8 +827,23 @@ cdef class SyclDevice(_SyclDevice): @property def filter_string(self): - """ For a parent device, returns a ``tuple (backend, device_kind, - relative_id)``. Raises an exception for sub-devices. + """ + For a parent device, returns a fully specified filter selector + string``backend:device_type:relative_id`` selecting the device. + + Raises an exception for sub-devices. + + :Example: + .. code-block:: python + + import dpctl + + # Create a SyclDevice with an explicit filter string, + # in this case the first level_zero gpu device. + level_zero_gpu = dpctl.SyclDevice("level_zero:gpu:0") + # filter_string property should be "level_zero:gpu:0" + dev = dpctl.SyclDevice(level_zero_gpu.filter_string) + assert level_zero_gpu == dev """ cdef DPCTLSyclDeviceRef pDRef = NULL cdef DPCTLSyclBackendType BTy @@ -846,3 +863,134 @@ cdef class SyclDevice(_SyclDevice): # this a sub-device, free it, and raise an exception DPCTLDevice_Delete(pDRef) raise TypeError("This SyclDevice is not a root device") + + cdef int get_backend_and_device_type_ordinal(self): + """ + If this device is a root ``sycl::device`` returns the ordinal + position of this device in the vector + ``sycl::device::get_devices(device_type_of_this_device)`` + filtered to contain only devices with the same backend as this + device. + + Returns -1 if the device is a sub-device, or the device could not + be found in the vector. + """ + cdef int64_t relId = DPCTLDeviceMgr_GetRelativeId(self._device_ref) + return relId + + cdef int get_device_type_ordinal(self): + """ + If this device is a root ``sycl::device`` returns the ordinal + position of this device in the vector + ``sycl::device::get_devices(device_type_of_this_device)`` + + Returns -1 if the device is a sub-device, or the device could not + be found in the vector. + """ + cdef DPCTLSyclDeviceType DTy + cdef int64_t relId = -1 + + DTy = DPCTLDevice_GetDeviceType(self._device_ref) + relId = DPCTLDeviceMgr_GetPositionInDevices( + self._device_ref, _backend_type._ALL_BACKENDS | DTy) + return relId + + cdef int get_backend_ordinal(self): + """ + If this device is a root ``sycl::device`` returns the ordinal + position of this device in the vector ``sycl::device::get_devices()`` + filtered to contain only devices with the same backend as this + device. + + Returns -1 if the device is a sub-device, or the device could not + be found in the vector. + """ + cdef DPCTLSyclBackendType BTy + cdef int64_t relId = -1 + + BTy = DPCTLDevice_GetBackend(self._device_ref) + relId = DPCTLDeviceMgr_GetPositionInDevices( + self._device_ref, BTy | _device_type._ALL_DEVICES) + return relId + + cdef int get_overall_ordinal(self): + """ + If this device is a root ``sycl::device`` returns the ordinal + position of this device in the vector ``sycl::device::get_devices()`` + filtered to contain only devices with the same backend as this + device. + + Returns -1 if the device is a sub-device, or the device could not + be found in the vector. + """ + cdef int64_t relId = -1 + + relId = DPCTLDeviceMgr_GetPositionInDevices( + self._device_ref, + (_backend_type._ALL_BACKENDS | _device_type._ALL_DEVICES) + ) + return relId + + def get_filter_string(self, include_backend=True, include_device_type=True): + """ + For a parent device returns a filter selector string + that includes backend or device type based on the value + of the given keyword arguments. + + Raises a TypeError if this devices is a sub-device, or + a ValueError if no match was found in the vector returned + by ``sycl::device::get_devices()``. + + :Example: + .. code-block:: python + + import dpctl + + # Create a GPU SyclDevice + gpu_dev = dpctl.SyclDevice("gpu:0") + # filter string should be "gpu:0" + fs = gpu_dev.get_filter_string(use_backend=False) + dev = dpctl.SyclDevice(fs) + assert gpu _dev == dev + """ + cdef int relId = -1 + cdef DPCTLSyclDeviceRef pDRef = NULL + cdef DPCTLSyclDeviceType DTy + cdef DPCTLSyclBackendType BTy + + if include_backend: + if include_device_type: + relId = self.get_backend_and_device_type_ordinal() + else: + relId = self.get_backend_ordinal() + else: + if include_device_type: + relId = self.get_device_type_ordinal() + else: + relId = self.get_overall_ordinal() + + if relId < 0: + pDRef = DPCTLDevice_GetParentDevice(self._device_ref) + if (pDRef is NULL): + raise ValueError + else: + # this a sub-device, free it, and raise an exception + DPCTLDevice_Delete(pDRef) + raise TypeError("This SyclDevice is not a root device") + else: + if include_backend: + BTy = DPCTLDevice_GetBackend(self._device_ref) + be_str = _backend_type_to_filter_string_part(BTy) + if include_device_type: + DTy = DPCTLDevice_GetDeviceType(self._device_ref) + dt_str = _device_type_to_filter_string_part(DTy) + return ":".join((be_str, dt_str, str(relId))) + else: + return ":".join((be_str, str(relId))) + else: + if include_device_type: + DTy = DPCTLDevice_GetDeviceType(self._device_ref) + dt_str = _device_type_to_filter_string_part(DTy) + return ":".join((dt_str, str(relId))) + else: + return str(relId) diff --git a/dpctl/tests/test_sycl_device.py b/dpctl/tests/test_sycl_device.py index e3451f59a5..e55a5ee225 100644 --- a/dpctl/tests/test_sycl_device.py +++ b/dpctl/tests/test_sycl_device.py @@ -591,13 +591,29 @@ def test_filter_string(valid_filter): ) -def test_filter_string2(): +def test_filter_string_property(): """ Test that filter_string reconstructs the same device. """ devices = dpctl.get_devices() for d in devices: - if d.default_selector_score > 0: + if d.default_selector_score >= 0: dev_id = d.filter_string d_r = dpctl.SyclDevice(dev_id) assert d == d_r + + +def test_filter_string_method(): + """ + Test that filter_string reconstructs the same device. + """ + devices = dpctl.get_devices() + for d in devices: + for be in [True, False]: + for dt in [True, False]: + if d.default_selector_score >= 0: + dev_id = d.get_filter_string( + include_backend=be, include_device_type=dt + ) + d_r = dpctl.SyclDevice(dev_id) + assert d == d_r, "Failed "