-
Notifications
You must be signed in to change notification settings - Fork 37
task: syclqueue.copy() method #2273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,6 +35,8 @@ from ._backend cimport ( # noqa: E211 | |
| DPCTLFilterSelector_Create, | ||
| DPCTLQueue_AreEq, | ||
| DPCTLQueue_Copy, | ||
| DPCTLQueue_CopyData, | ||
| DPCTLQueue_CopyDataWithEvents, | ||
| DPCTLQueue_Create, | ||
| DPCTLQueue_Delete, | ||
| DPCTLQueue_GetBackend, | ||
|
|
@@ -531,6 +533,82 @@ cdef DPCTLSyclEventRef _memcpy_impl( | |
| return ERef | ||
|
|
||
|
|
||
| cdef DPCTLSyclEventRef _copy_impl( | ||
| SyclQueue q, | ||
| object dst, | ||
| object src, | ||
| size_t byte_count, | ||
| DPCTLSyclEventRef *dep_events, | ||
| size_t dep_events_count | ||
| ) except *: | ||
| cdef void *c_dst_ptr = NULL | ||
| cdef void *c_src_ptr = NULL | ||
| cdef DPCTLSyclEventRef ERef = NULL | ||
| 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 = <void*>(<_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: # pragma: no cover | ||
| 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 " | ||
| "`dpctl.memory._Memory` or a type that " | ||
| "supports Python buffer protocol" | ||
| ) | ||
|
|
||
| if isinstance(dst, _Memory): | ||
| c_dst_ptr = <void*>(<_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: # 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 | ||
| else: | ||
| raise TypeError( | ||
| "Parameter `dst` should have either type " | ||
| "`dpctl.memory._Memory` or a type that " | ||
| "supports Python buffer protocol" | ||
| ) | ||
|
|
||
| if dep_events_count == 0 or dep_events is NULL: | ||
| ERef = DPCTLQueue_CopyData( | ||
| q._queue_ref, c_dst_ptr, c_src_ptr, byte_count | ||
| ) | ||
| else: | ||
| ERef = DPCTLQueue_CopyDataWithEvents( | ||
| q._queue_ref, | ||
| c_dst_ptr, | ||
| c_src_ptr, | ||
| byte_count, | ||
| 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 | ||
|
|
||
|
|
||
| cdef class _SyclQueue: | ||
| """ Barebone data owner class used by SyclQueue. | ||
| """ | ||
|
|
@@ -1421,6 +1499,82 @@ cdef class SyclQueue(_SyclQueue): | |
|
|
||
| return SyclEvent._create(ERef) | ||
|
|
||
| cpdef copy(self, dest, src, size_t count): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. one of the advantages of copy vs memcpy is that copy permits typed pointers instead of only We could have an optional arg that passes a numpy style dtype str (see WorkGroupMemory implementation) and checks that both pointers are appropriately sized within the type itself, before passing the the void ptr copies If not in this PR, a TODO for it would be nice, as somewhere down the line I believe we need |
||
| """Copy ``count`` bytes from ``src`` to ``dest`` and wait. | ||
|
|
||
| Internally, this dispatches ``sycl::queue::copy`` instantiated for | ||
| byte-sized elements. | ||
|
|
||
| This is a synchronizing variant corresponding to | ||
| :meth:`dpctl.SyclQueue.copy_async`. | ||
| """ | ||
| cdef DPCTLSyclEventRef ERef = NULL | ||
|
|
||
| ERef = _copy_impl(<SyclQueue>self, dest, src, count, NULL, 0) | ||
| if (ERef is NULL): | ||
| raise RuntimeError( | ||
| "SyclQueue.copy operation encountered an error" | ||
| ) | ||
| with nogil: | ||
| DPCTLEvent_Wait(ERef) | ||
| DPCTLEvent_Delete(ERef) | ||
|
|
||
| cpdef SyclEvent copy_async( | ||
| self, dest, src, size_t count, list dEvents=None | ||
| ): | ||
| """Copy ``count`` bytes from ``src`` to ``dest`` asynchronously. | ||
|
|
||
| Internally, this dispatches ``sycl::queue::copy`` instantiated for | ||
| byte-sized elements. | ||
|
|
||
| Args: | ||
| dest: | ||
| Destination USM object or Python object supporting | ||
| writable buffer protocol. | ||
| src: | ||
| Source USM object or Python object supporting buffer | ||
| protocol. | ||
| count (int): | ||
| Number of bytes to copy. | ||
| dEvents (List[dpctl.SyclEvent], optional): | ||
| Events that this copy depends on. | ||
|
|
||
| Returns: | ||
| dpctl.SyclEvent: | ||
| Event associated with the copy operation. | ||
| """ | ||
| cdef DPCTLSyclEventRef ERef = NULL | ||
| cdef DPCTLSyclEventRef *depEvents = NULL | ||
| cdef size_t nDE = 0 | ||
|
|
||
| if dEvents is None: | ||
| ERef = _copy_impl(<SyclQueue>self, dest, src, count, NULL, 0) | ||
| else: | ||
| nDE = len(dEvents) | ||
| depEvents = ( | ||
| <DPCTLSyclEventRef*>malloc(nDE*sizeof(DPCTLSyclEventRef)) | ||
| ) | ||
| if depEvents is NULL: | ||
| raise MemoryError() | ||
| else: | ||
| for idx, de in enumerate(dEvents): | ||
| if isinstance(de, SyclEvent): | ||
| depEvents[idx] = (<SyclEvent>de).get_event_ref() | ||
| else: | ||
| free(depEvents) | ||
| raise TypeError( | ||
| "A sequence of dpctl.SyclEvent is expected" | ||
| ) | ||
| ERef = _copy_impl(self, dest, src, count, depEvents, nDE) | ||
| free(depEvents) | ||
|
|
||
| if (ERef is NULL): | ||
| raise RuntimeError( | ||
| "SyclQueue.copy operation encountered an error" | ||
| ) | ||
|
|
||
| return SyclEvent._create(ERef) | ||
|
|
||
| cpdef prefetch(self, mem, size_t count=0): | ||
| cdef void *ptr | ||
| cdef DPCTLSyclEventRef ERef = NULL | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| # Data Parallel Control (dpctl) | ||
| # | ||
| # Copyright 2020-2025 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.copy.""" | ||
|
|
||
| import pytest | ||
|
|
||
| import dpctl | ||
| import dpctl.memory | ||
|
|
||
|
|
||
| def _create_memory(q): | ||
| nbytes = 1024 | ||
| mobj = dpctl.memory.MemoryUSMShared(nbytes, queue=q) | ||
| return mobj | ||
|
|
||
|
|
||
| def test_copy_copy_host_to_host(): | ||
| try: | ||
| q = dpctl.SyclQueue() | ||
| except dpctl.SyclQueueCreationError: | ||
| pytest.skip("Default constructor for SyclQueue failed") | ||
|
|
||
| src_buf = b"abcdefghijklmnopqrstuvwxyz" | ||
| dst_buf = bytearray(len(src_buf)) | ||
|
|
||
| q.copy(dst_buf, src_buf, len(src_buf)) | ||
|
|
||
| assert dst_buf == src_buf | ||
|
|
||
|
|
||
| def test_copy_async(): | ||
| try: | ||
| q = dpctl.SyclQueue() | ||
| except dpctl.SyclQueueCreationError: | ||
| pytest.skip("Default constructor for SyclQueue failed") | ||
|
|
||
| src_buf = b"abcdefghijklmnopqrstuvwxyz" | ||
| n = len(src_buf) | ||
| dst_buf = bytearray(n) | ||
| dst_buf2 = bytearray(n) | ||
|
|
||
| e = q.copy_async(dst_buf, src_buf, n) | ||
| e2 = q.copy_async(dst_buf2, src_buf, n, [e]) | ||
|
|
||
| e.wait() | ||
| e2.wait() | ||
| assert dst_buf == src_buf | ||
| assert dst_buf2 == src_buf | ||
|
|
||
|
|
||
| def test_copy_type_error(): | ||
| try: | ||
| q = dpctl.SyclQueue() | ||
| except dpctl.SyclQueueCreationError: | ||
| pytest.skip("Default constructor for SyclQueue failed") | ||
| mobj = _create_memory(q) | ||
|
|
||
| with pytest.raises(TypeError) as cm: | ||
| q.copy(None, mobj, 3) | ||
| assert "_Memory" in str(cm.value) | ||
|
|
||
| with pytest.raises(TypeError) as cm: | ||
| q.copy(mobj, None, 3) | ||
| assert "_Memory" in str(cm.value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this and
_memcpy_implare effectively the same thing, but one calls C-API forsycl_queue::copyand the othersycl_queue::memcpy. It may be worth considering if we can refactor even more, passing the function pointers to an impl func