From 81f6323c72040e383d4022d791ca9170eef68f1d Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Wed, 24 Mar 2021 18:52:00 -0500 Subject: [PATCH 01/22] Added DPCTLDeviceVector_CreateFromArray Added test to cover that functionality, added it to _backend.pyx --- dpctl-capi/include/dpctl_vector.h | 4 ++ dpctl-capi/source/dpctl_vector_templ.cpp | 16 ++++++++ .../tests/test_sycl_context_interface.cpp | 38 +++++++++++++++++++ dpctl/_backend.pxd | 13 +++++++ 4 files changed, 71 insertions(+) diff --git a/dpctl-capi/include/dpctl_vector.h b/dpctl-capi/include/dpctl_vector.h index c26ff566fe..39c1c7082c 100644 --- a/dpctl-capi/include/dpctl_vector.h +++ b/dpctl-capi/include/dpctl_vector.h @@ -41,6 +41,10 @@ DPCTL_C_EXTERN_C_BEGIN __dpctl_give DPCTL##EL##VectorRef DPCTL##EL##Vector_Create(); \ \ DPCTL_API \ + __dpctl_give DPCTL##EL##VectorRef DPCTL##EL##Vector_CreateFromArray( \ + size_t len, DPCTLSycl##EL##Ref *elems); \ + \ + DPCTL_API \ void DPCTL##EL##Vector_Delete(__dpctl_take DPCTL##EL##VectorRef Ref); \ \ DPCTL_API \ diff --git a/dpctl-capi/source/dpctl_vector_templ.cpp b/dpctl-capi/source/dpctl_vector_templ.cpp index 0ca30ba0a9..875f1ba8ca 100644 --- a/dpctl-capi/source/dpctl_vector_templ.cpp +++ b/dpctl-capi/source/dpctl_vector_templ.cpp @@ -47,6 +47,22 @@ __dpctl_give VECTOR(EL) FN(EL, Create)() } } +/*! + * @brief Creates a new std::vector of the opaque SYCL pointer types from given C array. + * + * @return A new dynamically allocated std::vector of opaque pointer types. + */ +__dpctl_give VECTOR(EL) FN(EL, CreateFromArray)(size_t n, __dpctl_keep SYCLREF(EL) *elems) +{ + try { + auto Vec = new vector_class(n); + Vec->assign(elems, elems + n); + return wrap(Vec); + } catch (std::bad_alloc const &ba) { + return nullptr; + } +} + /*! * @brief Frees all the elements of the passed in std::vector and then frees the * std::vector pointer. diff --git a/dpctl-capi/tests/test_sycl_context_interface.cpp b/dpctl-capi/tests/test_sycl_context_interface.cpp index 0676afacf1..fbccface2e 100644 --- a/dpctl-capi/tests/test_sycl_context_interface.cpp +++ b/dpctl-capi/tests/test_sycl_context_interface.cpp @@ -113,6 +113,44 @@ TEST_P(TestDPCTLContextInterface, Chk_CreateWithDevices) EXPECT_NO_FATAL_FAILURE(DPCTLContext_Delete(CRef)); } +TEST_P(TestDPCTLContextInterface, Chk_CreateWithDevices2) +{ + size_t nCUs = 0; + DPCTLSyclContextRef CRef = nullptr; + DPCTLSyclDeviceRef DRef = nullptr; + DPCTLDeviceVectorRef DVRef = nullptr; + EXPECT_NO_FATAL_FAILURE(DRef = DPCTLDevice_CreateFromSelector(DSRef)); + if (!DRef) + GTEST_SKIP_("Device not found"); + + /* TODO: Once we have wrappers for sub-device creation let us use those + * functions. + */ + EXPECT_NO_FATAL_FAILURE(nCUs = DPCTLDevice_GetMaxComputeUnits(DRef)); + if (nCUs) { + auto D = unwrap(DRef); + try { + auto subDevices = D->create_sub_devices< + info::partition_property::partition_equally>(nCUs / 2); + const size_t len = subDevices.size(); + auto ar = new DPCTLSyclDeviceRef[len]; + for(size_t i=0; i < len; ++i) { + ar[i] = wrap(new device(subDevices.at(i))); + } + EXPECT_NO_FATAL_FAILURE(DVRef = DPCTLDeviceVector_CreateFromArray(len, ar)); + EXPECT_NO_FATAL_FAILURE( + CRef = DPCTLContext_CreateFromDevices(DVRef, nullptr, 0)); + ASSERT_TRUE(CRef); + delete[] ar; + } catch (feature_not_supported const &fnse) { + GTEST_SKIP_("Skipping creating context for sub-devices"); + } + } + EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(DRef)); + EXPECT_NO_FATAL_FAILURE(DPCTLDeviceVector_Delete(DVRef)); + EXPECT_NO_FATAL_FAILURE(DPCTLContext_Delete(CRef)); +} + TEST_P(TestDPCTLContextInterface, Chk_AreEq) { DPCTLSyclContextRef CRef1 = nullptr, CRef2 = nullptr, CRef3 = nullptr; diff --git a/dpctl/_backend.pxd b/dpctl/_backend.pxd index d7750b3e0b..217f3f54b8 100644 --- a/dpctl/_backend.pxd +++ b/dpctl/_backend.pxd @@ -168,6 +168,9 @@ cdef extern from "dpctl_sycl_device_interface.h": cdef extern from "dpctl_sycl_device_manager.h": cdef struct DPCTLDeviceVector ctypedef DPCTLDeviceVector *DPCTLDeviceVectorRef + cdef DPCTLDeviceVectorRef DPCTLDeviceVector_CreateFromArray( + size_t nelems, + DPCTLSyclDeviceRef *elems) cdef void DPCTLDeviceVector_Delete(DPCTLDeviceVectorRef DVRef) cdef void DPCTLDeviceVector_Clear(DPCTLDeviceVectorRef DVRef) cdef size_t DPCTLDeviceVector_Size(DPCTLDeviceVectorRef DVRef) @@ -231,6 +234,16 @@ cdef extern from "dpctl_sycl_platform_interface.h": cdef extern from "dpctl_sycl_context_interface.h": + cdef DPCTLSyclContextRef DPCTLContext_Create( + const DPCTLSyclDeviceRef DRef, + error_handler_callback *error_handler, + int properties) + cdef DPCTLSyclContextRef DPCTLContext_CreateFromDevices( + const DPCTLDeviceVectorRef DVRef, + error_handler_callback *error_handler, + int properties) + cdef DPCTLSyclContextRef DPCTLContext_Copy( + const DPCTLSyclContextRef CRef) cdef bool DPCTLContext_AreEq(const DPCTLSyclContextRef CtxRef1, const DPCTLSyclContextRef CtxRef2) cdef DPCTLSyclBackendType DPCTLContext_GetBackend( From 79f31975b93256923946f4ba73e066107eebbe3a Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Wed, 24 Mar 2021 18:55:21 -0500 Subject: [PATCH 02/22] Added constructor for SyclContext --- dpctl/_sycl_context.pxd | 16 ++++-- dpctl/_sycl_context.pyx | 112 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 119 insertions(+), 9 deletions(-) diff --git a/dpctl/_sycl_context.pxd b/dpctl/_sycl_context.pxd index c2f12cad11..aada246d4a 100644 --- a/dpctl/_sycl_context.pxd +++ b/dpctl/_sycl_context.pxd @@ -21,15 +21,25 @@ """ from ._backend cimport DPCTLSyclContextRef +from ._sycl_device cimport SyclDevice from libcpp cimport bool +cdef class _SyclContext: + """ Data owner for SyclContext + """ + cdef DPCTLSyclContextRef _ctxt_ref + -cdef class SyclContext: +cdef class SyclContext(_SyclContext): ''' Wrapper class for a Sycl Context ''' - cdef DPCTLSyclContextRef _ctxt_ref @staticmethod - cdef SyclContext _create (DPCTLSyclContextRef ctxt) + cdef SyclContext _create (DPCTLSyclContextRef CRef) + @staticmethod + cdef void _init_helper(_SyclContext self, DPCTLSyclContextRef CRef) + cdef int _init_from__SyclContext(self, _SyclContext other) + cdef int _init_from_one_device(self, SyclDevice device, int props) + cdef int _init_from_devices(self, object devices, int props) cpdef bool equals (self, SyclContext ctxt) cdef DPCTLSyclContextRef get_context_ref (self) diff --git a/dpctl/_sycl_context.pyx b/dpctl/_sycl_context.pyx index 0953f5c79d..14420f3857 100644 --- a/dpctl/_sycl_context.pyx +++ b/dpctl/_sycl_context.pyx @@ -24,9 +24,22 @@ from __future__ import print_function import logging from ._backend cimport ( DPCTLSyclContextRef, + DPCTLSyclDeviceRef, + DPCTLContext_Create, + DPCTLContext_CreateFromDevices, + DPCTLContext_Copy, DPCTLContext_Delete, DPCTLContext_AreEq, + DPCTLDevice_Delete, + DPCTLDevice_Copy, + DPCTLDeviceVectorRef, + DPCTLDeviceVector_CreateFromArray, + DPCTLDeviceVector_Delete, + error_handler_callback ) +from ._sycl_queue cimport default_async_error_handler +from ._sycl_device cimport SyclDevice +from cpython.mem cimport PyMem_Malloc, PyMem_Free __all__ = [ "SyclContext", @@ -34,18 +47,105 @@ __all__ = [ _logger = logging.getLogger(__name__) +cdef class _SyclContext: + """ Data owner for SyclContext + """ + + def __dealloc__(self): + DPCTLContext_Delete(self._ctxt_ref) -cdef class SyclContext: + +cdef class SyclContext(_SyclContext): """ Python wrapper class for cl::sycl::context. """ + + @staticmethod + cdef void _init_helper(_SyclContext context, DPCTLSyclContextRef CRef): + context._ctxt_ref = CRef + @staticmethod cdef SyclContext _create (DPCTLSyclContextRef ctxt): - cdef SyclContext ret = SyclContext.__new__(SyclContext) - ret._ctxt_ref = ctxt - return ret + cdef _SyclContext ret = <_SyclContext>_SyclContext.__new__(_SyclContext) + SyclContext._init_helper(ret, ctxt) + return SyclContext(ret) - def __dealloc__ (self): - DPCTLContext_Delete(self._ctxt_ref) + cdef int _init_from__SyclContext(self, _SyclContext other): + self._ctxt_ref = DPCTLContext_Copy(other._ctxt_ref) + if (self._ctxt_ref is NULL): + return -1 + return 0 + + cdef int _init_from_one_device(self, SyclDevice device, int props): + cdef DPCTLSyclDeviceRef DRef = device.get_device_ref() + cdef error_handler_callback * eh_callback = \ + &default_async_error_handler + cdef DPCTLSyclContextRef CRef = DPCTLContext_Create(DRef, eh_callback, props) + if (CRef is NULL): + return -1 + SyclContext._init_helper(<_SyclContext> self, CRef) + return 0 + + cdef int _init_from_devices(self, object devices, int props): + cdef int num_devices = len(devices) + cdef int i = 0 + cdef int j + cdef size_t num_bytes + cdef DPCTLDeviceVectorRef DVRef + cdef error_handler_callback * eh_callback = \ + &default_async_error_handler + cdef DPCTLSyclContextRef CRef = NULL + cdef DPCTLSyclDeviceRef *elems + + if num_devices > 0: + num_bytes = num_devices * sizeof(DPCTLSyclDeviceRef *) + elems = PyMem_Malloc(num_bytes) + if (elems is NULL): + return -3 + for dev in devices: + if not isinstance(dev, SyclDevice): + elems[i] = NULL + else: + elems[i] = DPCTLDevice_Copy((dev).get_device_ref()) + if (elems[i] is NULL): + for j in range(0, i): + DPCTLDevice_Delete(elems[j]) + PyMem_Free(elems) + return -4 + i = i + 1 + DVRef = DPCTLDeviceVector_CreateFromArray(num_devices, elems) + if (DVRef is NULL): + for j in range(num_devices): + DPCTLDevice_Delete(elems[j]) + PyMem_Free(elems) + return -5 + PyMem_Free(elems) + else: + return -2 + DPCTLContext_CreateFromDevices(DVRef, eh_callback, props) + DPCTLDeviceVector_Delete(DVRef) + if (CRef is NULL): + return -1 + SyclContext._init_helper(<_SyclContext> self, CRef) + return 0 + + def __cinit__(self, arg=None): + """ SyclContext() - create a context for a default device + SyclContext(filter_selector_string) - create a context for specified device + SyclContext(SyclDevice_instance) - create a context for the given device + SyclContext((dev1, dev2, ...)) - create a context for given set of devices + """ + cdef int ret = 0 + if isinstance(arg, _SyclContext): + ret = self._init_from__SyclContext(<_SyclContext> arg) + elif isinstance(arg, SyclDevice): + ret = self._init_from_one_device( arg, 0) + elif isinstance(arg, (list, tuple)) and all([isinstance(argi, SyclDevice) for argi in arg]): + ret = self._init_from_devices(arg, 0) + else: + dev = SyclDevice(arg) + ret = self._init_from_one_device( dev, 0) + if (ret < 0): + raise ValueError(ret) cpdef bool equals (self, SyclContext ctxt): """ Returns true if the SyclContext argument has the same _context_ref From b57009f6d529c12cdbf1247bde197d15fd4e1eb1 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Thu, 25 Mar 2021 09:49:38 -0500 Subject: [PATCH 03/22] Added DPCTLContext_GetDevices(CRef) [X] Implementation [X] Header + Documentation comment [X] Two tests in test_sycl_context.cpp [X] Exported in `dpctl/_backend.pxd` --- .../include/dpctl_sycl_context_interface.h | 11 +++++++ .../source/dpctl_sycl_context_interface.cpp | 30 +++++++++++++++++++ .../tests/test_sycl_context_interface.cpp | 26 +++++++++++++++- dpctl/_backend.pxd | 2 ++ 4 files changed, 68 insertions(+), 1 deletion(-) diff --git a/dpctl-capi/include/dpctl_sycl_context_interface.h b/dpctl-capi/include/dpctl_sycl_context_interface.h index 23ae52884b..6c54d2c6c1 100644 --- a/dpctl-capi/include/dpctl_sycl_context_interface.h +++ b/dpctl-capi/include/dpctl_sycl_context_interface.h @@ -99,6 +99,17 @@ DPCTL_API __dpctl_give DPCTLSyclContextRef DPCTLContext_Copy(__dpctl_keep const DPCTLSyclContextRef CRef); +/*! + * @brief Returns a vector of devices associated with sycl::context referenced + * by DPCTLSyclContextRef object. + * + * @param CRef DPCTLSyclContexRef object to query. + * @return A DPCTLDeviceVectorRef with devices associated with given CRef. + */ +DPCTL_API +__dpctl_give DPCTLDeviceVectorRef +DPCTLContext_GetDevices(__dpctl_keep const DPCTLSyclContextRef CRef); + /*! * @brief Returns true if this SYCL context is a host context. * diff --git a/dpctl-capi/source/dpctl_sycl_context_interface.cpp b/dpctl-capi/source/dpctl_sycl_context_interface.cpp index 54c363b856..b5d10df9d9 100644 --- a/dpctl-capi/source/dpctl_sycl_context_interface.cpp +++ b/dpctl-capi/source/dpctl_sycl_context_interface.cpp @@ -116,6 +116,36 @@ DPCTLContext_Copy(__dpctl_keep const DPCTLSyclContextRef CRef) } } +__dpctl_give DPCTLDeviceVectorRef +DPCTLContext_GetDevices(__dpctl_keep const DPCTLSyclContextRef CRef) +{ + auto Context = unwrap(CRef); + if (!Context) { + std::cerr << "Can not retrieve devices from DPCTLSyclContextRef as input is a nullptr\n"; + return nullptr; + } + vector_class *DevicesVectorPtr = nullptr; + try { + DevicesVectorPtr = new vector_class(); + } catch (std::bad_alloc const &ba) { + // \todo log error + std::cerr << ba.what() << '\n'; + return nullptr; + } + try { + auto Devices = Context->get_devices(); + DevicesVectorPtr->reserve(Devices.size()); + for(const auto &Dev : Devices) { + DevicesVectorPtr->emplace_back(wrap(new device(Dev))); + } + return wrap(DevicesVectorPtr); + } catch (std::bad_alloc const &ba) { + // \todo log error + std::cerr << ba.what() << '\n'; + return nullptr; + } +} + bool DPCTLContext_IsHost(__dpctl_keep const DPCTLSyclContextRef CtxRef) { auto Ctx = unwrap(CtxRef); diff --git a/dpctl-capi/tests/test_sycl_context_interface.cpp b/dpctl-capi/tests/test_sycl_context_interface.cpp index fbccface2e..66dd72b3db 100644 --- a/dpctl-capi/tests/test_sycl_context_interface.cpp +++ b/dpctl-capi/tests/test_sycl_context_interface.cpp @@ -113,12 +113,13 @@ TEST_P(TestDPCTLContextInterface, Chk_CreateWithDevices) EXPECT_NO_FATAL_FAILURE(DPCTLContext_Delete(CRef)); } -TEST_P(TestDPCTLContextInterface, Chk_CreateWithDevices2) +TEST_P(TestDPCTLContextInterface, Chk_CreateWithDevices_GetDevices) { size_t nCUs = 0; DPCTLSyclContextRef CRef = nullptr; DPCTLSyclDeviceRef DRef = nullptr; DPCTLDeviceVectorRef DVRef = nullptr; + DPCTLDeviceVectorRef Res_DVRef = nullptr; EXPECT_NO_FATAL_FAILURE(DRef = DPCTLDevice_CreateFromSelector(DSRef)); if (!DRef) GTEST_SKIP_("Device not found"); @@ -141,6 +142,9 @@ TEST_P(TestDPCTLContextInterface, Chk_CreateWithDevices2) EXPECT_NO_FATAL_FAILURE( CRef = DPCTLContext_CreateFromDevices(DVRef, nullptr, 0)); ASSERT_TRUE(CRef); + EXPECT_NO_FATAL_FAILURE( + Res_DVRef = DPCTLContext_GetDevices(CRef)); + ASSERT_TRUE(DPCTLDeviceVector_Size(Res_DVRef) == len); delete[] ar; } catch (feature_not_supported const &fnse) { GTEST_SKIP_("Skipping creating context for sub-devices"); @@ -149,6 +153,26 @@ TEST_P(TestDPCTLContextInterface, Chk_CreateWithDevices2) EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(DRef)); EXPECT_NO_FATAL_FAILURE(DPCTLDeviceVector_Delete(DVRef)); EXPECT_NO_FATAL_FAILURE(DPCTLContext_Delete(CRef)); + EXPECT_NO_FATAL_FAILURE(DPCTLDeviceVector_Delete(Res_DVRef)); +} + +TEST_P(TestDPCTLContextInterface, Chk_GetDevices) +{ + DPCTLSyclContextRef CRef = nullptr; + DPCTLSyclDeviceRef DRef = nullptr; + DPCTLDeviceVectorRef DVRef = nullptr; + EXPECT_NO_FATAL_FAILURE(DRef = DPCTLDevice_CreateFromSelector(DSRef)); + if (!DRef) + GTEST_SKIP_("Device not found"); + EXPECT_NO_FATAL_FAILURE(CRef = DPCTLContext_Create(DRef, nullptr, 0)); + ASSERT_TRUE(CRef); + EXPECT_NO_FATAL_FAILURE( + DVRef = DPCTLContext_GetDevices(CRef)); + ASSERT_TRUE(DVRef); + EXPECT_TRUE(DPCTLDeviceVector_Size(DVRef) == 1); + EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(DRef)); + EXPECT_NO_FATAL_FAILURE(DPCTLContext_Delete(CRef)); + EXPECT_NO_FATAL_FAILURE(DPCTLDeviceVector_Delete(DVRef)); } TEST_P(TestDPCTLContextInterface, Chk_AreEq) diff --git a/dpctl/_backend.pxd b/dpctl/_backend.pxd index 217f3f54b8..8870c54e78 100644 --- a/dpctl/_backend.pxd +++ b/dpctl/_backend.pxd @@ -244,6 +244,8 @@ cdef extern from "dpctl_sycl_context_interface.h": int properties) cdef DPCTLSyclContextRef DPCTLContext_Copy( const DPCTLSyclContextRef CRef) + cdef DPCTLDeviceVectorRef DPCTLContext_GetDevices( + const DPCTLSyclContextRef CRef) cdef bool DPCTLContext_AreEq(const DPCTLSyclContextRef CtxRef1, const DPCTLSyclContextRef CtxRef2) cdef DPCTLSyclBackendType DPCTLContext_GetBackend( From 7738442c26aa6b238e2fc506a5943217138e7da4 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Thu, 25 Mar 2021 10:56:40 -0500 Subject: [PATCH 04/22] SyclContext.get_devices added Returns list of SyclDevice objects associated with the given queue. --- dpctl/_sycl_context.pyx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/dpctl/_sycl_context.pyx b/dpctl/_sycl_context.pyx index 14420f3857..bca443b96f 100644 --- a/dpctl/_sycl_context.pyx +++ b/dpctl/_sycl_context.pyx @@ -27,6 +27,7 @@ from ._backend cimport ( DPCTLSyclDeviceRef, DPCTLContext_Create, DPCTLContext_CreateFromDevices, + DPCTLContext_GetDevices, DPCTLContext_Copy, DPCTLContext_Delete, DPCTLContext_AreEq, @@ -34,6 +35,8 @@ from ._backend cimport ( DPCTLDevice_Copy, DPCTLDeviceVectorRef, DPCTLDeviceVector_CreateFromArray, + DPCTLDeviceVector_GetAt, + DPCTLDeviceVector_Size, DPCTLDeviceVector_Delete, error_handler_callback ) @@ -165,3 +168,17 @@ cdef class SyclContext(_SyclContext): SyclContext cast to a size_t. """ return int(self._ctx_ref) + + def get_devices (self): + cdef DPCTLDeviceVectorRef DVRef = DPCTLContext_GetDevices(self.get_context_ref()) + cdef size_t num_devs + cdef size_t i + cdef DPCTLSyclDeviceRef DRef + if (DVRef is NULL): + raise ValueError("Internal error: NULL device vector encountered") + num_devs = DPCTLDeviceVector_Size(DVRef) + devices = [] + for i in range(num_devs): + DRef = DPCTLDeviceVector_GetAt(DVRef, i) + devices.append(SyclDevice._create(DRef)) + return devices From db3c5279b80e3526aa40582add6f27680c7e8ea5 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Thu, 25 Mar 2021 10:57:37 -0500 Subject: [PATCH 05/22] added some tests for sycl context --- dpctl/tests/__init__.py | 1 + dpctl/tests/test_sycl_context.py | 360 +++++++++++++++++++++++++++++++ 2 files changed, 361 insertions(+) create mode 100644 dpctl/tests/test_sycl_context.py diff --git a/dpctl/tests/__init__.py b/dpctl/tests/__init__.py index c9c39ae415..2170cd60ba 100644 --- a/dpctl/tests/__init__.py +++ b/dpctl/tests/__init__.py @@ -19,6 +19,7 @@ from .test_dparray import * from .test_sycl_device import * +from .test_sycl_context import * from .test_sycl_kernel_submit import * from .test_sycl_platform import * from .test_sycl_program import * diff --git a/dpctl/tests/test_sycl_context.py b/dpctl/tests/test_sycl_context.py new file mode 100644 index 0000000000..7c72412470 --- /dev/null +++ b/dpctl/tests/test_sycl_context.py @@ -0,0 +1,360 @@ +# 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 SyclQueue class. +""" + +import dpctl +import pytest + +list_of_standard_selectors = [ + dpctl.select_accelerator_device, + dpctl.select_cpu_device, + dpctl.select_default_device, + dpctl.select_gpu_device, + dpctl.select_host_device, +] + +list_of_valid_filter_selectors = [ + "opencl", + "opencl:gpu", + "opencl:cpu", + "opencl:gpu:0", + "gpu", + "cpu", + "level_zero", + "level_zero:gpu", + "opencl:cpu:0", + "level_zero:gpu:0", + "gpu:0", + "gpu:1", + "1", +] + +list_of_invalid_filter_selectors = [ + "-1", + "opencl:gpu:-1", + "level_zero:cpu:0", + "abc", +] + +# Unit test cases that will be run for every device +def check_get_max_compute_units(device): + max_compute_units = device.max_compute_units + assert max_compute_units > 0 + + +def check_get_max_work_item_dims(device): + max_work_item_dims = device.max_work_item_dims + assert max_work_item_dims > 0 + + +def check_get_max_work_item_sizes(device): + max_work_item_sizes = device.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.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.max_num_sub_groups + # Special case for FPGA simulator + if device.is_accelerator or device.is_host: + assert max_num_sub_groups >= 0 + else: + assert max_num_sub_groups > 0 + + +def check_has_aspect_host(device): + try: + device.has_aspect_host + except Exception: + pytest.fail("has_aspect_host call failed") + + +def check_has_aspect_cpu(device): + try: + device.has_aspect_cpu + except Exception: + pytest.fail("has_aspect_cpu call failed") + + +def check_has_aspect_gpu(device): + try: + device.has_aspect_gpu + except Exception: + pytest.fail("has_aspect_gpu call failed") + + +def check_has_aspect_accelerator(device): + try: + device.has_aspect_accelerator + except Exception: + pytest.fail("has_aspect_accelerator call failed") + + +def check_has_aspect_custom(device): + try: + device.has_aspect_custom + except Exception: + pytest.fail("has_aspect_custom call failed") + + +def check_has_aspect_fp16(device): + try: + device.has_aspect_fp16 + except Exception: + pytest.fail("has_aspect_fp16 call failed") + + +def check_has_aspect_fp64(device): + try: + device.has_aspect_fp64 + except Exception: + pytest.fail("has_aspect_fp64 call failed") + + +def check_has_aspect_int64_base_atomics(device): + try: + device.has_aspect_int64_base_atomics + except Exception: + pytest.fail("has_aspect_int64_base_atomics call failed") + + +def check_has_aspect_int64_extended_atomics(device): + try: + device.has_aspect_int64_extended_atomics + except Exception: + pytest.fail("has_aspect_int64_extended_atomics call failed") + + +def check_has_aspect_image(device): + try: + device.has_aspect_image + except Exception: + pytest.fail("has_aspect_image call failed") + + +def check_has_aspect_online_compiler(device): + try: + device.has_aspect_online_compiler + except Exception: + pytest.fail("has_aspect_online_compiler call failed") + + +def check_has_aspect_online_linker(device): + try: + device.has_aspect_online_linker + except Exception: + pytest.fail("has_aspect_online_linker call failed") + + +def check_has_aspect_queue_profiling(device): + try: + device.has_aspect_queue_profiling + except Exception: + pytest.fail("has_aspect_queue_profiling call failed") + + +def check_has_aspect_usm_device_allocations(device): + try: + device.has_aspect_usm_device_allocations + except Exception: + pytest.fail("has_aspect_usm_device_allocations call failed") + + +def check_has_aspect_usm_host_allocations(device): + try: + device.has_aspect_usm_host_allocations + except Exception: + pytest.fail("has_aspect_usm_host_allocations call failed") + + +def check_has_aspect_usm_shared_allocations(device): + try: + device.has_aspect_usm_shared_allocations + except Exception: + pytest.fail("has_aspect_usm_shared_allocations call failed") + + +def check_has_aspect_usm_restricted_shared_allocations(device): + try: + device.has_aspect_usm_restricted_shared_allocations + except Exception: + pytest.fail("has_aspect_usm_restricted_shared_allocations call failed") + + +def check_has_aspect_usm_system_allocator(device): + try: + device.has_aspect_usm_system_allocator + except Exception: + pytest.fail("has_aspect_usm_system_allocator 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, + check_get_max_work_item_sizes, + check_get_max_work_group_size, + check_get_max_num_sub_groups, + check_is_accelerator, + check_is_cpu, + check_is_gpu, + check_is_host, + check_has_aspect_host, + check_has_aspect_cpu, + check_has_aspect_gpu, + check_has_aspect_accelerator, + check_has_aspect_custom, + check_has_aspect_fp16, + check_has_aspect_fp64, + check_has_aspect_int64_base_atomics, + check_has_aspect_int64_extended_atomics, + check_has_aspect_image, + check_has_aspect_online_compiler, + check_has_aspect_online_linker, + check_has_aspect_queue_profiling, + check_has_aspect_usm_device_allocations, + check_has_aspect_usm_host_allocations, + check_has_aspect_usm_shared_allocations, + check_has_aspect_usm_restricted_shared_allocations, + check_has_aspect_usm_system_allocator, +] + + +@pytest.fixture(params=list_of_valid_filter_selectors) +def valid_filter(request): + return request.param + + +@pytest.fixture(params=list_of_invalid_filter_selectors) +def invalid_filter(request): + return request.param + + +@pytest.fixture(params=list_of_standard_selectors) +def device_selector(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. + """ + try: + device = device_selector() + if device.default_selector_score < 0: + pytest.skip() + ctx = dpctl.SyclContext(device) + devs = ctx.get_devices() + assert len(devs) == 1 + check(devs[0]) + except ValueError: + pytest.skip() + + +def test_current_device(check): + """Test is the device for the current queue is valid.""" + try: + q = dpctl.get_current_queue() + except Exception: + pytest.fail("Encountered an exception inside get_current_queue().") + device = q.get_sycl_device() + ctx = q.get_sycl_context() + devs = ctx.get_devices() + # add check that device is among devs + check(devs[0]) + + +def test_valid_filter_selectors(valid_filter, check): + """Tests if we can create a SyclDevice using a supported filter selector string.""" + device = None + try: + ctx = dpctl.SyclContext(valid_filter) + device = ctx.get_devices() + except ValueError: + pytest.skip("Failed to create context with supported filter") + check(device[0]) + + +def test_invalid_filter_selectors(invalid_filter): + """An invalid filter string should always be caught and a SyclQueueCreationError + raised. + """ + with pytest.raises(ValueError): + q = dpctl.SyclContext(invalid_filter) + + +def test_context_not_equals(): + try: + ctx_gpu = dpctl.SyclContext("gpu") + except ValueError: + pytest.skip() + try: + ctx_cpu = dpctl.SyclContext("cpu") + except ValueError: + pytest.skip() + assert not ctx_cpu.equals(ctx_gpu) + + +def test_context_equals(): + try: + ctx1 = dpctl.SyclContext("gpu") + ctx0 = dpctl.SyclContext("gpu") + except dpctl.SyclQueueCreationError: + pytest.skip() + assert ctx0.equals(ctx1) From d8e129b162cf52cb1e1390181c177f201fa0d83a Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Thu, 25 Mar 2021 11:29:55 -0500 Subject: [PATCH 06/22] Added __repr__ Added method device_count (perhaps could be a property), and used it to give different __repr__ outputs for single-device contexts and multi-device context instances. --- dpctl/_sycl_context.pyx | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/dpctl/_sycl_context.pyx b/dpctl/_sycl_context.pyx index bca443b96f..ec2aaf4388 100644 --- a/dpctl/_sycl_context.pyx +++ b/dpctl/_sycl_context.pyx @@ -170,6 +170,9 @@ cdef class SyclContext(_SyclContext): return int(self._ctx_ref) def get_devices (self): + """ + Returns the list of SyclDevice objects associated with SyclContext instance. + """ cdef DPCTLDeviceVectorRef DVRef = DPCTLContext_GetDevices(self.get_context_ref()) cdef size_t num_devs cdef size_t i @@ -181,4 +184,30 @@ cdef class SyclContext(_SyclContext): for i in range(num_devs): DRef = DPCTLDeviceVector_GetAt(DVRef, i) devices.append(SyclDevice._create(DRef)) + DPCTLDeviceVector_Delete(DVRef) return devices + + def device_count (self): + """ + Returns the number of sycl devices associated with SyclContext instance. + """ + cdef DPCTLDeviceVectorRef DVRef = DPCTLContext_GetDevices(self.get_context_ref()) + cdef size_t num_devs + cdef size_t i + cdef DPCTLSyclDeviceRef DRef + if (DVRef is NULL): + raise ValueError("Internal error: NULL device vector encountered") + num_devs = DPCTLDeviceVector_Size(DVRef) + DPCTLDeviceVector_Delete(DVRef) + return num_devs + + @property + def __name__(self): + return "SyclContext" + + def __repr__(self): + cdef size_t n = self.device_count() + if n == 1: + return ("".format(hex(id(self)))) + else: + return ("".format(n, hex(id(self)))) From 490efd05f813c3ce708c9d327d7da71c4e8693f1 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Thu, 25 Mar 2021 11:34:11 -0500 Subject: [PATCH 07/22] clang-format ran on dpctl-capi changes --- dpctl-capi/include/dpctl_vector.h | 2 +- .../source/dpctl_sycl_context_interface.cpp | 17 +++++++------- dpctl-capi/source/dpctl_vector_templ.cpp | 8 ++++--- .../tests/test_sycl_context_interface.cpp | 23 +++++++++---------- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/dpctl-capi/include/dpctl_vector.h b/dpctl-capi/include/dpctl_vector.h index 39c1c7082c..cb4850a92b 100644 --- a/dpctl-capi/include/dpctl_vector.h +++ b/dpctl-capi/include/dpctl_vector.h @@ -42,7 +42,7 @@ DPCTL_C_EXTERN_C_BEGIN \ DPCTL_API \ __dpctl_give DPCTL##EL##VectorRef DPCTL##EL##Vector_CreateFromArray( \ - size_t len, DPCTLSycl##EL##Ref *elems); \ + size_t len, DPCTLSycl##EL##Ref *elems); \ \ DPCTL_API \ void DPCTL##EL##Vector_Delete(__dpctl_take DPCTL##EL##VectorRef Ref); \ diff --git a/dpctl-capi/source/dpctl_sycl_context_interface.cpp b/dpctl-capi/source/dpctl_sycl_context_interface.cpp index b5d10df9d9..61863f9aa1 100644 --- a/dpctl-capi/source/dpctl_sycl_context_interface.cpp +++ b/dpctl-capi/source/dpctl_sycl_context_interface.cpp @@ -121,8 +121,9 @@ DPCTLContext_GetDevices(__dpctl_keep const DPCTLSyclContextRef CRef) { auto Context = unwrap(CRef); if (!Context) { - std::cerr << "Can not retrieve devices from DPCTLSyclContextRef as input is a nullptr\n"; - return nullptr; + std::cerr << "Can not retrieve devices from DPCTLSyclContextRef as " + "input is a nullptr\n"; + return nullptr; } vector_class *DevicesVectorPtr = nullptr; try { @@ -133,12 +134,12 @@ DPCTLContext_GetDevices(__dpctl_keep const DPCTLSyclContextRef CRef) return nullptr; } try { - auto Devices = Context->get_devices(); - DevicesVectorPtr->reserve(Devices.size()); - for(const auto &Dev : Devices) { - DevicesVectorPtr->emplace_back(wrap(new device(Dev))); - } - return wrap(DevicesVectorPtr); + auto Devices = Context->get_devices(); + DevicesVectorPtr->reserve(Devices.size()); + for (const auto &Dev : Devices) { + DevicesVectorPtr->emplace_back(wrap(new device(Dev))); + } + return wrap(DevicesVectorPtr); } catch (std::bad_alloc const &ba) { // \todo log error std::cerr << ba.what() << '\n'; diff --git a/dpctl-capi/source/dpctl_vector_templ.cpp b/dpctl-capi/source/dpctl_vector_templ.cpp index 875f1ba8ca..737a47aaf7 100644 --- a/dpctl-capi/source/dpctl_vector_templ.cpp +++ b/dpctl-capi/source/dpctl_vector_templ.cpp @@ -48,15 +48,17 @@ __dpctl_give VECTOR(EL) FN(EL, Create)() } /*! - * @brief Creates a new std::vector of the opaque SYCL pointer types from given C array. + * @brief Creates a new std::vector of the opaque SYCL pointer types from given + * C array. * * @return A new dynamically allocated std::vector of opaque pointer types. */ -__dpctl_give VECTOR(EL) FN(EL, CreateFromArray)(size_t n, __dpctl_keep SYCLREF(EL) *elems) +__dpctl_give VECTOR(EL) + FN(EL, CreateFromArray)(size_t n, __dpctl_keep SYCLREF(EL) * elems) { try { auto Vec = new vector_class(n); - Vec->assign(elems, elems + n); + Vec->assign(elems, elems + n); return wrap(Vec); } catch (std::bad_alloc const &ba) { return nullptr; diff --git a/dpctl-capi/tests/test_sycl_context_interface.cpp b/dpctl-capi/tests/test_sycl_context_interface.cpp index 66dd72b3db..890e9c4ee4 100644 --- a/dpctl-capi/tests/test_sycl_context_interface.cpp +++ b/dpctl-capi/tests/test_sycl_context_interface.cpp @@ -133,19 +133,19 @@ TEST_P(TestDPCTLContextInterface, Chk_CreateWithDevices_GetDevices) try { auto subDevices = D->create_sub_devices< info::partition_property::partition_equally>(nCUs / 2); - const size_t len = subDevices.size(); - auto ar = new DPCTLSyclDeviceRef[len]; - for(size_t i=0; i < len; ++i) { - ar[i] = wrap(new device(subDevices.at(i))); - } - EXPECT_NO_FATAL_FAILURE(DVRef = DPCTLDeviceVector_CreateFromArray(len, ar)); + const size_t len = subDevices.size(); + auto ar = new DPCTLSyclDeviceRef[len]; + for (size_t i = 0; i < len; ++i) { + ar[i] = wrap(new device(subDevices.at(i))); + } + EXPECT_NO_FATAL_FAILURE( + DVRef = DPCTLDeviceVector_CreateFromArray(len, ar)); EXPECT_NO_FATAL_FAILURE( CRef = DPCTLContext_CreateFromDevices(DVRef, nullptr, 0)); ASSERT_TRUE(CRef); - EXPECT_NO_FATAL_FAILURE( - Res_DVRef = DPCTLContext_GetDevices(CRef)); - ASSERT_TRUE(DPCTLDeviceVector_Size(Res_DVRef) == len); - delete[] ar; + EXPECT_NO_FATAL_FAILURE(Res_DVRef = DPCTLContext_GetDevices(CRef)); + ASSERT_TRUE(DPCTLDeviceVector_Size(Res_DVRef) == len); + delete[] ar; } catch (feature_not_supported const &fnse) { GTEST_SKIP_("Skipping creating context for sub-devices"); } @@ -166,8 +166,7 @@ TEST_P(TestDPCTLContextInterface, Chk_GetDevices) GTEST_SKIP_("Device not found"); EXPECT_NO_FATAL_FAILURE(CRef = DPCTLContext_Create(DRef, nullptr, 0)); ASSERT_TRUE(CRef); - EXPECT_NO_FATAL_FAILURE( - DVRef = DPCTLContext_GetDevices(CRef)); + EXPECT_NO_FATAL_FAILURE(DVRef = DPCTLContext_GetDevices(CRef)); ASSERT_TRUE(DVRef); EXPECT_TRUE(DPCTLDeviceVector_Size(DVRef) == 1); EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(DRef)); From a8b45e9e6e3aa1ca90f1dedd26b7d8fc899b1e62 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Thu, 25 Mar 2021 13:58:56 -0500 Subject: [PATCH 08/22] Added DPCTLContext_DeviceCount --- .../include/dpctl_sycl_context_interface.h | 10 ++++++++++ .../source/dpctl_sycl_context_interface.cpp | 18 ++++++++++++++++++ .../tests/test_sycl_context_interface.cpp | 1 + dpctl/_backend.pxd | 1 + 4 files changed, 30 insertions(+) diff --git a/dpctl-capi/include/dpctl_sycl_context_interface.h b/dpctl-capi/include/dpctl_sycl_context_interface.h index 6c54d2c6c1..4920964359 100644 --- a/dpctl-capi/include/dpctl_sycl_context_interface.h +++ b/dpctl-capi/include/dpctl_sycl_context_interface.h @@ -99,6 +99,16 @@ DPCTL_API __dpctl_give DPCTLSyclContextRef DPCTLContext_Copy(__dpctl_keep const DPCTLSyclContextRef CRef); +/*! + * @brief Returns the number of devices associated with sycl::context referenced + * by DPCTLSyclContextRef object. + * + * @param CRef DPCTLSyclContexRef object to query. + * @return A positive count on success or zero on error. + */ +DPCTL_API +size_t DPCTLContext_DeviceCount(__dpctl_keep const DPCTLSyclContextRef CRef); + /*! * @brief Returns a vector of devices associated with sycl::context referenced * by DPCTLSyclContextRef object. diff --git a/dpctl-capi/source/dpctl_sycl_context_interface.cpp b/dpctl-capi/source/dpctl_sycl_context_interface.cpp index 61863f9aa1..90e145769c 100644 --- a/dpctl-capi/source/dpctl_sycl_context_interface.cpp +++ b/dpctl-capi/source/dpctl_sycl_context_interface.cpp @@ -147,6 +147,24 @@ DPCTLContext_GetDevices(__dpctl_keep const DPCTLSyclContextRef CRef) } } +size_t DPCTLContext_DeviceCount(__dpctl_keep const DPCTLSyclContextRef CRef) +{ + auto Context = unwrap(CRef); + if (!Context) { + std::cerr << "Can not retrieve devices from DPCTLSyclContextRef as " + "input is a nullptr\n"; + return 0; + } + try { + auto Devices = Context->get_devices(); + return Devices.size(); + } catch (std::bad_alloc const &ba) { + // \todo log error + std::cerr << ba.what() << '\n'; + return 0; + } +} + bool DPCTLContext_IsHost(__dpctl_keep const DPCTLSyclContextRef CtxRef) { auto Ctx = unwrap(CtxRef); diff --git a/dpctl-capi/tests/test_sycl_context_interface.cpp b/dpctl-capi/tests/test_sycl_context_interface.cpp index 890e9c4ee4..697fb0fcb5 100644 --- a/dpctl-capi/tests/test_sycl_context_interface.cpp +++ b/dpctl-capi/tests/test_sycl_context_interface.cpp @@ -143,6 +143,7 @@ TEST_P(TestDPCTLContextInterface, Chk_CreateWithDevices_GetDevices) EXPECT_NO_FATAL_FAILURE( CRef = DPCTLContext_CreateFromDevices(DVRef, nullptr, 0)); ASSERT_TRUE(CRef); + ASSERT_TRUE(DPCTLContext_DeviceCount(CRef) == len); EXPECT_NO_FATAL_FAILURE(Res_DVRef = DPCTLContext_GetDevices(CRef)); ASSERT_TRUE(DPCTLDeviceVector_Size(Res_DVRef) == len); delete[] ar; diff --git a/dpctl/_backend.pxd b/dpctl/_backend.pxd index 8870c54e78..d5980e3e74 100644 --- a/dpctl/_backend.pxd +++ b/dpctl/_backend.pxd @@ -246,6 +246,7 @@ cdef extern from "dpctl_sycl_context_interface.h": const DPCTLSyclContextRef CRef) cdef DPCTLDeviceVectorRef DPCTLContext_GetDevices( const DPCTLSyclContextRef CRef) + cdef size_t DPCTLContext_DeviceCount(const DPCTLSyclContextRef CRef) cdef bool DPCTLContext_AreEq(const DPCTLSyclContextRef CtxRef1, const DPCTLSyclContextRef CtxRef2) cdef DPCTLSyclBackendType DPCTLContext_GetBackend( From bc644b7513c8bc82e7f570e3f41c684317f6ae73 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Thu, 25 Mar 2021 14:06:14 -0500 Subject: [PATCH 09/22] Used DPCTLContext_DeviceCount for effiency and to simplify code --- dpctl/_sycl_context.pyx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/dpctl/_sycl_context.pyx b/dpctl/_sycl_context.pyx index ec2aaf4388..2d30aca3f8 100644 --- a/dpctl/_sycl_context.pyx +++ b/dpctl/_sycl_context.pyx @@ -27,6 +27,7 @@ from ._backend cimport ( DPCTLSyclDeviceRef, DPCTLContext_Create, DPCTLContext_CreateFromDevices, + DPCTLContext_DeviceCount, DPCTLContext_GetDevices, DPCTLContext_Copy, DPCTLContext_Delete, @@ -191,15 +192,12 @@ cdef class SyclContext(_SyclContext): """ Returns the number of sycl devices associated with SyclContext instance. """ - cdef DPCTLDeviceVectorRef DVRef = DPCTLContext_GetDevices(self.get_context_ref()) - cdef size_t num_devs - cdef size_t i - cdef DPCTLSyclDeviceRef DRef - if (DVRef is NULL): - raise ValueError("Internal error: NULL device vector encountered") - num_devs = DPCTLDeviceVector_Size(DVRef) - DPCTLDeviceVector_Delete(DVRef) - return num_devs + cdef size_t num_devs = DPCTLContext_DeviceCount(self.get_context_ref()) + if num_devs: + return num_devs + else: + raise ValueError("An error was encountered quering the number of devices " + "associated with this context") @property def __name__(self): From 0a389b200db3dac66ab5491b1e9fe447db3b83a6 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Fri, 26 Mar 2021 08:44:30 -0500 Subject: [PATCH 10/22] SyclQueue/SyclContext constructor from device change When creating from a device, we must look up cached DeviceAndContext pair first, and only if that fails call DPCTLContext_Create SyclContext was not doing a look-up, and SyclQueue was not doing the creation is the lookup were to fail. --- dpctl/_sycl_context.pyx | 18 ++++++++++++++---- dpctl/_sycl_queue.pyx | 9 +++++++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/dpctl/_sycl_context.pyx b/dpctl/_sycl_context.pyx index 2d30aca3f8..56d8afca27 100644 --- a/dpctl/_sycl_context.pyx +++ b/dpctl/_sycl_context.pyx @@ -39,7 +39,9 @@ from ._backend cimport ( DPCTLDeviceVector_GetAt, DPCTLDeviceVector_Size, DPCTLDeviceVector_Delete, - error_handler_callback + error_handler_callback, + DPCTL_DeviceAndContextPair, + DPCTLDeviceMgr_GetDeviceAndContextPair, ) from ._sycl_queue cimport default_async_error_handler from ._sycl_device cimport SyclDevice @@ -81,11 +83,19 @@ cdef class SyclContext(_SyclContext): cdef int _init_from_one_device(self, SyclDevice device, int props): cdef DPCTLSyclDeviceRef DRef = device.get_device_ref() + cdef DPCTLSyclContextRef CRef = NULL cdef error_handler_callback * eh_callback = \ &default_async_error_handler - cdef DPCTLSyclContextRef CRef = DPCTLContext_Create(DRef, eh_callback, props) - if (CRef is NULL): - return -1 + cdef DPCTL_DeviceAndContextPair dev_ctx + # look up cached contexts for root devices first + dev_ctx = DPCTLDeviceMgr_GetDeviceAndContextPair(DRef) + if (dev_ctx.CRef is NULL) or (dev_ctx.DRef is NULL): + # look-up failed, create a new one + CRef = DPCTLContext_Create(DRef, eh_callback, props) + if (CRef is NULL): + return -1 + else: + CRef = dev_ctx.CRef SyclContext._init_helper(<_SyclContext> self, CRef) return 0 diff --git a/dpctl/_sycl_queue.pyx b/dpctl/_sycl_queue.pyx index dc49c69be6..249f291fba 100644 --- a/dpctl/_sycl_queue.pyx +++ b/dpctl/_sycl_queue.pyx @@ -25,6 +25,7 @@ from ._backend cimport ( _arg_data_type, _backend_type, _queue_property_type, + DPCTLContext_Create, DPCTLContext_Delete, DPCTLDefaultSelector_Create, DPCTLDevice_CreateFromSelector, @@ -262,8 +263,12 @@ cdef class SyclQueue: CRef = DPCTLDeviceMgr_GetCachedContext(DRef) if (CRef is NULL): - DPCTLDevice_Delete(DRef) - return -3 + # look-up failed (was not a root device?) + # create a new context + CRef = DPCTLContext_Create(DRef, NULL, 0) + if (CRef is NULL): + DPCTLDevice_Delete(DRef) + return -3 QRef = DPCTLQueue_Create( CRef, DRef, From 9fe57dc5579268e2b2ab98ac4aa8f93e7f054aa3 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Fri, 26 Mar 2021 08:53:48 -0500 Subject: [PATCH 11/22] Added handling of returned error code --- dpctl/_sycl_context.pyx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/dpctl/_sycl_context.pyx b/dpctl/_sycl_context.pyx index 56d8afca27..8369d907df 100644 --- a/dpctl/_sycl_context.pyx +++ b/dpctl/_sycl_context.pyx @@ -159,7 +159,17 @@ cdef class SyclContext(_SyclContext): dev = SyclDevice(arg) ret = self._init_from_one_device( dev, 0) if (ret < 0): - raise ValueError(ret) + if (ret == -1): + raise ValueError("Context failed to be created.") + if (ret == -2): + raise TypeError("List of devices to create context from must be non-empty.") + if (ret == -3): + raise MemoryError("Could not allocate necessary temporary memory.") + if (ret == -4): + raise ValueError("Internal Error: Could not create a copy of a sycl device.") + if (ret == -5): + raise ValueError("Internal Error: Creation of DeviceVector failed.") + raise ValueError("Unrecognized error code ({}) encountered.".format(ret)) cpdef bool equals (self, SyclContext ctxt): """ Returns true if the SyclContext argument has the same _context_ref From e889a009646030431346168bd0602a28a60b408d Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Fri, 26 Mar 2021 09:55:54 -0500 Subject: [PATCH 12/22] DPCTLContext_GetDevices checks for runtime_error per PR review --- dpctl-capi/source/dpctl_sycl_context_interface.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dpctl-capi/source/dpctl_sycl_context_interface.cpp b/dpctl-capi/source/dpctl_sycl_context_interface.cpp index 90e145769c..084be6ffc9 100644 --- a/dpctl-capi/source/dpctl_sycl_context_interface.cpp +++ b/dpctl-capi/source/dpctl_sycl_context_interface.cpp @@ -144,6 +144,10 @@ DPCTLContext_GetDevices(__dpctl_keep const DPCTLSyclContextRef CRef) // \todo log error std::cerr << ba.what() << '\n'; return nullptr; + } catch (const runtime_error &re) { + // \todo log error + std::cerr << re.what() << '\n'; + return nullptr; } } From 0cf32b9f1ba1767ddcc593ea072933adaf21be34 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Fri, 26 Mar 2021 10:20:39 -0500 Subject: [PATCH 13/22] NULL-initialized DVRef --- dpctl/_sycl_context.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpctl/_sycl_context.pyx b/dpctl/_sycl_context.pyx index 8369d907df..eb4adf35e2 100644 --- a/dpctl/_sycl_context.pyx +++ b/dpctl/_sycl_context.pyx @@ -104,7 +104,7 @@ cdef class SyclContext(_SyclContext): cdef int i = 0 cdef int j cdef size_t num_bytes - cdef DPCTLDeviceVectorRef DVRef + cdef DPCTLDeviceVectorRef DVRef = NULL cdef error_handler_callback * eh_callback = \ &default_async_error_handler cdef DPCTLSyclContextRef CRef = NULL From 02481f37ca08f018201608ca6ddbce88b274d466 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Fri, 26 Mar 2021 10:22:57 -0500 Subject: [PATCH 14/22] Marking *elem with __dpctl_keep per review --- dpctl-capi/include/dpctl_vector.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpctl-capi/include/dpctl_vector.h b/dpctl-capi/include/dpctl_vector.h index cb4850a92b..4ed7ecef31 100644 --- a/dpctl-capi/include/dpctl_vector.h +++ b/dpctl-capi/include/dpctl_vector.h @@ -42,7 +42,7 @@ DPCTL_C_EXTERN_C_BEGIN \ DPCTL_API \ __dpctl_give DPCTL##EL##VectorRef DPCTL##EL##Vector_CreateFromArray( \ - size_t len, DPCTLSycl##EL##Ref *elems); \ + size_t len, __dpctl_keep DPCTLSycl##EL##Ref *elems); \ \ DPCTL_API \ void DPCTL##EL##Vector_Delete(__dpctl_take DPCTL##EL##VectorRef Ref); \ From feeaf8e6a30537bba1905191fe9eaeeb9f5a9621 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Fri, 26 Mar 2021 10:52:58 -0500 Subject: [PATCH 15/22] Change device_count to a property Added two more tests to test_sycl_context.py One of them is skipped due to a bug in DPC++, which is expected to be fixed in update 2 of oneAPI 2021 --- dpctl/_sycl_context.pyx | 7 ++++--- dpctl/tests/test_sycl_context.py | 24 +++++++++++++++++++++++- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/dpctl/_sycl_context.pyx b/dpctl/_sycl_context.pyx index eb4adf35e2..1ccfffa8bf 100644 --- a/dpctl/_sycl_context.pyx +++ b/dpctl/_sycl_context.pyx @@ -207,10 +207,11 @@ cdef class SyclContext(_SyclContext): devices.append(SyclDevice._create(DRef)) DPCTLDeviceVector_Delete(DVRef) return devices - + + @property def device_count (self): """ - Returns the number of sycl devices associated with SyclContext instance. + The number of sycl devices associated with SyclContext instance. """ cdef size_t num_devs = DPCTLContext_DeviceCount(self.get_context_ref()) if num_devs: @@ -224,7 +225,7 @@ cdef class SyclContext(_SyclContext): return "SyclContext" def __repr__(self): - cdef size_t n = self.device_count() + cdef size_t n = self.device_count if n == 1: return ("".format(hex(id(self)))) else: diff --git a/dpctl/tests/test_sycl_context.py b/dpctl/tests/test_sycl_context.py index 7c72412470..35f02d15ec 100644 --- a/dpctl/tests/test_sycl_context.py +++ b/dpctl/tests/test_sycl_context.py @@ -14,12 +14,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -""" Defines unit test cases for the SyclQueue class. +""" Defines unit test cases for the SyclContxt class. """ import dpctl import pytest + list_of_standard_selectors = [ dpctl.select_accelerator_device, dpctl.select_cpu_device, @@ -358,3 +359,24 @@ def test_context_equals(): except dpctl.SyclQueueCreationError: pytest.skip() assert ctx0.equals(ctx1) + + +def test_context_can_be_used_in_queue(valid_filter): + try: + ctx = dpctl.SyclContext(valid_filter) + except ValueError: + pytest.skip() + devs = ctx.get_devices() + assert len(devs) == ctx.device_count + for d in devs: + q = dpctl.SyclQueue(ctx, d) + + +@pytest.mark.xfail(reason="DPC++ bug in device equality") +def test_context_can_be_used_in_queue2(valid_filter): + d = dpctl.SyclDevice(valid_filter) + if d.default_selector_score < 0: + # skip test for devices rejected by default selector + pytest.skip() + ctx = dpctl.SyclContext(d) + q = dpctl.SyclQueue(ctx, d) From 51bbb9eaad9c027242e6c8a6017a970397719ec4 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Mon, 29 Mar 2021 16:05:20 -0500 Subject: [PATCH 16/22] Added docstring for _create explaining that it deleted arg Also fixed alignment of colons in the docstring. --- dpctl/_sycl_queue.pyx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dpctl/_sycl_queue.pyx b/dpctl/_sycl_queue.pyx index 249f291fba..b44b98a58d 100644 --- a/dpctl/_sycl_queue.pyx +++ b/dpctl/_sycl_queue.pyx @@ -292,7 +292,7 @@ cdef class SyclQueue: Creates device from device selector, then calls helper function above. Returns: - 0 : normal execution + 0 : normal execution -1 : filter selector could not be created (malformed?) -2 : Device could not be created from filter selector -3 : Context creation/look-up failed @@ -363,12 +363,18 @@ cdef class SyclQueue: @staticmethod cdef SyclQueue _create(DPCTLSyclQueueRef qref): + """ + This function calls DPCTLQueue_Delete(qref). + The user of this function must pass a copy to keep the + qref argument alive. + """ if qref is NULL: raise SyclQueueCreationError("Queue creation failed.") cdef _SyclQueue ret = _SyclQueue.__new__(_SyclQueue) ret._context = SyclContext._create(DPCTLQueue_GetContext(qref)) ret._device = SyclDevice._create(DPCTLQueue_GetDevice(qref)) ret._queue_ref = qref + # ret is a temporary, and will call DPCTLQueue_Delete(qref) return SyclQueue(ret) @staticmethod From d3a24930f4fb62555fdac149a321b1976b829ac0 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Mon, 29 Mar 2021 16:25:34 -0500 Subject: [PATCH 17/22] Fixed build break by removing use of DeviceAndContextPair --- dpctl/_sycl_context.pyx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/dpctl/_sycl_context.pyx b/dpctl/_sycl_context.pyx index 1ccfffa8bf..b1f4de7019 100644 --- a/dpctl/_sycl_context.pyx +++ b/dpctl/_sycl_context.pyx @@ -40,8 +40,7 @@ from ._backend cimport ( DPCTLDeviceVector_Size, DPCTLDeviceVector_Delete, error_handler_callback, - DPCTL_DeviceAndContextPair, - DPCTLDeviceMgr_GetDeviceAndContextPair, + DPCTLDeviceMgr_GetCachedContext, ) from ._sycl_queue cimport default_async_error_handler from ._sycl_device cimport SyclDevice @@ -71,6 +70,11 @@ cdef class SyclContext(_SyclContext): @staticmethod cdef SyclContext _create (DPCTLSyclContextRef ctxt): + """ + Calls DPCTLContext_Delete(ctxt). + + Users should pass a copy if they intend to keep the argument ctxt alive. + """ cdef _SyclContext ret = <_SyclContext>_SyclContext.__new__(_SyclContext) SyclContext._init_helper(ret, ctxt) return SyclContext(ret) @@ -86,16 +90,13 @@ cdef class SyclContext(_SyclContext): cdef DPCTLSyclContextRef CRef = NULL cdef error_handler_callback * eh_callback = \ &default_async_error_handler - cdef DPCTL_DeviceAndContextPair dev_ctx # look up cached contexts for root devices first - dev_ctx = DPCTLDeviceMgr_GetDeviceAndContextPair(DRef) - if (dev_ctx.CRef is NULL) or (dev_ctx.DRef is NULL): + CRef = DPCTLDeviceMgr_GetCachedContext(DRef) + if (CRef is NULL): # look-up failed, create a new one CRef = DPCTLContext_Create(DRef, eh_callback, props) if (CRef is NULL): return -1 - else: - CRef = dev_ctx.CRef SyclContext._init_helper(<_SyclContext> self, CRef) return 0 From fb0b8f011b195581bc35f080cd1242fd0b7457d2 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Mon, 29 Mar 2021 16:55:37 -0500 Subject: [PATCH 18/22] Added doc string for _create method Prominently state that _create deletes its argument reference variable. DPCTL opaque pointers will be consumed by most _init_* functions. Enabled test which was xfailed waiting for the DPCPP update. --- dpctl/_sycl_device.pyx | 13 +++++++++---- dpctl/_sycl_platform.pyx | 9 +++++++-- dpctl/tests/test_sycl_context.py | 1 - 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/dpctl/_sycl_device.pyx b/dpctl/_sycl_device.pyx index 1bfcff7313..9a91937102 100644 --- a/dpctl/_sycl_device.pyx +++ b/dpctl/_sycl_device.pyx @@ -132,9 +132,16 @@ cdef class SyclDevice(_SyclDevice): @staticmethod cdef SyclDevice _create(DPCTLSyclDeviceRef dref): + """ + This function calls DPCTLDevice_Delete(dref). + + The user of this function must pass a copy to keep the + dref argument alive. + """ cdef _SyclDevice ret = _SyclDevice.__new__(_SyclDevice) # Initialize the attributes of the SyclDevice object SyclDevice._init_helper(<_SyclDevice> ret, dref) + # ret is a temporary, and _SyclDevice.__dealloc__ will delete dref return SyclDevice(ret) cdef int _init_from__SyclDevice(self, _SyclDevice other): @@ -152,6 +159,8 @@ cdef class SyclDevice(_SyclDevice): cdef int _init_from_selector(self, DPCTLSyclDeviceSelectorRef DSRef): # Initialize the attributes of the SyclDevice object cdef DPCTLSyclDeviceRef DRef = DPCTLDevice_CreateFromSelector(DSRef) + # Free up the device selector + DPCTLDeviceSelector_Delete(DSRef) if DRef is NULL: return -1 else: @@ -172,8 +181,6 @@ cdef class SyclDevice(_SyclDevice): raise ValueError( "Could not create a SyclDevice with the selector string" ) - # Free up the device selector - DPCTLDeviceSelector_Delete(DSRef) elif isinstance(arg, unicode): string = bytes(unicode(arg), "utf-8") filter_c_str = string @@ -183,8 +190,6 @@ cdef class SyclDevice(_SyclDevice): raise ValueError( "Could not create a SyclDevice with the selector string" ) - # Free up the device selector - DPCTLDeviceSelector_Delete(DSRef) elif isinstance(arg, _SyclDevice): ret = self._init_from__SyclDevice(arg) if ret == -1: diff --git a/dpctl/_sycl_platform.pyx b/dpctl/_sycl_platform.pyx index a27638a93f..0a028e7da7 100644 --- a/dpctl/_sycl_platform.pyx +++ b/dpctl/_sycl_platform.pyx @@ -75,6 +75,12 @@ cdef class SyclPlatform(_SyclPlatform): @staticmethod cdef SyclPlatform _create(DPCTLSyclPlatformRef pref): + """ + This function calls DPCTLPlatform_Delete(pref). + + The user of this function must pass a copy to keep the + pref argument alive. + """ cdef _SyclPlatform p = _SyclPlatform.__new__(_SyclPlatform) # Initialize the attributes of the SyclPlatform object SyclPlatform._init_helper(<_SyclPlatform>p, pref) @@ -92,13 +98,12 @@ cdef class SyclPlatform(_SyclPlatform): cdef DPCTLSyclDeviceSelectorRef DSRef = NULL DSRef = DPCTLFilterSelector_Create(string) ret = self._init_from_selector(DSRef) - # Free up the device selector - DPCTLDeviceSelector_Delete(DSRef) return ret cdef int _init_from_selector(self, DPCTLSyclDeviceSelectorRef DSRef): # Initialize the SyclPlatform from a DPCTLSyclDeviceSelectorRef cdef DPCTLSyclPlatformRef PRef = DPCTLPlatform_CreateFromSelector(DSRef) + DPCTLDeviceSelector_Delete(DSRef) if PRef is NULL: return -1 else: diff --git a/dpctl/tests/test_sycl_context.py b/dpctl/tests/test_sycl_context.py index 35f02d15ec..8685b2431d 100644 --- a/dpctl/tests/test_sycl_context.py +++ b/dpctl/tests/test_sycl_context.py @@ -372,7 +372,6 @@ def test_context_can_be_used_in_queue(valid_filter): q = dpctl.SyclQueue(ctx, d) -@pytest.mark.xfail(reason="DPC++ bug in device equality") def test_context_can_be_used_in_queue2(valid_filter): d = dpctl.SyclDevice(valid_filter) if d.default_selector_score < 0: From cb88f4f13583cccc3d5ccbd87581c7a214a4ebae Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Wed, 31 Mar 2021 14:57:12 -0500 Subject: [PATCH 19/22] simplification of test_sycl_context_interface per PR feedback --- .../tests/test_sycl_context_interface.cpp | 47 ++++--------------- 1 file changed, 8 insertions(+), 39 deletions(-) diff --git a/dpctl-capi/tests/test_sycl_context_interface.cpp b/dpctl-capi/tests/test_sycl_context_interface.cpp index 697fb0fcb5..f6ae1a7d09 100644 --- a/dpctl-capi/tests/test_sycl_context_interface.cpp +++ b/dpctl-capi/tests/test_sycl_context_interface.cpp @@ -43,16 +43,20 @@ DEFINE_SIMPLE_CONVERSION_FUNCTIONS(vector_class, struct TestDPCTLContextInterface : public ::testing::TestWithParam { - DPCTLSyclDeviceSelectorRef DSRef = nullptr; + DPCTLSyclDeviceRef DRef = nullptr; TestDPCTLContextInterface() { - EXPECT_NO_FATAL_FAILURE(DSRef = DPCTLFilterSelector_Create(GetParam())); + auto DS = DPCTLFilterSelector_Create(GetParam()); + if (DS) { + EXPECT_NO_FATAL_FAILURE(DRef = DPCTLDevice_CreateFromSelector(DS)); + } + DPCTLDeviceSelector_Delete(DS); } void SetUp() { - if (!DSRef) { + if (!DRef) { auto message = "Skipping as no device of type " + std::string(GetParam()) + "."; GTEST_SKIP_(message.c_str()); @@ -61,20 +65,15 @@ struct TestDPCTLContextInterface : public ::testing::TestWithParam ~TestDPCTLContextInterface() { - EXPECT_NO_FATAL_FAILURE(DPCTLDeviceSelector_Delete(DSRef)); + EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(DRef)); } }; TEST_P(TestDPCTLContextInterface, Chk_Create) { DPCTLSyclContextRef CRef = nullptr; - DPCTLSyclDeviceRef DRef = nullptr; - EXPECT_NO_FATAL_FAILURE(DRef = DPCTLDevice_CreateFromSelector(DSRef)); - if (!DRef) - GTEST_SKIP_("Device not found"); EXPECT_NO_FATAL_FAILURE(CRef = DPCTLContext_Create(DRef, nullptr, 0)); ASSERT_TRUE(CRef); - EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(DRef)); EXPECT_NO_FATAL_FAILURE(DPCTLContext_Delete(CRef)); } @@ -82,11 +81,7 @@ TEST_P(TestDPCTLContextInterface, Chk_CreateWithDevices) { size_t nCUs = 0; DPCTLSyclContextRef CRef = nullptr; - DPCTLSyclDeviceRef DRef = nullptr; DPCTLDeviceVectorRef DVRef = nullptr; - EXPECT_NO_FATAL_FAILURE(DRef = DPCTLDevice_CreateFromSelector(DSRef)); - if (!DRef) - GTEST_SKIP_("Device not found"); /* TODO: Once we have wrappers for sub-device creation let us use those * functions. @@ -108,7 +103,6 @@ TEST_P(TestDPCTLContextInterface, Chk_CreateWithDevices) GTEST_SKIP_("Skipping creating context for sub-devices"); } } - EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(DRef)); EXPECT_NO_FATAL_FAILURE(DPCTLDeviceVector_Delete(DVRef)); EXPECT_NO_FATAL_FAILURE(DPCTLContext_Delete(CRef)); } @@ -117,12 +111,8 @@ TEST_P(TestDPCTLContextInterface, Chk_CreateWithDevices_GetDevices) { size_t nCUs = 0; DPCTLSyclContextRef CRef = nullptr; - DPCTLSyclDeviceRef DRef = nullptr; DPCTLDeviceVectorRef DVRef = nullptr; DPCTLDeviceVectorRef Res_DVRef = nullptr; - EXPECT_NO_FATAL_FAILURE(DRef = DPCTLDevice_CreateFromSelector(DSRef)); - if (!DRef) - GTEST_SKIP_("Device not found"); /* TODO: Once we have wrappers for sub-device creation let us use those * functions. @@ -151,7 +141,6 @@ TEST_P(TestDPCTLContextInterface, Chk_CreateWithDevices_GetDevices) GTEST_SKIP_("Skipping creating context for sub-devices"); } } - EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(DRef)); EXPECT_NO_FATAL_FAILURE(DPCTLDeviceVector_Delete(DVRef)); EXPECT_NO_FATAL_FAILURE(DPCTLContext_Delete(CRef)); EXPECT_NO_FATAL_FAILURE(DPCTLDeviceVector_Delete(Res_DVRef)); @@ -160,17 +149,12 @@ TEST_P(TestDPCTLContextInterface, Chk_CreateWithDevices_GetDevices) TEST_P(TestDPCTLContextInterface, Chk_GetDevices) { DPCTLSyclContextRef CRef = nullptr; - DPCTLSyclDeviceRef DRef = nullptr; DPCTLDeviceVectorRef DVRef = nullptr; - EXPECT_NO_FATAL_FAILURE(DRef = DPCTLDevice_CreateFromSelector(DSRef)); - if (!DRef) - GTEST_SKIP_("Device not found"); EXPECT_NO_FATAL_FAILURE(CRef = DPCTLContext_Create(DRef, nullptr, 0)); ASSERT_TRUE(CRef); EXPECT_NO_FATAL_FAILURE(DVRef = DPCTLContext_GetDevices(CRef)); ASSERT_TRUE(DVRef); EXPECT_TRUE(DPCTLDeviceVector_Size(DVRef) == 1); - EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(DRef)); EXPECT_NO_FATAL_FAILURE(DPCTLContext_Delete(CRef)); EXPECT_NO_FATAL_FAILURE(DPCTLDeviceVector_Delete(DVRef)); } @@ -178,12 +162,8 @@ TEST_P(TestDPCTLContextInterface, Chk_GetDevices) TEST_P(TestDPCTLContextInterface, Chk_AreEq) { DPCTLSyclContextRef CRef1 = nullptr, CRef2 = nullptr, CRef3 = nullptr; - DPCTLSyclDeviceRef DRef = nullptr; bool are_eq = true, are_not_eq = false; - EXPECT_NO_FATAL_FAILURE(DRef = DPCTLDevice_CreateFromSelector(DSRef)); - if (!DRef) - GTEST_SKIP_("Device not found"); EXPECT_NO_FATAL_FAILURE(CRef1 = DPCTLContext_Create(DRef, nullptr, 0)); EXPECT_NO_FATAL_FAILURE(CRef2 = DPCTLContext_Copy(CRef1)); // TODO: This work till DPC++ does not have a default context per device, @@ -198,7 +178,6 @@ TEST_P(TestDPCTLContextInterface, Chk_AreEq) EXPECT_TRUE(are_eq); EXPECT_FALSE(are_not_eq); - EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(DRef)); EXPECT_NO_FATAL_FAILURE(DPCTLContext_Delete(CRef1)); EXPECT_NO_FATAL_FAILURE(DPCTLContext_Delete(CRef2)); EXPECT_NO_FATAL_FAILURE(DPCTLContext_Delete(CRef3)); @@ -207,12 +186,8 @@ TEST_P(TestDPCTLContextInterface, Chk_AreEq) TEST_P(TestDPCTLContextInterface, Chk_IsHost) { DPCTLSyclContextRef CRef = nullptr; - DPCTLSyclDeviceRef DRef = nullptr; bool is_host_device = false, is_host_context = false; - EXPECT_NO_FATAL_FAILURE(DRef = DPCTLDevice_CreateFromSelector(DSRef)); - if (!DRef) - GTEST_SKIP_("Device not found"); EXPECT_NO_FATAL_FAILURE(CRef = DPCTLContext_Create(DRef, nullptr, 0)); ASSERT_TRUE(CRef); @@ -220,20 +195,15 @@ TEST_P(TestDPCTLContextInterface, Chk_IsHost) EXPECT_NO_FATAL_FAILURE(is_host_context = DPCTLContext_IsHost(CRef)); EXPECT_TRUE(is_host_device == is_host_context); - EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(DRef)); EXPECT_NO_FATAL_FAILURE(DPCTLContext_Delete(CRef)); } TEST_P(TestDPCTLContextInterface, Chk_GetBackend) { DPCTLSyclContextRef CRef = nullptr; - DPCTLSyclDeviceRef DRef = nullptr; DPCTLSyclBackendType context_backend = DPCTL_UNKNOWN_BACKEND, device_backend = DPCTL_UNKNOWN_BACKEND; - EXPECT_NO_FATAL_FAILURE(DRef = DPCTLDevice_CreateFromSelector(DSRef)); - if (!DRef) - GTEST_SKIP_("Device not found"); EXPECT_NO_FATAL_FAILURE(CRef = DPCTLContext_Create(DRef, nullptr, 0)); ASSERT_TRUE(CRef); @@ -241,7 +211,6 @@ TEST_P(TestDPCTLContextInterface, Chk_GetBackend) EXPECT_NO_FATAL_FAILURE(context_backend = DPCTLContext_GetBackend(CRef)); EXPECT_TRUE(device_backend == context_backend); - EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(DRef)); EXPECT_NO_FATAL_FAILURE(DPCTLContext_Delete(CRef)); } From fe5782b4e7dddbc463073a1993db482c49d2f71e Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Wed, 31 Mar 2021 15:11:14 -0500 Subject: [PATCH 20/22] removed catch of bad_alloc per PR feedback --- dpctl-capi/source/dpctl_sycl_context_interface.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/dpctl-capi/source/dpctl_sycl_context_interface.cpp b/dpctl-capi/source/dpctl_sycl_context_interface.cpp index 084be6ffc9..809f963435 100644 --- a/dpctl-capi/source/dpctl_sycl_context_interface.cpp +++ b/dpctl-capi/source/dpctl_sycl_context_interface.cpp @@ -159,14 +159,8 @@ size_t DPCTLContext_DeviceCount(__dpctl_keep const DPCTLSyclContextRef CRef) "input is a nullptr\n"; return 0; } - try { - auto Devices = Context->get_devices(); - return Devices.size(); - } catch (std::bad_alloc const &ba) { - // \todo log error - std::cerr << ba.what() << '\n'; - return 0; - } + const auto Devices = Context->get_devices(); + return Devices.size(); } bool DPCTLContext_IsHost(__dpctl_keep const DPCTLSyclContextRef CtxRef) From fdfbe1bfccbef9f25c3016a4765ec0bd72bc8ed0 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Wed, 31 Mar 2021 15:39:22 -0500 Subject: [PATCH 21/22] DPCTLDeviceVector_CreateFromArray now copies devices too Modified test to reflect that. --- dpctl-capi/source/dpctl_vector_templ.cpp | 10 +++++++--- dpctl-capi/tests/test_sycl_context_interface.cpp | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/dpctl-capi/source/dpctl_vector_templ.cpp b/dpctl-capi/source/dpctl_vector_templ.cpp index 737a47aaf7..470b0e7097 100644 --- a/dpctl-capi/source/dpctl_vector_templ.cpp +++ b/dpctl-capi/source/dpctl_vector_templ.cpp @@ -49,7 +49,7 @@ __dpctl_give VECTOR(EL) FN(EL, Create)() /*! * @brief Creates a new std::vector of the opaque SYCL pointer types from given - * C array. + * C array with deep copy. * * @return A new dynamically allocated std::vector of opaque pointer types. */ @@ -57,8 +57,12 @@ __dpctl_give VECTOR(EL) FN(EL, CreateFromArray)(size_t n, __dpctl_keep SYCLREF(EL) * elems) { try { - auto Vec = new vector_class(n); - Vec->assign(elems, elems + n); + auto Vec = new vector_class(); + for (size_t i = 0; i < n; ++i) { + auto Ref = unwrap(elems[i]); + Vec->emplace_back( + wrap(new std::remove_pointer::type(*Ref))); + } return wrap(Vec); } catch (std::bad_alloc const &ba) { return nullptr; diff --git a/dpctl-capi/tests/test_sycl_context_interface.cpp b/dpctl-capi/tests/test_sycl_context_interface.cpp index f6ae1a7d09..8f647130da 100644 --- a/dpctl-capi/tests/test_sycl_context_interface.cpp +++ b/dpctl-capi/tests/test_sycl_context_interface.cpp @@ -126,7 +126,7 @@ TEST_P(TestDPCTLContextInterface, Chk_CreateWithDevices_GetDevices) const size_t len = subDevices.size(); auto ar = new DPCTLSyclDeviceRef[len]; for (size_t i = 0; i < len; ++i) { - ar[i] = wrap(new device(subDevices.at(i))); + ar[i] = wrap(&subDevices.at(i)); } EXPECT_NO_FATAL_FAILURE( DVRef = DPCTLDeviceVector_CreateFromArray(len, ar)); From f5846cf9273683eb58c97f060937a2878e631a33 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Wed, 31 Mar 2021 15:43:10 -0500 Subject: [PATCH 22/22] Since DPCTLDeviceVector_CreateFromArray makes copies of devices references by opaque pointers in the given array, making these copies is removed from Cython. --- dpctl/_sycl_context.pyx | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/dpctl/_sycl_context.pyx b/dpctl/_sycl_context.pyx index b1f4de7019..c61eb0defa 100644 --- a/dpctl/_sycl_context.pyx +++ b/dpctl/_sycl_context.pyx @@ -88,8 +88,8 @@ cdef class SyclContext(_SyclContext): cdef int _init_from_one_device(self, SyclDevice device, int props): cdef DPCTLSyclDeviceRef DRef = device.get_device_ref() cdef DPCTLSyclContextRef CRef = NULL - cdef error_handler_callback * eh_callback = \ - &default_async_error_handler + cdef error_handler_callback * eh_callback = ( + &default_async_error_handler) # look up cached contexts for root devices first CRef = DPCTLDeviceMgr_GetCachedContext(DRef) if (CRef is NULL): @@ -103,11 +103,11 @@ cdef class SyclContext(_SyclContext): cdef int _init_from_devices(self, object devices, int props): cdef int num_devices = len(devices) cdef int i = 0 - cdef int j + cdef int j = 0 cdef size_t num_bytes cdef DPCTLDeviceVectorRef DVRef = NULL - cdef error_handler_callback * eh_callback = \ - &default_async_error_handler + cdef error_handler_callback * eh_callback = ( + &default_async_error_handler) cdef DPCTLSyclContextRef CRef = NULL cdef DPCTLSyclDeviceRef *elems @@ -120,17 +120,14 @@ cdef class SyclContext(_SyclContext): if not isinstance(dev, SyclDevice): elems[i] = NULL else: - elems[i] = DPCTLDevice_Copy((dev).get_device_ref()) + elems[i] = (dev).get_device_ref() if (elems[i] is NULL): - for j in range(0, i): - DPCTLDevice_Delete(elems[j]) PyMem_Free(elems) return -4 i = i + 1 + # CreateFromArray will make copies of devices referenced by elems DVRef = DPCTLDeviceVector_CreateFromArray(num_devices, elems) if (DVRef is NULL): - for j in range(num_devices): - DPCTLDevice_Delete(elems[j]) PyMem_Free(elems) return -5 PyMem_Free(elems)