From 86625c83bcc936c2c9bb9d7d9d8fe53df382cfee Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Fri, 3 Sep 2021 14:18:57 -0500 Subject: [PATCH 1/9] Updated some method calls, e.g. removed SyclQueue.get_sycl_backend() method --- dpctl/_sycl_queue_manager.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpctl/_sycl_queue_manager.pyx b/dpctl/_sycl_queue_manager.pyx index 363d3381c4..53d5058d04 100644 --- a/dpctl/_sycl_queue_manager.pyx +++ b/dpctl/_sycl_queue_manager.pyx @@ -78,7 +78,7 @@ cdef class _SyclQueueManager: Returns: backend_type: The SYCL backend for the currently selected queue. """ - return self.get_current_queue().get_sycl_backend() + return self.get_current_queue().backend cpdef get_current_device_type(self): """ @@ -88,7 +88,7 @@ cdef class _SyclQueueManager: device_type: The SYCL device type for the currently selected queue. Possible values can be gpu, cpu, accelerator, or host. """ - return self.get_current_queue().get_sycl_device().device_type + return self.get_current_queue().sycl_device.device_type cpdef SyclQueue get_current_queue(self): """ From 21b990e33818700c68de5fa4c4b6bd4056dcef90 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Fri, 3 Sep 2021 16:51:09 -0500 Subject: [PATCH 2/9] Added check in deallocation that sycl.queue is SyclQueue type Initialized DPCTLSyclUSMRef reference variable. --- dpctl/memory/_memory.pyx | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/dpctl/memory/_memory.pyx b/dpctl/memory/_memory.pyx index beacd34dc6..69c62f39ed 100644 --- a/dpctl/memory/_memory.pyx +++ b/dpctl/memory/_memory.pyx @@ -34,6 +34,7 @@ from dpctl._backend cimport ( # noqa: E211 DPCTLaligned_alloc_host, DPCTLaligned_alloc_shared, DPCTLContext_Delete, + DPCTLDevice_Copy, DPCTLEvent_Delete, DPCTLEvent_Wait, DPCTLfree_with_queue, @@ -48,6 +49,7 @@ from dpctl._backend cimport ( # noqa: E211 DPCTLSyclContextRef, DPCTLSyclDeviceRef, DPCTLSyclEventRef, + DPCTLSyclQueueRef, DPCTLSyclUSMRef, DPCTLUSM_GetPointerDevice, DPCTLUSM_GetPointerType, @@ -138,7 +140,7 @@ cdef class _Memory: cdef _cinit_alloc(self, Py_ssize_t alignment, Py_ssize_t nbytes, bytes ptr_type, SyclQueue queue): - cdef DPCTLSyclUSMRef p + cdef DPCTLSyclUSMRef p = NULL self._cinit_empty() @@ -215,10 +217,12 @@ cdef class _Memory: ) def __dealloc__(self): - if (self.refobj is None and self.memory_ptr): - DPCTLfree_with_queue( - self.memory_ptr, self.queue.get_queue_ref() - ) + if (self.refobj is None): + if self.memory_ptr: + if (type(self.queue) is SyclQueue): + DPCTLfree_with_queue( + self.memory_ptr, self.queue.get_queue_ref() + ) self._cinit_empty() cdef _getbuffer(self, Py_buffer *buffer, int flags): @@ -267,7 +271,7 @@ cdef class _Memory: property _queue: """ :class:`dpctl.SyclQueue` with :class:`dpctl.SyclContext` the - USM pointer is bound to and :class:`dpctl.SyclDevice` it was + USM allocation is bound to and :class:`dpctl.SyclDevice` it was allocated on. """ def __get__(self): @@ -477,8 +481,10 @@ cdef class _Memory: cdef DPCTLSyclDeviceRef dref = DPCTLUSM_GetPointerDevice( p, ctx.get_context_ref() ) - - return SyclDevice._create(dref) + cdef DPCTLSyclDeviceRef dref_copy = DPCTLDevice_Copy(dref) + if (dref_copy is NULL): + raise RuntimeError("Could not create a copy of sycl device") + return SyclDevice._create(dref_copy) # deletes the argument @staticmethod cdef bytes get_pointer_type(DPCTLSyclUSMRef p, SyclContext ctx): From d57e9536701b42e5e39f889f2d67cb1c227c4bc8 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Sun, 5 Sep 2021 13:34:15 -0500 Subject: [PATCH 3/9] Fixed a typo in expected capsule name "SyclContexRef"->"SyclContextRef" --- dpctl/memory/_sycl_usm_array_interface_utils.pxi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpctl/memory/_sycl_usm_array_interface_utils.pxi b/dpctl/memory/_sycl_usm_array_interface_utils.pxi index 4606caeb17..446515e5d4 100644 --- a/dpctl/memory/_sycl_usm_array_interface_utils.pxi +++ b/dpctl/memory/_sycl_usm_array_interface_utils.pxi @@ -54,7 +54,7 @@ cdef DPCTLSyclQueueRef get_queue_ref_from_ptr_and_syclobj( if pycapsule.PyCapsule_IsValid(cap, "SyclQueueRef"): q = SyclQueue(cap) return _queue_ref_copy_from_SyclQueue(ptr, q) - elif pycapsule.PyCapsule_IsValid(cap, "SyclContexRef"): + elif pycapsule.PyCapsule_IsValid(cap, "SyclContextRef"): ctx = SyclContext(cap) return _queue_ref_copy_from_USMRef_and_SyclContext(ptr, ctx) else: From 5c0132c783067a56df972f08353bea8ef68e3d4d Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Fri, 3 Sep 2021 09:30:55 -0500 Subject: [PATCH 4/9] Transition test_sycl_usm to use pytest --- dpctl/tests/test_sycl_usm.py | 644 +++++++++++++++++------------------ 1 file changed, 312 insertions(+), 332 deletions(-) diff --git a/dpctl/tests/test_sycl_usm.py b/dpctl/tests/test_sycl_usm.py index 27287c6b3d..396cac5c7e 100644 --- a/dpctl/tests/test_sycl_usm.py +++ b/dpctl/tests/test_sycl_usm.py @@ -17,9 +17,8 @@ """Defines unit test cases for the Memory classes in _memory.pyx. """ -import unittest - import numpy as np +import pytest import dpctl from dpctl.memory import ( @@ -45,212 +44,215 @@ def __sycl_usm_array_interface(self): return iface -class TestMemory(unittest.TestCase): - @unittest.skipUnless( - has_sycl_platforms(), "No SYCL devices except the default host device." - ) - def test_memory_create(self): - nbytes = 1024 - queue = dpctl.get_current_queue() - mobj = MemoryUSMShared(nbytes, alignment=64, queue=queue) - self.assertEqual(mobj.nbytes, nbytes) - self.assertTrue(hasattr(mobj, "__sycl_usm_array_interface__")) - - @unittest.skipUnless( - has_sycl_platforms(), "No SYCL devices except the default host device." - ) - def test_memory_create_with_np(self): - nbytes = 16384 - mobj = dpctl.memory.MemoryUSMShared(np.int64(nbytes)) - self.assertEqual(mobj.nbytes, nbytes) - self.assertTrue(hasattr(mobj, "__sycl_usm_array_interface__")) - - def _create_memory(self): - nbytes = 1024 - queue = dpctl.get_current_queue() - mobj = MemoryUSMShared(nbytes, alignment=64, queue=queue) - return mobj - - def _create_host_buf(self, nbytes): - ba = bytearray(nbytes) - for i in range(nbytes): - ba[i] = (i % 32) + ord("a") - return ba - - @unittest.skipUnless( - has_sycl_platforms(), "No SYCL devices except the default host device." - ) - def test_memory_without_context(self): - mobj = self._create_memory() - - # Without context - self.assertEqual(mobj.get_usm_type(), "shared") - - @unittest.skipUnless(has_cpu(), "No OpenCL CPU queues available") - def test_memory_cpu_context(self): - mobj = self._create_memory() - - # CPU context - with dpctl.device_context("opencl:cpu:0"): - # type respective to the context in which - # memory was created - usm_type = mobj.get_usm_type() - self.assertEqual(usm_type, "shared") - - current_queue = dpctl.get_current_queue() - # type as view from current queue - usm_type = mobj.get_usm_type(current_queue) - # type can be unknown if current queue is - # not in the same SYCL context - self.assertTrue(usm_type in ["unknown", "shared"]) - - @unittest.skipUnless(has_gpu(), "No OpenCL GPU queues available") - def test_memory_gpu_context(self): - mobj = self._create_memory() - - # GPU context - with dpctl.device_context("opencl:gpu:0"): - usm_type = mobj.get_usm_type() - self.assertEqual(usm_type, "shared") - current_queue = dpctl.get_current_queue() - usm_type = mobj.get_usm_type(current_queue) - self.assertTrue(usm_type in ["unknown", "shared"]) - - @unittest.skipUnless( - has_sycl_platforms(), "No SYCL devices except the default host device." - ) - def test_buffer_protocol(self): - mobj = self._create_memory() - mv1 = memoryview(mobj) - mv2 = memoryview(mobj) - self.assertEqual(mv1, mv2) - - @unittest.skipUnless( - has_sycl_platforms(), "No SYCL devices except the default host device." - ) - def test_copy_host_roundtrip(self): - mobj = self._create_memory() - host_src_obj = self._create_host_buf(mobj.nbytes) - mobj.copy_from_host(host_src_obj) - host_dest_obj = mobj.copy_to_host() - del mobj - self.assertEqual(host_src_obj, host_dest_obj) - - @unittest.skipUnless( - has_sycl_platforms(), "No SYCL devices except the default host device." - ) - def test_zero_copy(self): - mobj = self._create_memory() - mobj2 = type(mobj)(mobj) +@pytest.mark.skipif( + not has_sycl_platforms(), + reason="No SYCL devices except the default host device.", +) +def test_memory_create(): + nbytes = 1024 + queue = dpctl.get_current_queue() + mobj = MemoryUSMShared(nbytes, alignment=64, queue=queue) + assert mobj.nbytes == nbytes + assert hasattr(mobj, "__sycl_usm_array_interface__") - self.assertTrue(mobj2.reference_obj is mobj) - mobj_data = mobj.__sycl_usm_array_interface__["data"] - mobj2_data = mobj2.__sycl_usm_array_interface__["data"] - self.assertEqual(mobj_data, mobj2_data) - @unittest.skipUnless( - has_sycl_platforms(), "No SYCL devices except the default host device." - ) - def test_pickling(self): - import pickle - - mobj = self._create_memory() - host_src_obj = self._create_host_buf(mobj.nbytes) - mobj.copy_from_host(host_src_obj) - - mobj_reconstructed = pickle.loads(pickle.dumps(mobj)) - self.assertEqual( - type(mobj), - type(mobj_reconstructed), - "Pickling should preserve type", - ) - self.assertEqual( - mobj.tobytes(), - mobj_reconstructed.tobytes(), - "Pickling should preserve buffer content", - ) - self.assertNotEqual( - mobj._pointer, - mobj_reconstructed._pointer, - "Pickling/unpickling changes pointer", - ) - - -class _TestMemoryUSMBase: - """Base tests for MemoryUSM*""" - - def setUp(self): - pass - - @unittest.skipUnless( - has_sycl_platforms(), "No SYCL devices except the default host device." - ) - def test_create_with_size_and_alignment_and_queue(self): - q = dpctl.get_current_queue() - m = self.MemoryUSMClass(1024, alignment=64, queue=q) - self.assertEqual(m.nbytes, 1024) - self.assertEqual(m.get_usm_type(), self.usm_type) - - @unittest.skipUnless( - has_sycl_platforms(), "No SYCL devices except the default host device." - ) - def test_create_with_size_and_queue(self): - q = dpctl.get_current_queue() - m = self.MemoryUSMClass(1024, queue=q) - self.assertEqual(m.nbytes, 1024) - self.assertEqual(m.get_usm_type(), self.usm_type) - - @unittest.skipUnless( - has_sycl_platforms(), "No SYCL devices except the default host device." - ) - def test_create_with_size_and_alignment(self): - m = self.MemoryUSMClass(1024, alignment=64) - self.assertEqual(m.nbytes, 1024) - self.assertEqual(m.get_usm_type(), self.usm_type) +@pytest.mark.skipif( + not has_sycl_platforms(), + reason="No SYCL devices except the default host device.", +) +def test_memory_create_with_np(): + nbytes = 16384 + mobj = dpctl.memory.MemoryUSMShared(np.int64(nbytes)) + assert mobj.nbytes == nbytes + assert hasattr(mobj, "__sycl_usm_array_interface__") - @unittest.skipUnless( - has_sycl_platforms(), "No SYCL devices except the default host device." - ) - def test_create_with_only_size(self): - m = self.MemoryUSMClass(1024) - self.assertEqual(m.nbytes, 1024) - self.assertEqual(m.get_usm_type(), self.usm_type) - @unittest.skipUnless( - has_sycl_platforms(), "No SYCL Devices except the default host device." - ) - def test_sycl_usm_array_interface(self): - m = self.MemoryUSMClass(256) - m2 = Dummy(m.nbytes) - hb = np.random.randint(0, 256, size=256, dtype="|u1") - m2.copy_from_host(hb) - # test that USM array interface works with SyclContext as 'syclobj' - m.copy_from_device(m2) - self.assertTrue(np.array_equal(m.copy_to_host(), hb)) +def _create_memory(): + nbytes = 1024 + queue = dpctl.get_current_queue() + mobj = MemoryUSMShared(nbytes, alignment=64, queue=queue) + return mobj + + +def _create_host_buf(nbytes): + ba = bytearray(nbytes) + for i in range(nbytes): + ba[i] = (i % 32) + ord("a") + return ba + + +@pytest.mark.skipif( + not has_sycl_platforms(), + reason="No SYCL devices except the default host device.", +) +def test_memory_without_context(): + mobj = _create_memory() + + # Without context + assert mobj.get_usm_type() == "shared" + + +@pytest.mark.skipif(not has_cpu(), reason="No SYCL CPU device available.") +def test_memory_cpu_context(): + mobj = _create_memory() + + # CPU context + with dpctl.device_context("opencl:cpu:0"): + # type respective to the context in which + # memory was created + usm_type = mobj.get_usm_type() + assert usm_type == "shared" + + current_queue = dpctl.get_current_queue() + # type as view from current queue + usm_type = mobj.get_usm_type(current_queue) + # type can be unknown if current queue is + # not in the same SYCL context + assert usm_type in ["unknown", "shared"] + +@pytest.mark.skipif(not has_gpu(), reason="No OpenCL GPU queues available") +def test_memory_gpu_context(): + mobj = _create_memory() -class TestMemoryUSMShared(_TestMemoryUSMBase, unittest.TestCase): - """Tests for MemoryUSMShared""" + # GPU context + with dpctl.device_context("opencl:gpu:0"): + usm_type = mobj.get_usm_type() + assert usm_type == "shared" + current_queue = dpctl.get_current_queue() + usm_type = mobj.get_usm_type(current_queue) + assert usm_type in ["unknown", "shared"] - def setUp(self): - self.MemoryUSMClass = MemoryUSMShared - self.usm_type = "shared" +@pytest.mark.skipif( + not has_sycl_platforms(), + reason="No SYCL devices except the default host device.", +) +def test_buffer_protocol(): + mobj = _create_memory() + mv1 = memoryview(mobj) + mv2 = memoryview(mobj) + assert mv1 == mv2 + + +@pytest.mark.skipif( + not has_sycl_platforms(), + reason="No SYCL devices except the default host device.", +) +def test_copy_host_roundtrip(): + mobj = _create_memory() + host_src_obj = _create_host_buf(mobj.nbytes) + mobj.copy_from_host(host_src_obj) + host_dest_obj = mobj.copy_to_host() + del mobj + assert host_src_obj == host_dest_obj + + +@pytest.mark.skipif( + not has_sycl_platforms(), + reason="No SYCL devices except the default host device.", +) +def test_zero_copy(): + mobj = _create_memory() + mobj2 = type(mobj)(mobj) + + assert mobj2.reference_obj is mobj + mobj_data = mobj.__sycl_usm_array_interface__["data"] + mobj2_data = mobj2.__sycl_usm_array_interface__["data"] + assert mobj_data == mobj2_data + + +@pytest.mark.skipif( + not has_sycl_platforms(), + reason="No SYCL devices except the default host device.", +) +def test_pickling(): + import pickle + + mobj = _create_memory() + host_src_obj = _create_host_buf(mobj.nbytes) + mobj.copy_from_host(host_src_obj) + + mobj_reconstructed = pickle.loads(pickle.dumps(mobj)) + assert type(mobj) == type( + mobj_reconstructed + ), "Pickling should preserve type" + assert ( + mobj.tobytes() == mobj_reconstructed.tobytes() + ), "Pickling should preserve buffer content" + assert ( + mobj._pointer != mobj_reconstructed._pointer + ), "Pickling/unpickling should be changing pointer" + + +@pytest.fixture(params=[MemoryUSMShared, MemoryUSMDevice, MemoryUSMHost]) +def memory_ctor(request): + return request.param + + +def expected_usm_type(ctor): + mapping = { + MemoryUSMShared: "shared", + MemoryUSMDevice: "device", + MemoryUSMHost: "host", + } + return mapping.get(ctor, "unknown") + + +@pytest.mark.skipif( + not has_sycl_platforms(), + reason="No SYCL devices except the default host device.", +) +def test_create_with_size_and_alignment_and_queue(memory_ctor): + q = dpctl.SyclQueue() + m = memory_ctor(1024, alignment=64, queue=q) + assert m.nbytes == 1024 + assert m.get_usm_type() == expected_usm_type(memory_ctor) + + +@pytest.mark.skipif( + not has_sycl_platforms(), + reason="No SYCL devices except the default host device.", +) +def test_create_with_size_and_queue(memory_ctor): + q = dpctl.SyclQueue() + m = memory_ctor(1024, queue=q) + assert m.nbytes == 1024 + assert m.get_usm_type() == expected_usm_type(memory_ctor) -class TestMemoryUSMHost(_TestMemoryUSMBase, unittest.TestCase): - """Tests for MemoryUSMHost""" - def setUp(self): - self.MemoryUSMClass = MemoryUSMHost - self.usm_type = "host" +@pytest.mark.skipif( + not has_sycl_platforms(), + reason="No SYCL devices except the default host device.", +) +def test_create_with_size_and_alignment(memory_ctor): + m = memory_ctor(1024, alignment=64) + assert m.nbytes == 1024 + assert m.get_usm_type() == expected_usm_type(memory_ctor) + +@pytest.mark.skipif( + not has_sycl_platforms(), + reason="No SYCL devices except the default host device.", +) +def test_create_with_only_size(memory_ctor): + m = memory_ctor(1024) + assert m.nbytes == 1024 + assert m.get_usm_type() == expected_usm_type(memory_ctor) -class TestMemoryUSMDevice(_TestMemoryUSMBase, unittest.TestCase): - """Tests for MemoryUSMDevice""" - def setUp(self): - self.MemoryUSMClass = MemoryUSMDevice - self.usm_type = "device" +@pytest.mark.skipif( + not has_sycl_platforms(), + reason="No SYCL devices except the default host device.", +) +def test_sycl_usm_array_interface(memory_ctor): + m = memory_ctor(256) + m2 = Dummy(m.nbytes) + hb = np.random.randint(0, 256, size=256, dtype="|u1") + m2.copy_from_host(hb) + # test that USM array interface works with SyclContext as 'syclobj' + m.copy_from_device(m2) + assert np.array_equal(m.copy_to_host(), hb) class View: @@ -272,140 +274,118 @@ def __sycl_usm_array_interface__(self): return sua_iface -class TestMemoryWithView(unittest.TestCase): - def test_suai_non_contig_1D(self): - """ - Test of zero-copy using sycl_usm_array_interface with non-contiguous - data. - """ - - MemoryUSMClass = MemoryUSMShared - try: - buf = MemoryUSMClass(32) - except Exception: - self.skipTest("MemoryUSMShared could not be allocated") - host_canary = np.full((buf.nbytes,), 77, dtype="|u1") - buf.copy_from_host(host_canary) - n1d = 10 - step_1d = 2 - offset = 8 - v = View(buf, shape=(n1d,), strides=(step_1d,), offset=offset) - buf2 = MemoryUSMClass(v) - expected_nbytes = ( - np.flip( - host_canary[offset : offset + n1d * step_1d : step_1d] - ).ctypes.data - + 1 - - host_canary[offset:].ctypes.data - ) - self.assertEqual(buf2.nbytes, expected_nbytes) - inset_canary = np.arange(0, buf2.nbytes, dtype="|u1") - buf2.copy_from_host(inset_canary) - res = buf.copy_to_host() - del buf - del buf2 - expected_res = host_canary.copy() - expected_res[offset : offset + (n1d - 1) * step_1d + 1] = inset_canary - self.assertTrue(np.array_equal(res, expected_res)) - - def test_suai_non_contig_2D(self): - MemoryUSMClass = MemoryUSMDevice - try: - buf = MemoryUSMClass(20) - except Exception: - self.skipTest("MemoryUSMShared could not be allocated") - host_canary = np.arange(20, dtype="|u1") - buf.copy_from_host(host_canary) - shape_2d = (2, 2) - strides_2d = (10, -2) - offset = 9 - idx = [] - for i0 in range(shape_2d[0]): - for i1 in range(shape_2d[1]): - idx.append(offset + i0 * strides_2d[0] + i1 * strides_2d[1]) - idx.sort() - v = View(buf, shape=shape_2d, strides=strides_2d, offset=offset) - buf2 = MemoryUSMClass(v) - expected_nbytes = idx[-1] - idx[0] + 1 - self.assertEqual(buf2.nbytes, expected_nbytes) - inset_canary = np.full((buf2.nbytes), 255, dtype="|u1") - buf2.copy_from_host(inset_canary) - res = buf.copy_to_host() - del buf - del buf2 - expected_res = host_canary.copy() - expected_res[idx[0] : idx[-1] + 1] = inset_canary - self.assertTrue(np.array_equal(res, expected_res)) - - -class TestAsUSMMemory(unittest.TestCase): - def _with_constructor(self, buffer_cls): - try: - buf = buffer_cls(64) - except Exception: - self.SkipTest( - "{} could not be allocated".format(buffer_cls.__name__) - ) - # reuse queue from buffer's SUAI - v = View(buf, shape=(64,), strides=(1,), offset=0) - m = as_usm_memory(v) - self.assertTrue(m.get_usm_type() == buf.get_usm_type()) - self.assertTrue(m._pointer == buf._pointer) - self.assertTrue(m.sycl_device == buf.sycl_device) - # Use SyclContext - v = View( - buf, shape=(64,), strides=(1,), offset=0, syclobj=buf.sycl_context - ) - m = as_usm_memory(v) - self.assertTrue(m.get_usm_type() == buf.get_usm_type()) - self.assertTrue(m._pointer == buf._pointer) - self.assertTrue(m.sycl_device == buf.sycl_device) - # Use queue capsule - v = View( - buf, - shape=(64,), - strides=(1,), - offset=0, - syclobj=buf._queue._get_capsule(), - ) - m = as_usm_memory(v) - self.assertTrue(m.get_usm_type() == buf.get_usm_type()) - self.assertTrue(m._pointer == buf._pointer) - self.assertTrue(m.sycl_device == buf.sycl_device) - # Use context capsule - v = View( - buf, - shape=(64,), - strides=(1,), - offset=0, - syclobj=buf.sycl_context._get_capsule(), - ) - m = as_usm_memory(v) - self.assertTrue(m.get_usm_type() == buf.get_usm_type()) - self.assertTrue(m._pointer == buf._pointer) - self.assertTrue(m.sycl_device == buf.sycl_device) - # Use filter string - v = View( - buf, - shape=(64,), - strides=(1,), - offset=0, - syclobj=buf.sycl_device.filter_string, - ) - m = as_usm_memory(v) - self.assertTrue(m.get_usm_type() == buf.get_usm_type()) - self.assertTrue(m._pointer == buf._pointer) - self.assertTrue(m.sycl_device == buf.sycl_device) - - def test_from_device(self): - self._with_constructor(MemoryUSMDevice) - - def test_from_shared(self): - self._with_constructor(MemoryUSMShared) - - def test_from_host(self): - self._with_constructor(MemoryUSMHost) - - -if __name__ == "__main__": - unittest.main() +def test_suai_non_contig_1D(memory_ctor): + """ + Test of zero-copy using sycl_usm_array_interface with non-contiguous + data. + """ + + try: + buf = memory_ctor(32) + except Exception: + pytest.skip("{} could not be allocated".format(memory_ctor.__name__)) + host_canary = np.full((buf.nbytes,), 77, dtype="|u1") + buf.copy_from_host(host_canary) + n1d = 10 + step_1d = 2 + offset = 8 + v = View(buf, shape=(n1d,), strides=(step_1d,), offset=offset) + buf2 = memory_ctor(v) + expected_nbytes = ( + np.flip( + host_canary[offset : offset + n1d * step_1d : step_1d] + ).ctypes.data + + 1 + - host_canary[offset:].ctypes.data + ) + assert buf2.nbytes == expected_nbytes + inset_canary = np.arange(0, buf2.nbytes, dtype="|u1") + buf2.copy_from_host(inset_canary) + res = buf.copy_to_host() + del buf + del buf2 + expected_res = host_canary.copy() + expected_res[offset : offset + (n1d - 1) * step_1d + 1] = inset_canary + assert np.array_equal(res, expected_res) + + +def test_suai_non_contig_2D(memory_ctor): + try: + buf = memory_ctor(20) + except Exception: + pytest.skip("{} could not be allocated".format(memory_ctor.__name__)) + host_canary = np.arange(20, dtype="|u1") + buf.copy_from_host(host_canary) + shape_2d = (2, 2) + strides_2d = (10, -2) + offset = 9 + idx = [] + for i0 in range(shape_2d[0]): + for i1 in range(shape_2d[1]): + idx.append(offset + i0 * strides_2d[0] + i1 * strides_2d[1]) + idx.sort() + v = View(buf, shape=shape_2d, strides=strides_2d, offset=offset) + buf2 = memory_ctor(v) + expected_nbytes = idx[-1] - idx[0] + 1 + assert buf2.nbytes == expected_nbytes + inset_canary = np.full((buf2.nbytes), 255, dtype="|u1") + buf2.copy_from_host(inset_canary) + res = buf.copy_to_host() + del buf + del buf2 + expected_res = host_canary.copy() + expected_res[idx[0] : idx[-1] + 1] = inset_canary + assert np.array_equal(res, expected_res) + + +def check_view(v): + """ + Memory object created from duck __sycl_usm_array_interface__ argument + should be consistent with the buffer from which the argument was constructed + """ + assert type(v) is View + buf = v.buffer_ + m = as_usm_memory(v) + assert m.get_usm_type() == buf.get_usm_type() + assert m._pointer == buf._pointer + assert m.sycl_device == buf.sycl_device + + +def test_with_constructor(memory_ctor): + try: + buf = memory_ctor(64) + except Exception: + pytest.skip("{} could not be allocated".format(memory_ctor.__name__)) + # reuse queue from buffer's SUAI + v = View(buf, shape=(64,), strides=(1,), offset=0) + check_view(v) + # Use SyclContext + v = View(buf, shape=(64,), strides=(1,), offset=0, syclobj=buf.sycl_context) + check_view(v) + # Use queue capsule + v = View( + buf, + shape=(64,), + strides=(1,), + offset=0, + syclobj=buf._queue._get_capsule(), + ) + check_view(v) + # Use context capsule + v = View( + buf, + shape=(64,), + strides=(1,), + offset=0, + syclobj=buf.sycl_context._get_capsule(), + ) + check_view(v) + # Use filter string + v = View( + buf, + shape=(64,), + strides=(1,), + offset=0, + syclobj=buf.sycl_device.filter_string, + ) + check_view(v) From 96df456a382ff2031d84013083a9f3f0a245d3b4 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Fri, 3 Sep 2021 11:03:00 -0500 Subject: [PATCH 5/9] Improve dpctl.memory._memory coverage 1. Added a test to check MemoryConstructor(other_memory_object) 2. Add test_sycl_usm test to use copy_from_device 3. Added tests to check validation of __sycl_usm_array_interface__ in memory objects --- dpctl/tests/test_sycl_usm.py | 262 +++++++++++++++++++++++++++++++---- 1 file changed, 237 insertions(+), 25 deletions(-) diff --git a/dpctl/tests/test_sycl_usm.py b/dpctl/tests/test_sycl_usm.py index 396cac5c7e..036fc7e391 100644 --- a/dpctl/tests/test_sycl_usm.py +++ b/dpctl/tests/test_sycl_usm.py @@ -48,12 +48,20 @@ def __sycl_usm_array_interface(self): not has_sycl_platforms(), reason="No SYCL devices except the default host device.", ) -def test_memory_create(): +def test_memory_create(memory_ctor): + import sys + nbytes = 1024 - queue = dpctl.get_current_queue() - mobj = MemoryUSMShared(nbytes, alignment=64, queue=queue) + queue = dpctl.SyclQueue() + mobj = memory_ctor(nbytes, alignment=64, queue=queue) assert mobj.nbytes == nbytes assert hasattr(mobj, "__sycl_usm_array_interface__") + assert len(mobj) == nbytes + assert mobj.size == nbytes + assert mobj._context == queue.sycl_context + assert type(repr(mobj)) is str + assert type(bytes(mobj)) is bytes + assert sys.getsizeof(mobj) > nbytes @pytest.mark.skipif( @@ -69,7 +77,7 @@ def test_memory_create_with_np(): def _create_memory(): nbytes = 1024 - queue = dpctl.get_current_queue() + queue = dpctl.SyclQueue() mobj = MemoryUSMShared(nbytes, alignment=64, queue=queue) return mobj @@ -90,25 +98,24 @@ def test_memory_without_context(): # Without context assert mobj.get_usm_type() == "shared" + assert mobj.get_usm_type(syclobj=dpctl.SyclContext()) == "shared" @pytest.mark.skipif(not has_cpu(), reason="No SYCL CPU device available.") def test_memory_cpu_context(): mobj = _create_memory() - # CPU context - with dpctl.device_context("opencl:cpu:0"): - # type respective to the context in which - # memory was created - usm_type = mobj.get_usm_type() - assert usm_type == "shared" + # type respective to the context in which + # memory was created + usm_type = mobj.get_usm_type() + assert usm_type == "shared" - current_queue = dpctl.get_current_queue() - # type as view from current queue - usm_type = mobj.get_usm_type(current_queue) - # type can be unknown if current queue is - # not in the same SYCL context - assert usm_type in ["unknown", "shared"] + cpu_queue = dpctl.SyclQueue("cpu") + # type as view from CPU queue + usm_type = mobj.get_usm_type(cpu_queue) + # type can be unknown if current queue is + # not in the same SYCL context + assert usm_type in ["unknown", "shared"] @pytest.mark.skipif(not has_gpu(), reason="No OpenCL GPU queues available") @@ -116,12 +123,11 @@ def test_memory_gpu_context(): mobj = _create_memory() # GPU context - with dpctl.device_context("opencl:gpu:0"): - usm_type = mobj.get_usm_type() - assert usm_type == "shared" - current_queue = dpctl.get_current_queue() - usm_type = mobj.get_usm_type(current_queue) - assert usm_type in ["unknown", "shared"] + usm_type = mobj.get_usm_type() + assert usm_type == "shared" + gpu_queue = dpctl.SyclQueue("opencl:gpu") + usm_type = mobj.get_usm_type(gpu_queue) + assert usm_type in ["unknown", "shared"] @pytest.mark.skipif( @@ -166,10 +172,10 @@ def test_zero_copy(): not has_sycl_platforms(), reason="No SYCL devices except the default host device.", ) -def test_pickling(): +def test_pickling(memory_ctor): import pickle - mobj = _create_memory() + mobj = memory_ctor(1024, alignment=64) host_src_obj = _create_host_buf(mobj.nbytes) mobj.copy_from_host(host_src_obj) @@ -185,6 +191,22 @@ def test_pickling(): ), "Pickling/unpickling should be changing pointer" +@pytest.mark.skipif( + not has_sycl_platforms(), + reason="No SYCL devices except the default host device.", +) +def test_pickling_reconstructor_invalid_type(memory_ctor): + import pickle + + mobj = memory_ctor(1024, alignment=64) + good_pickle_bytes = pickle.dumps(mobj) + usm_types = expected_usm_type(memory_ctor).encode("utf-8") + i = good_pickle_bytes.index(usm_types) + bad_pickle_bytes = good_pickle_bytes[:i] + b"u" + good_pickle_bytes[i + 1 :] + with pytest.raises(ValueError): + pickle.loads(bad_pickle_bytes) + + @pytest.fixture(params=[MemoryUSMShared, MemoryUSMDevice, MemoryUSMHost]) def memory_ctor(request): return request.param @@ -256,12 +278,15 @@ def test_sycl_usm_array_interface(memory_ctor): class View: - def __init__(self, buf, shape, strides, offset, syclobj=None): + def __init__( + self, buf, shape, strides, offset, syclobj=None, transf_fn=None + ): self.buffer_ = buf self.shape_ = shape self.strides_ = strides self.offset_ = offset self.syclobj_ = syclobj + self.transf_fn_ = transf_fn @property def __sycl_usm_array_interface__(self): @@ -271,6 +296,8 @@ def __sycl_usm_array_interface__(self): sua_iface["strides"] = self.strides_ if self.syclobj_: sua_iface["syclobj"] = self.syclobj_ + if self.transf_fn_: + sua_iface = self.transf_fn_(sua_iface) return sua_iface @@ -338,6 +365,106 @@ def test_suai_non_contig_2D(memory_ctor): assert np.array_equal(res, expected_res) +def test_suai_invalid_suai(): + n_bytes = 2 * 3 * 5 * 128 + try: + q = dpctl.SyclQueue() + except dpctl.SyclQueueCreationError: + pytest.skip("Could not create default queue") + try: + buf = MemoryUSMShared(n_bytes, queue=q) + except Exception: + pytest.skip("USM-shared allocation failed") + + # different syclobj values + class DuckSyclObject: + def __init__(self, syclobj): + self.syclobj = syclobj + + def _get_capsule(self): + return self.syclobj._get_capsule() + + ctx = q.sycl_context + for syclobj in [ + q, + DuckSyclObject(q), + q._get_capsule(), + ctx, + DuckSyclObject(ctx), + ctx._get_capsule(), + ]: + v = View(buf, shape=(n_bytes,), strides=(1,), offset=0, syclobj=syclobj) + MemoryUSMShared(v) + with pytest.raises(ValueError): + MemoryUSMDevice(v) + with pytest.raises(ValueError): + MemoryUSMHost(v) + + # version validation + def invalid_version(suai_iface): + "Set version to invalid" + suai_iface["version"] = 0 + return suai_iface + + v = View( + buf, shape=(n_bytes,), strides=(1,), offset=0, transf_fn=invalid_version + ) + with pytest.raises(ValueError): + MemoryUSMShared(v) + + # data validation + def invalid_data(suai_iface): + "Set data to invalid" + suai_iface["data"] = tuple() + return suai_iface + + v = View( + buf, shape=(n_bytes,), strides=(1,), offset=0, transf_fn=invalid_data + ) + with pytest.raises(ValueError): + MemoryUSMShared(v) + # set shape to a negative value + v = View(buf, shape=(-n_bytes,), strides=(2,), offset=0) + with pytest.raises(ValueError): + MemoryUSMShared(v) + v = View(buf, shape=(-n_bytes,), strides=None, offset=0) + with pytest.raises(ValueError): + MemoryUSMShared(v) + # shape validation + v = View(buf, shape=None, strides=(1,), offset=0) + with pytest.raises(ValueError): + MemoryUSMShared(v) + + # typestr validation + def invalid_typestr(suai_iface): + suai_iface["typestr"] = "invalid" + return suai_iface + + v = View( + buf, shape=(n_bytes,), strides=(1,), offset=0, transf_fn=invalid_typestr + ) + with pytest.raises(ValueError): + MemoryUSMShared(v) + + def unsupported_typestr(suai_iface): + suai_iface["typestr"] = "O" + return suai_iface + + v = View( + buf, + shape=(n_bytes,), + strides=(1,), + offset=0, + transf_fn=unsupported_typestr, + ) + with pytest.raises(ValueError): + MemoryUSMShared(v) + # set strides to invalid value + v = View(buf, shape=(n_bytes,), strides=Ellipsis, offset=0) + with pytest.raises(ValueError): + MemoryUSMShared(v) + + def check_view(v): """ Memory object created from duck __sycl_usm_array_interface__ argument @@ -389,3 +516,88 @@ def test_with_constructor(memory_ctor): syclobj=buf.sycl_device.filter_string, ) check_view(v) + + +@pytest.mark.skipif( + not has_sycl_platforms(), + reason="No SYCL devices except the default host device.", +) +def test_cpython_api(memory_ctor): + import ctypes + import sys + + mobj = memory_ctor(1024) + mod = sys.modules[mobj.__class__.__module__] + # get capsules storing function pointers + mem_ptr_fn_cap = mod.__pyx_capi__["get_usm_pointer"] + mem_ctx_fn_cap = mod.__pyx_capi__["get_context"] + mem_nby_fn_cap = mod.__pyx_capi__["get_nbytes"] + # construct Python callable to invoke "get_usm_pointer" + cap_ptr_fn = ctypes.pythonapi.PyCapsule_GetPointer + cap_ptr_fn.restype = ctypes.c_void_p + cap_ptr_fn.argtypes = [ctypes.py_object, ctypes.c_char_p] + mem_ptr_fn_ptr = cap_ptr_fn( + mem_ptr_fn_cap, b"DPCTLSyclUSMRef (struct Py_MemoryObject *)" + ) + mem_ctx_fn_ptr = cap_ptr_fn( + mem_ctx_fn_cap, b"DPCTLSyclContextRef (struct Py_MemoryObject *)" + ) + mem_nby_fn_ptr = cap_ptr_fn( + mem_nby_fn_cap, b"size_t (struct Py_MemoryObject *)" + ) + callable_maker = ctypes.PYFUNCTYPE(ctypes.c_void_p, ctypes.py_object) + get_ptr_fn = callable_maker(mem_ptr_fn_ptr) + get_ctx_fn = callable_maker(mem_ctx_fn_ptr) + get_nby_fn = callable_maker(mem_nby_fn_ptr) + + capi_ptr = get_ptr_fn(mobj) + direct_ptr = mobj._pointer + assert capi_ptr == direct_ptr + capi_ctx_ref = get_ctx_fn(mobj) + direct_ctx_ref = mobj._context.addressof_ref() + assert capi_ctx_ref == direct_ctx_ref + capi_nbytes = get_nby_fn(mobj) + direct_nbytes = mobj.nbytes + assert capi_nbytes == direct_nbytes + + +def test_memory_construction_from_other_memory_objects(): + try: + q = dpctl.SyclQueue() + except dpctl.SyclQueueCreationError: + pytest.skip("Default queue could not be created") + m_sh = MemoryUSMShared(256, queue=q) + m_de = MemoryUSMDevice(256, queue=q) + m_ho = MemoryUSMHost(256, queue=q) + with pytest.raises(ValueError): + MemoryUSMDevice(m_sh) + with pytest.raises(ValueError): + MemoryUSMHost(m_de) + with pytest.raises(ValueError): + MemoryUSMShared(m_ho) + m1 = MemoryUSMDevice(m_sh, copy=True) + m2 = MemoryUSMHost(m_de, copy=True) + m3 = MemoryUSMShared(m_de, copy=True) + assert bytes(m1) == bytes(m_sh) + assert bytes(m2) == bytes(m3) + + +def test_memory_copy_between_contexts(): + try: + q = dpctl.SyclQueue("cpu") + except dpctl.SyclQueueCreationError: + pytest.skip("CPU queue could not be created") + d = q.sycl_device + n = d.max_compute_units + n_half = n // 2 + d0, d1 = d.create_sub_devices(partition=[n_half, n - n_half]) + q0 = dpctl.SyclQueue(d0) + q1 = dpctl.SyclQueue(d1) + m0 = MemoryUSMDevice(256, queue=q0) + m1 = MemoryUSMDevice(256, queue=q1) + host_buf = b"abcd" * 64 + m0.copy_from_host(host_buf) + m1.copy_from_device(m0) + copy_buf = bytearray(256) + m1.copy_to_host(copy_buf) + assert host_buf == copy_buf From 500c1233c222a7a23e77beb81df7b4fb0504bb43 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Fri, 3 Sep 2021 14:19:59 -0500 Subject: [PATCH 6/9] Transitioned test_sycl_queue_manager to use pytest over unittest Added tests to excercise device_context on all platforms --- dpctl/tests/test_sycl_queue_manager.py | 208 +++++++++++++++---------- 1 file changed, 125 insertions(+), 83 deletions(-) diff --git a/dpctl/tests/test_sycl_queue_manager.py b/dpctl/tests/test_sycl_queue_manager.py index bca73853df..5ff33e09b3 100644 --- a/dpctl/tests/test_sycl_queue_manager.py +++ b/dpctl/tests/test_sycl_queue_manager.py @@ -17,100 +17,142 @@ """Defines unit test cases for the SyclQueueManager class. """ -import unittest +import pytest import dpctl from ._helper import has_cpu, has_gpu, has_sycl_platforms -@unittest.skipIf(not has_sycl_platforms(), "No SYCL platforms available") -class TestIsInDeviceContext(unittest.TestCase): - def test_is_in_device_context_outside_device_ctxt(self): - self.assertFalse(dpctl.is_in_device_context()) +@pytest.mark.skipif( + not has_sycl_platforms(), reason="No SYCL platforms available" +) +def test_is_in_device_context_outside_device_ctxt(): + assert not dpctl.is_in_device_context() - @unittest.skipUnless(has_gpu(), "No OpenCL GPU queues available") - def test_is_in_device_context_inside_device_ctxt(self): + +@pytest.mark.skipif(not has_gpu(), reason="No OpenCL GPU queues available") +def test_is_in_device_context_inside_device_ctxt_gpu(): + with dpctl.device_context("opencl:gpu:0"): + assert dpctl.is_in_device_context() + + +@pytest.mark.skipif(not has_cpu(), reason="No OpenCL CPU queues available") +def test_is_in_device_context_inside_device_ctxt_cpu(): + with dpctl.device_context("opencl:cpu:0"): + assert dpctl.is_in_device_context() + + +@pytest.mark.skipif(not has_gpu(), reason="No OpenCL GPU queues available") +@pytest.mark.skipif(not has_cpu(), reason="No OpenCL CPU queues available") +def test_is_in_device_context_inside_nested_device_ctxt(): + with dpctl.device_context("opencl:cpu:0"): with dpctl.device_context("opencl:gpu:0"): - self.assertTrue(dpctl.is_in_device_context()) + assert dpctl.is_in_device_context() + assert dpctl.is_in_device_context() + assert not dpctl.is_in_device_context() + + +@pytest.mark.skipif(not has_cpu(), reason="No OpenCL CPU queues available") +def test_is_in_device_context_inside_nested_device_ctxt_cpu(): + cpu = dpctl.SyclDevice("cpu") + n = cpu.max_compute_units + n_half = n // 2 + try: + d0, d1 = cpu.create_subdevices(partition=[n_half, n - n_half]) + except Exception: + pytest.skip("Could not create subdevices") + assert 0 == dpctl.get_num_activated_queues() + with dpctl.device_context(d0): + assert 1 == dpctl.get_num_activated_queues() + with dpctl.device_context(d1): + assert 2 == dpctl.get_num_activated_queues() + assert dpctl.is_in_device_context() + assert dpctl.is_in_device_context() + assert 1 == dpctl.get_num_activated_queues() + assert not dpctl.is_in_device_context() + assert 0 == dpctl.get_num_activated_queues() + + +@pytest.mark.skipif( + not has_sycl_platforms(), reason="No SYCL platforms available" +) +def test_get_current_device_type_outside_device_ctxt(): + assert dpctl.get_current_device_type() is not None + + +@pytest.mark.skipif( + not has_sycl_platforms(), reason="No SYCL platforms available" +) +@pytest.mark.skipif(not has_gpu(), reason="No OpenCL GPU queues available") +def test_get_current_device_type_inside_device_ctxt(): + assert dpctl.get_current_device_type() is not None + + with dpctl.device_context("opencl:gpu:0"): + assert dpctl.get_current_device_type() == dpctl.device_type.gpu + + assert dpctl.get_current_device_type() is not None + + +@pytest.mark.skipif(not has_cpu(), reason="No OpenCL CPU queues available") +@pytest.mark.skipif(not has_gpu(), reason="No OpenCL GPU queues available") +def test_get_current_device_type_inside_nested_device_ctxt(): + assert dpctl.get_current_device_type() is not None + + with dpctl.device_context("opencl:cpu:0"): + assert dpctl.get_current_device_type() == dpctl.device_type.cpu - @unittest.skipUnless(has_gpu(), "No OpenCL GPU queues available") - @unittest.skipUnless(has_cpu(), "No OpenCL CPU queues available") - def test_is_in_device_context_inside_nested_device_ctxt(self): - with dpctl.device_context("opencl:cpu:0"): - with dpctl.device_context("opencl:gpu:0"): - self.assertTrue(dpctl.is_in_device_context()) - self.assertTrue(dpctl.is_in_device_context()) - self.assertFalse(dpctl.is_in_device_context()) + with dpctl.device_context("opencl:gpu:0"): + assert dpctl.get_current_device_type() == dpctl.device_type.gpu + assert dpctl.get_current_device_type() == dpctl.device_type.cpu + assert dpctl.get_current_device_type() is not None + + +@pytest.mark.skipif( + not has_sycl_platforms(), reason="No SYCL platforms available" +) +def test_num_current_queues_outside_with_clause(): + assert 0 == dpctl.get_num_activated_queues() + + +@pytest.mark.skipif(not has_gpu(), reason="No OpenCL GPU queues available") +@pytest.mark.skipif(not has_cpu(), reason="No OpenCL CPU queues available") +def test_num_current_queues_inside_with_clause(): + with dpctl.device_context("opencl:cpu:0"): + assert 1 == dpctl.get_num_activated_queues() + with dpctl.device_context("opencl:gpu:0"): + assert 2 == dpctl.get_num_activated_queues() + assert 0 == dpctl.get_num_activated_queues() -@unittest.skipIf(not has_sycl_platforms(), "No SYCL platforms available") -@unittest.skipUnless(has_gpu(), "No OpenCL GPU queues available") -class TestGetCurrentDevice(unittest.TestCase): - def test_get_current_device_type_outside_device_ctxt(self): - self.assertNotEqual(dpctl.get_current_device_type(), None) - def test_get_current_device_type_inside_device_ctxt(self): - self.assertNotEqual(dpctl.get_current_device_type(), None) +@pytest.mark.skipif(not has_gpu(), reason="No OpenCL GPU queues available") +@pytest.mark.skipif(not has_cpu(), reason="No OpenCL CPU queues available") +def test_num_current_queues_inside_threads(): + from threading import Thread + def SessionThread(): + assert dpctl.get_num_activated_queues() == 0 with dpctl.device_context("opencl:gpu:0"): - self.assertEqual( - dpctl.get_current_device_type(), dpctl.device_type.gpu - ) - - self.assertNotEqual(dpctl.get_current_device_type(), None) - - @unittest.skipUnless(has_cpu(), "No OpenCL CPU queues available") - def test_get_current_device_type_inside_nested_device_ctxt(self): - self.assertNotEqual(dpctl.get_current_device_type(), None) - - with dpctl.device_context("opencl:cpu:0"): - self.assertEqual( - dpctl.get_current_device_type(), dpctl.device_type.cpu - ) - - with dpctl.device_context("opencl:gpu:0"): - self.assertEqual( - dpctl.get_current_device_type(), dpctl.device_type.gpu - ) - self.assertEqual( - dpctl.get_current_device_type(), dpctl.device_type.cpu - ) - - self.assertNotEqual(dpctl.get_current_device_type(), None) - - -@unittest.skipIf(not has_sycl_platforms(), "No SYCL platforms available") -class TestGetCurrentQueueInMultipleThreads(unittest.TestCase): - def test_num_current_queues_outside_with_clause(self): - self.assertEqual(dpctl.get_num_activated_queues(), 0) - - @unittest.skipUnless(has_gpu(), "No OpenCL GPU queues available") - @unittest.skipUnless(has_cpu(), "No OpenCL CPU queues available") - def test_num_current_queues_inside_with_clause(self): - with dpctl.device_context("opencl:cpu:0"): - self.assertEqual(dpctl.get_num_activated_queues(), 1) - with dpctl.device_context("opencl:gpu:0"): - self.assertEqual(dpctl.get_num_activated_queues(), 2) - self.assertEqual(dpctl.get_num_activated_queues(), 0) - - @unittest.skipUnless(has_gpu(), "No OpenCL GPU queues available") - @unittest.skipUnless(has_cpu(), "No OpenCL CPU queues available") - def test_num_current_queues_inside_threads(self): - from threading import Thread - - def SessionThread(self): - self.assertEqual(dpctl.get_num_activated_queues(), 0) - with dpctl.device_context("opencl:gpu:0"): - self.assertEqual(dpctl.get_num_activated_queues(), 1) - - Session1 = Thread(target=SessionThread(self)) - Session2 = Thread(target=SessionThread(self)) - with dpctl.device_context("opencl:cpu:0"): - self.assertEqual(dpctl.get_num_activated_queues(), 1) - Session1.start() - Session2.start() - - -if __name__ == "__main__": - unittest.main() + assert dpctl.get_num_activated_queues() == 1 + + Session1 = Thread(target=SessionThread()) + Session2 = Thread(target=SessionThread()) + with dpctl.device_context("opencl:cpu:0"): + assert dpctl.get_num_activated_queues() == 1 + Session1.start() + Session2.start() + + +@pytest.mark.skipif( + not has_sycl_platforms(), reason="No SYCL platforms available" +) +def test_get_current_backend(): + dpctl.get_current_backend() + dpctl.get_current_device_type() + q = dpctl.SyclQueue() + dpctl.set_global_queue(q) + if has_gpu(): + dpctl.set_global_queue("gpu") + elif has_cpu(): + dpctl.set_global_queue("cpu") From 5604af9aabe34b8b7af04a036f96dbd14995c2d8 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Fri, 3 Sep 2021 16:52:55 -0500 Subject: [PATCH 7/9] Transitioned two more files from unittest to pytests It appears that unittest-based tests did not participate in the coverage collection data. --- dpctl/tests/test_sycl_kernel_submit.py | 85 ++++++++++++-------------- dpctl/tests/test_sycl_queue_memcpy.py | 71 ++++++++++----------- 2 files changed, 71 insertions(+), 85 deletions(-) diff --git a/dpctl/tests/test_sycl_kernel_submit.py b/dpctl/tests/test_sycl_kernel_submit.py index 5c8c2ce331..cafe88825a 100644 --- a/dpctl/tests/test_sycl_kernel_submit.py +++ b/dpctl/tests/test_sycl_kernel_submit.py @@ -18,60 +18,55 @@ """ import ctypes -import unittest import numpy as np +import pytest import dpctl import dpctl.memory as dpctl_mem import dpctl.program as dpctl_prog -from ._helper import has_gpu +def test_create_program_from_source(): + try: + q = dpctl.SyclQueue("opencl", property="enable_profiling") + except dpctl.SyclQueueCreationError: + pytest.skip("OpenCL queue could not be created") + oclSrc = " \ + kernel void axpy(global int* a, global int* b, global int* c, int d) { \ + size_t index = get_global_id(0); \ + c[index] = d*a[index] + b[index]; \ + }" + prog = dpctl_prog.create_program_from_source(q, oclSrc) + axpyKernel = prog.get_sycl_kernel("axpy") -@unittest.skipUnless(has_gpu(), "No OpenCL GPU queues available") -class Test1DKernelSubmit(unittest.TestCase): - def test_create_program_from_source(self): - oclSrc = " \ - kernel void axpy(global int* a, global int* b, global int* c, int d) { \ - size_t index = get_global_id(0); \ - c[index] = d*a[index] + b[index]; \ - }" - q = dpctl.SyclQueue("opencl:gpu", property="enable_profiling") - prog = dpctl_prog.create_program_from_source(q, oclSrc) - axpyKernel = prog.get_sycl_kernel("axpy") + n_elems = 1024 * 512 + bufBytes = n_elems * np.dtype("i").itemsize + abuf = dpctl_mem.MemoryUSMShared(bufBytes, queue=q) + bbuf = dpctl_mem.MemoryUSMShared(bufBytes, queue=q) + cbuf = dpctl_mem.MemoryUSMShared(bufBytes, queue=q) + a = np.ndarray((n_elems,), buffer=abuf, dtype="i") + b = np.ndarray((n_elems,), buffer=bbuf, dtype="i") + c = np.ndarray((n_elems,), buffer=cbuf, dtype="i") + a[:] = np.arange(n_elems) + b[:] = np.arange(n_elems, 0, -1) + c[:] = 0 + d = 2 + args = [] - n_elems = 1024 * 512 - bufBytes = n_elems * np.dtype("i").itemsize - abuf = dpctl_mem.MemoryUSMShared(bufBytes, queue=q) - bbuf = dpctl_mem.MemoryUSMShared(bufBytes, queue=q) - cbuf = dpctl_mem.MemoryUSMShared(bufBytes, queue=q) - a = np.ndarray((n_elems,), buffer=abuf, dtype="i") - b = np.ndarray((n_elems,), buffer=bbuf, dtype="i") - c = np.ndarray((n_elems,), buffer=cbuf, dtype="i") - a[:] = np.arange(n_elems) - b[:] = np.arange(n_elems, 0, -1) - c[:] = 0 - d = 2 - args = [] + args.append(a.base) + args.append(b.base) + args.append(c.base) + args.append(ctypes.c_int(d)) - args.append(a.base) - args.append(b.base) - args.append(c.base) - args.append(ctypes.c_int(d)) + r = [ + n_elems, + ] - r = [ - n_elems, - ] - - timer = dpctl.SyclTimer() - with timer(q): - q.submit(axpyKernel, args, r) - ref_c = a * d + b - host_dt, device_dt = timer.dt - self.assertTrue(host_dt > device_dt) - self.assertTrue(np.allclose(c, ref_c)) - - -if __name__ == "__main__": - unittest.main() + timer = dpctl.SyclTimer() + with timer(q): + q.submit(axpyKernel, args, r) + ref_c = a * d + b + host_dt, device_dt = timer.dt + assert host_dt > device_dt + assert np.allclose(c, ref_c) diff --git a/dpctl/tests/test_sycl_queue_memcpy.py b/dpctl/tests/test_sycl_queue_memcpy.py index ce68183156..f5cddc87b8 100644 --- a/dpctl/tests/test_sycl_queue_memcpy.py +++ b/dpctl/tests/test_sycl_queue_memcpy.py @@ -17,7 +17,7 @@ """Defines unit test cases for the SyclQueue.memcpy. """ -import unittest +import pytest import dpctl import dpctl.memory @@ -25,52 +25,43 @@ from ._helper import has_sycl_platforms -class TestQueueMemcpy(unittest.TestCase): - def _create_memory(self): - nbytes = 1024 - mobj = dpctl.memory.MemoryUSMShared(nbytes) - return mobj +def _create_memory(): + nbytes = 1024 + mobj = dpctl.memory.MemoryUSMShared(nbytes) + return mobj - @unittest.skipUnless( - has_sycl_platforms(), "No SYCL devices except the default host device." - ) - def test_memcpy_copy_usm_to_usm(self): - mobj1 = self._create_memory() - mobj2 = self._create_memory() - q = dpctl.get_current_queue() - mv1 = memoryview(mobj1) - mv2 = memoryview(mobj2) +@pytest.mark.skipif( + not has_sycl_platforms(), + reason="No SYCL devices except the default host device.", +) +def test_memcpy_copy_usm_to_usm(): + mobj1 = _create_memory() + mobj2 = _create_memory() + q = dpctl.SyclQueue() - mv1[:3] = b"123" + mv1 = memoryview(mobj1) + mv2 = memoryview(mobj2) - q.memcpy(mobj2, mobj1, 3) + mv1[:3] = b"123" - self.assertEqual(mv2[:3], b"123") + q.memcpy(mobj2, mobj1, 3) - @unittest.skipUnless( - has_sycl_platforms(), "No SYCL devices except the default host device." - ) - def test_memcpy_type_error(self): - mobj = self._create_memory() - q = mobj._queue + assert mv2[:3], b"123" - with self.assertRaises(TypeError) as cm: - q.memcpy(None, mobj, 3) - self.assertEqual(type(cm.exception), TypeError) - self.assertEqual( - str(cm.exception), "Parameter `dest` should have type _Memory." - ) +# @pytest.mark.skipif( +# not has_sycl_platforms(), +# reason="No SYCL devices except the default host device." +# ) +def test_memcpy_type_error(): + mobj = _create_memory() + q = mobj._queue - with self.assertRaises(TypeError) as cm: - q.memcpy(mobj, None, 3) + with pytest.raises(TypeError) as cm: + q.memcpy(None, mobj, 3) + assert "`dest`" in str(cm.value) - self.assertEqual(type(cm.exception), TypeError) - self.assertEqual( - str(cm.exception), "Parameter `src` should have type _Memory." - ) - - -if __name__ == "__main__": - unittest.main() + with pytest.raises(TypeError) as cm: + q.memcpy(mobj, None, 3) + assert "`src`" in str(cm.value) From 53fb3425f3cbf77410133acc302ab34d7ab3aead Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Sat, 4 Sep 2021 13:48:49 -0500 Subject: [PATCH 8/9] test_sycl_program now uses pytest --- dpctl/tests/test_sycl_program.py | 175 ++++++++++++++++--------------- 1 file changed, 88 insertions(+), 87 deletions(-) diff --git a/dpctl/tests/test_sycl_program.py b/dpctl/tests/test_sycl_program.py index f36c32446b..f69fb8c410 100644 --- a/dpctl/tests/test_sycl_program.py +++ b/dpctl/tests/test_sycl_program.py @@ -18,95 +18,96 @@ """ import os -import unittest + +import pytest import dpctl import dpctl.program as dpctl_prog -from ._helper import has_gpu - - -@unittest.skipUnless(has_gpu(), "No OpenCL GPU queues available") -class TestProgramFromOCLSource(unittest.TestCase): - def test_create_program_from_source(self): - oclSrc = " \ - kernel void add(global int* a, global int* b, global int* c) { \ - size_t index = get_global_id(0); \ - c[index] = a[index] + b[index]; \ - } \ - kernel void axpy(global int* a, global int* b, global int* c, int d) { \ - size_t index = get_global_id(0); \ - c[index] = a[index] + d*b[index]; \ - }" - q = dpctl.SyclQueue("opencl:gpu") - prog = dpctl_prog.create_program_from_source(q, oclSrc) - self.assertIsNotNone(prog) - - self.assertTrue(prog.has_sycl_kernel("add")) - self.assertTrue(prog.has_sycl_kernel("axpy")) - - addKernel = prog.get_sycl_kernel("add") - axpyKernel = prog.get_sycl_kernel("axpy") - - self.assertEqual(addKernel.get_function_name(), "add") - self.assertEqual(axpyKernel.get_function_name(), "axpy") - self.assertEqual(addKernel.get_num_args(), 3) - self.assertEqual(axpyKernel.get_num_args(), 4) - - -@unittest.skipUnless(has_gpu(), "No OpenCL GPU queues available") -class TestProgramFromSPRIV(unittest.TestCase): - def test_create_program_from_spirv(self): - - CURR_DIR = os.path.dirname(os.path.abspath(__file__)) - spirv_file = os.path.join(CURR_DIR, "input_files/multi_kernel.spv") - with open(spirv_file, "rb") as fin: - spirv = fin.read() - q = dpctl.SyclQueue("opencl:gpu") - prog = dpctl_prog.create_program_from_spirv(q, spirv) - self.assertIsNotNone(prog) - self.assertTrue(prog.has_sycl_kernel("add")) - self.assertTrue(prog.has_sycl_kernel("axpy")) - - addKernel = prog.get_sycl_kernel("add") - axpyKernel = prog.get_sycl_kernel("axpy") - - self.assertEqual(addKernel.get_function_name(), "add") - self.assertEqual(axpyKernel.get_function_name(), "axpy") - self.assertEqual(addKernel.get_num_args(), 3) - self.assertEqual(axpyKernel.get_num_args(), 4) - - -@unittest.skipUnless( - has_gpu(backend=dpctl.backend_type.level_zero), - "No Level0 GPU queues available", + +def get_spirv_abspath(fn): + curr_dir = os.path.dirname(os.path.abspath(__file__)) + spirv_file = os.path.join(curr_dir, "input_files", fn) + return spirv_file + + +def test_create_program_from_source_ocl(): + oclSrc = " \ + kernel void add(global int* a, global int* b, global int* c) { \ + size_t index = get_global_id(0); \ + c[index] = a[index] + b[index]; \ + } \ + kernel void axpy(global int* a, global int* b, global int* c, int d) { \ + size_t index = get_global_id(0); \ + c[index] = a[index] + d*b[index]; \ + }" + try: + q = dpctl.SyclQueue("opencl") + except dpctl.SyclQueueCreationError: + pytest.skip("No OpenCL queue is available") + prog = dpctl_prog.create_program_from_source(q, oclSrc) + assert prog is not None + + assert prog.has_sycl_kernel("add") + assert prog.has_sycl_kernel("axpy") + + addKernel = prog.get_sycl_kernel("add") + axpyKernel = prog.get_sycl_kernel("axpy") + + assert "add" == addKernel.get_function_name() + assert "axpy" == axpyKernel.get_function_name() + assert 3 == addKernel.get_num_args() + assert 4 == axpyKernel.get_num_args() + + +def test_create_program_from_spirv_ocl(): + try: + q = dpctl.SyclQueue("opencl") + except dpctl.SyclQueueCreationError: + pytest.skip("No OpenCL queue is available") + spirv_file = get_spirv_abspath("multi_kernel.spv") + with open(spirv_file, "rb") as fin: + spirv = fin.read() + prog = dpctl_prog.create_program_from_spirv(q, spirv) + assert prog is not None + assert prog.has_sycl_kernel("add") + assert prog.has_sycl_kernel("axpy") + + addKernel = prog.get_sycl_kernel("add") + axpyKernel = prog.get_sycl_kernel("axpy") + + assert "add" == addKernel.get_function_name() + assert "axpy" == axpyKernel.get_function_name() + assert 3 == addKernel.get_num_args() + assert 4 == axpyKernel.get_num_args() + + +def test_create_program_from_spirv_l0(): + try: + q = dpctl.SyclQueue("level_zero") + except dpctl.SyclQueueCreationError: + pytest.skip("No Level-zero queue is available") + spirv_file = get_spirv_abspath("multi_kernel.spv") + with open(spirv_file, "rb") as fin: + spirv = fin.read() + dpctl_prog.create_program_from_spirv(q, spirv) + + +@pytest.mark.xfail( + reason="Level-zero backend does not support compilation from source" ) -class TestProgramForLevel0GPU(unittest.TestCase): - - import sys - - def test_create_program_from_spirv(self): - CURR_DIR = os.path.dirname(os.path.abspath(__file__)) - spirv_file = os.path.join(CURR_DIR, "input_files/multi_kernel.spv") - with open(spirv_file, "rb") as fin: - spirv = fin.read() - q = dpctl.SyclQueue("level_zero:gpu") - dpctl_prog.create_program_from_spirv(q, spirv) - - @unittest.expectedFailure - def test_create_program_from_source(self): - oclSrc = " \ - kernel void add(global int* a, global int* b, global int* c) { \ - size_t index = get_global_id(0); \ - c[index] = a[index] + b[index]; \ - } \ - kernel void axpy(global int* a, global int* b, global int* c, int d) { \ - size_t index = get_global_id(0); \ - c[index] = a[index] + d*b[index]; \ - }" - q = dpctl.SyclQueue("level_zero:gpu") - dpctl_prog.create_program_from_source(q, oclSrc) - - -if __name__ == "__main__": - unittest.main() +def test_create_program_from_source_l0(): + try: + q = dpctl.SyclQueue("level_zero") + except dpctl.SyclQueueCreationError: + pytest.skip("No Level-zero queue is available") + oclSrc = " \ + kernel void add(global int* a, global int* b, global int* c) { \ + size_t index = get_global_id(0); \ + c[index] = a[index] + b[index]; \ + } \ + kernel void axpy(global int* a, global int* b, global int* c, int d) { \ + size_t index = get_global_id(0); \ + c[index] = a[index] + d*b[index]; \ + }" + dpctl_prog.create_program_from_source(q, oclSrc) From ba40dabd5d2db5fa182f6105140d96c69964659c Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Sat, 4 Sep 2021 14:26:13 -0500 Subject: [PATCH 9/9] test_dparray now uses pytest instead of unittest --- dpctl/tests/test_dparray.py | 139 ++++++++++++++++++++---------------- 1 file changed, 79 insertions(+), 60 deletions(-) diff --git a/dpctl/tests/test_dparray.py b/dpctl/tests/test_dparray.py index 881aa79f44..810f502e23 100644 --- a/dpctl/tests/test_dparray.py +++ b/dpctl/tests/test_dparray.py @@ -17,85 +17,104 @@ """Unit test cases for dpctl.tensor.numpy_usm_shared. """ -import unittest - import numpy from dpctl.tensor import numpy_usm_shared as dparray -class Test_dparray(unittest.TestCase): - def setUp(self): - self.X = dparray.ndarray((256, 4), dtype="d") - self.X.fill(1.0) +def get_arg(): + X = dparray.ndarray((256, 4), dtype="d") + X.fill(1.0) + return X + + +def test_dparray_type(): + X = get_arg() + assert isinstance(X, dparray.ndarray) + + +def test_dparray_as_ndarray_self(): + X = get_arg() + Y = X.as_ndarray() + assert type(Y) == numpy.ndarray + + +def test_dparray_as_ndarray(): + X = get_arg() + Y = dparray.as_ndarray(X) + assert type(Y) == numpy.ndarray + + +def test_dparray_from_ndarray(): + X = get_arg() + Y = dparray.as_ndarray(X) + dp1 = dparray.from_ndarray(Y) + assert isinstance(dp1, dparray.ndarray) + + +def test_multiplication_dparray(): + C = get_arg() * 5 + assert isinstance(C, dparray.ndarray) + + +def test_inplace_sub(): + X = get_arg() + X -= 1 - def test_dparray_type(self): - self.assertIsInstance(self.X, dparray.ndarray) - def test_dparray_as_ndarray_self(self): - Y = self.X.as_ndarray() - self.assertEqual(type(Y), numpy.ndarray) +def test_dparray_through_python_func(): + def func_operation_with_const(dpctl_array): + return dpctl_array * 2.0 + 13 - def test_dparray_as_ndarray(self): - Y = dparray.as_ndarray(self.X) - self.assertEqual(type(Y), numpy.ndarray) + C = get_arg() * 5 + dp_func = func_operation_with_const(C) + assert isinstance(dp_func, dparray.ndarray) - def test_dparray_from_ndarray(self): - Y = dparray.as_ndarray(self.X) - dp1 = dparray.from_ndarray(Y) - self.assertIsInstance(dp1, dparray.ndarray) - def test_multiplication_dparray(self): - C = self.X * 5 - self.assertIsInstance(C, dparray.ndarray) +def test_dparray_mixing_dpctl_and_numpy(): + dp_numpy = numpy.ones((256, 4), dtype="d") + X = get_arg() + res = dp_numpy * X + assert isinstance(X, dparray.ndarray) + assert isinstance(res, dparray.ndarray) - def test_inplace_sub(self): - self.X -= 1 - def test_dparray_through_python_func(self): - def func_operation_with_const(dpctl_array): - return dpctl_array * 2.0 + 13 +def test_dparray_shape(): + X = get_arg() + res = X.shape + assert res == (256, 4) - C = self.X * 5 - dp_func = func_operation_with_const(C) - self.assertIsInstance(dp_func, dparray.ndarray) - def test_dparray_mixing_dpctl_and_numpy(self): - dp_numpy = numpy.ones((256, 4), dtype="d") - res = dp_numpy * self.X - self.assertIsInstance(self.X, dparray.ndarray) - self.assertIsInstance(res, dparray.ndarray) +def test_dparray_T(): + X = get_arg() + res = X.T + assert res.shape == (4, 256) - def test_dparray_shape(self): - res = self.X.shape - self.assertEqual(res, (256, 4)) - def test_dparray_T(self): - res = self.X.T - self.assertEqual(res.shape, (4, 256)) +def test_numpy_ravel_with_dparray(): + X = get_arg() + res = numpy.ravel(X) + assert res.shape == (1024,) - def test_numpy_ravel_with_dparray(self): - res = numpy.ravel(self.X) - self.assertEqual(res.shape, (1024,)) - def test_numpy_sum_with_dparray(self): - res = numpy.sum(self.X) - self.assertEqual(res, 1024.0) +def test_numpy_sum_with_dparray(): + X = get_arg() + res = numpy.sum(X) + assert res == 1024.0 - def test_numpy_sum_with_dparray_out(self): - res = dparray.empty((self.X.shape[1],), dtype=self.X.dtype) - res2 = numpy.sum(self.X, axis=0, out=res) - self.assertTrue(res is res2) - self.assertIsInstance(res2, dparray.ndarray) - def test_frexp_with_out(self): - X = dparray.array([0.5, 4.7]) - mant = dparray.empty((2,), dtype="d") - exp = dparray.empty((2,), dtype="i4") - res = numpy.frexp(X, out=(mant, exp)) - self.assertTrue(res[0] is mant) - self.assertTrue(res[1] is exp) +def test_numpy_sum_with_dparray_out(): + X = get_arg() + res = dparray.empty((X.shape[1],), dtype=X.dtype) + res2 = numpy.sum(X, axis=0, out=res) + assert res is res2 + assert isinstance(res2, dparray.ndarray) -if __name__ == "__main__": - unittest.main() +def test_frexp_with_out(): + X = dparray.array([0.5, 4.7]) + mant = dparray.empty((2,), dtype="d") + exp = dparray.empty((2,), dtype="i4") + res = numpy.frexp(X, out=(mant, exp)) + assert res[0] is mant + assert res[1] is exp