From f6b8ed5703327acf7820c226e6934edfbe370111 Mon Sep 17 00:00:00 2001 From: Lukas Sommer Date: Tue, 14 Jan 2025 14:26:47 +0000 Subject: [PATCH 1/4] Copy from/to multidimensional buffers Signed-off-by: Lukas Sommer --- dpctl/_sycl_queue.pyx | 37 ++++++++++++++++++++----- dpctl/tests/test_sycl_queue_memcpy.py | 40 +++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 7 deletions(-) diff --git a/dpctl/_sycl_queue.pyx b/dpctl/_sycl_queue.pyx index c20d8724f5..d606607657 100644 --- a/dpctl/_sycl_queue.pyx +++ b/dpctl/_sycl_queue.pyx @@ -65,7 +65,15 @@ import ctypes from .enum_types import backend_type from cpython cimport pycapsule -from cpython.buffer cimport PyObject_CheckBuffer +from cpython.buffer cimport ( + PyObject_CheckBuffer, + Py_buffer, + PyObject_GetBuffer, + PyBUF_SIMPLE, + PyBUF_ANY_CONTIGUOUS, + PyBUF_WRITABLE, + PyBuffer_Release +) from cpython.ref cimport Py_DECREF, Py_INCREF, PyObject from libc.stdlib cimport free, malloc @@ -338,14 +346,20 @@ cdef DPCTLSyclEventRef _memcpy_impl( cdef void *c_dst_ptr = NULL cdef void *c_src_ptr = NULL cdef DPCTLSyclEventRef ERef = NULL - cdef const unsigned char[::1] src_host_buf = None - cdef unsigned char[::1] dst_host_buf = None + cdef Py_buffer src_buf_view + cdef Py_buffer dst_buf_view + cdef bint src_is_buf = False + cdef bint dst_is_buf = False + cdef int ret_code = 0 if isinstance(src, _Memory): c_src_ptr = (<_Memory>src).get_data_ptr() elif _is_buffer(src): - src_host_buf = src - c_src_ptr = &src_host_buf[0] + ret_code = PyObject_GetBuffer(src, &src_buf_view, PyBUF_SIMPLE | PyBUF_ANY_CONTIGUOUS) + if ret_code != 0: + raise RuntimeError("Could not access buffer") + c_src_ptr = src_buf_view.buf + src_is_buf = True else: raise TypeError( "Parameter `src` should have either type " @@ -356,8 +370,11 @@ cdef DPCTLSyclEventRef _memcpy_impl( if isinstance(dst, _Memory): c_dst_ptr = (<_Memory>dst).get_data_ptr() elif _is_buffer(dst): - dst_host_buf = dst - c_dst_ptr = &dst_host_buf[0] + ret_code = PyObject_GetBuffer(dst, &dst_buf_view, PyBUF_SIMPLE | PyBUF_ANY_CONTIGUOUS | PyBUF_WRITABLE) + if ret_code != 0: + raise RuntimeError("Could not access buffer") + c_dst_ptr = dst_buf_view.buf + dst_is_buf = True else: raise TypeError( "Parameter `dst` should have either type " @@ -376,6 +393,12 @@ cdef DPCTLSyclEventRef _memcpy_impl( dep_events, dep_events_count ) + + if src_is_buf: + PyBuffer_Release(&src_buf_view) + if dst_is_buf: + PyBuffer_Release(&dst_buf_view) + return ERef diff --git a/dpctl/tests/test_sycl_queue_memcpy.py b/dpctl/tests/test_sycl_queue_memcpy.py index 1756cca40a..45a117231f 100644 --- a/dpctl/tests/test_sycl_queue_memcpy.py +++ b/dpctl/tests/test_sycl_queue_memcpy.py @@ -22,6 +22,8 @@ import dpctl import dpctl.memory +import numpy as np + def _create_memory(q): nbytes = 1024 @@ -97,6 +99,44 @@ def test_memcpy_copy_host_to_host(): assert dst_buf == src_buf +def test_2D_memcpy_copy_host_to_usm(): + try: + q = dpctl.SyclQueue() + except dpctl.SyclQueueCreationError: + pytest.skip("Default constructor for SyclQueue failed") + usm_obj = _create_memory(q) + + n = 12 + canary = bytearray([i for i in range(n)]) + host_obj = np.frombuffer(canary, dtype=np.uint8).reshape(3, 4) + + q.memcpy(usm_obj, host_obj, len(canary)) + + mv2 = memoryview(usm_obj) + + assert mv2[: len(canary)] == canary + + +def test_2D_memcpy_copy_usm_to_host(): + try: + q = dpctl.SyclQueue() + except dpctl.SyclQueueCreationError: + pytest.skip("Default constructor for SyclQueue failed") + usm_obj = _create_memory(q) + mv2 = memoryview(usm_obj) + + n = 12 + shape = (3, 4) + for id in range(n): + mv2[id] = id + + host_obj = np.ones(shape, dtype=np.uint8) + + q.memcpy(host_obj, usm_obj, n) + + assert np.array_equal(host_obj, np.arange(n, dtype=np.uint8).reshape(shape)) + + def test_memcpy_async(): try: q = dpctl.SyclQueue() From c7406dbed536fa5558be955a9ab7b6064433f563 Mon Sep 17 00:00:00 2001 From: Lukas Sommer Date: Tue, 4 Feb 2025 14:53:49 +0000 Subject: [PATCH 2/4] Sort includes --- dpctl/_sycl_queue.pyx | 8 ++++---- dpctl/tests/test_sycl_queue_memcpy.py | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/dpctl/_sycl_queue.pyx b/dpctl/_sycl_queue.pyx index d606607657..3476aa94f8 100644 --- a/dpctl/_sycl_queue.pyx +++ b/dpctl/_sycl_queue.pyx @@ -66,13 +66,13 @@ from .enum_types import backend_type from cpython cimport pycapsule from cpython.buffer cimport ( - PyObject_CheckBuffer, Py_buffer, - PyObject_GetBuffer, - PyBUF_SIMPLE, PyBUF_ANY_CONTIGUOUS, + PyBUF_SIMPLE, PyBUF_WRITABLE, - PyBuffer_Release + PyBuffer_Release, + PyObject_CheckBuffer, + PyObject_GetBuffer, ) from cpython.ref cimport Py_DECREF, Py_INCREF, PyObject from libc.stdlib cimport free, malloc diff --git a/dpctl/tests/test_sycl_queue_memcpy.py b/dpctl/tests/test_sycl_queue_memcpy.py index 45a117231f..d134323e74 100644 --- a/dpctl/tests/test_sycl_queue_memcpy.py +++ b/dpctl/tests/test_sycl_queue_memcpy.py @@ -17,13 +17,12 @@ """Defines unit test cases for the SyclQueue.memcpy. """ +import numpy as np import pytest import dpctl import dpctl.memory -import numpy as np - def _create_memory(q): nbytes = 1024 From cd5fcafef121a6aac6b74a16975394da3312db81 Mon Sep 17 00:00:00 2001 From: Lukas Sommer Date: Wed, 5 Feb 2025 12:49:00 +0000 Subject: [PATCH 3/4] Insert no coverage pragma --- dpctl/_sycl_queue.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpctl/_sycl_queue.pyx b/dpctl/_sycl_queue.pyx index 3476aa94f8..018506eb78 100644 --- a/dpctl/_sycl_queue.pyx +++ b/dpctl/_sycl_queue.pyx @@ -356,7 +356,7 @@ cdef DPCTLSyclEventRef _memcpy_impl( c_src_ptr = (<_Memory>src).get_data_ptr() elif _is_buffer(src): ret_code = PyObject_GetBuffer(src, &src_buf_view, PyBUF_SIMPLE | PyBUF_ANY_CONTIGUOUS) - if ret_code != 0: + if ret_code != 0: # pragma: no cover raise RuntimeError("Could not access buffer") c_src_ptr = src_buf_view.buf src_is_buf = True @@ -371,7 +371,7 @@ cdef DPCTLSyclEventRef _memcpy_impl( c_dst_ptr = (<_Memory>dst).get_data_ptr() elif _is_buffer(dst): ret_code = PyObject_GetBuffer(dst, &dst_buf_view, PyBUF_SIMPLE | PyBUF_ANY_CONTIGUOUS | PyBUF_WRITABLE) - if ret_code != 0: + if ret_code != 0: # pragma: no cover raise RuntimeError("Could not access buffer") c_dst_ptr = dst_buf_view.buf dst_is_buf = True From a8bc75e84827a71c7eec3de6546b7718de3b7e89 Mon Sep 17 00:00:00 2001 From: Lukas Sommer Date: Thu, 6 Feb 2025 15:16:50 +0000 Subject: [PATCH 4/4] Releas source buffer --- dpctl/_sycl_queue.pyx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dpctl/_sycl_queue.pyx b/dpctl/_sycl_queue.pyx index 018506eb78..86ef08f584 100644 --- a/dpctl/_sycl_queue.pyx +++ b/dpctl/_sycl_queue.pyx @@ -372,6 +372,8 @@ cdef DPCTLSyclEventRef _memcpy_impl( elif _is_buffer(dst): ret_code = PyObject_GetBuffer(dst, &dst_buf_view, PyBUF_SIMPLE | PyBUF_ANY_CONTIGUOUS | PyBUF_WRITABLE) if ret_code != 0: # pragma: no cover + if src_is_buf: + PyBuffer_Release(&src_buf_view) raise RuntimeError("Could not access buffer") c_dst_ptr = dst_buf_view.buf dst_is_buf = True