Skip to content

Commit 3d69291

Browse files
authored
Merge pull request #2361 from IntelPython/add_sycl_queue_memset
Add `dpctl.SyclQueue.memset()` method
2 parents 56bb4f1 + 2359b0c commit 3d69291

8 files changed

Lines changed: 512 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
* Added a number of `sycl::device` info queries to `dpctl.SyclDevice` [gh-2324](https://github.com/IntelPython/dpctl/pull/2324)
1212
* Added `sycl::info::context` queries `sycl_platform`, `atomic_memory_order_capabilities`, `atomic_fence_order_capabilities`, `atomic_memory_scope_capabilities`, and `atomic_fence_scope_capabilities` to `dpctl.SyclContext` [gh-2354](https://github.com/IntelPython/dpctl/pull/2354)
1313
* Added `create_kernel_bundle_from_sycl_source`, `is_sycl_source_compilation_available`, and `dpctl.SyclDevice.can_compile` for supporting the creation of `dpctl.SyclKernelBundle`s from SYCL source strings via DPC++ extension, as well as corresponding C-API functions to support it [gh-2206](https://github.com/IntelPython/dpctl/pull/2206)
14+
* Added `dpctl.SyclQueue.memset` and `dpctl.SyclQueue.memset_async` methods [gh-2361](https://github.com/IntelPython/dpctl/pull/2361)
15+
* Added `DPCTLQueue_MemsetWithEvents` C-API function to support `dpctl.SyclQueue.memset_async` [gh-2361](https://github.com/IntelPython/dpctl/pull/2361)
1416

1517
### Changed
1618
* Bump minimum NumPy version to 1.26 [gh-2192](https://github.com/IntelPython/dpctl/pull/2192)

dpctl/_backend.pxd

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
types defined by dpctl's C API.
2222
"""
2323

24-
from libc.stdint cimport int64_t, uint32_t, uint64_t
24+
from libc.stdint cimport int64_t, uint8_t, uint32_t, uint64_t
2525
from libcpp cimport bool
2626

2727

@@ -677,8 +677,15 @@ cdef extern from "syclinterface/dpctl_sycl_queue_interface.h":
677677
cdef DPCTLSyclEventRef DPCTLQueue_Memset(
678678
const DPCTLSyclQueueRef Q,
679679
void *Dest,
680-
int Val,
680+
uint8_t Val,
681681
size_t Count)
682+
cdef DPCTLSyclEventRef DPCTLQueue_MemsetWithEvents(
683+
const DPCTLSyclQueueRef Q,
684+
void *Dest,
685+
uint8_t Val,
686+
size_t Count,
687+
const DPCTLSyclEventRef *depEvents,
688+
size_t depEventsCount)
682689
cdef DPCTLSyclEventRef DPCTLQueue_Prefetch(
683690
const DPCTLSyclQueueRef Q,
684691
const void *Src,

dpctl/_sycl_queue.pxd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ cdef public api class SyclQueue (_SyclQueue) [
107107
cpdef SyclEvent copy_async(
108108
self, dest, src, size_t count, list dEvents=*, str dtype=*
109109
)
110+
cpdef memset(self, mem, int val, size_t count=*)
111+
cpdef SyclEvent memset_async(
112+
self, mem, int val, size_t count=*, list dEvents=*
113+
)
110114
cpdef prefetch(self, ptr, size_t count=*)
111115
cpdef mem_advise(self, ptr, size_t count, int mem)
112116
cpdef SyclEvent submit_barrier(self, dependent_events=*)

dpctl/_sycl_queue.pyx

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ from ._backend cimport ( # noqa: E211
4949
DPCTLQueue_MemAdvise,
5050
DPCTLQueue_Memcpy,
5151
DPCTLQueue_MemcpyWithEvents,
52+
DPCTLQueue_Memset,
53+
DPCTLQueue_MemsetWithEvents,
5254
DPCTLQueue_Prefetch,
5355
DPCTLQueue_SubmitBarrierForEvents,
5456
DPCTLQueue_SubmitNDRange,
@@ -86,6 +88,7 @@ from cpython.buffer cimport (
8688
PyObject_GetBuffer,
8789
)
8890
from cpython.ref cimport Py_INCREF, PyObject
91+
from libc.stdint cimport uint8_t
8992
from libc.stdlib cimport free, malloc
9093

9194
import collections.abc
@@ -602,6 +605,35 @@ cdef DPCTLSyclEventRef _copy_impl(
602605
)
603606

604607

608+
cdef DPCTLSyclEventRef _memset_impl(
609+
SyclQueue q,
610+
object mem,
611+
uint8_t val,
612+
size_t count,
613+
DPCTLSyclEventRef *dep_events,
614+
size_t dep_events_count,
615+
) except *:
616+
cdef void *ptr = NULL
617+
cdef DPCTLSyclEventRef ERef = NULL
618+
619+
if isinstance(mem, _Memory):
620+
ptr = <void*>(<_Memory>mem).get_data_ptr()
621+
else:
622+
raise TypeError("Parameter `mem` should have type _Memory")
623+
624+
if count <= 0 or count > mem.nbytes:
625+
count = mem.nbytes
626+
627+
if dep_events_count == 0 or dep_events is NULL:
628+
ERef = DPCTLQueue_Memset(q._queue_ref, ptr, val, count)
629+
else:
630+
ERef = DPCTLQueue_MemsetWithEvents(
631+
q._queue_ref, ptr, val, count, dep_events, dep_events_count
632+
)
633+
634+
return ERef
635+
636+
605637
cdef class _SyclQueue:
606638
""" Barebone data owner class used by SyclQueue.
607639
"""
@@ -1594,6 +1626,125 @@ cdef class SyclQueue(_SyclQueue):
15941626

15951627
return SyclEvent._create(ERef)
15961628

1629+
cpdef memset(self, mem, int val, size_t count=0):
1630+
"""Fill USM allocation ``mem`` with the byte value ``val`` and wait.
1631+
1632+
Internally, this dispatches ``sycl::queue::memset``. The operation is
1633+
byte-wise: ``count`` bytes are set, each to the same value ``val``.
1634+
1635+
This is a synchronizing variant corresponding to
1636+
:meth:`dpctl.SyclQueue.memset_async`.
1637+
1638+
Args:
1639+
mem:
1640+
Destination USM allocation, an instance of
1641+
:class:`dpctl.memory._Memory`.
1642+
val (int):
1643+
Value to fill ``mem`` with. Following ``sycl::queue::memset``,
1644+
it is interpreted as an ``unsigned char``, i.e. only the least
1645+
significant byte is used.
1646+
count (int, optional):
1647+
Number of bytes to fill. If ``0`` or greater than the size of
1648+
``mem``, the whole allocation is filled. Default: ``0``.
1649+
1650+
Raises:
1651+
TypeError:
1652+
If ``mem`` is not an instance of :class:`dpctl.memory._Memory`.
1653+
OverflowError:
1654+
If ``val`` does not fit in a C ``int`` or ``count`` is
1655+
negative.
1656+
RuntimeError:
1657+
If the memset operation encountered an error.
1658+
"""
1659+
cdef DPCTLSyclEventRef ERef = NULL
1660+
cdef uint8_t byte_val = <uint8_t>val
1661+
1662+
ERef = _memset_impl(<SyclQueue>self, mem, byte_val, count, NULL, 0)
1663+
if (ERef is NULL):
1664+
raise RuntimeError(
1665+
"SyclQueue.memset operation encountered an error"
1666+
)
1667+
with nogil:
1668+
DPCTLEvent_Wait(ERef)
1669+
DPCTLEvent_Delete(ERef)
1670+
1671+
cpdef SyclEvent memset_async(
1672+
self, mem, int val, size_t count=0, list dEvents=None
1673+
):
1674+
"""Fill USM allocation ``mem`` with the byte value ``val``
1675+
asynchronously.
1676+
1677+
Internally, this dispatches ``sycl::queue::memset``. The operation is
1678+
byte-wise: ``count`` bytes are set, each to the same value ``val``.
1679+
1680+
Note:
1681+
The returned event does not keep ``mem`` alive. Keep ``mem``
1682+
alive until the event completes, otherwise its USM allocation
1683+
may be freed mid-operation, causing a use-after-free.
1684+
1685+
Args:
1686+
mem:
1687+
Destination USM allocation, an instance of
1688+
:class:`dpctl.memory._Memory`.
1689+
val (int):
1690+
Value to fill ``mem`` with. Following ``sycl::queue::memset``,
1691+
it is interpreted as an ``unsigned char``, i.e. only the least
1692+
significant byte is used.
1693+
count (int, optional):
1694+
Number of bytes to fill. If ``0`` or greater than the size of
1695+
``mem``, the whole allocation is filled. Default: ``0``.
1696+
dEvents (List[dpctl.SyclEvent], optional):
1697+
Events that this operation depends on.
1698+
1699+
Returns:
1700+
dpctl.SyclEvent:
1701+
Event associated with the memset operation.
1702+
1703+
Raises:
1704+
TypeError:
1705+
If ``mem`` is not an instance of :class:`dpctl.memory._Memory`,
1706+
or ``dEvents`` is not a sequence of :class:`dpctl.SyclEvent`.
1707+
OverflowError:
1708+
If ``val`` does not fit in a C ``int`` or ``count`` is
1709+
negative.
1710+
RuntimeError:
1711+
If the memset operation encountered an error.
1712+
"""
1713+
cdef DPCTLSyclEventRef ERef = NULL
1714+
cdef DPCTLSyclEventRef *depEvents = NULL
1715+
cdef size_t nDE = 0
1716+
cdef uint8_t byte_val = <uint8_t>val
1717+
1718+
if dEvents is None:
1719+
ERef = _memset_impl(<SyclQueue>self, mem, byte_val, count, NULL, 0)
1720+
else:
1721+
nDE = len(dEvents)
1722+
depEvents = (
1723+
<DPCTLSyclEventRef*>malloc(nDE*sizeof(DPCTLSyclEventRef))
1724+
)
1725+
if depEvents is NULL:
1726+
raise MemoryError()
1727+
try:
1728+
for idx, de in enumerate(dEvents):
1729+
if isinstance(de, SyclEvent):
1730+
depEvents[idx] = (<SyclEvent>de).get_event_ref()
1731+
else:
1732+
raise TypeError(
1733+
"A sequence of dpctl.SyclEvent is expected"
1734+
)
1735+
ERef = _memset_impl(
1736+
<SyclQueue>self, mem, byte_val, count, depEvents, nDE
1737+
)
1738+
finally:
1739+
free(depEvents)
1740+
1741+
if (ERef is NULL):
1742+
raise RuntimeError(
1743+
"SyclQueue.memset_async operation encountered an error"
1744+
)
1745+
1746+
return SyclEvent._create(ERef)
1747+
15971748
cpdef prefetch(self, mem, size_t count=0):
15981749
cdef void *ptr
15991750
cdef DPCTLSyclEventRef ERef = NULL

0 commit comments

Comments
 (0)