Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [dev] - XXX. XX, XXXX

### Added
* `dpctl.SyclQueue.copy` and `dpctl.SyclQueue.copy_async` methods [gh-2273](https://github.com/IntelPython/dpctl/pull/2273)

### Change

Expand Down
12 changes: 12 additions & 0 deletions dpctl/_backend.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,18 @@ cdef extern from "syclinterface/dpctl_sycl_queue_interface.h":
size_t Count,
const DPCTLSyclEventRef *depEvents,
size_t depEventsCount)
cdef DPCTLSyclEventRef DPCTLQueue_CopyData(
const DPCTLSyclQueueRef Q,
void *Dest,
const void *Src,
size_t Count)
cdef DPCTLSyclEventRef DPCTLQueue_CopyDataWithEvents(
const DPCTLSyclQueueRef Q,
void *Dest,
const void *Src,
size_t Count,
const DPCTLSyclEventRef *depEvents,
size_t depEventsCount)
cdef DPCTLSyclEventRef DPCTLQueue_Memset(
const DPCTLSyclQueueRef Q,
void *Dest,
Expand Down
2 changes: 2 additions & 0 deletions dpctl/_sycl_queue.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ cdef public api class SyclQueue (_SyclQueue) [
cdef DPCTLSyclQueueRef get_queue_ref(self)
cpdef memcpy(self, dest, src, size_t count)
cpdef SyclEvent memcpy_async(self, dest, src, size_t count, list dEvents=*)
cpdef copy(self, dest, src, size_t count)
cpdef SyclEvent copy_async(self, dest, src, size_t count, list dEvents=*)
cpdef prefetch(self, ptr, size_t count=*)
cpdef mem_advise(self, ptr, size_t count, int mem)
cpdef SyclEvent submit_barrier(self, dependent_events=*)
Expand Down
154 changes: 154 additions & 0 deletions dpctl/_sycl_queue.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -531,6 +533,82 @@ cdef DPCTLSyclEventRef _memcpy_impl(
return ERef


cdef DPCTLSyclEventRef _copy_impl(
Comment thread
jharlow-intel marked this conversation as resolved.
Outdated
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.
"""
Expand Down Expand Up @@ -1421,6 +1499,82 @@ cdef class SyclQueue(_SyclQueue):

return SyclEvent._create(ERef)

cpdef copy(self, dest, src, size_t count):
Comment thread
jharlow-intel marked this conversation as resolved.
Outdated
"""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)
Comment thread
jharlow-intel marked this conversation as resolved.
Outdated

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
Expand Down
78 changes: 78 additions & 0 deletions dpctl/tests/test_sycl_queue_copy.py
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():
Comment thread
jharlow-intel marked this conversation as resolved.
Outdated
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)
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,47 @@ DPCTLQueue_MemcpyWithEvents(__dpctl_keep const DPCTLSyclQueueRef QRef,
__dpctl_keep const DPCTLSyclEventRef *DepEvents,
size_t DepEventsCount);

/*!
* @brief C-API wrapper for ``sycl::queue::copy``.
*
* @param QRef An opaque pointer to the ``sycl::queue``.
* @param Dest A destination pointer.
* @param Src A source pointer.
* @param Count A number of bytes to copy.
* @return An opaque pointer to the ``sycl::event`` returned by the
* ``sycl::queue::copy`` function.
* @ingroup QueueInterface
*/
DPCTL_API
__dpctl_give DPCTLSyclEventRef
DPCTLQueue_CopyData(__dpctl_keep const DPCTLSyclQueueRef QRef,
void *Dest,
const void *Src,
size_t Count);

/*!
* @brief C-API wrapper for ``sycl::queue::copy``.
*
* @param QRef An opaque pointer to the ``sycl::queue``.
* @param Dest A destination pointer.
* @param Src A source pointer.
* @param Count A number of bytes to copy.
* @param DepEvents A pointer to array of DPCTLSyclEventRef opaque
* pointers to dependent events.
* @param DepEventsCount A number of dependent events.
* @return An opaque pointer to the ``sycl::event`` returned by the
* ``sycl::queue::copy`` function.
* @ingroup QueueInterface
*/
DPCTL_API
__dpctl_give DPCTLSyclEventRef
DPCTLQueue_CopyDataWithEvents(__dpctl_keep const DPCTLSyclQueueRef QRef,
void *Dest,
const void *Src,
size_t Count,
__dpctl_keep const DPCTLSyclEventRef *DepEvents,
size_t DepEventsCount);

/*!
* @brief C-API wrapper for ``sycl::queue::prefetch``.
*
Expand Down
Loading
Loading