• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IntelPython / dpctl / 14537256782

18 Apr 2025 03:10PM UTC coverage: 86.41% (+0.001%) from 86.409%
14537256782

Pull #2056

github

web-flow
Merge f63bdbb79 into f57963e87
Pull Request #2056: extend pre-commit hooks with cython-lint

3014 of 3710 branches covered (81.24%)

Branch coverage included in aggregate %.

205 of 263 new or added lines in 18 files covered. (77.95%)

4 existing lines in 3 files now uncovered.

12189 of 13884 relevant lines covered (87.79%)

7003.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.13
/dpctl/memory/_memory.pyx
1
#                      Data Parallel Control (dpctl)
1✔
2
#
3
# Copyright 2020-2025 Intel Corporation
4
#
5
# Licensed under the Apache License, Version 2.0 (the "License");
6
# you may not use this file except in compliance with the License.
7
# You may obtain a copy of the License at
8
#
9
#    http://www.apache.org/licenses/LICENSE-2.0
10
#
11
# Unless required by applicable law or agreed to in writing, software
12
# distributed under the License is distributed on an "AS IS" BASIS,
13
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
# See the License for the specific language governing permissions and
15
# limitations under the License.
16

17
# distutils: language = c++
18
# cython: language_level=3
19
# cython: linetrace=True
20

21
"""This file implements Python buffer protocol using Sycl USM shared and host
22
allocators. The USM device allocator is also exposed through this module for
23
use in other Python modules.
24
"""
25

26

27
import dpctl
1✔
28

29
from cpython cimport Py_buffer, pycapsule
30
from cpython.bytes cimport PyBytes_AS_STRING, PyBytes_FromStringAndSize
31

32
from dpctl._backend cimport (  # noqa: E211
33
    DPCTLaligned_alloc_device,
34
    DPCTLaligned_alloc_host,
35
    DPCTLaligned_alloc_shared,
36
    DPCTLContext_AreEq,
37
    DPCTLContext_Delete,
38
    DPCTLDevice_Copy,
39
    DPCTLEvent_Delete,
40
    DPCTLEvent_Wait,
41
    DPCTLmalloc_device,
42
    DPCTLmalloc_host,
43
    DPCTLmalloc_shared,
44
    DPCTLQueue_Copy,
45
    DPCTLQueue_Create,
46
    DPCTLQueue_Delete,
47
    DPCTLQueue_GetContext,
48
    DPCTLQueue_Memcpy,
49
    DPCTLQueue_MemcpyWithEvents,
50
    DPCTLQueue_Memset,
51
    DPCTLSyclContextRef,
52
    DPCTLSyclDeviceRef,
53
    DPCTLSyclEventRef,
54
    DPCTLSyclQueueRef,
55
    DPCTLSyclUSMRef,
56
    DPCTLUSM_GetPointerDevice,
57
    DPCTLUSM_GetPointerType,
58
    _usm_type,
59
)
60

61
from .._sycl_context cimport SyclContext
62
from .._sycl_device cimport SyclDevice
63
from .._sycl_queue cimport SyclQueue
64
from .._sycl_queue_manager cimport get_device_cached_queue
65

66
import collections
1✔
67
import numbers
1✔
68

69
import numpy as np
1✔
70

71
__all__ = [
1✔
72
    "MemoryUSMShared",
73
    "MemoryUSMHost",
74
    "MemoryUSMDevice",
75
    "USMAllocationError",
76
]
77

78
include "_sycl_usm_array_interface_utils.pxi"
79

80
cdef extern from "_opaque_smart_ptr.hpp":
81
    void * OpaqueSmartPtr_Make(void *, DPCTLSyclQueueRef) nogil
82
    void * OpaqueSmartPtr_Copy(void *) nogil
83
    void OpaqueSmartPtr_Delete(void *) nogil
84
    void * OpaqueSmartPtr_Get(void *) nogil
85

86

87
class USMAllocationError(Exception):
1✔
88
    """
89
    An exception raised when Universal Shared Memory (USM) allocation
90
    call returns a null pointer, signaling a failure to perform the allocation.
91
    Some common reasons for allocation failure are:
92

93
        * insufficient free memory to perform the allocation request
94
        * allocation size exceeds the maximum supported by targeted backend
95
    """
96
    pass
97

98

99
cdef void copy_via_host(void *dest_ptr, SyclQueue dest_queue,
1✔
100
                        void *src_ptr, SyclQueue src_queue, size_t nbytes):
101
    """
102
    Copies `nbytes` bytes from `src_ptr` USM memory to
103
    `dest_ptr` USM memory using host as the intermediary.
104

105
    This is useful when `src_ptr` and `dest_ptr` are bound to incompatible
106
    SYCL contexts.
107
    """
108
    # could also have used bytearray(nbytes)
109
    cdef unsigned char[::1] host_buf = np.empty((nbytes,), dtype="|u1")
1✔
110
    cdef DPCTLSyclEventRef E1Ref = NULL
1✔
111
    cdef DPCTLSyclEventRef *depEvs = [NULL,]
1✔
112
    cdef DPCTLSyclEventRef E2Ref = NULL
1✔
113

114
    E1Ref = DPCTLQueue_Memcpy(
1✔
115
        src_queue.get_queue_ref(),
1✔
116
        <void *>&host_buf[0],
1✔
117
        src_ptr,
118
        nbytes
119
    )
120
    depEvs[0] = E1Ref
1✔
121
    E2Ref = DPCTLQueue_MemcpyWithEvents(
1✔
122
        dest_queue.get_queue_ref(),
1✔
123
        dest_ptr,
124
        <void *>&host_buf[0],
1✔
125
        nbytes,
126
        depEvs,
127
        1
128
    )
129
    DPCTLEvent_Delete(E1Ref)
1✔
130
    with nogil:
1✔
131
        DPCTLEvent_Wait(E2Ref)
1✔
132
    DPCTLEvent_Delete(E2Ref)
1✔
133

134

135
def _to_memory(unsigned char[::1] b, str usm_kind):
1✔
136
    """
137
    Constructs Memory of the same size as the argument
138
    and copies data into it"""
139
    cdef _Memory res
140

141
    if (usm_kind == "shared"):
1✔
142
        res = MemoryUSMShared(len(b))
1✔
143
    elif (usm_kind == "device"):
1✔
144
        res = MemoryUSMDevice(len(b))
1✔
145
    elif (usm_kind == "host"):
1✔
146
        res = MemoryUSMHost(len(b))
1✔
147
    else:
148
        raise ValueError(
1✔
149
            "Unrecognized usm_kind={} stored in the "
150
            "pickle".format(usm_kind)
1✔
151
        )
152
    res.copy_from_host(b)
1✔
153

154
    return res
1✔
155

156

157
cdef class _Memory:
158
    """ Internal class implementing methods common to
159
        MemoryUSMShared, MemoryUSMDevice, MemoryUSMHost
160
    """
161
    cdef _cinit_empty(self):
1✔
162
        self._memory_ptr = NULL
1✔
163
        self._opaque_ptr = NULL
1✔
164
        self.nbytes = 0
1✔
165
        self.queue = None
1✔
166
        self.refobj = None
1✔
167

168
    cdef _cinit_alloc(self, Py_ssize_t alignment, Py_ssize_t nbytes,
1✔
169
                      bytes ptr_type, SyclQueue queue):
170
        cdef DPCTLSyclUSMRef p = NULL
1✔
171
        cdef DPCTLSyclQueueRef QRef = NULL
1✔
172

173
        self._cinit_empty()
1✔
174

175
        if (nbytes > 0):
1✔
176
            if queue is None:
1✔
177
                queue = get_device_cached_queue(dpctl.SyclDevice())
1✔
178

179
            QRef = queue.get_queue_ref()
1✔
180
            if (ptr_type == b"shared"):
1✔
181
                if alignment > 0:
1✔
182
                    with nogil:
1✔
183
                        p = DPCTLaligned_alloc_shared(
1✔
184
                            alignment, nbytes, QRef
185
                        )
186
                else:
187
                    with nogil:
1✔
188
                        p = DPCTLmalloc_shared(nbytes, QRef)
1✔
189
            elif (ptr_type == b"host"):
1✔
190
                if alignment > 0:
1✔
191
                    with nogil:
1✔
192
                        p = DPCTLaligned_alloc_host(
1✔
193
                            alignment, nbytes, QRef
194
                        )
195
                else:
196
                    with nogil:
1✔
197
                        p = DPCTLmalloc_host(nbytes, QRef)
1✔
198
            elif (ptr_type == b"device"):
1✔
199
                if (alignment > 0):
1✔
200
                    with nogil:
1✔
201
                        p = DPCTLaligned_alloc_device(
1✔
202
                            alignment, nbytes, QRef
203
                        )
204
                else:
205
                    with nogil:
1✔
206
                        p = DPCTLmalloc_device(nbytes, QRef)
1✔
207
            else:
208
                raise RuntimeError(
×
209
                    "Pointer type '{}' is not recognized".format(
×
210
                        ptr_type.decode("UTF-8")
×
211
                    )
212
                )
213

214
            if (p):
1✔
215
                self._memory_ptr = p
1✔
216
                self._opaque_ptr = OpaqueSmartPtr_Make(p, QRef)
1✔
217
                self.nbytes = nbytes
1✔
218
                self.queue = queue
1✔
219
            else:
220
                raise USMAllocationError(
×
221
                    "USM allocation failed"
222
                )
223
        else:
224
            raise ValueError(
×
225
                "Number of bytes of request allocation must be positive."
226
            )
227

228
    cdef _cinit_other(self, object other):
1✔
229
        cdef _Memory other_mem
230
        if isinstance(other, _Memory):
1✔
231
            other_mem = <_Memory> other
1✔
232
            self.nbytes = other_mem.nbytes
1✔
233
            self.queue = other_mem.queue
1✔
234
            if other_mem._opaque_ptr is NULL:
1✔
235
                self._memory_ptr = other_mem._memory_ptr
1✔
236
                self._opaque_ptr = NULL
1✔
237
                self.refobj = other.reference_obj
1✔
238
            else:
239
                self._memory_ptr = other_mem._memory_ptr
1✔
240
                self._opaque_ptr = OpaqueSmartPtr_Copy(other_mem._opaque_ptr)
1✔
241
                self.refobj = None
1✔
242
        elif hasattr(other, "__sycl_usm_array_interface__"):
1✔
243
            other_iface = other.__sycl_usm_array_interface__
1✔
244
            if isinstance(other_iface, dict):
1✔
245
                other_buf = _USMBufferData.from_sycl_usm_ary_iface(other_iface)
1✔
246
                self._opaque_ptr = NULL
1✔
247
                self._memory_ptr = <DPCTLSyclUSMRef>other_buf.p
1✔
248
                self.nbytes = other_buf.nbytes
1✔
249
                self.queue = other_buf.queue
1✔
250
                self.refobj = other
1✔
251
            else:
252
                raise ValueError(
1✔
253
                    "Argument {} does not correctly expose"
254
                    "`__sycl_usm_array_interface__`.".format(other)
1✔
255
                )
256
        else:
257
            raise ValueError(
×
258
                "Argument {} does not expose "
259
                "`__sycl_usm_array_interface__`.".format(other)
×
260
            )
261

262
    def __dealloc__(self):
263
        if not (self._opaque_ptr is NULL):
1✔
264
            OpaqueSmartPtr_Delete(self._opaque_ptr)
1✔
265
        self._cinit_empty()
1✔
266

267
    cdef DPCTLSyclUSMRef get_data_ptr(self):
1✔
268
        return self._memory_ptr
1✔
269

270
    cdef void* get_opaque_ptr(self):
1✔
271
        return self._opaque_ptr
1✔
272

273
    cdef _getbuffer(self, Py_buffer *buffer, int flags):
1✔
274
        # memory_ptr is Ref which is pointer to SYCL type. For USM it is void*.
275
        cdef SyclContext ctx = self._context
1✔
276
        cdef _usm_type UsmTy = DPCTLUSM_GetPointerType(
1✔
277
            self._memory_ptr, ctx.get_context_ref()
1✔
278
        )
279
        if UsmTy == _usm_type._USM_DEVICE:
1✔
280
            raise ValueError("USM Device memory is not host accessible")
×
281
        buffer.buf = <void *>self._memory_ptr
1✔
282
        buffer.format = "B"                     # byte
1✔
283
        buffer.internal = NULL                  # see References
1✔
284
        buffer.itemsize = 1
1✔
285
        buffer.len = self.nbytes
1✔
286
        buffer.ndim = 1
1✔
287
        buffer.obj = self
1✔
288
        buffer.readonly = 0
1✔
289
        buffer.shape = &self.nbytes
1✔
290
        buffer.strides = &buffer.itemsize
1✔
291
        buffer.suboffsets = NULL                # for pointer arrays only
1✔
292

293
    property nbytes:
294
        """Extent of this USM buffer in bytes."""
295
        def __get__(self):
296
            return self.nbytes
1✔
297

298
    property size:
299
        """Extent of this USM buffer in bytes."""
300
        def __get__(self):
301
            return self.nbytes
1✔
302

303
    property _pointer:
304
        """
305
        USM pointer at the start of this buffer
306
        represented as Python integer.
307
        """
308
        def __get__(self):
309
            return <size_t>(self._memory_ptr)
1✔
310

311
    property _context:
312
        """:class:`dpctl.SyclContext` the USM pointer is bound to. """
313
        def __get__(self):
314
            return self.queue.get_sycl_context()
1✔
315

316
    property _queue:
317
        """
318
        :class:`dpctl.SyclQueue` with :class:`dpctl.SyclContext` the
319
        USM allocation is bound to and :class:`dpctl.SyclDevice` it was
320
        allocated on.
321
        """
322
        def __get__(self):
323
            return self.queue
1✔
324

325
    property reference_obj:
326
        """
327
        Reference to the Python object owning this USM buffer.
328
        """
329
        def __get__(self):
330
            return self.refobj
1✔
331

332
    property sycl_context:
333
        """:class:`dpctl.SyclContext` the USM pointer is bound to."""
334
        def __get__(self):
335
            return self.queue.get_sycl_context()
1✔
336

337
    property sycl_device:
338
        """:class:`dpctl.SyclDevice` the USM pointer is bound to."""
339
        def __get__(self):
340
            return self.queue.get_sycl_device()
1✔
341

342
    property sycl_queue:
343
        """
344
        :class:`dpctl.SyclQueue` with :class:`dpctl.SyclContext` the
345
        USM allocation is bound to and :class:`dpctl.SyclDevice` it was
346
        allocated on.
347
        """
348
        def __get__(self):
349
            return self.queue
1✔
350

351
    def __repr__(self):
352
        return (
1✔
353
            "<SYCL(TM) USM-{} allocation of {} bytes at {}>"
354
            .format(
1✔
355
                self.get_usm_type(),
1✔
356
                self.nbytes,
1✔
357
                hex(<object>(<size_t>self._memory_ptr))
1✔
358
            )
359
        )
360

361
    def __len__(self):
362
        return self.nbytes
1✔
363

364
    def __sizeof__(self):
1✔
365
        return self.nbytes
1✔
366

367
    def __bytes__(self):
1✔
368
        return self.tobytes()
1✔
369

370
    def __reduce__(self):
1✔
371
        return _to_memory, (self.copy_to_host(), self.get_usm_type())
1✔
372

373
    property __sycl_usm_array_interface__:
374
        """
375
        Dictionary encoding information about USM allocation.
376

377
        Contains the following fields:
378

379
            * ``"data"`` (Tuple[int, bool])
380
                unified address space pointer presented as Python integer
381
                and a Boolean value of 'writable' flag. If ``False`` the
382
                allocation is read-only. The return flag is always set to
383
                writable.
384
            * ``"shape"`` (Tuple[int])
385
                Extent of array in bytes. Shape is always 1-tuple for
386
                this object.
387
            * ``"strides"`` (Options[Tuple[int]])
388
                Strides describing array layout, or ``None`` if allocation is
389
                C-contiguous. Always ``None``.
390
            * ``"typestr"`` (str)
391
                Typestring encoding values of allocation. This field is always
392
                set to ``"|u1"`` representing unsigned bytes.
393
            * ``"version"`` (int)
394
                Always ``1``.
395
            * ``"syclobj"`` (:class:`dpctl.SyclQueue`)
396
                Queue associated with this class instance.
397

398
        """
399
        def __get__(self):
400
            cdef dict iface = {
401
                "data": (<size_t>(<void *>self._memory_ptr),
1✔
402
                         True),  # bool(self.writable)),
1✔
403
                "shape": (self.nbytes,),
1✔
404
                "strides": None,
1✔
405
                "typestr": "|u1",
406
                "version": 1,
407
                "syclobj": self.queue
1✔
408
            }
409
            return iface
1✔
410

411
    def get_usm_type(self, syclobj=None):
1✔
412
        """
413
        get_usm_type(syclobj=None)
414

415
        Returns the type of USM allocation using :class:`dpctl.SyclContext`
416
        carried by ``syclobj`` keyword argument. Value of ``None`` is understood
417
        to query against ``self.sycl_context`` - the context used to create the
418
        allocation.
419
        """
420
        cdef SyclContext ctx
421
        cdef SyclQueue q
422
        if syclobj is None:
1✔
423
            ctx = self._context
1✔
424
            return _Memory.get_pointer_type(
1✔
425
                self._memory_ptr, ctx
1✔
426
            ).decode("UTF-8")
1✔
427
        elif isinstance(syclobj, SyclContext):
1✔
428
            ctx = <SyclContext>(syclobj)
1✔
429
            return _Memory.get_pointer_type(
1✔
430
                self._memory_ptr, ctx
1✔
431
            ).decode("UTF-8")
1✔
432
        elif isinstance(syclobj, SyclQueue):
1✔
433
            q = <SyclQueue>(syclobj)
1✔
434
            ctx = q.get_sycl_context()
1✔
435
            return _Memory.get_pointer_type(
1✔
436
                self._memory_ptr, ctx
1✔
437
            ).decode("UTF-8")
1✔
438
        raise TypeError(
1✔
439
            "syclobj keyword can be either None, or an instance of "
440
            "SyclContext or SyclQueue"
441
        )
442

443
    def get_usm_type_enum(self, syclobj=None):
1✔
444
        """
445
        get_usm_type(syclobj=None)
446

447
        Returns the type of USM allocation using :class:`dpctl.SyclContext`
448
        carried by ``syclobj`` keyword argument. Value of ``None`` is understood
449
        to query against ``self.sycl_context`` - the context used to create the
450
        allocation.
451
        """
452
        cdef SyclContext ctx
453
        cdef SyclQueue q
454
        if syclobj is None:
1✔
455
            ctx = self._context
1✔
456
            return _Memory.get_pointer_type_enum(
1✔
457
                self._memory_ptr, ctx
1✔
458
            )
459
        elif isinstance(syclobj, SyclContext):
1✔
460
            ctx = <SyclContext>(syclobj)
1✔
461
            return _Memory.get_pointer_type_enum(
1✔
462
                self._memory_ptr, ctx
1✔
463
            )
464
        elif isinstance(syclobj, SyclQueue):
1✔
465
            q = <SyclQueue>(syclobj)
1✔
466
            ctx = q.get_sycl_context()
1✔
467
            return _Memory.get_pointer_type_enum(
1✔
468
                self._memory_ptr, ctx
1✔
469
            )
470
        raise TypeError(
1✔
471
            "syclobj keyword can be either None, or an instance of "
472
            "SyclContext or SyclQueue"
473
        )
474

475
    cpdef copy_to_host(self, obj=None):
1✔
476
        """
477
        Copy content of instance's memory into memory of ``obj``, or allocate
478
        NumPy array of ``obj`` is ``None``.
479
        """
480
        # Cython does the right thing here
481
        cdef unsigned char[::1] host_buf = obj
1✔
482
        cdef DPCTLSyclEventRef ERef = NULL
1✔
483

484
        if (host_buf is None):
1✔
485
            # Python object did not have buffer interface
486
            # allocate new memory
487
            obj = np.empty((self.nbytes,), dtype="|u1")
1✔
488
            host_buf = obj
1✔
489
        elif (<Py_ssize_t>len(host_buf) < self.nbytes):
1✔
490
            raise ValueError(
×
491
                "Destination object is too small to accommodate {} bytes"
492
                .format(self.nbytes)
×
493
            )
494
        # call kernel to copy from
495
        ERef = DPCTLQueue_Memcpy(
1✔
496
            self.queue.get_queue_ref(),
1✔
497
            <void *>&host_buf[0],      # destination
1✔
498
            <void *>self._memory_ptr,  # source
499
            <size_t>self.nbytes
500
        )
501
        with nogil:
1✔
502
            DPCTLEvent_Wait(ERef)
1✔
503
        DPCTLEvent_Delete(ERef)
1✔
504

505
        return obj
1✔
506

507
    cpdef copy_from_host(self, object obj):
1✔
508
        """
509
        Copy content of Python buffer provided by ``obj`` to instance memory.
510
        """
511
        cdef const unsigned char[::1] host_buf = obj
1✔
512
        cdef Py_ssize_t buf_len = len(host_buf)
1✔
513
        cdef DPCTLSyclEventRef ERef = NULL
1✔
514

515
        if (buf_len > self.nbytes):
1✔
516
            raise ValueError(
×
517
                "Source object is too large to be accommodated in {} bytes "
518
                "buffer".format(self.nbytes)
×
519
            )
520
        # call kernel to copy from
521
        ERef = DPCTLQueue_Memcpy(
1✔
522
            self.queue.get_queue_ref(),
1✔
523
            <void *>self._memory_ptr,  # destination
524
            <void *>&host_buf[0],      # source
1✔
525
            <size_t>buf_len
526
        )
527
        with nogil:
1✔
528
            DPCTLEvent_Wait(ERef)
1✔
529
        DPCTLEvent_Delete(ERef)
1✔
530

531
    cpdef copy_from_device(self, object sycl_usm_ary):
1✔
532
        """
533
        Copy SYCL memory underlying the argument object into
534
        the memory of the instance
535
        """
536
        cdef _USMBufferData src_buf
537
        cdef DPCTLSyclEventRef ERef = NULL
1✔
538
        cdef bint same_contexts = False
1✔
539
        cdef SyclQueue this_queue = None
1✔
540
        cdef SyclQueue src_queue = None
1✔
541

542
        if not hasattr(sycl_usm_ary, "__sycl_usm_array_interface__"):
1✔
543
            raise ValueError(
×
544
                "Object does not implement "
545
                "`__sycl_usm_array_interface__` protocol"
546
            )
547
        sycl_usm_ary_iface = sycl_usm_ary.__sycl_usm_array_interface__
1✔
548
        if isinstance(sycl_usm_ary_iface, dict):
1✔
549
            src_buf = _USMBufferData.from_sycl_usm_ary_iface(sycl_usm_ary_iface)
1✔
550

551
            if (src_buf.nbytes > self.nbytes):
1✔
552
                raise ValueError(
×
553
                    "Source object is too large to "
554
                    "be accommondated in {} bytes buffer".format(self.nbytes)
×
555
                )
556

557
            src_queue = src_buf.queue
1✔
558
            this_queue = self.queue
1✔
559
            same_contexts = DPCTLContext_AreEq(
1✔
560
                src_queue.get_sycl_context().get_context_ref(),
1✔
561
                this_queue.get_sycl_context().get_context_ref()
1✔
562
                )
563
            if (same_contexts):
1✔
564
                ERef = DPCTLQueue_Memcpy(
1✔
565
                    this_queue.get_queue_ref(),
1✔
566
                    <void *>self._memory_ptr,
567
                    <void *>src_buf.p,
568
                    <size_t>src_buf.nbytes
569
                )
570
                with nogil:
1✔
571
                    DPCTLEvent_Wait(ERef)
1✔
572
                DPCTLEvent_Delete(ERef)
1✔
573
            else:
574
                copy_via_host(
1✔
575
                    <void *>self._memory_ptr, this_queue,  # dest
576
                    <void *>src_buf.p, src_queue,          # src
577
                    <size_t>src_buf.nbytes
1✔
578
                )
579
        else:
580
            raise TypeError
×
581

582
    cpdef memset(self, unsigned short val = 0):
1✔
583
        """
584
        Populates this USM allocation with given value.
585
        """
586
        cdef DPCTLSyclEventRef ERef = NULL
1✔
587

588
        ERef = DPCTLQueue_Memset(
1✔
589
            self.queue.get_queue_ref(),
1✔
590
            <void *>self._memory_ptr,  # destination
591
            <int> val,
592
            self.nbytes)
593

594
        if ERef is not NULL:
1✔
595
            DPCTLEvent_Wait(ERef)
1✔
596
            DPCTLEvent_Delete(ERef)
1✔
597
            return
1✔
598
        else:
599
            raise RuntimeError(
×
600
                "Call to memset resulted in an error"
601
            )
602

603
    cpdef bytes tobytes(self):
1✔
604
        """
605
        Constructs bytes object populated with copy of USM memory.
606
        """
607
        cdef Py_ssize_t nb = self.nbytes
1✔
608
        cdef bytes b = PyBytes_FromStringAndSize(NULL, nb)
1✔
609
        # convert bytes to memory view
610
        cdef unsigned char* ptr = <unsigned char*>PyBytes_AS_STRING(b)
1✔
611
        # string is null terminated
612
        cdef unsigned char[::1] mv = (<unsigned char[:(nb + 1):1]>ptr)[:nb]
1✔
613
        self.copy_to_host(mv)  # output is discarded
1✔
614
        return b
1✔
615

616
    @staticmethod
617
    cdef SyclDevice get_pointer_device(DPCTLSyclUSMRef p, SyclContext ctx):
1✔
618
        """
619
        Returns sycl device used to allocate given pointer ``p`` in
620
        given sycl context ``ctx``
621
        """
622
        cdef DPCTLSyclDeviceRef dref = DPCTLUSM_GetPointerDevice(
1✔
623
            p, ctx.get_context_ref()
1✔
624
        )
625
        cdef DPCTLSyclDeviceRef dref_copy = DPCTLDevice_Copy(dref)
1✔
626
        if (dref_copy is NULL):
1✔
627
            raise RuntimeError("Could not create a copy of sycl device")
×
628
        return SyclDevice._create(dref_copy)  # deletes the argument
1✔
629

630
    @staticmethod
631
    cdef bytes get_pointer_type(DPCTLSyclUSMRef p, SyclContext ctx):
1✔
632
        """
633
        get_pointer_type(p, ctx)
634

635
        Gives the SYCL(TM) USM pointer type, using ``sycl::get_pointer_type``,
636
        returning one of 4 possible strings: ``'shared'``, ``'host'``,
637
        ``'device'``, or ``'unknown'``.
638

639
        Args:
640
            p: DPCTLSyclUSMRef
641
                A pointer to test the type of.
642
            ctx: :class:`dpctl.SyclContext`
643
                Python object providing :class:`dpctl.SyclContext` against
644
                which to query for the pointer type.
645
        Returns:
646
            ``b'unknown'`` if the pointer does not represent USM allocation
647
            made using given context. Otherwise, returns ``b'shared'``,
648
            ``b'device'``, or ``b'host'`` type of the allocation.
649
        """
650
        cdef _usm_type usm_ty = DPCTLUSM_GetPointerType(
1✔
651
            p, ctx.get_context_ref()
1✔
652
        )
653
        if usm_ty == _usm_type._USM_DEVICE:
1✔
654
            return b"device"
1✔
655
        elif usm_ty == _usm_type._USM_SHARED:
656
            return b"shared"
1✔
657
        elif usm_ty == _usm_type._USM_HOST:
658
            return b"host"
1✔
659
        else:
NEW
660
            return b"unknown"
×
661

662
    @staticmethod
663
    cdef _usm_type get_pointer_type_enum(DPCTLSyclUSMRef p, SyclContext ctx):
1✔
664
        """
665
        get_pointer_type(p, ctx)
666

667
        Gives the SYCL(TM) USM pointer type, using ``sycl::get_pointer_type``,
668
        returning an enum value.
669

670
        Args:
671
            p: DPCTLSyclUSMRef
672
                A pointer to test the type of.
673
            ctx: :class:`dpctl.SyclContext`
674
                Python object providing :class:`dpctl.SyclContext` against
675
                which to query for the pointer type.
676
        Returns:
677
            An enum value corresponding to the type of allocation.
678
        """
679
        cdef _usm_type usm_ty = DPCTLUSM_GetPointerType(
1✔
680
            p, ctx.get_context_ref()
1✔
681
        )
682
        return usm_ty
1✔
683

684
    @staticmethod
685
    cdef object create_from_usm_pointer_size_qref(
1✔
686
        DPCTLSyclUSMRef USMRef, Py_ssize_t nbytes,
687
        DPCTLSyclQueueRef QRef, object memory_owner=None
688
    ):
689
        r"""
690
        Create appropriate ``MemoryUSM*`` object from pre-allocated
691
        USM memory bound to SYCL context in the reference SYCL queue.
692

693
        Memory will be freed by ``MemoryUSM*`` object for default
694
        value of ``memory_owner`` keyword. The non-default value should
695
        be an object whose dealloc slot frees the memory.
696

697
        The object may not be a no-op dummy Python object to
698
        delay freeing the memory until later times.
699
        """
700
        cdef _usm_type usm_ty
701
        cdef DPCTLSyclContextRef CRef = NULL
1✔
702
        cdef DPCTLSyclQueueRef QRef_copy = NULL
1✔
703
        cdef _Memory _mem
704
        cdef object mem_ty
705
        if nbytes <= 0:
1✔
706
            raise ValueError("Number of bytes must must be positive")
×
707
        if (QRef is NULL):
1✔
708
            raise TypeError("Argument DPCTLSyclQueueRef is NULL")
×
709
        CRef = DPCTLQueue_GetContext(QRef)
1✔
710
        if (CRef is NULL):
1✔
711
            raise ValueError("Could not retrieve context from QRef")
×
712
        usm_ty = DPCTLUSM_GetPointerType(USMRef, CRef)
1✔
713
        DPCTLContext_Delete(CRef)
1✔
714
        if usm_ty == _usm_type._USM_SHARED:
1✔
715
            mem_ty = MemoryUSMShared
1✔
716
        elif usm_ty == _usm_type._USM_DEVICE:
717
            mem_ty = MemoryUSMDevice
1✔
718
        elif usm_ty == _usm_type._USM_HOST:
719
            mem_ty = MemoryUSMHost
1✔
720
        else:
721
            raise ValueError(
×
722
                "Argument pointer is not bound to "
723
                "context in the given queue"
724
            )
725
        res = _Memory.__new__(_Memory)
1✔
726
        _mem = <_Memory> res
1✔
727
        _mem._cinit_empty()
1✔
728
        _mem.nbytes = nbytes
1✔
729
        QRef_copy = DPCTLQueue_Copy(QRef)
1✔
730
        if QRef_copy is NULL:
1✔
731
            raise ValueError("Referenced queue could not be copied.")
×
732
        try:
1✔
733
            # _create steals ownership of QRef_copy
734
            _mem.queue = SyclQueue._create(QRef_copy)
1✔
735
        except dpctl.SyclQueueCreationError as sqce:
×
736
            raise ValueError(
×
737
                "SyclQueue object could not be created from "
738
                "copy of referenced queue"
739
            ) from sqce
×
740
        if memory_owner is None:
1✔
741
            _mem._memory_ptr = USMRef
×
742
            # assume ownership of USM allocation via smart pointer
743
            _mem._opaque_ptr = OpaqueSmartPtr_Make(<void *>USMRef, QRef)
×
744
            _mem.refobj = None
×
745
        else:
746
            _mem._memory_ptr = USMRef
1✔
747
            _mem._opaque_ptr = NULL
1✔
748
            _mem.refobj = memory_owner
1✔
749
        _out = mem_ty(<object>_mem)
1✔
750
        return _out
1✔
751

752

753
cdef class MemoryUSMShared(_Memory):
754
    """
755
    MemoryUSMShared(nbytes, alignment=0, queue=None)
756

757
    An object representing allocation of SYCL USM-shared memory.
758

759
    Args:
760
        nbytes (int)
761
            number of bytes to allocated.
762
            Expected to be positive.
763
        alignment (Optional[int]):
764
            allocation alignment request. Non-positive
765
            ``alignment`` values are not ignored and
766
            the unaligned allocator ``sycl::malloc_device``
767
            is used to make allocation instead.
768
            Default: `0`.
769
        queue (Optional[:class:`dpctl.SyclQueue`]):
770
            SYCL queue associated with return allocation
771
            instance. Allocation is performed on the device
772
            associated with the queue, and is bound to
773
            SYCL context from the queue.
774
            If ``queue`` is ``None`` a cached
775
            default-constructed :class:`dpctl.SyclQueue` is
776
            used to allocate memory.
777
    """
778
    def __cinit__(self, other, *, Py_ssize_t alignment=0,
779
                  SyclQueue queue=None, int copy=False):
780
        if (isinstance(other, numbers.Integral)):
1✔
781
            self._cinit_alloc(alignment, <Py_ssize_t>other, b"shared", queue)
1✔
782
        else:
783
            self._cinit_other(other)
1✔
784
            if (self.get_usm_type() != "shared"):
1✔
785
                if copy:
1✔
786
                    self._cinit_alloc(0, <Py_ssize_t>self.nbytes,
1✔
787
                                      b"shared", queue)
788
                    self.copy_from_device(other)
1✔
789
                else:
790
                    raise ValueError(
1✔
791
                        "USM pointer in the argument {} is not a "
792
                        "USM shared pointer. "
793
                        "Zero-copy operation is not possible with "
794
                        "copy=False. "
795
                        "Either use copy=True, or use a constructor "
796
                        "appropriate for "
797
                        "type '{}'".format(other, self.get_usm_type())
1✔
798
                    )
799

800
    def __getbuffer__(self, Py_buffer *buffer, int flags):
801
        self._getbuffer(buffer, flags)
1✔
802

803

804
cdef class MemoryUSMHost(_Memory):
805
    """
806
    MemoryUSMHost(nbytes, alignment=0, queue=None)
807

808
    An object representing allocation of SYCL USM-host memory.
809

810
    Args:
811
        nbytes (int)
812
            number of bytes to allocated.
813
            Expected to be positive.
814
        alignment (Optional[int]):
815
            allocation alignment request. Non-positive
816
            ``alignment`` values are not ignored and
817
            the unaligned allocator ``sycl::malloc_device``
818
            is used to make allocation instead.
819
            Default: `0`.
820
        queue (Optional[:class:`dpctl.SyclQueue`]):
821
            SYCL queue associated with return allocation
822
            instance. Allocation is made in host memory accessible
823
            to all device in the SYCL context from the queue.
824
            Allocation is bound to SYCL context from the queue.
825
            If ``queue`` is ``None`` a cached
826
            default-constructed :class:`dpctl.SyclQueue` is
827
            used to allocate memory.
828
    """
829
    def __cinit__(self, other, *, Py_ssize_t alignment=0,
830
                  SyclQueue queue=None, int copy=False):
831
        if (isinstance(other, numbers.Integral)):
1✔
832
            self._cinit_alloc(alignment, <Py_ssize_t>other, b"host", queue)
1✔
833
        else:
834
            self._cinit_other(other)
1✔
835
            if (self.get_usm_type() != "host"):
1✔
836
                if copy:
1✔
837
                    self._cinit_alloc(
1✔
838
                        0, <Py_ssize_t>self.nbytes, b"host", queue
839
                    )
840
                    self.copy_from_device(other)
1✔
841
                else:
842
                    raise ValueError(
1✔
843
                        "USM pointer in the argument {} is "
844
                        "not a USM host pointer. "
845
                        "Zero-copy operation is not possible with copy=False. "
846
                        "Either use copy=True, or use a constructor "
847
                        "appropriate for type '{}'".format(
1✔
848
                            other, self.get_usm_type()
1✔
849
                        )
850
                    )
851

852
    def __getbuffer__(self, Py_buffer *buffer, int flags):
853
        self._getbuffer(buffer, flags)
1✔
854

855

856
cdef class MemoryUSMDevice(_Memory):
857
    """
858
    MemoryUSMDevice(nbytes, alignment=0, queue=None)
859

860
    Class representing allocation of SYCL USM-device memory.
861

862
    Args:
863
        nbytes (int)
864
            number of bytes to allocated.
865
            Expected to be positive.
866
        alignment (Optional[int]):
867
            allocation alignment request. Non-positive
868
            ``alignment`` values are not ignored and
869
            the unaligned allocator ``sycl::malloc_device``
870
            is used to make allocation instead.
871
            Default: `0`.
872
        queue (Optional[:class:`dpctl.SyclQueue`]):
873
            SYCL queue associated with return allocation
874
            instance. Allocation is performed on the device
875
            associated with the queue, and is bound to
876
            SYCL context from the queue.
877
            If ``queue`` is ``None`` a cached
878
            default-constructed :class:`dpctl.SyclQueue` is
879
            used to allocate memory.
880
    """
881
    def __cinit__(self, other, *, Py_ssize_t alignment=0,
882
                  SyclQueue queue=None, int copy=False):
883
        if (isinstance(other, numbers.Integral)):
1✔
884
            self._cinit_alloc(alignment, <Py_ssize_t>other, b"device", queue)
1✔
885
        else:
886
            self._cinit_other(other)
1✔
887
            if (self.get_usm_type() != "device"):
1✔
888
                if copy:
1✔
889
                    self._cinit_alloc(
1✔
890
                        0, <Py_ssize_t>self.nbytes, b"device", queue
891
                    )
892
                    self.copy_from_device(other)
1✔
893
                else:
894
                    raise ValueError(
1✔
895
                        "USM pointer in the argument {} is not "
896
                        "a USM device pointer. "
897
                        "Zero-copy operation is not possible with copy=False. "
898
                        "Either use copy=True, or use a constructor "
899
                        "appropriate for type '{}'".format(
1✔
900
                            other, self.get_usm_type()
1✔
901
                        )
902
                    )
903

904

905
def as_usm_memory(obj):
1✔
906
    """
907
    as_usm_memory(obj)
908

909
    Converts Python object with ``__sycl_usm_array_interface__`` property
910
    to one of :class:`.MemoryUSMShared`, :class:`.MemoryUSMDevice`, or
911
    :class:`.MemoryUSMHost` instances depending on the type of USM allocation
912
    they represent.
913

914
    Raises:
915
        ValueError
916
            When object does not expose the ``__sycl_usm_array_interface__``,
917
            or it is malformed
918
        TypeError
919
            When unexpected types of entries in the interface are encountered
920
        SyclQueueCreationError
921
            When a :class:`dpctl.SyclQueue` could not be created from the
922
            information given by the interface
923
    """
924
    cdef _Memory res = _Memory.__new__(_Memory)
1✔
925
    cdef str kind
926
    res._cinit_empty()
1✔
927
    res._cinit_other(obj)
1✔
928
    kind = res.get_usm_type()
1✔
929
    if kind == "shared":
1✔
930
        return MemoryUSMShared(res)
1✔
931
    elif kind == "device":
1✔
932
        return MemoryUSMDevice(res)
1✔
933
    elif kind == "host":
1✔
934
        return MemoryUSMHost(res)
1✔
935
    else:
936
        raise ValueError(
×
937
            "Could not determine the type "
938
            "USM allocation represented by argument {}".
×
939
            format(obj)
×
940
        )
941

942

943
cdef api void * Memory_GetOpaquePointer(_Memory obj):
1✔
944
    "Opaque pointer value"
945
    return obj.get_opaque_ptr()
1✔
946

947
cdef api DPCTLSyclUSMRef Memory_GetUsmPointer(_Memory obj):
1✔
948
    "Pointer of USM allocation"
949
    return obj.get_data_ptr()
1✔
950

951
cdef api DPCTLSyclContextRef Memory_GetContextRef(_Memory obj):
1✔
952
    "Context reference to which USM allocation is bound"
953
    return obj.queue._context.get_context_ref()
1✔
954

955
cdef api DPCTLSyclQueueRef Memory_GetQueueRef(_Memory obj):
1✔
956
    """Queue associated with this allocation, used
957
    for copying, population, etc."""
958
    return obj.queue.get_queue_ref()
1✔
959

960
cdef api size_t Memory_GetNumBytes(_Memory obj):
1✔
961
    "Size of the allocation in bytes."
962
    return <size_t>obj.nbytes
1✔
963

964
cdef api object Memory_Make(
1✔
965
    DPCTLSyclUSMRef ptr,
966
    size_t nbytes,
967
    DPCTLSyclQueueRef QRef,
968
    object owner
969
):
970
    "Create _Memory Python object from preallocated memory."
971
    return _Memory.create_from_usm_pointer_size_qref(
1✔
972
        ptr, nbytes, QRef, memory_owner=owner
1✔
973
    )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc