From b79d642064acc48d945510b4e671689603e3ce61 Mon Sep 17 00:00:00 2001 From: Diptorup Deb Date: Sun, 14 Feb 2021 22:36:13 -0600 Subject: [PATCH 1/8] Refactor the SyclDevice class to not expose _SyclDevice to users. --- .../source/dpctl_sycl_queue_manager.cpp | 1 + dpctl/_sycl_device.pxd | 2 + dpctl/_sycl_device.pyx | 178 ++++++++++++++++++ dpctl/tests/test_sycl_device.py | 24 +++ 4 files changed, 205 insertions(+) diff --git a/dpctl-capi/source/dpctl_sycl_queue_manager.cpp b/dpctl-capi/source/dpctl_sycl_queue_manager.cpp index 1c63a3eeac..408cb8094c 100644 --- a/dpctl-capi/source/dpctl_sycl_queue_manager.cpp +++ b/dpctl-capi/source/dpctl_sycl_queue_manager.cpp @@ -25,6 +25,7 @@ //===----------------------------------------------------------------------===// #include "dpctl_sycl_queue_manager.h" #include "Support/CBindingWrapping.h" +#include "dpctl_sycl_enum_types.h" #include /* SYCL headers */ #include #include diff --git a/dpctl/_sycl_device.pxd b/dpctl/_sycl_device.pxd index 2e0c76a296..a6efc424ca 100644 --- a/dpctl/_sycl_device.pxd +++ b/dpctl/_sycl_device.pxd @@ -46,6 +46,8 @@ cdef class _SyclDevice: cdef uint32_t _max_num_sub_groups cdef bool _int64_base_atomics cdef bool _int64_extended_atomics + + cdef DPCTLSyclDeviceRef get_device_ref(self) cpdef get_backend(self) cpdef get_device_name(self) diff --git a/dpctl/_sycl_device.pyx b/dpctl/_sycl_device.pyx index e8ea50bfc5..84eedf8d35 100644 --- a/dpctl/_sycl_device.pyx +++ b/dpctl/_sycl_device.pyx @@ -244,6 +244,40 @@ cdef class _SyclDevice: """ return self._host_device + cpdef is_accelerator(self): + """ Returns True if the SyclDevice instance is a SYCL accelerator + device. + + Returns: + bool: True if the SyclDevice is a SYCL accelerator device, + else False. + """ + return self._accelerator_device + + cpdef is_cpu(self): + """ Returns True if the SyclDevice instance is a SYCL CPU device. + + Returns: + bool: True if the SyclDevice is a SYCL CPU device, else False. + """ + return self._cpu_device + + cpdef is_gpu(self): + """ Returns True if the SyclDevice instance is a SYCL GPU device. + + Returns: + bool: True if the SyclDevice is a SYCL GPU device, else False. + """ + return self._gpu_device + + cpdef is_host(self): + """ Returns True if the SyclDevice instance is a SYCL host device. + + Returns: + bool: True if the SyclDevice is a SYCL host device, else False. + """ + return self._host_device + cdef DPCTLSyclDeviceRef get_device_ref (self): """ Returns the DPCTLSyclDeviceRef pointer for this class. """ @@ -403,6 +437,150 @@ cdef class SyclDevice(_SyclDevice): return "".format(hex(id(self))) + @property + def __name__(): + return "SyclDevice" + + def __repr__(self): + return "".format(hex(id(self))) + + +cdef class SyclDevice(_SyclDevice): + """ Python equivalent for cl::sycl::device class. + + There are two ways of creating a SyclDevice instance: + + - by directly passing in a filter string to the class constructor. The + filter string needs to conform to the the `DPC++ filter selector SYCL + extension `_. + + :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"): + level_zero_gpu.dump_device_info() + + - by calling one of the device selector helper functions: + + :func:`dpctl.select_accelerator_device()`, + :func:`dpctl.select_cpu_device()`, + :func:`dpctl.select_default_device()`, + :func:`dpctl.select_gpu_device()`, + :func:`dpctl.select_host_device()`. + + + :Example: + .. code-block:: python + + import dpctl + + # Create a SyclDevice of type GPU based on whatever is returned + # by the SYCL `gpu_selector` device selector class. + gpu = dpctl.select_gpu_device(): + gpu.dump_device_info() + + """ + @staticmethod + cdef void _init_helper(SyclDevice device, DPCTLSyclDeviceRef DRef): + device._device_ref = DRef + device._device_name = DPCTLDevice_GetName(DRef) + device._driver_version = DPCTLDevice_GetDriverInfo(DRef) + device._int64_base_atomics = DPCTLDevice_HasInt64BaseAtomics(DRef) + device._int64_extended_atomics = ( + DPCTLDevice_HasInt64ExtendedAtomics(DRef) + ) + device._max_compute_units = DPCTLDevice_GetMaxComputeUnits(DRef) + device._max_num_sub_groups = DPCTLDevice_GetMaxNumSubGroups(DRef) + device._max_work_group_size = DPCTLDevice_GetMaxWorkGroupSize(DRef) + device._max_work_item_dims = DPCTLDevice_GetMaxWorkItemDims(DRef) + device._max_work_item_sizes = DPCTLDevice_GetMaxWorkItemSizes(DRef) + device._vendor_name = DPCTLDevice_GetVendorName(DRef) + device._accelerator_device = DPCTLDevice_IsAccelerator(DRef) + device._cpu_device = DPCTLDevice_IsCPU(DRef) + device._gpu_device = DPCTLDevice_IsGPU(DRef) + device._host_device = DPCTLDevice_IsHost(DRef) + + @staticmethod + cdef SyclDevice _create(DPCTLSyclDeviceRef dref): + cdef SyclDevice ret = _SyclDevice.__new__(_SyclDevice) + # Initialize the attributes of the SyclDevice object + SyclDevice._init_helper(ret, dref) + return SyclDevice(ret) + + cdef void _init_from__SyclDevice(self, _SyclDevice other): + self._device_ref = DPCTLDevice_Copy(other._device_ref) + self._device_name = DPCTLDevice_GetName(self._device_ref) + self._driver_version = DPCTLDevice_GetDriverInfo(self._device_ref) + self._int64_base_atomics = other._int64_base_atomics + self._int64_extended_atomics = other._int64_extended_atomics + self._max_compute_units = other._max_compute_units + self._max_num_sub_groups = other._max_num_sub_groups + self._max_work_group_size = other._max_work_group_size + self._max_work_item_dims = other._max_work_item_dims + self._max_work_item_sizes = ( + DPCTLDevice_GetMaxWorkItemSizes(self._device_ref) + ) + self._vendor_name = DPCTLDevice_GetVendorName(self._device_ref) + self._accelerator_device = other._accelerator_device + self._cpu_device = other._cpu_device + self._gpu_device = other._gpu_device + self._host_device = other._host_device + + cdef int _init_from_selector(self, DPCTLSyclDeviceSelectorRef DSRef): + # Initialize the attributes of the SyclDevice object + DRef = DPCTLDevice_CreateFromSelector(DSRef) + if DRef is NULL: + return -1 + else: + SyclDevice._init_helper(self, DRef) + return 0 + + def __cinit__(self, arg=None): + cdef DPCTLSyclDeviceSelectorRef DSRef = NULL + cdef DPCTLSyclDeviceRef DRef = NULL + cdef const char *filter_c_str = NULL + cdef int ret = 0 + + if type(arg) is unicode: + string = bytes(arg, "utf-8") + filter_c_str = string + DSRef = DPCTLFilterSelector_Create(filter_c_str) + ret = self._init_from_selector(DSRef) + if ret == -1: + raise ValueError("Could not create a Device with the selector") + # Free up the device selector + DPCTLDeviceSelector_Delete(DSRef) + elif isinstance(arg, unicode): + string = bytes(unicode(arg), "utf-8") + filter_c_str = string + DSRef = DPCTLFilterSelector_Create(filter_c_str) + if ret == -1: + raise ValueError("Could not create a Device with the selector") + # Free up the device selector + DPCTLDeviceSelector_Delete(DSRef) + elif isinstance(arg, _SyclDevice): + self._init_from__SyclDevice(arg) + elif arg is None: + DSRef = DPCTLDefaultSelector_Create() + self._init_from_selector(DSRef) + else: + raise ValueError( + "Invalid argument. Argument should be a str object specifying " + "a SYCL filter selector string." + ) + + @property + def __name__(self): + return "SyclDevice" + + def __repr__(self): + return "".format(hex(id(self))) + + cpdef select_accelerator_device(): """ A wrapper for SYCL's `accelerator_selector` device_selector class. diff --git a/dpctl/tests/test_sycl_device.py b/dpctl/tests/test_sycl_device.py index 7df90c6df3..8e0f967370 100644 --- a/dpctl/tests/test_sycl_device.py +++ b/dpctl/tests/test_sycl_device.py @@ -162,6 +162,30 @@ def device_selector(request): def check(request): return request.param + def check_is_accelerator(self, device): + try: + device.is_accelerator() + except Exception: + pytest.fail("is_accelerator call failed") + + def check_is_cpu(self, device): + try: + device.is_cpu() + except Exception: + pytest.fail("is_cpu call failed") + + def check_is_gpu(self, device): + try: + device.is_gpu() + except Exception: + pytest.fail("is_gpu call failed") + + def check_is_host(self, device): + try: + device.is_host() + except Exception: + pytest.fail("is_hostcall failed") + def test_standard_selectors(device_selector, check): """Tests if the standard SYCL device_selectors are able to select a From 4f6606a4e50d5ed510c74b50edeed924f4219c70 Mon Sep 17 00:00:00 2001 From: Diptorup Deb Date: Wed, 17 Feb 2021 13:15:20 -0600 Subject: [PATCH 2/8] Change pytest fixtures for the SyclDevice class. --- dpctl/tests/test_sycl_device.py | 118 +++++++++++++++++++++++++------- 1 file changed, 95 insertions(+), 23 deletions(-) diff --git a/dpctl/tests/test_sycl_device.py b/dpctl/tests/test_sycl_device.py index 8e0f967370..d38aeec9ca 100644 --- a/dpctl/tests/test_sycl_device.py +++ b/dpctl/tests/test_sycl_device.py @@ -128,6 +128,97 @@ def check_is_host(device): pytest.fail("is_hostcall failed") +list_of_checks = [ + check_get_max_compute_units, + check_get_max_work_item_dims, + check_get_max_work_item_sizes, + check_get_max_work_group_size, + check_get_max_num_sub_groups, + check_has_int64_base_atomics, + check_has_int64_extended_atomics, + check_is_accelerator, + check_is_cpu, + check_is_gpu, + check_is_host, +] + +# Unit test cases that will be run for every device +def check_get_max_compute_units(device): + max_compute_units = device.get_max_compute_units() + assert max_compute_units > 0 + + +def check_get_max_work_item_dims(device): + max_work_item_dims = device.get_max_work_item_dims() + assert max_work_item_dims > 0 + + +def check_get_max_work_item_sizes(device): + max_work_item_sizes = device.get_max_work_item_sizes() + for size in max_work_item_sizes: + assert size is not None + + +def check_get_max_work_group_size(device): + max_work_group_size = device.get_max_work_group_size() + # Special case for FPGA simulator + if device.is_accelerator(): + assert max_work_group_size >= 0 + else: + assert max_work_group_size > 0 + + +def check_get_max_num_sub_groups(device): + max_num_sub_groups = device.get_max_num_sub_groups() + # Special case for FPGA simulator + if device.is_accelerator(): + assert max_num_sub_groups >= 0 + else: + assert max_num_sub_groups > 0 + + +def check_has_int64_base_atomics(device): + try: + device.has_int64_base_atomics() + except Exception: + pytest.fail("has_int64_base_atomics call failed") + + +def check_has_int64_extended_atomics(device): + try: + device.has_int64_extended_atomics() + except Exception: + pytest.fail("has_int64_extended_atomics call failed") + + +def check_is_accelerator(device): + try: + device.is_accelerator() + except Exception: + pytest.fail("is_accelerator call failed") + + +def check_is_cpu(device): + try: + device.is_cpu() + except Exception: + pytest.fail("is_cpu call failed") + + +def check_is_gpu(device): + try: + device.is_gpu() + except Exception: + pytest.fail("is_gpu call failed") + + +def check_is_host(device): + try: + device.is_host() + except Exception: + pytest.fail("is_hostcall failed") + + list_of_checks = [ check_get_max_compute_units, check_get_max_work_item_dims, @@ -162,29 +253,10 @@ def device_selector(request): def check(request): return request.param - def check_is_accelerator(self, device): - try: - device.is_accelerator() - except Exception: - pytest.fail("is_accelerator call failed") - - def check_is_cpu(self, device): - try: - device.is_cpu() - except Exception: - pytest.fail("is_cpu call failed") - - def check_is_gpu(self, device): - try: - device.is_gpu() - except Exception: - pytest.fail("is_gpu call failed") - - def check_is_host(self, device): - try: - device.is_host() - except Exception: - pytest.fail("is_hostcall failed") + +@pytest.fixture(params=list_of_checks) +def check(request): + return request.param def test_standard_selectors(device_selector, check): From 4d3b106ddd5d348717e68a060dd7f11635c50468 Mon Sep 17 00:00:00 2001 From: Diptorup Deb Date: Wed, 17 Feb 2021 22:44:05 -0600 Subject: [PATCH 3/8] Add a new _sycl_device_factory and dpctl_sycl_device_manager.{h|cpp} - Add a new dpctl_sycl_device_manager module to store a cache of root devices along with a cached context per device. - The device manager includes functions to get a vector of devices and the number of root devices of a specific type and backend combination. - Move the DPCTLDevice_DumpInfo() to a new module called dpctl_sycl_device_manager, and rename it to DPCTLDeviceMgr_PrintDeviceInfo(). - Introduce a new _sycl_device_factory module in the Python API. - Move device selector functions to the _sycl_device_factory module. - Add functions to get list of SYCL devies in dpctl. --- .../include/dpctl_sycl_device_interface.h | 8 - .../include/dpctl_sycl_device_manager.h | 157 +++++++++ .../source/dpctl_sycl_device_interface.cpp | 52 +-- .../source/dpctl_sycl_device_manager.cpp | 273 ++++++++++++++++ .../source/dpctl_sycl_queue_manager.cpp | 1 - dpctl-capi/source/dpctl_vector_templ.cpp | 51 +-- dpctl-capi/tests/test_sycl_device_manager.cpp | 140 ++++++++ .../test_sycl_device_selector_interface.cpp | 11 +- dpctl-capi/tests/test_sycl_queue_manager.cpp | 4 +- dpctl/__init__.pxd | 1 + dpctl/__init__.py | 10 + dpctl/_backend.pxd | 16 +- dpctl/_sycl_device.pxd | 8 - dpctl/_sycl_device.pyx | 300 +----------------- dpctl/_sycl_device_factory.pxd | 31 ++ dpctl/_sycl_device_factory.pyx | 274 ++++++++++++++++ dpctl/enum_types.py | 13 +- dpctl/tests/test_sycl_device.py | 96 ------ dpctl/tests/test_sycl_device_factory.py | 184 +++++++++++ setup.py | 7 + 20 files changed, 1149 insertions(+), 488 deletions(-) create mode 100644 dpctl-capi/include/dpctl_sycl_device_manager.h create mode 100644 dpctl-capi/source/dpctl_sycl_device_manager.cpp create mode 100644 dpctl-capi/tests/test_sycl_device_manager.cpp create mode 100644 dpctl/_sycl_device_factory.pxd create mode 100644 dpctl/_sycl_device_factory.pyx create mode 100644 dpctl/tests/test_sycl_device_factory.py diff --git a/dpctl-capi/include/dpctl_sycl_device_interface.h b/dpctl-capi/include/dpctl_sycl_device_interface.h index f5a55507aa..99e9f7a2c4 100644 --- a/dpctl-capi/include/dpctl_sycl_device_interface.h +++ b/dpctl-capi/include/dpctl_sycl_device_interface.h @@ -69,14 +69,6 @@ DPCTL_API __dpctl_give DPCTLSyclDeviceRef DPCTLDevice_CreateFromSelector( __dpctl_keep const DPCTLSyclDeviceSelectorRef DSRef); -/*! - * @brief Prints out some of the info::deivice attributes for the device. - * - * @param DRef A DPCTLSyclDeviceRef pointer. - */ -DPCTL_API -void DPCTLDevice_DumpInfo(__dpctl_keep const DPCTLSyclDeviceRef DRef); - /*! * @brief Deletes a DPCTLSyclDeviceRef pointer after casting to to sycl::device. * diff --git a/dpctl-capi/include/dpctl_sycl_device_manager.h b/dpctl-capi/include/dpctl_sycl_device_manager.h new file mode 100644 index 0000000000..f4425af054 --- /dev/null +++ b/dpctl-capi/include/dpctl_sycl_device_manager.h @@ -0,0 +1,157 @@ +//===-- dpctl_sycl_device_manager.h - A manager for sycl devices -*-C++-*- ===// +// +// Data Parallel Control (dpCtl) +// +// Copyright 2020-2021 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file declares a set of helper functions to query about the available +/// SYCL devices and backends on the system. +/// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "Support/DllExport.h" +#include "Support/ExternC.h" +#include "Support/MemOwnershipAttrs.h" +#include "dpctl_data_types.h" +#include "dpctl_sycl_types.h" +#include "dpctl_vector.h" + +DPCTL_C_EXTERN_C_BEGIN + +/** + * @defgroup DeviceManager Device management helper functions + */ + +/*! + * @brief Contains a #DPCTLSyclDeviceRef and #DPCTLSyclContextRef 2-tuple that + * contains a sycl::device and a sycl::context associated with that device. + */ +typedef struct DeviceAndContextPair +{ + DPCTLSyclDeviceRef DRef; + DPCTLSyclContextRef CRef; +} DPCTL_DeviceAndContextPair; + +// Declares a set of types abd functions to deal with vectors of +// DPCTLSyclDeviceRef. Refer dpctl_vector_macros.h +DPCTL_DECLARE_VECTOR(Device) + +/*! + * @brief Checks if two ::DPCTLSyclDeviceRef objects point to the same + * sycl::device. + * + * DPC++ 2021.1.2 has some bugs that prevent the equality of sycl::device + * objects to work correctly. The DPCTLDeviceMgr_AreEq implements a workaround + * to check if two sycl::device pointers are equivalent. Since, DPC++ uses + * std::shared_pointer wrappers for sycl::device objects we check if the raw + * pointer (shared_pointer.get()) for each device are the same. One caveat is + * that the trick works only for non-host devices. The function evaluates host + * devices separately and always assumes that all host devices are equivalent, + * while checking for the raw pointer equivalent for all other types of devices. + * The workaround will be removed once DPC++ is fixed to correctly check device + * equivalence. + * + * @param DRef1 First opaque pointer to a sycl device. + * @param DRef2 Second opaque pointer to a sycl device. + * @return True if the underlying sycl::device are same, false otherwise. + * @ingroup DeviceManager + */ +bool DPCTLDeviceMgr_AreEq(__dpctl_keep const DPCTLSyclDeviceRef DRef1, + __dpctl_keep const DPCTLSyclDeviceRef DRef2); + +/*! + * @brief Returns a pointer to a std::vector + * containing the set of ::DPCTLSyclDeviceRef pointers matching the passed in + * device_identifier bit flag. + * + * The device_identifier can be a combination of #DPCTLSyclBackendType and + * #DPCTLSyclDeviceType bit flags. The function returns all devices that + * match the specified bit flags. For example, + * + * @code + * // Returns all opencl devices + * DPCTLDeviceMgr_GetDevices(DPCTLSyclBackendType::DPCTL_OPENCL); + * + * // Returns all opencl gpu devices + * DPCTLDeviceMgr_GetDevices( + * DPCTLSyclBackendType::DPCTL_OPENCL|DPCTLSyclDeviceType::DPCTL_GPU); + * + * // Returns all gpu devices + * DPCTLDeviceMgr_GetDevices(DPCTLSyclDeviceType::DPCTL_GPU); + * @endcode + * + * @param device_identifier A bitflag that can be any combination of + * #DPCTLSyclBackendType and #DPCTLSyclDeviceType + * enum values. + * @return A #DPCTLDeviceVectorRef containing #DPCTLSyclDeviceRef objects + * that match the device identifier bit flags. + * @ingroup DeviceManager + */ +DPCTL_API +__dpctl_give DPCTLDeviceVectorRef +DPCTLDeviceMgr_GetDevices(int device_identifier); + +/*! + * @brief Returns the default sycl context inside an opaque DPCTLSyclContextRef + * pointer for the DPCTLSyclDeviceRef input argument. + * + * @param DRef A pointer to a sycl::device that will be used to + * search an internal map containing a cached "default" + * sycl::context for the device. + * @return A #DPCTL_DeviceAndContextPair struct containing the cached + * #DPCTLSyclContextRef associated with the #DPCTLSyclDeviceRef argument passed + * to the function. The DPCTL_DeviceAndContextPair also contains a + * #DPCTLSyclDeviceRef pointer pointing to the same device as the input + * #DPCTLSyclDeviceRef. The returned #DPCTLSyclDeviceRef was cached along with + * the #DPCTLSyclContextRef. This is a workaround till device equality is + * properly fixed in DPC++. If the #DPCTLSyclDeviceRef is not found in the cache + * then DPCTL_DeviceAndContextPair contains a pair of nullptr. + * @ingroup DeviceManager + */ +DPCTL_API +DPCTL_DeviceAndContextPair DPCTLDeviceMgr_GetDeviceAndContextPair( + __dpctl_keep const DPCTLSyclDeviceRef DRef); + +/*! + * @brief Get the number of available devices for given backend and device type + * combination. + * + * @param device_identifier Identifies a device using a combination of + * #DPCTLSyclBackendType and #DPCTLSyclDeviceType + * enum values. The argument can be either one of + * the enum values or a bitwise OR-ed combination. + * @return The number of available devices satisfying the condition specified + * by the device_identifier bit flag. + * @ingroup DeviceManager + */ +DPCTL_API +size_t DPCTLDeviceMgr_GetNumDevices(int device_identifier); + +/*! + * @brief Prints out the info::deivice attributes for the device that are + * currently supported by dpCtl. + * + * @param DRef A #DPCTLSyclDeviceRef opaque pointer. + * @ingroup DeviceManager + */ +DPCTL_API +void DPCTLDeviceMgr_PrintDeviceInfo(__dpctl_keep const DPCTLSyclDeviceRef DRef); + +DPCTL_C_EXTERN_C_END diff --git a/dpctl-capi/source/dpctl_sycl_device_interface.cpp b/dpctl-capi/source/dpctl_sycl_device_interface.cpp index 28b033114c..0cbf77f1b4 100644 --- a/dpctl-capi/source/dpctl_sycl_device_interface.cpp +++ b/dpctl-capi/source/dpctl_sycl_device_interface.cpp @@ -27,10 +27,9 @@ #include "dpctl_sycl_device_interface.h" #include "../helper/include/dpctl_utils_helper.h" #include "Support/CBindingWrapping.h" +#include "dpctl_sycl_device_manager.h" #include /* SYCL headers */ #include -#include -#include using namespace cl::sycl; @@ -41,31 +40,6 @@ DEFINE_SIMPLE_CONVERSION_FUNCTIONS(device, DPCTLSyclDeviceRef) DEFINE_SIMPLE_CONVERSION_FUNCTIONS(device_selector, DPCTLSyclDeviceSelectorRef) DEFINE_SIMPLE_CONVERSION_FUNCTIONS(platform, DPCTLSyclPlatformRef) -/*! - * @brief Helper function to print the metadata for a sycl::device. - * - * @param Device My Param doc - */ -void dump_device_info(const device &Device) -{ - std::stringstream ss; - - ss << std::setw(4) << " " << std::left << std::setw(16) << "Name" - << Device.get_info() << '\n'; - ss << std::setw(4) << " " << std::left << std::setw(16) << "Driver version" - << Device.get_info() << '\n'; - ss << std::setw(4) << " " << std::left << std::setw(16) << "Vendor" - << Device.get_info() << '\n'; - ss << std::setw(4) << " " << std::left << std::setw(16) << "Profile" - << Device.get_info() << '\n'; - ss << std::setw(4) << " " << std::left << std::setw(16) << "Device type"; - - auto devTy = Device.get_info(); - ss << DPCTL_DeviceTypeToStr(devTy); - - std::cout << ss.str(); -} - } /* end of anonymous namespace */ __dpctl_give DPCTLSyclDeviceRef @@ -123,18 +97,6 @@ __dpctl_give DPCTLSyclDeviceRef DPCTLDevice_CreateFromSelector( } } -/*! - * Prints some of the device info metadata for the device corresponding to the - * specified sycl::queue. Currently, device name, driver version, device - * vendor, and device profile are printed out. More attributed may be added - * later. - */ -void DPCTLDevice_DumpInfo(__dpctl_keep const DPCTLSyclDeviceRef DRef) -{ - auto Device = unwrap(DRef); - dump_device_info(*Device); -} - void DPCTLDevice_Delete(__dpctl_take DPCTLSyclDeviceRef DRef) { delete unwrap(DRef); @@ -432,11 +394,11 @@ bool DPCTLDevice_IsHostUnifiedMemory(__dpctl_keep const DPCTLSyclDeviceRef DRef) return ret; } -bool DPCTLDevice_AreEq(__dpctl_keep const DPCTLSyclDeviceRef DevRef1, - __dpctl_keep const DPCTLSyclDeviceRef DevRef2) +bool DPCTLDevice_AreEq(__dpctl_keep const DPCTLSyclDeviceRef DRef1, + __dpctl_keep const DPCTLSyclDeviceRef DRef2) { - if (!(DevRef1 && DevRef2)) - // \todo handle error - return false; - return (*unwrap(DevRef1) == *unwrap(DevRef2)); + // Note: DPCPP does not yet support device equality of the form: + // *unwrap(DevRef1) == *unwrap(DevRef2). Till DPCPP is fixed we use the + // custom equality checker implemented inside DPCTLDeviceMgr. + return DPCTLDeviceMgr_AreEq(DRef1, DRef2); } diff --git a/dpctl-capi/source/dpctl_sycl_device_manager.cpp b/dpctl-capi/source/dpctl_sycl_device_manager.cpp new file mode 100644 index 0000000000..5c9fbe9ea5 --- /dev/null +++ b/dpctl-capi/source/dpctl_sycl_device_manager.cpp @@ -0,0 +1,273 @@ +//===-------- dpctl_sycl_device_manager.cpp - helpers for sycl devices ------=// +// +// Data Parallel Control (dpCtl) +// +// Copyright 2020-2021 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements the functions declared in dpctl_sycl_device_manager.h. +/// +//===----------------------------------------------------------------------===// + +#include "dpctl_sycl_device_manager.h" +#include "../helper/include/dpctl_utils_helper.h" +#include "Support/CBindingWrapping.h" +#include "dpctl_sycl_enum_types.h" +#include /* SYCL headers */ +#include +#include +#include + +using namespace cl::sycl; + +namespace +{ + +// Create wrappers for C Binding types (see CBindingWrapping.h). +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(device, DPCTLSyclDeviceRef) +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(context, DPCTLSyclContextRef) + +/* Checks if two devices are equal based on the underlying native pointer. + */ +bool deviceEqChecker(const device &D1, const device &D2) +{ + if (D1.is_host() && D2.is_host()) { + return true; + } + else if ((D1.is_host() && !D2.is_host()) || (D2.is_host() && !D1.is_host())) + { + return false; + } + else { + return D1.get() == D2.get(); + } +} + +/* + * Helper function to print the metadata for a sycl::device. + */ +void print_device_info(const device &Device) +{ + std::stringstream ss; + + ss << std::setw(4) << " " << std::left << std::setw(16) << "Name" + << Device.get_info() << '\n'; + ss << std::setw(4) << " " << std::left << std::setw(16) << "Driver version" + << Device.get_info() << '\n'; + ss << std::setw(4) << " " << std::left << std::setw(16) << "Vendor" + << Device.get_info() << '\n'; + ss << std::setw(4) << " " << std::left << std::setw(16) << "Profile" + << Device.get_info() << '\n'; + ss << std::setw(4) << " " << std::left << std::setw(16) << "Device type"; + + auto devTy = Device.get_info(); + ss << DPCTL_DeviceTypeToStr(devTy); + + std::cout << ss.str(); +} + +/* + * Helper class to store DPCTLSyclDeviceType and DPCTLSyclBackendType attributes + * for a device along with the SYCL device. + */ +struct DeviceWrapper +{ + device SyclDevice; + DPCTLSyclBackendType Bty; + DPCTLSyclDeviceType Dty; + + DeviceWrapper(const device &Device) + : SyclDevice(Device), Bty(DPCTL_SyclBackendToDPCTLBackendType( + Device.get_platform().get_backend())), + Dty(DPCTL_SyclDeviceTypeToDPCTLDeviceType( + Device.get_info())) + { + } + + // The constructor is provided for convenience, so that we do not have to + // lookup the BackendType and DeviceType if not needed. + DeviceWrapper(const device &Device, + DPCTLSyclBackendType Bty, + DPCTLSyclDeviceType Dty) + : SyclDevice(Device), Bty(Bty), Dty(Dty) + { + } +}; + +auto getHash(const device &d) +{ + if (d.is_host()) { + return std::hash{}(-1); + } + else { + return std::hash{}(d.get()); + } +} + +struct DeviceHasher +{ + size_t operator()(const DeviceWrapper &d) const + { + return getHash(d.SyclDevice); + } +}; + +struct DeviceEqPred +{ + bool operator()(const DeviceWrapper &d1, const DeviceWrapper &d2) const + { + return deviceEqChecker(d1.SyclDevice, d2.SyclDevice); + } +}; + +struct DeviceCacheBuilder +{ + using DeviceCache = + std::unordered_map; + /* This function implements a workaround to the current lack of a default + * context per root device in DPC++. The map stores a "default" context for + * each root device, and the QMgrHelper uses the map whenever it creates a + * new queue for a root device. By doing so, we avoid the performance + * overhead of context creation for every queue. + * + * The singleton pattern implemented here ensures that the map is created + * once in a thread-safe manner. Since, the map is ony read post-creation we + * do not need any further protection to ensure thread-safety. + */ + static const DeviceCache &getDeviceCache() + { + static DeviceCache *cache = new DeviceCache([] { + DeviceCache cache_l; + default_selector mRanker; + auto Platforms = platform::get_platforms(); + for (const auto &P : Platforms) { + auto Devices = P.get_devices(); + for (const auto &D : Devices) { + if (mRanker(D) < 0) + continue; + auto entry = cache_l.emplace(D, D); + if (!entry.second) { + std::cerr << "Fatal Error during device cache " + "construction.\n"; + std::terminate(); + } + } + } + return cache_l; + }()); + + return *cache; + } +}; + +} // namespace + +#undef EL +#define EL Device +#include "dpctl_vector_templ.cpp" +#undef EL + +bool DPCTLDeviceMgr_AreEq(__dpctl_keep const DPCTLSyclDeviceRef DRef1, + __dpctl_keep const DPCTLSyclDeviceRef DRef2) +{ + auto D1 = unwrap(DRef1); + auto D2 = unwrap(DRef2); + if (D1 && D2) + return deviceEqChecker(*D1, *D2); + else + return false; +} + +DPCTL_DeviceAndContextPair DPCTLDeviceMgr_GetDeviceAndContextPair( + __dpctl_keep const DPCTLSyclDeviceRef DRef) +{ + DPCTL_DeviceAndContextPair rPair{nullptr, nullptr}; + auto Device = unwrap(DRef); + if (!Device) { + return rPair; + } + DeviceWrapper DWrapper{*Device, DPCTLSyclBackendType::DPCTL_UNKNOWN_BACKEND, + DPCTLSyclDeviceType::DPCTL_UNKNOWN_DEVICE}; + auto &cache = DeviceCacheBuilder::getDeviceCache(); + auto entry = cache.find(DWrapper); + if (entry != cache.end()) { + try { + rPair.DRef = wrap(new device(entry->first.SyclDevice)); + rPair.CRef = wrap(new context(entry->second)); + } catch (std::bad_alloc const &ba) { + std::cerr << ba.what() << std::endl; + rPair.DRef = nullptr; + rPair.CRef = nullptr; + } + } + return rPair; +} + +__dpctl_give DPCTLDeviceVectorRef +DPCTLDeviceMgr_GetDevices(int device_identifier) +{ + vector_class *Devices = nullptr; + + try { + Devices = new vector_class(); + } catch (std::bad_alloc const &ba) { + return nullptr; + } + auto &cache = DeviceCacheBuilder::getDeviceCache(); + Devices->reserve(cache.size()); + for (const auto &entry : cache) { + if ((device_identifier & entry.first.Bty) && + (device_identifier & entry.first.Dty)) + { + Devices->emplace_back(wrap(new device(entry.first.SyclDevice))); + } + } + // the wrap function is defined inside dpctl_vector_templ.cpp + return wrap(Devices); +} + +/*! + * Returns the number of available devices for a specific backend and device + * type combination. + */ +size_t DPCTLDeviceMgr_GetNumDevices(int device_identifier) +{ + size_t nDevices = 0; + auto &cache = DeviceCacheBuilder::getDeviceCache(); + for (const auto &entry : cache) + if ((device_identifier & entry.first.Bty) && + (device_identifier & entry.first.Dty)) + ++nDevices; + + return nDevices; +} + +/*! + * Prints some of the device info metadata for the device corresponding to the + * specified sycl::queue. Currently, device name, driver version, device + * vendor, and device profile are printed out. More attributed may be added + * later. + */ +void DPCTLDeviceMgr_PrintDeviceInfo(__dpctl_keep const DPCTLSyclDeviceRef DRef) +{ + auto Device = unwrap(DRef); + if (Device) + print_device_info(*Device); + else { + std::cout << "Device is not valid (NULL). Cannot print device info.\n"; + } +} diff --git a/dpctl-capi/source/dpctl_sycl_queue_manager.cpp b/dpctl-capi/source/dpctl_sycl_queue_manager.cpp index 408cb8094c..1c63a3eeac 100644 --- a/dpctl-capi/source/dpctl_sycl_queue_manager.cpp +++ b/dpctl-capi/source/dpctl_sycl_queue_manager.cpp @@ -25,7 +25,6 @@ //===----------------------------------------------------------------------===// #include "dpctl_sycl_queue_manager.h" #include "Support/CBindingWrapping.h" -#include "dpctl_sycl_enum_types.h" #include /* SYCL headers */ #include #include diff --git a/dpctl-capi/source/dpctl_vector_templ.cpp b/dpctl-capi/source/dpctl_vector_templ.cpp index 1a83769871..da060d8a6e 100644 --- a/dpctl-capi/source/dpctl_vector_templ.cpp +++ b/dpctl-capi/source/dpctl_vector_templ.cpp @@ -55,10 +55,11 @@ __dpctl_give VECTOR(EL) FN(EL, Create)() void FN(EL, Delete)(__dpctl_take VECTOR(EL) VRef) { auto Vec = unwrap(VRef); - - for (auto i = 0ul; i < Vec->size(); ++i) { - auto D = unwrap((*Vec)[i]); - delete D; + if (Vec) { + for (auto i = 0ul; i < Vec->size(); ++i) { + auto D = unwrap((*Vec)[i]); + delete D; + } } delete Vec; } @@ -70,12 +71,13 @@ void FN(EL, Delete)(__dpctl_take VECTOR(EL) VRef) void FN(EL, Clear)(__dpctl_keep VECTOR(EL) VRef) { auto Vec = unwrap(VRef); - - for (auto i = 0ul; i < Vec->size(); ++i) { - auto D = unwrap((*Vec)[i]); - delete D; + if (Vec) { + for (auto i = 0ul; i < Vec->size(); ++i) { + auto D = unwrap((*Vec)[i]); + delete D; + } + Vec->clear(); } - Vec->clear(); } /*! @@ -84,7 +86,11 @@ void FN(EL, Clear)(__dpctl_keep VECTOR(EL) VRef) */ size_t FN(EL, Size)(__dpctl_keep VECTOR(EL) VRef) { - return unwrap(VRef)->size(); + auto V = unwrap(VRef); + if (V) + return V->size(); + else + return 0; } /*! @@ -95,18 +101,19 @@ size_t FN(EL, Size)(__dpctl_keep VECTOR(EL) VRef) SYCLREF(EL) FN(EL, GetAt)(__dpctl_keep VECTOR(EL) VRef, size_t index) { auto Vec = unwrap(VRef); - SYCLREF(EL) ret, copy = nullptr; - try { - ret = Vec->at(index); - auto Ref = unwrap(ret); - copy = wrap(new std::remove_pointer::type(*Ref)); - } catch (std::out_of_range const &oor) { - std::cerr << oor.what() << '\n'; - } catch (std::bad_alloc const &ba) { - // \todo log error - std::cerr << ba.what() << '\n'; - return nullptr; + SYCLREF(EL) copy = nullptr; + if (Vec) { + try { + auto ret = Vec->at(index); + auto Ref = unwrap(ret); + copy = wrap(new std::remove_pointer::type(*Ref)); + } catch (std::out_of_range const &oor) { + std::cerr << oor.what() << '\n'; + } catch (std::bad_alloc const &ba) { + // \todo log error + std::cerr << ba.what() << '\n'; + return nullptr; + } } - return copy; } diff --git a/dpctl-capi/tests/test_sycl_device_manager.cpp b/dpctl-capi/tests/test_sycl_device_manager.cpp new file mode 100644 index 0000000000..912e74aed8 --- /dev/null +++ b/dpctl-capi/tests/test_sycl_device_manager.cpp @@ -0,0 +1,140 @@ +//===------- test_sycl_device_manager.cpp - Test cases for device manager ===// +// +// Data Parallel Control (dpCtl) +// +// Copyright 2020-2021 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file has unit test cases for functions defined in +/// dpctl_sycl_device_manager.h. +/// +//===----------------------------------------------------------------------===// + +#include "dpctl_sycl_device_interface.h" +#include "dpctl_sycl_device_manager.h" +#include "dpctl_sycl_device_selector_interface.h" +#include + +struct TestDPCTLDeviceManager : public ::testing::TestWithParam +{ + DPCTLSyclDeviceSelectorRef DSRef = nullptr; + DPCTLSyclDeviceRef DRef = nullptr; + + TestDPCTLDeviceManager() + { + EXPECT_NO_FATAL_FAILURE(DSRef = DPCTLFilterSelector_Create(GetParam())); + EXPECT_NO_FATAL_FAILURE(DRef = DPCTLDevice_CreateFromSelector(DSRef)); + } + + void SetUp() + { + if (!DRef) { + auto message = "Skipping as no device of type " + + std::string(GetParam()) + "."; + GTEST_SKIP_(message.c_str()); + } + } + + ~TestDPCTLDeviceManager() + { + EXPECT_NO_FATAL_FAILURE(DPCTLDeviceSelector_Delete(DSRef)); + EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(DRef)); + } +}; + +TEST_P(TestDPCTLDeviceManager, Chk_AreEq) +{ + auto DRef2 = DPCTLDevice_CreateFromSelector(DSRef); + bool compare = false; + EXPECT_NO_FATAL_FAILURE(compare = DPCTLDeviceMgr_AreEq(DRef, DRef2)); + EXPECT_TRUE(compare); +} + +TEST_P(TestDPCTLDeviceManager, Chk_PrintDeviceInfo) +{ + EXPECT_NO_FATAL_FAILURE(DPCTLDeviceMgr_PrintDeviceInfo(DRef)); +} + +TEST_P(TestDPCTLDeviceManager, Chk_GetDeviceAndContextPair) +{ + DPCTL_DeviceAndContextPair deviceAndContext; + EXPECT_NO_FATAL_FAILURE(deviceAndContext = + DPCTLDeviceMgr_GetDeviceAndContextPair(DRef)); + ASSERT_TRUE(deviceAndContext.CRef != nullptr); + ASSERT_TRUE(deviceAndContext.DRef != nullptr); +} + +INSTANTIATE_TEST_SUITE_P(DeviceMgrFunctions, + TestDPCTLDeviceManager, + ::testing::Values("opencl:gpu:0", + "opencl:cpu:0", + "level_zero:gpu:0")); + +struct TestDPCTLDeviceVector : public ::testing::TestWithParam +{ + DPCTLDeviceVectorRef DV = nullptr; + size_t nDevices = 0; + + TestDPCTLDeviceVector() + { + EXPECT_NO_FATAL_FAILURE(DV = DPCTLDeviceMgr_GetDevices(GetParam())); + EXPECT_TRUE(DV != nullptr); + EXPECT_NO_FATAL_FAILURE(nDevices = DPCTLDeviceVector_Size(DV)); + } + + void SetUp() + { + if (!nDevices) { + GTEST_SKIP_("Skipping as no devices returned for identifier"); + } + } + + ~TestDPCTLDeviceVector() + { + EXPECT_NO_FATAL_FAILURE(DPCTLDeviceVector_Clear(DV)); + EXPECT_NO_FATAL_FAILURE(DPCTLDeviceVector_Delete(DV)); + } +}; + +TEST_P(TestDPCTLDeviceVector, Chk_GetAt) +{ + for (auto i = 0ul; i < nDevices; ++i) { + DPCTLSyclDeviceRef DRef = nullptr; + EXPECT_NO_FATAL_FAILURE(DRef = DPCTLDeviceVector_GetAt(DV, i)); + ASSERT_TRUE(DRef != nullptr); + } +} + +INSTANTIATE_TEST_SUITE_P( + GetDevices, + TestDPCTLDeviceVector, + ::testing::Values(DPCTLSyclBackendType::DPCTL_HOST, + DPCTLSyclBackendType::DPCTL_LEVEL_ZERO, + DPCTLSyclBackendType::DPCTL_OPENCL, + DPCTLSyclBackendType::DPCTL_OPENCL | + DPCTLSyclDeviceType::DPCTL_GPU)); + +TEST(TestDPCTLDeviceVector, Chk_DPCTLDeviceVector_Create) +{ + DPCTLDeviceVectorRef DVRef = nullptr; + size_t nDevices = 0; + EXPECT_NO_FATAL_FAILURE(DVRef = DPCTLDeviceVector_Create()); + ASSERT_TRUE(DVRef != nullptr); + EXPECT_NO_FATAL_FAILURE(nDevices = DPCTLDeviceVector_Size(DVRef)); + EXPECT_TRUE(nDevices == 0); + EXPECT_NO_FATAL_FAILURE(DPCTLDeviceVector_Delete(DVRef)); +} diff --git a/dpctl-capi/tests/test_sycl_device_selector_interface.cpp b/dpctl-capi/tests/test_sycl_device_selector_interface.cpp index 4dc83ce473..2225c4378a 100644 --- a/dpctl-capi/tests/test_sycl_device_selector_interface.cpp +++ b/dpctl-capi/tests/test_sycl_device_selector_interface.cpp @@ -26,6 +26,7 @@ #include "Support/CBindingWrapping.h" #include "dpctl_sycl_device_interface.h" +#include "dpctl_sycl_device_manager.h" #include "dpctl_sycl_device_selector_interface.h" #include #include @@ -98,7 +99,7 @@ TEST_F(TestDeviceSelectorInterface, Chk_DPCTLAcceleratorSelector_Create) DPCTLSyclDeviceRef DRef = nullptr; EXPECT_NO_FATAL_FAILURE(DRef = DPCTLDevice_CreateFromSelector(DSRef)); ASSERT_TRUE(DRef != nullptr); - EXPECT_NO_FATAL_FAILURE(DPCTLDevice_DumpInfo(DRef)); + EXPECT_NO_FATAL_FAILURE(DPCTLDeviceMgr_PrintDeviceInfo(DRef)); EXPECT_TRUE(DPCTLDevice_IsAccelerator(DRef)); EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(DRef)); } @@ -113,7 +114,7 @@ TEST_F(TestDeviceSelectorInterface, Chk_DPCTLDefaultSelector_Create) DPCTLSyclDeviceRef DRef = nullptr; EXPECT_NO_FATAL_FAILURE(DRef = DPCTLDevice_CreateFromSelector(DSRef)); ASSERT_TRUE(DRef != nullptr); - EXPECT_NO_FATAL_FAILURE(DPCTLDevice_DumpInfo(DRef)); + EXPECT_NO_FATAL_FAILURE(DPCTLDeviceMgr_PrintDeviceInfo(DRef)); EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(DRef)); } EXPECT_NO_FATAL_FAILURE(DPCTLDeviceSelector_Delete(DSRef)); @@ -127,7 +128,7 @@ TEST_F(TestDeviceSelectorInterface, Chk_DPCTLCPUSelector_Create) DPCTLSyclDeviceRef DRef = nullptr; EXPECT_NO_FATAL_FAILURE(DRef = DPCTLDevice_CreateFromSelector(DSRef)); ASSERT_TRUE(DRef != nullptr); - EXPECT_NO_FATAL_FAILURE(DPCTLDevice_DumpInfo(DRef)); + EXPECT_NO_FATAL_FAILURE(DPCTLDeviceMgr_PrintDeviceInfo(DRef)); EXPECT_TRUE(DPCTLDevice_IsCPU(DRef)); EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(DRef)); } @@ -142,7 +143,7 @@ TEST_F(TestDeviceSelectorInterface, Chk_DPCTLGPUSelector_Create) DPCTLSyclDeviceRef DRef = nullptr; EXPECT_NO_FATAL_FAILURE(DRef = DPCTLDevice_CreateFromSelector(DSRef)); ASSERT_TRUE(DRef != nullptr); - EXPECT_NO_FATAL_FAILURE(DPCTLDevice_DumpInfo(DRef)); + EXPECT_NO_FATAL_FAILURE(DPCTLDeviceMgr_PrintDeviceInfo(DRef)); EXPECT_TRUE(DPCTLDevice_IsGPU(DRef)); EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(DRef)); } @@ -157,7 +158,7 @@ TEST_F(TestDeviceSelectorInterface, Chk_DPCTLHostSelector_Create) DPCTLSyclDeviceRef DRef = nullptr; EXPECT_NO_FATAL_FAILURE(DRef = DPCTLDevice_CreateFromSelector(DSRef)); ASSERT_TRUE(DRef != nullptr); - EXPECT_NO_FATAL_FAILURE(DPCTLDevice_DumpInfo(DRef)); + EXPECT_NO_FATAL_FAILURE(DPCTLDeviceMgr_PrintDeviceInfo(DRef)); // FIXME: DPCPP's host_selector returns a CPU device for some reason. // EXPECT_TRUE(DPCTLDevice_IsHost(DRef)); EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(DRef)); diff --git a/dpctl-capi/tests/test_sycl_queue_manager.cpp b/dpctl-capi/tests/test_sycl_queue_manager.cpp index 5dc56796e8..a5eb406b62 100644 --- a/dpctl-capi/tests/test_sycl_queue_manager.cpp +++ b/dpctl-capi/tests/test_sycl_queue_manager.cpp @@ -25,6 +25,7 @@ //===----------------------------------------------------------------------===// #include "dpctl_sycl_context_interface.h" #include "dpctl_sycl_device_interface.h" +#include "dpctl_sycl_device_manager.h" #include "dpctl_sycl_queue_interface.h" #include "dpctl_sycl_queue_manager.h" #include @@ -203,7 +204,8 @@ TEST_F(TestDPCTLSyclQueueManager, CheckDPCTLDumpDeviceInfo) if (!has_devices()) GTEST_SKIP_("Skipping: No Sycl devices.\n"); auto q = DPCTLQueueMgr_GetCurrentQueue(); - EXPECT_NO_FATAL_FAILURE(DPCTLDevice_DumpInfo(DPCTLQueue_GetDevice(q))); + EXPECT_NO_FATAL_FAILURE( + DPCTLDeviceMgr_PrintDeviceInfo(DPCTLQueue_GetDevice(q))); EXPECT_NO_FATAL_FAILURE(DPCTLQueue_Delete(q)); } diff --git a/dpctl/__init__.pxd b/dpctl/__init__.pxd index 8cb1203423..d6a443f135 100644 --- a/dpctl/__init__.pxd +++ b/dpctl/__init__.pxd @@ -23,6 +23,7 @@ from dpctl._sycl_context cimport * from dpctl._sycl_device cimport * +from dpctl._sycl_device_factory cimport * from dpctl._sycl_event cimport * from dpctl._sycl_queue cimport * from dpctl._sycl_queue_manager cimport * diff --git a/dpctl/__init__.py b/dpctl/__init__.py index e10b437e44..90dcf7ccf0 100644 --- a/dpctl/__init__.py +++ b/dpctl/__init__.py @@ -38,6 +38,8 @@ from dpctl._sycl_context import __all__ as _sycl_context__all__ from dpctl._sycl_device import * from dpctl._sycl_device import __all__ as _sycl_device__all__ +from dpctl._sycl_device_factory import * +from dpctl._sycl_device_factory import __all__ as _sycl_device_factory__all__ from dpctl._sycl_event import * from dpctl._sycl_event import __all__ as _sycl_event__all__ from dpctl._sycl_queue import * @@ -50,6 +52,7 @@ __all__ = ( _sycl_context__all__ + _sycl_device__all__ + + _sycl_device_factory__all__ + _sycl_event__all__ + _sycl_queue__all__ + _sycl_qm__all__ @@ -71,3 +74,10 @@ def get_include(): __version__ = get_versions()["version"] del get_versions +del _sycl_context__all__ +del _sycl_device__all__ +del _sycl_device_factory__all__ +del _sycl_event__all__ +del _sycl_queue__all__ +del _sycl_qm__all__ +del _enum_types_all__ diff --git a/dpctl/_backend.pxd b/dpctl/_backend.pxd index 4e28a9968b..ef1ae5a973 100644 --- a/dpctl/_backend.pxd +++ b/dpctl/_backend.pxd @@ -103,7 +103,6 @@ cdef extern from "dpctl_sycl_device_interface.h": cdef DPCTLSyclDeviceRef DPCTLDevice_Create() cdef DPCTLSyclDeviceRef DPCTLDevice_CreateFromSelector( const DPCTLSyclDeviceSelectorRef DSRef) - cdef void DPCTLDevice_DumpInfo(const DPCTLSyclDeviceRef DRef) cdef void DPCTLDevice_Delete(DPCTLSyclDeviceRef DRef) cdef DPCTLSyclBackendType DPCTLDevice_GetBackend( const DPCTLSyclDeviceRef DRef) @@ -128,6 +127,21 @@ cdef extern from "dpctl_sycl_device_interface.h": cdef bool DPCTLDevice_IsHostUnifiedMemory(const DPCTLSyclDeviceRef DRef) +cdef extern from "dpctl_sycl_device_manager.h": + cdef struct DPCTLDeviceVector + ctypedef DPCTLDeviceVector *DPCTLDeviceVectorRef + + cdef void DPCTLDeviceVector_Delete(DPCTLDeviceVectorRef DVRef) + cdef void DPCTLDeviceVector_Clear(DPCTLDeviceVectorRef DVRef) + cdef size_t DPCTLDeviceVector_Size(DPCTLDeviceVectorRef DVRef) + cdef DPCTLSyclDeviceRef DPCTLDeviceVector_GetAt( + DPCTLDeviceVectorRef DVRef, + size_t index) + cdef DPCTLDeviceVectorRef DPCTLDeviceMgr_GetDevices(int device_identifier) + cdef size_t DPCTLDeviceMgr_GetNumDevices(int device_identifier) + cdef void DPCTLDeviceMgr_PrintDeviceInfo(const DPCTLSyclDeviceRef DRef) + + cdef extern from "dpctl_sycl_device_selector_interface.h": DPCTLSyclDeviceSelectorRef DPCTLAcceleratorSelector_Create() DPCTLSyclDeviceSelectorRef DPCTLDefaultSelector_Create() diff --git a/dpctl/_sycl_device.pxd b/dpctl/_sycl_device.pxd index a6efc424ca..79267e3c22 100644 --- a/dpctl/_sycl_device.pxd +++ b/dpctl/_sycl_device.pxd @@ -46,8 +46,6 @@ cdef class _SyclDevice: cdef uint32_t _max_num_sub_groups cdef bool _int64_base_atomics cdef bool _int64_extended_atomics - - cdef DPCTLSyclDeviceRef get_device_ref(self) cpdef get_backend(self) cpdef get_device_name(self) @@ -75,9 +73,3 @@ cdef class SyclDevice(_SyclDevice): cdef void _init_from__SyclDevice(self, _SyclDevice other) cdef int _init_from_selector(self, DPCTLSyclDeviceSelectorRef DSRef) - -cpdef select_accelerator_device() -cpdef select_cpu_device() -cpdef select_default_device() -cpdef select_gpu_device() -cpdef select_host_device() diff --git a/dpctl/_sycl_device.pyx b/dpctl/_sycl_device.pyx index 84eedf8d35..9ad3247bed 100644 --- a/dpctl/_sycl_device.pyx +++ b/dpctl/_sycl_device.pyx @@ -23,14 +23,11 @@ from ._backend cimport ( _backend_type, _device_type, - DPCTLAcceleratorSelector_Create, - DPCTLCPUSelector_Create, DPCTLDefaultSelector_Create, DPCTLCString_Delete, DPCTLDevice_Copy, DPCTLDevice_CreateFromSelector, DPCTLDevice_Delete, - DPCTLDevice_DumpInfo, DPCTLDevice_GetBackend, DPCTLDevice_GetDeviceType, DPCTLDevice_GetDriverInfo, @@ -47,10 +44,9 @@ from ._backend cimport ( DPCTLDevice_IsCPU, DPCTLDevice_IsGPU, DPCTLDevice_IsHost, - DPCTLDeviceSelector_Delete, + DPCTLDeviceMgr_PrintDeviceInfo, DPCTLFilterSelector_Create, - DPCTLGPUSelector_Create, - DPCTLHostSelector_Create, + DPCTLDeviceSelector_Delete, DPCTLSize_t_Array_Delete, DPCTLSyclBackendType, DPCTLSyclDeviceRef, @@ -62,11 +58,6 @@ import warnings __all__ = [ "SyclDevice", - "select_accelerator_device", - "select_cpu_device", - "select_default_device", - "select_gpu_device", - "select_host_device", ] @@ -88,13 +79,13 @@ cdef class _SyclDevice: "WARNING: dump_device_info is depracated and will be removed in " "a future release of dpctl. Use print_device_info instead." ) - DPCTLDevice_DumpInfo(self._device_ref) + DPCTLDeviceMgr_PrintDeviceInfo(self._device_ref) def print_device_info(self): """ Print information about the SYCL device. """ - DPCTLDevice_DumpInfo(self._device_ref) + DPCTLDeviceMgr_PrintDeviceInfo(self._device_ref) cpdef get_backend(self): """Returns the backend_type enum value for this device @@ -244,40 +235,6 @@ cdef class _SyclDevice: """ return self._host_device - cpdef is_accelerator(self): - """ Returns True if the SyclDevice instance is a SYCL accelerator - device. - - Returns: - bool: True if the SyclDevice is a SYCL accelerator device, - else False. - """ - return self._accelerator_device - - cpdef is_cpu(self): - """ Returns True if the SyclDevice instance is a SYCL CPU device. - - Returns: - bool: True if the SyclDevice is a SYCL CPU device, else False. - """ - return self._cpu_device - - cpdef is_gpu(self): - """ Returns True if the SyclDevice instance is a SYCL GPU device. - - Returns: - bool: True if the SyclDevice is a SYCL GPU device, else False. - """ - return self._gpu_device - - cpdef is_host(self): - """ Returns True if the SyclDevice instance is a SYCL host device. - - Returns: - bool: True if the SyclDevice is a SYCL host device, else False. - """ - return self._host_device - cdef DPCTLSyclDeviceRef get_device_ref (self): """ Returns the DPCTLSyclDeviceRef pointer for this class. """ @@ -435,252 +392,3 @@ cdef class SyclDevice(_SyclDevice): def __repr__(self): return "".format(hex(id(self))) - - - @property - def __name__(): - return "SyclDevice" - - def __repr__(self): - return "".format(hex(id(self))) - - -cdef class SyclDevice(_SyclDevice): - """ Python equivalent for cl::sycl::device class. - - There are two ways of creating a SyclDevice instance: - - - by directly passing in a filter string to the class constructor. The - filter string needs to conform to the the `DPC++ filter selector SYCL - extension `_. - - :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"): - level_zero_gpu.dump_device_info() - - - by calling one of the device selector helper functions: - - :func:`dpctl.select_accelerator_device()`, - :func:`dpctl.select_cpu_device()`, - :func:`dpctl.select_default_device()`, - :func:`dpctl.select_gpu_device()`, - :func:`dpctl.select_host_device()`. - - - :Example: - .. code-block:: python - - import dpctl - - # Create a SyclDevice of type GPU based on whatever is returned - # by the SYCL `gpu_selector` device selector class. - gpu = dpctl.select_gpu_device(): - gpu.dump_device_info() - - """ - @staticmethod - cdef void _init_helper(SyclDevice device, DPCTLSyclDeviceRef DRef): - device._device_ref = DRef - device._device_name = DPCTLDevice_GetName(DRef) - device._driver_version = DPCTLDevice_GetDriverInfo(DRef) - device._int64_base_atomics = DPCTLDevice_HasInt64BaseAtomics(DRef) - device._int64_extended_atomics = ( - DPCTLDevice_HasInt64ExtendedAtomics(DRef) - ) - device._max_compute_units = DPCTLDevice_GetMaxComputeUnits(DRef) - device._max_num_sub_groups = DPCTLDevice_GetMaxNumSubGroups(DRef) - device._max_work_group_size = DPCTLDevice_GetMaxWorkGroupSize(DRef) - device._max_work_item_dims = DPCTLDevice_GetMaxWorkItemDims(DRef) - device._max_work_item_sizes = DPCTLDevice_GetMaxWorkItemSizes(DRef) - device._vendor_name = DPCTLDevice_GetVendorName(DRef) - device._accelerator_device = DPCTLDevice_IsAccelerator(DRef) - device._cpu_device = DPCTLDevice_IsCPU(DRef) - device._gpu_device = DPCTLDevice_IsGPU(DRef) - device._host_device = DPCTLDevice_IsHost(DRef) - - @staticmethod - cdef SyclDevice _create(DPCTLSyclDeviceRef dref): - cdef SyclDevice ret = _SyclDevice.__new__(_SyclDevice) - # Initialize the attributes of the SyclDevice object - SyclDevice._init_helper(ret, dref) - return SyclDevice(ret) - - cdef void _init_from__SyclDevice(self, _SyclDevice other): - self._device_ref = DPCTLDevice_Copy(other._device_ref) - self._device_name = DPCTLDevice_GetName(self._device_ref) - self._driver_version = DPCTLDevice_GetDriverInfo(self._device_ref) - self._int64_base_atomics = other._int64_base_atomics - self._int64_extended_atomics = other._int64_extended_atomics - self._max_compute_units = other._max_compute_units - self._max_num_sub_groups = other._max_num_sub_groups - self._max_work_group_size = other._max_work_group_size - self._max_work_item_dims = other._max_work_item_dims - self._max_work_item_sizes = ( - DPCTLDevice_GetMaxWorkItemSizes(self._device_ref) - ) - self._vendor_name = DPCTLDevice_GetVendorName(self._device_ref) - self._accelerator_device = other._accelerator_device - self._cpu_device = other._cpu_device - self._gpu_device = other._gpu_device - self._host_device = other._host_device - - cdef int _init_from_selector(self, DPCTLSyclDeviceSelectorRef DSRef): - # Initialize the attributes of the SyclDevice object - DRef = DPCTLDevice_CreateFromSelector(DSRef) - if DRef is NULL: - return -1 - else: - SyclDevice._init_helper(self, DRef) - return 0 - - def __cinit__(self, arg=None): - cdef DPCTLSyclDeviceSelectorRef DSRef = NULL - cdef DPCTLSyclDeviceRef DRef = NULL - cdef const char *filter_c_str = NULL - cdef int ret = 0 - - if type(arg) is unicode: - string = bytes(arg, "utf-8") - filter_c_str = string - DSRef = DPCTLFilterSelector_Create(filter_c_str) - ret = self._init_from_selector(DSRef) - if ret == -1: - raise ValueError("Could not create a Device with the selector") - # Free up the device selector - DPCTLDeviceSelector_Delete(DSRef) - elif isinstance(arg, unicode): - string = bytes(unicode(arg), "utf-8") - filter_c_str = string - DSRef = DPCTLFilterSelector_Create(filter_c_str) - if ret == -1: - raise ValueError("Could not create a Device with the selector") - # Free up the device selector - DPCTLDeviceSelector_Delete(DSRef) - elif isinstance(arg, _SyclDevice): - self._init_from__SyclDevice(arg) - elif arg is None: - DSRef = DPCTLDefaultSelector_Create() - self._init_from_selector(DSRef) - else: - raise ValueError( - "Invalid argument. Argument should be a str object specifying " - "a SYCL filter selector string." - ) - - @property - def __name__(self): - return "SyclDevice" - - def __repr__(self): - return "".format(hex(id(self))) - - -cpdef select_accelerator_device(): - """ A wrapper for SYCL's `accelerator_selector` device_selector class. - - Returns: - A new SyclDevice object containing the SYCL device returned by the - `accelerator_selector`. - Raises: - A ValueError is raised if the SYCL `accelerator_selector` is unable to - select a device. - """ - cdef DPCTLSyclDeviceSelectorRef DSRef = DPCTLAcceleratorSelector_Create() - cdef DPCTLSyclDeviceRef DRef = DPCTLDevice_CreateFromSelector(DSRef) - # Free up the device selector - DPCTLDeviceSelector_Delete(DSRef) - if DRef is NULL: - raise ValueError("Device unavailable.") - # The _create call frees DSRef and DRef - Device = SyclDevice._create(DRef) - return Device - - -cpdef select_cpu_device(): - """ A wrapper for SYCL's `cpu_selector` device_selector class. - - Returns: - A new SyclDevice object containing the SYCL device returned by the - `cpu_selector`. - Raises: - A ValueError is raised if the SYCL `cpu_seector` is unable to select a - device. - """ - cdef DPCTLSyclDeviceSelectorRef DSRef = DPCTLCPUSelector_Create() - cdef DPCTLSyclDeviceRef DRef = DPCTLDevice_CreateFromSelector(DSRef) - # Free up the device selector - DPCTLDeviceSelector_Delete(DSRef) - if DRef is NULL: - raise ValueError("Device unavailable.") - # The _create call frees DSRef and DRef - Device = SyclDevice._create(DRef) - return Device - - -cpdef select_default_device(): - """ A wrapper for SYCL's `default_selector` device_selector class. - - Returns: - A new SyclDevice object containing the SYCL device returned by the - `default_selector`. - Raises: - A ValueError is raised if the SYCL `default_seector` is unable to - select a device. - """ - cdef DPCTLSyclDeviceSelectorRef DSRef = DPCTLDefaultSelector_Create() - cdef DPCTLSyclDeviceRef DRef = DPCTLDevice_CreateFromSelector(DSRef) - # Free up the device selector - DPCTLDeviceSelector_Delete(DSRef) - if DRef is NULL: - raise ValueError("Device unavailable.") - # The _create call frees DSRef and DRef - Device = SyclDevice._create(DRef) - return Device - - -cpdef select_gpu_device(): - """ A wrapper for SYCL's `gpu_selector` device_selector class. - - Returns: - A new SyclDevice object containing the SYCL device returned by the - `gpu_selector`. - Raises: - A ValueError is raised if the SYCL `gpu_seector` is unable to select a - device. - """ - cdef DPCTLSyclDeviceSelectorRef DSRef = DPCTLGPUSelector_Create() - cdef DPCTLSyclDeviceRef DRef = DPCTLDevice_CreateFromSelector(DSRef) - # Free up the device selector - DPCTLDeviceSelector_Delete(DSRef) - if DRef is NULL: - raise ValueError("Device unavailable.") - # The _create call frees DSRef and DRef - Device = SyclDevice._create(DRef) - return Device - - -cpdef select_host_device(): - """ A wrapper for SYCL's `host_selector` device_selector class. - - Returns: - A new SyclDevice object containing the SYCL device returned by the - `host_selector`. - Raises: - A ValueError is raised if the SYCL `host_seector` is unable to select a - device. - """ - cdef DPCTLSyclDeviceSelectorRef DSRef = DPCTLHostSelector_Create() - cdef DPCTLSyclDeviceRef DRef = DPCTLDevice_CreateFromSelector(DSRef) - # Free up the device selector - DPCTLDeviceSelector_Delete(DSRef) - if DRef is NULL: - raise ValueError("Device unavailable.") - # The _create call frees DSRef and DRef - Device = SyclDevice._create(DRef) - return Device diff --git a/dpctl/_sycl_device_factory.pxd b/dpctl/_sycl_device_factory.pxd new file mode 100644 index 0000000000..239d2b398a --- /dev/null +++ b/dpctl/_sycl_device_factory.pxd @@ -0,0 +1,31 @@ +# Data Parallel Control (dpCtl) +# +# Copyright 2020-2021 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# distutils: language = c++ +# cython: language_level=3 + +""" The file declares several helper functions to create SyclDevice objects +from SYCL standard device_selectors, to get a list of SyclDevices for a +specific backend or device_type. +""" + + +cpdef select_accelerator_device() +cpdef select_cpu_device() +cpdef select_default_device() +cpdef select_gpu_device() +cpdef select_host_device() +cpdef list get_devices(backend=*, device_type=*) diff --git a/dpctl/_sycl_device_factory.pyx b/dpctl/_sycl_device_factory.pyx new file mode 100644 index 0000000000..1b8fbc249a --- /dev/null +++ b/dpctl/_sycl_device_factory.pyx @@ -0,0 +1,274 @@ +# Data Parallel Control (dpCtl) +# +# Copyright 2020-2021 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# distutils: language = c++ +# cython: language_level=3 + +""" This module implements several device creation helper functions: + + - wrapper functions to create a SyclDevice from the standard SYCL + device selector classes. + - functions to return a list of devices based on a specified device_type or + backend_type combination. +""" + +from ._backend cimport ( + _backend_type, + _device_type, + DPCTLAcceleratorSelector_Create, + DPCTLCPUSelector_Create, + DPCTLDefaultSelector_Create, + DPCTLDevice_CreateFromSelector, + DPCTLDeviceMgr_GetDevices, + DPCTLDeviceSelector_Delete, + DPCTLDeviceVectorRef, + DPCTLDeviceVector_Delete, + DPCTLDeviceVector_GetAt, + DPCTLDeviceVector_Size, + DPCTLGPUSelector_Create, + DPCTLHostSelector_Create, + DPCTLSyclBackendType, + DPCTLSyclDeviceRef, + DPCTLSyclDeviceSelectorRef, + DPCTLSyclDeviceType, +) +from ._sycl_device cimport SyclDevice +from . import backend_type, device_type as device_type_t + +__all__ = [ + "get_devices", + "select_accelerator_device", + "select_cpu_device", + "select_default_device", + "select_gpu_device", + "select_host_device", +] + + +cdef _backend_type _string_to_dpctl_sycl_backend_ty(str backend_str): + backend_str = backend_str.strip().lower() + if backend_str == "all": + return _backend_type._ALL_BACKENDS + elif backend_str == "cuda": + return _backend_type._CUDA + elif backend_str == "host": + return _backend_type._HOST + elif backend_str == "level_zero": + return _backend_type._LEVEL_ZERO + elif backend_str == "opencl": + return _backend_type._OPENCL + else: + return _backend_type._UNKNOWN_BACKEND + + +cdef _device_type _string_to_dpctl_sycl_device_ty(str dty_str): + dty_str = dty_str.strip().lower() + if dty_str == "accelerator": + return _device_type._ACCELERATOR + elif dty_str == "all": + return _device_type._ALL_DEVICES + elif dty_str == "automatic": + return _device_type._AUTOMATIC + elif dty_str == "cpu": + return _device_type._CPU + elif dty_str == "custom": + return _device_type._CUSTOM + elif dty_str == "gpu": + return _device_type._GPU + elif dty_str == "host_device": + return _device_type._HOST_DEVICE + else: + return _device_type._UNKNOWN_DEVICE + + +cdef _backend_type _enum_to_dpctl_sycl_backend_ty(BTy): + if BTy == backend_type.all: + return _backend_type._ALL_BACKENDS + elif BTy == backend_type.cuda: + return _backend_type._CUDA + elif BTy == backend_type.host: + return _backend_type._HOST + elif BTy == backend_type.level_zero: + return _backend_type._LEVEL_ZERO + elif BTy == backend_type.opencl: + return _backend_type._OPENCL + else: + return _backend_type._UNKNOWN_BACKEND + + +cdef _device_type _enum_to_dpctl_sycl_device_ty(DTy): + if DTy == device_type_t.all: + return _device_type._ALL_DEVICES + elif DTy == device_type_t.accelerator: + return _device_type._ACCELERATOR + elif DTy == device_type_t.automatic: + return _device_type._AUTOMATIC + elif DTy == device_type_t.cpu: + return _device_type._CPU + elif DTy == device_type_t.custom: + return _device_type._CUSTOM + elif DTy == device_type_t.gpu: + return _device_type._GPU + elif DTy == device_type_t.host_device: + return _device_type._HOST_DEVICE + else: + return _device_type._UNKNOWN_DEVICE + + +cdef list _get_devices(DPCTLDeviceVectorRef DVRef): + cdef list devices = [] + cdef size_t nelems = 0 + if DVRef: + nelems = DPCTLDeviceVector_Size(DVRef) + for i in range(0, nelems): + DRef = DPCTLDeviceVector_GetAt(DVRef, i) + D = SyclDevice._create(DRef) + devices.append(D) + + return devices + + +cpdef list get_devices(backend=backend_type.all, device_type=device_type_t.all): + cdef DPCTLSyclBackendType BTy = _backend_type._ALL_BACKENDS + cdef DPCTLSyclDeviceType DTy = _device_type._ALL_DEVICES + cdef DPCTLDeviceVectorRef DVRef = NULL + cdef list devices + + if isinstance(backend, str): + BTy = _string_to_dpctl_sycl_backend_ty(backend) + elif isinstance(backend, backend_type): + BTy = _enum_to_dpctl_sycl_backend_ty(backend) + else: + raise TypeError( + "backend should be specified as a str or an " + "enum_types.backend_type" + ) + + if isinstance(device_type, str): + DTy = _string_to_dpctl_sycl_device_ty(device_type) + elif isinstance(device_type, device_type_t): + DTy = _enum_to_dpctl_sycl_device_ty(device_type) + else: + raise TypeError( + "device type should be specified as a str or an " + "enum_types.device_type" + ) + + DVRef = DPCTLDeviceMgr_GetDevices(BTy | DTy) + devices = _get_devices(DVRef) + DPCTLDeviceVector_Delete(DVRef) + + return devices + + +cpdef select_accelerator_device(): + """ A wrapper for SYCL's `accelerator_selector` device_selector class. + + Returns: + A new SyclDevice object containing the SYCL device returned by the + `accelerator_selector`. + Raises: + A ValueError is raised if the SYCL `accelerator_selector` is unable to + select a device. + """ + cdef DPCTLSyclDeviceSelectorRef DSRef = DPCTLAcceleratorSelector_Create() + cdef DPCTLSyclDeviceRef DRef = DPCTLDevice_CreateFromSelector(DSRef) + # Free up the device selector + DPCTLDeviceSelector_Delete(DSRef) + if DRef is NULL: + raise ValueError("Device unavailable.") + Device = SyclDevice._create(DRef) + return Device + + +cpdef select_cpu_device(): + """ A wrapper for SYCL's `cpu_selector` device_selector class. + + Returns: + A new SyclDevice object containing the SYCL device returned by the + `cpu_selector`. + Raises: + A ValueError is raised if the SYCL `cpu_seector` is unable to select a + device. + """ + cdef DPCTLSyclDeviceSelectorRef DSRef = DPCTLCPUSelector_Create() + cdef DPCTLSyclDeviceRef DRef = DPCTLDevice_CreateFromSelector(DSRef) + # Free up the device selector + DPCTLDeviceSelector_Delete(DSRef) + if DRef is NULL: + raise ValueError("Device unavailable.") + Device = SyclDevice._create(DRef) + return Device + + +cpdef select_default_device(): + """ A wrapper for SYCL's `default_selector` device_selector class. + + Returns: + A new SyclDevice object containing the SYCL device returned by the + `default_selector`. + Raises: + A ValueError is raised if the SYCL `default_seector` is unable to + select a device. + """ + cdef DPCTLSyclDeviceSelectorRef DSRef = DPCTLDefaultSelector_Create() + cdef DPCTLSyclDeviceRef DRef = DPCTLDevice_CreateFromSelector(DSRef) + # Free up the device selector + DPCTLDeviceSelector_Delete(DSRef) + if DRef is NULL: + raise ValueError("Device unavailable.") + Device = SyclDevice._create(DRef) + return Device + + +cpdef select_gpu_device(): + """ A wrapper for SYCL's `gpu_selector` device_selector class. + + Returns: + A new SyclDevice object containing the SYCL device returned by the + `gpu_selector`. + Raises: + A ValueError is raised if the SYCL `gpu_seector` is unable to select a + device. + """ + cdef DPCTLSyclDeviceSelectorRef DSRef = DPCTLGPUSelector_Create() + cdef DPCTLSyclDeviceRef DRef = DPCTLDevice_CreateFromSelector(DSRef) + # Free up the device selector + DPCTLDeviceSelector_Delete(DSRef) + if DRef is NULL: + raise ValueError("Device unavailable.") + Device = SyclDevice._create(DRef) + return Device + + +cpdef select_host_device(): + """ A wrapper for SYCL's `host_selector` device_selector class. + + Returns: + A new SyclDevice object containing the SYCL device returned by the + `host_selector`. + Raises: + A ValueError is raised if the SYCL `host_seector` is unable to select a + device. + """ + cdef DPCTLSyclDeviceSelectorRef DSRef = DPCTLHostSelector_Create() + cdef DPCTLSyclDeviceRef DRef = DPCTLDevice_CreateFromSelector(DSRef) + # Free up the device selector + DPCTLDeviceSelector_Delete(DSRef) + if DRef is NULL: + raise ValueError("Device unavailable.") + Device = SyclDevice._create(DRef) + return Device diff --git a/dpctl/enum_types.py b/dpctl/enum_types.py index 69e8e97fb9..ed96823fed 100644 --- a/dpctl/enum_types.py +++ b/dpctl/enum_types.py @@ -41,12 +41,14 @@ class device_type(Enum): accelerator 3 host_device 4 ================== ============ - """ - gpu = auto() - cpu = auto() + all = auto() accelerator = auto() + automatic = auto() + cpu = auto() + custom = auto() + gpu = auto() host_device = auto() @@ -65,7 +67,8 @@ class backend_type(Enum): """ - opencl = auto() - level_zero = auto() + all = auto() cuda = auto() host = auto() + level_zero = auto() + opencl = auto() diff --git a/dpctl/tests/test_sycl_device.py b/dpctl/tests/test_sycl_device.py index d38aeec9ca..7df90c6df3 100644 --- a/dpctl/tests/test_sycl_device.py +++ b/dpctl/tests/test_sycl_device.py @@ -128,97 +128,6 @@ def check_is_host(device): pytest.fail("is_hostcall failed") -list_of_checks = [ - check_get_max_compute_units, - check_get_max_work_item_dims, - check_get_max_work_item_sizes, - check_get_max_work_group_size, - check_get_max_num_sub_groups, - check_has_int64_base_atomics, - check_has_int64_extended_atomics, - check_is_accelerator, - check_is_cpu, - check_is_gpu, - check_is_host, -] - -# Unit test cases that will be run for every device -def check_get_max_compute_units(device): - max_compute_units = device.get_max_compute_units() - assert max_compute_units > 0 - - -def check_get_max_work_item_dims(device): - max_work_item_dims = device.get_max_work_item_dims() - assert max_work_item_dims > 0 - - -def check_get_max_work_item_sizes(device): - max_work_item_sizes = device.get_max_work_item_sizes() - for size in max_work_item_sizes: - assert size is not None - - -def check_get_max_work_group_size(device): - max_work_group_size = device.get_max_work_group_size() - # Special case for FPGA simulator - if device.is_accelerator(): - assert max_work_group_size >= 0 - else: - assert max_work_group_size > 0 - - -def check_get_max_num_sub_groups(device): - max_num_sub_groups = device.get_max_num_sub_groups() - # Special case for FPGA simulator - if device.is_accelerator(): - assert max_num_sub_groups >= 0 - else: - assert max_num_sub_groups > 0 - - -def check_has_int64_base_atomics(device): - try: - device.has_int64_base_atomics() - except Exception: - pytest.fail("has_int64_base_atomics call failed") - - -def check_has_int64_extended_atomics(device): - try: - device.has_int64_extended_atomics() - except Exception: - pytest.fail("has_int64_extended_atomics call failed") - - -def check_is_accelerator(device): - try: - device.is_accelerator() - except Exception: - pytest.fail("is_accelerator call failed") - - -def check_is_cpu(device): - try: - device.is_cpu() - except Exception: - pytest.fail("is_cpu call failed") - - -def check_is_gpu(device): - try: - device.is_gpu() - except Exception: - pytest.fail("is_gpu call failed") - - -def check_is_host(device): - try: - device.is_host() - except Exception: - pytest.fail("is_hostcall failed") - - list_of_checks = [ check_get_max_compute_units, check_get_max_work_item_dims, @@ -254,11 +163,6 @@ def check(request): return request.param -@pytest.fixture(params=list_of_checks) -def check(request): - return request.param - - def test_standard_selectors(device_selector, check): """Tests if the standard SYCL device_selectors are able to select a device. diff --git a/dpctl/tests/test_sycl_device_factory.py b/dpctl/tests/test_sycl_device_factory.py new file mode 100644 index 0000000000..ee718e9d39 --- /dev/null +++ b/dpctl/tests/test_sycl_device_factory.py @@ -0,0 +1,184 @@ +# Data Parallel Control (dpctl) +# +# Copyright 2020-2021 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" Defines unit test cases for the _sycl_device_factory module +""" + +import dpctl +from dpctl import backend_type as bty, device_type as dty +import pytest + +argument_list_1 = [ + (bty.level_zero, dty.gpu), + (bty.opencl, dty.gpu), + (bty.opencl, dty.cpu), + (bty.host, dty.host_device), +] + +argument_list_2 = [ + ("host", "host_device"), + ("level_zero", "gpu"), + ("opencl", "gpu"), + ("opencl", "cpu"), +] + +list_of_backend_str = [ + "host", + "level_zero", + "opencl", +] + +list_of_device_type_str = [ + "host_device", + "gpu", + "cpu", +] + + +def string_to_device_type(dty_str): + if dty_str == "accelerator": + return dty.accelerator + elif dty_str == "cpu": + return dty.cpu + elif dty_str == "host_device": + return dty.host_device + elif dty_str == "gpu": + return dty.gpu + + +def string_to_backend_type(bty_str): + if bty_str == "cuda": + return bty.cuda + elif bty_str == "host": + return bty.host + elif bty_str == "level_zero": + return bty.level_zero + elif bty_str == "opencl": + return bty.opencl + + +@pytest.fixture(params=argument_list_1) +def enum_args(request): + return request.param + + +@pytest.fixture(params=argument_list_2) +def str_args(request): + return request.param + + +@pytest.fixture(params=[item for item in bty]) +def backend(request): + return request.param + + +@pytest.fixture(params=list_of_backend_str) +def backend_str(request): + return request.param + + +@pytest.fixture(params=[item for item in dty]) +def device_type(request): + return request.param + + +@pytest.fixture(params=list_of_device_type_str) +def device_type_str(request): + return request.param + + +def check_if_device_type_is_valid(devices): + for d in devices: + assert d.get_device_type() in set(item for item in dty) + + +def check_if_backend_is_valid(devices): + for d in devices: + assert d.get_backend() in set(item for item in bty) + + +def check_if_backend_matches(devices, backend): + for d in devices: + assert d.get_backend() == backend + + +def check_if_device_type_matches(devices, device_type): + for d in devices: + assert d.get_device_type() == device_type + + +def test_get_devices_with_string_args(str_args): + devices = dpctl.get_devices(backend=str_args[0], device_type=str_args[1]) + if len(devices): + d = string_to_device_type(str_args[1]) + b = string_to_backend_type(str_args[0]) + check_if_backend_matches(devices, b) + check_if_device_type_matches(devices, d) + else: + pytest.skip() + + +def test_get_devices_with_enum_args(enum_args): + devices = dpctl.get_devices(backend=enum_args[0], device_type=enum_args[1]) + if len(devices): + check_if_backend_matches(devices, enum_args[0]) + check_if_device_type_matches(devices, enum_args[1]) + else: + pytest.skip() + + +def test_get_devices_with_backend_enum(backend): + devices = dpctl.get_devices(backend=backend) + if len(devices): + check_if_device_type_is_valid(devices) + check_if_backend_is_valid(devices) + if backend != bty.all: + check_if_backend_matches(devices, backend) + + else: + pytest.skip() + + +def test_get_devices_with_backend_str(backend_str): + print(backend_str) + devices = dpctl.get_devices(backend=backend_str) + if len(devices): + b = string_to_backend_type(backend_str) + check_if_backend_matches(devices, b) + check_if_device_type_is_valid(devices) + else: + pytest.skip() + + +def test_get_devices_with_device_type_enum(device_type): + devices = dpctl.get_devices(device_type=device_type) + if len(devices): + if device_type != dty.all: + check_if_device_type_matches(devices, device_type) + check_if_device_type_is_valid(devices) + check_if_backend_is_valid(devices) + else: + pytest.skip() + + +def test_get_devices_with_device_type_str(device_type_str): + devices = dpctl.get_devices(device_type=device_type_str) + if len(devices): + d = string_to_device_type(device_type_str) + check_if_device_type_matches(devices, d) + check_if_device_type_is_valid(devices) + else: + pytest.skip() diff --git a/setup.py b/setup.py index e2b13d9cae..5b6916af47 100644 --- a/setup.py +++ b/setup.py @@ -173,6 +173,13 @@ def extensions(): ], **extension_args ), + Extension( + "dpctl._sycl_device_factory", + [ + os.path.join("dpctl", "_sycl_device_factory.pyx"), + ], + **extension_args + ), Extension( "dpctl._sycl_event", [ From 206ceda161886cb544c45dca2ae28f627201a913 Mon Sep 17 00:00:00 2001 From: Diptorup Deb Date: Wed, 3 Mar 2021 16:44:58 -0600 Subject: [PATCH 4/8] Fix spelling typo. --- dpctl/_sycl_device.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpctl/_sycl_device.pyx b/dpctl/_sycl_device.pyx index 9ad3247bed..bcb3cc873c 100644 --- a/dpctl/_sycl_device.pyx +++ b/dpctl/_sycl_device.pyx @@ -76,7 +76,7 @@ cdef class _SyclDevice: """ Print information about the SYCL device. """ warnings.warn( - "WARNING: dump_device_info is depracated and will be removed in " + "WARNING: dump_device_info is deprecated and will be removed in " "a future release of dpctl. Use print_device_info instead." ) DPCTLDeviceMgr_PrintDeviceInfo(self._device_ref) From e63c67aace7a6b6eec8d0e86f1e1c3f7d5436401 Mon Sep 17 00:00:00 2001 From: Diptorup Deb Date: Mon, 1 Mar 2021 16:04:26 -0600 Subject: [PATCH 5/8] Change how error codes are handled in windows batch scripts. --- conda-recipe/bld.bat | 4 ++-- conda-recipe/run_test.bat | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/conda-recipe/bld.bat b/conda-recipe/bld.bat index 0793695ed8..6f2650120f 100644 --- a/conda-recipe/bld.bat +++ b/conda-recipe/bld.bat @@ -1,12 +1,12 @@ call "%ONEAPI_ROOT%\compiler\latest\env\vars.bat" -IF %ERRORLEVEL% NEQ 0 ( +if errorlevel 1 ( echo "oneAPI compiler activation failed" exit /b 1 ) "%PYTHON%" setup.py clean --all "%PYTHON%" setup.py install -IF %ERRORLEVEL% NEQ 0 exit /b 1 +if errorlevel 1 exit 1 rem Build wheel package if NOT "%WHEELS_OUTPUT_FOLDER%"=="" ( diff --git a/conda-recipe/run_test.bat b/conda-recipe/run_test.bat index d552d9cb86..0d7f07db97 100644 --- a/conda-recipe/run_test.bat +++ b/conda-recipe/run_test.bat @@ -1,5 +1,5 @@ call "%ONEAPI_ROOT%\compiler\latest\env\vars.bat" -IF %ERRORLEVEL% NEQ 0 ( +if errorlevel 1 ( echo "oneAPI compiler activation failed%" exit /b 1 ) @@ -9,7 +9,7 @@ set ERRORLEVEL= @echo on "%PYTHON%" -c "import dpctl" -IF %ERRORLEVEL% NEQ 0 exit /b 1 +if errorlevel 1 exit 1 pytest -q -ra --disable-warnings --pyargs dpctl -vv -IF %ERRORLEVEL% NEQ 0 exit /b 1 +if errorlevel 1 exit 1 From 9f0303b9f4db6dce57069d460e48f1a28195b9ec Mon Sep 17 00:00:00 2001 From: Diptorup Deb Date: Wed, 3 Mar 2021 14:05:08 -0600 Subject: [PATCH 6/8] Ignore .dll files. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index cb9a2ada8c..367d6a754c 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ __pycache__/ *.so *.exe *.lib +*.dll # CMake build and local install directory build From 6fad0925b374718375f829f0ad343d426ed5de39 Mon Sep 17 00:00:00 2001 From: Diptorup Deb Date: Wed, 3 Mar 2021 14:06:19 -0600 Subject: [PATCH 7/8] Replace tabs by space. --- conda-recipe/bld.bat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conda-recipe/bld.bat b/conda-recipe/bld.bat index 6f2650120f..e3203d5e13 100644 --- a/conda-recipe/bld.bat +++ b/conda-recipe/bld.bat @@ -1,7 +1,7 @@ call "%ONEAPI_ROOT%\compiler\latest\env\vars.bat" if errorlevel 1 ( - echo "oneAPI compiler activation failed" - exit /b 1 + echo "oneAPI compiler activation failed" + exit /b 1 ) "%PYTHON%" setup.py clean --all From a8e49c5c7648961fe596e104a1fa6cc4c133e075 Mon Sep 17 00:00:00 2001 From: Diptorup Deb Date: Wed, 3 Mar 2021 14:08:50 -0600 Subject: [PATCH 8/8] Fix typo and set the DPCPP_FOUND variable in FindDPCPP.cmake. --- dpctl-capi/CMakeLists.txt | 8 +++----- dpctl-capi/cmake/modules/FindDPCPP.cmake | 2 ++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dpctl-capi/CMakeLists.txt b/dpctl-capi/CMakeLists.txt index 2d3dec6287..466a419fbf 100644 --- a/dpctl-capi/CMakeLists.txt +++ b/dpctl-capi/CMakeLists.txt @@ -41,7 +41,7 @@ if(WIN32) set(CMAKE_LINKER:PATH "${DPCPP_ROOT}/bin/lld-link") message(STATUS "Resetting CXX compiler to: " ${CMAKE_CXX_COMPILER}) message(STATUS "Resetting C compiler to: " ${CMAKE_C_COMPILER}) - message(STATUS "Resetting Linker to: " ${CMAKE_LINK}) + message(STATUS "Resetting Linker to: " ${CMAKE_LINKER}) set(WARNING_FLAGS "-Wall -Wextra -Winit-self -Wunused-function -Wuninitialized -Wmissing-declarations") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} -Qstd=c++17") @@ -108,10 +108,8 @@ if(DPCTL_ENABLE_LO_PROGRAM_CREATION) endif() install( - TARGETS - DPCTLSyclInterface - LIBRARY DESTINATION - "${CMAKE_INSTALL_PREFIX}/lib/" + TARGETS DPCTLSyclInterface + LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/ ) # Install all headers diff --git a/dpctl-capi/cmake/modules/FindDPCPP.cmake b/dpctl-capi/cmake/modules/FindDPCPP.cmake index b23b972908..a19a435761 100644 --- a/dpctl-capi/cmake/modules/FindDPCPP.cmake +++ b/dpctl-capi/cmake/modules/FindDPCPP.cmake @@ -100,12 +100,14 @@ if(${dpcpp_result} MATCHES "0") set(DPCPP_SYCL_LIBRARY ${DPCPP_INSTALL_DIR}/lib/libsycl.so) set(DPCPP_OPENCL_LIBRARY ${DPCPP_INSTALL_DIR}/lib/libOpenCL.so) endif() + set(DPCPP_FOUND TRUE) else() message(STATUS "DPCPP needed to build dpctl_sycl_interface") return() endif() find_package_handle_standard_args(DPCPP DEFAULT_MSG + DPCPP_FOUND DPCPP_VERSION DPCPP_INCLUDE_DIR DPCPP_SYCL_INCLUDE_DIR