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

IntelPython / dpctl / 13519751605

25 Feb 2025 11:05AM UTC coverage: 88.126% (-0.1%) from 88.233%
13519751605

Pull #1984

github

web-flow
Merge 2eaf45316 into 5e1c87d5d
Pull Request #1984: Add support for work_group_memory extension

3160 of 3654 branches covered (86.48%)

Branch coverage included in aggregate %.

9 of 31 new or added lines in 2 files covered. (29.03%)

51 existing lines in 1 file now uncovered.

11943 of 13484 relevant lines covered (88.57%)

7170.08 hits per line

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

82.8
/dpctl/_sycl_queue.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
""" Implements SyclQueue Cython extension type.
22
"""
23

24
from ._backend cimport (  # noqa: E211
25
    DPCTLContext_Create,
26
    DPCTLContext_Delete,
27
    DPCTLDefaultSelector_Create,
28
    DPCTLDevice_Copy,
29
    DPCTLDevice_CreateFromSelector,
30
    DPCTLDevice_Delete,
31
    DPCTLDeviceMgr_GetCachedContext,
32
    DPCTLDeviceSelector_Delete,
33
    DPCTLEvent_Delete,
34
    DPCTLEvent_Wait,
35
    DPCTLFilterSelector_Create,
36
    DPCTLQueue_AreEq,
37
    DPCTLQueue_Copy,
38
    DPCTLQueue_Create,
39
    DPCTLQueue_Delete,
40
    DPCTLQueue_GetBackend,
41
    DPCTLQueue_GetContext,
42
    DPCTLQueue_GetDevice,
43
    DPCTLQueue_HasEnableProfiling,
44
    DPCTLQueue_Hash,
45
    DPCTLQueue_IsInOrder,
46
    DPCTLQueue_MemAdvise,
47
    DPCTLQueue_Memcpy,
48
    DPCTLQueue_MemcpyWithEvents,
49
    DPCTLQueue_Prefetch,
50
    DPCTLQueue_SubmitBarrierForEvents,
51
    DPCTLQueue_SubmitNDRange,
52
    DPCTLQueue_SubmitRange,
53
    DPCTLQueue_Wait,
54
    DPCTLSyclContextRef,
55
    DPCTLSyclDeviceSelectorRef,
56
    DPCTLSyclEventRef,
57
    DPCTLWorkGroupMemory_Available,
58
    DPCTLWorkGroupMemory_Create,
59
    DPCTLWorkGroupMemory_Delete,
60
    _arg_data_type,
61
    _backend_type,
62
    _queue_property_type,
63
)
64
from .memory._memory cimport _Memory
65

66
import ctypes
1✔
67

68
from .enum_types import backend_type
1✔
69

70
from cpython cimport pycapsule
71
from cpython.buffer cimport (
72
    Py_buffer,
73
    PyBUF_ANY_CONTIGUOUS,
74
    PyBUF_SIMPLE,
75
    PyBUF_WRITABLE,
76
    PyBuffer_Release,
77
    PyObject_CheckBuffer,
78
    PyObject_GetBuffer,
79
)
80
from cpython.ref cimport Py_DECREF, Py_INCREF, PyObject
81
from libc.stdlib cimport free, malloc
82

83
import collections.abc
1✔
84
import logging
1✔
85

86

87
cdef extern from "_host_task_util.hpp":
88
    DPCTLSyclEventRef async_dec_ref(DPCTLSyclQueueRef, PyObject **, size_t, DPCTLSyclEventRef *, size_t, int *) nogil
89

90

91
__all__ = [
1✔
92
    "SyclQueue",
93
    "SyclKernelInvalidRangeError",
94
    "SyclKernelSubmitError",
95
    "SyclQueueCreationError",
96
]
97

98

99
_logger = logging.getLogger(__name__)
1✔
100

101

102
cdef class kernel_arg_type_attribute:
103
    cdef str parent_name
104
    cdef str attr_name
105
    cdef int attr_value
106

107
    def __cinit__(self, str parent, str name, int value):
108
        self.parent_name = parent
1✔
109
        self.attr_name = name
1✔
110
        self.attr_value = value
1✔
111

112
    def __repr__(self):
113
        return f"<{self.parent_name}.{self.attr_name}: {self.attr_value}>"
1✔
114

115
    def __str__(self):
116
        return f"<{self.parent_name}.{self.attr_name}: {self.attr_value}>"
1✔
117

118
    @property
119
    def name(self):
120
        return self.attr_name
1✔
121

122
    @property
123
    def value(self):
124
        return self.attr_value
1✔
125

126

127
cdef class _kernel_arg_type:
128
    """
129
    An enumeration of supported kernel argument types in
130
    :func:`dpctl.SyclQueue.submit`
131
    """
132
    cdef str _name
133

134
    def __cinit__(self):
135
        self._name = "kernel_arg_type"
1✔
136

137

138
    @property
139
    def __name__(self):
140
        return self._name
1✔
141

142
    def __repr__(self):
143
        return "<enum 'kernel_arg_type'>"
1✔
144

145
    def __str__(self):
146
        return "<enum 'kernel_arg_type'>"
1✔
147

148
    @property
149
    def dpctl_int8(self):
150
        cdef str p_name = "dpctl_int8"
1✔
151
        return kernel_arg_type_attribute(
1✔
152
            self._name,
153
            p_name,
154
            _arg_data_type._INT8_T
1✔
155
        )
156

157
    @property
158
    def dpctl_uint8(self):
159
        cdef str p_name = "dpctl_uint8"
1✔
160
        return kernel_arg_type_attribute(
1✔
161
            self._name,
162
            p_name,
163
            _arg_data_type._UINT8_T
1✔
164
        )
165

166
    @property
167
    def dpctl_int16(self):
168
        cdef str p_name = "dpctl_int16"
1✔
169
        return kernel_arg_type_attribute(
1✔
170
            self._name,
171
            p_name,
172
            _arg_data_type._INT16_T
1✔
173
        )
174

175
    @property
176
    def dpctl_uint16(self):
177
        cdef str p_name = "dpctl_uint16"
1✔
178
        return kernel_arg_type_attribute(
1✔
179
            self._name,
180
            p_name,
181
            _arg_data_type._UINT16_T
1✔
182
        )
183

184
    @property
185
    def dpctl_int32(self):
186
        cdef str p_name = "dpctl_int32"
1✔
187
        return kernel_arg_type_attribute(
1✔
188
            self._name,
189
            p_name,
190
            _arg_data_type._INT32_T
1✔
191
        )
192

193
    @property
194
    def dpctl_uint32(self):
195
        cdef str p_name = "dpctl_uint32"
1✔
196
        return kernel_arg_type_attribute(
1✔
197
            self._name,
198
            p_name,
199
            _arg_data_type._UINT32_T
1✔
200
        )
201

202
    @property
203
    def dpctl_int64(self):
204
        cdef str p_name = "dpctl_int64"
1✔
205
        return kernel_arg_type_attribute(
1✔
206
            self._name,
207
            p_name,
208
            _arg_data_type._INT64_T
1✔
209
        )
210

211
    @property
212
    def dpctl_uint64(self):
213
        cdef str p_name = "dpctl_uint64"
1✔
214
        return kernel_arg_type_attribute(
1✔
215
            self._name,
216
            p_name,
217
            _arg_data_type._UINT64_T
1✔
218
        )
219

220
    @property
221
    def dpctl_float32(self):
222
        cdef str p_name = "dpctl_float32"
1✔
223
        return kernel_arg_type_attribute(
1✔
224
            self._name,
225
            p_name,
226
            _arg_data_type._FLOAT
1✔
227
        )
228

229
    @property
230
    def dpctl_float64(self):
231
        cdef str p_name = "dpctl_float64"
1✔
232
        return kernel_arg_type_attribute(
1✔
233
            self._name,
234
            p_name,
235
            _arg_data_type._DOUBLE
1✔
236
        )
237

238
    @property
239
    def dpctl_void_ptr(self):
240
        cdef str p_name = "dpctl_void_ptr"
1✔
241
        return kernel_arg_type_attribute(
1✔
242
            self._name,
243
            p_name,
244
            _arg_data_type._VOID_PTR
1✔
245
        )
246

247
    @property
248
    def dpctl_local_accessor(self):
249
        cdef str p_name = "dpctl_local_accessor"
1✔
250
        return kernel_arg_type_attribute(
1✔
251
            self._name,
252
            p_name,
253
            _arg_data_type._LOCAL_ACCESSOR
1✔
254
        )
255

256
    @property
257
    def dpctl_work_group_memory(self):
258
        cdef str p_name = "dpctl_work_group_memory"
1✔
259
        return kernel_arg_type_attribute(
1✔
260
            self._name,
261
            p_name,
262
            _arg_data_type._WORK_GROUP_MEMORY
1✔
263
        )
264

265

266
kernel_arg_type = _kernel_arg_type()
1✔
267

268

269
cdef class SyclKernelSubmitError(Exception):
270
    """
271
    A ``SyclKernelSubmitError`` exception is raised when
272
    the provided :class:`.program.SyclKernel` could not be
273
    submitted to the :class:`.SyclQueue`.
274

275
    """
276
    pass
277

278

279
cdef class SyclKernelInvalidRangeError(Exception):
280
    """
281
    A ``SyclKernelInvalidRangeError`` is raised when the provided
282
    range has less than one or more than three dimensions.
283
    """
284
    pass
285

286

287
cdef class SyclQueueCreationError(Exception):
288
    """
289
    A ``SyclQueueCreationError`` exception is raised when a
290
    :class:`.SyclQueue` could not be created.
291

292
    :class:`.SyclQueue` creation can fail if the filter
293
    string is invalid, or the backend or device type values are not supported.
294

295
    """
296
    pass
297

298

299
cdef int _parse_queue_properties(object prop) except *:
1✔
300
    cdef int res = 0
1✔
301
    cdef object props
302
    if isinstance(prop, int):
1✔
303
        return <int>prop
1✔
304
    if not isinstance(prop, (tuple, list)):
1✔
305
        props = (prop, )
1✔
306
    else:
307
        props = prop
1✔
308
    for p in props:
1✔
309
        if isinstance(p, int):
1✔
310
            res = res | <int> p
1✔
311
        elif isinstance(p, str):
1✔
312
            if (p == "in_order"):
1✔
313
                res = res | _queue_property_type._IN_ORDER
1✔
314
            elif (p == "enable_profiling"):
1✔
315
                res = res | _queue_property_type._ENABLE_PROFILING
1✔
316
            elif (p == "default"):
1✔
317
                res = res | _queue_property_type._DEFAULT_PROPERTY
1✔
318
            else:
319
                raise ValueError(
1✔
320
                    (
321
                        "queue property '{}' is not understood, "
322
                        "expecting 'in_order', 'enable_profiling', or 'default'"
323
                    ).format(prop)
1✔
324
                )
325
        else:
326
            raise ValueError(
1✔
327
                "queue property '{}' is not understood.".format(prop)
1✔
328
            )
329
    return res
1✔
330

331

332
cdef void _queue_capsule_deleter(object o) noexcept:
1✔
333
    cdef DPCTLSyclQueueRef QRef = NULL
1✔
334
    if pycapsule.PyCapsule_IsValid(o, "SyclQueueRef"):
1✔
335
        QRef = <DPCTLSyclQueueRef> pycapsule.PyCapsule_GetPointer(
1✔
336
            o, "SyclQueueRef"
337
        )
338
        DPCTLQueue_Delete(QRef)
1✔
339
    elif pycapsule.PyCapsule_IsValid(o, "used_SyclQueueRef"):
1✔
340
        QRef = <DPCTLSyclQueueRef> pycapsule.PyCapsule_GetPointer(
1✔
341
            o, "used_SyclQueueRef"
342
        )
343
        DPCTLQueue_Delete(QRef)
1✔
344

345

346
cdef bint _is_buffer(object o):
1✔
347
    return PyObject_CheckBuffer(o)
1✔
348

349

350
cdef DPCTLSyclEventRef _memcpy_impl(
1✔
351
     SyclQueue q,
352
     object dst,
353
     object src,
354
     size_t byte_count,
355
     DPCTLSyclEventRef *dep_events,
356
     size_t dep_events_count
357
) except *:
358
    cdef void *c_dst_ptr = NULL
1✔
359
    cdef void *c_src_ptr = NULL
1✔
360
    cdef DPCTLSyclEventRef ERef = NULL
1✔
361
    cdef Py_buffer src_buf_view
362
    cdef Py_buffer dst_buf_view
363
    cdef bint src_is_buf = False
1✔
364
    cdef bint dst_is_buf = False
1✔
365
    cdef int ret_code = 0
1✔
366

367
    if isinstance(src, _Memory):
1✔
368
        c_src_ptr = <void*>(<_Memory>src).get_data_ptr()
1✔
369
    elif _is_buffer(src):
1✔
370
        ret_code = PyObject_GetBuffer(src, &src_buf_view, PyBUF_SIMPLE | PyBUF_ANY_CONTIGUOUS)
1✔
371
        if ret_code != 0: # pragma: no cover
UNCOV
372
            raise RuntimeError("Could not access buffer")
×
373
        c_src_ptr = src_buf_view.buf
1✔
374
        src_is_buf = True
1✔
375
    else:
376
        raise TypeError(
1✔
377
             "Parameter `src` should have either type "
378
             "`dpctl.memory._Memory` or a type that "
379
             "supports Python buffer protocol"
380
       )
381

382
    if isinstance(dst, _Memory):
1✔
383
        c_dst_ptr = <void*>(<_Memory>dst).get_data_ptr()
1✔
384
    elif _is_buffer(dst):
1✔
385
        ret_code = PyObject_GetBuffer(dst, &dst_buf_view, PyBUF_SIMPLE | PyBUF_ANY_CONTIGUOUS | PyBUF_WRITABLE)
1✔
386
        if ret_code != 0: # pragma: no cover
UNCOV
387
            if src_is_buf:
×
388
                PyBuffer_Release(&src_buf_view)
×
389
            raise RuntimeError("Could not access buffer")
×
390
        c_dst_ptr = dst_buf_view.buf
1✔
391
        dst_is_buf = True
1✔
392
    else:
393
        raise TypeError(
1✔
394
             "Parameter `dst` should have either type "
395
             "`dpctl.memory._Memory` or a type that "
396
             "supports Python buffer protocol"
397
       )
398

399
    if dep_events_count == 0 or dep_events is NULL:
1✔
400
        ERef = DPCTLQueue_Memcpy(q._queue_ref, c_dst_ptr, c_src_ptr, byte_count)
1✔
401
    else:
402
        ERef = DPCTLQueue_MemcpyWithEvents(
1✔
403
            q._queue_ref,
404
            c_dst_ptr,
405
            c_src_ptr,
406
            byte_count,
407
            dep_events,
408
            dep_events_count
1✔
409
        )
410

411
    if src_is_buf:
1✔
412
        PyBuffer_Release(&src_buf_view)
1✔
413
    if dst_is_buf:
1✔
414
        PyBuffer_Release(&dst_buf_view)
1✔
415

416
    return ERef
1✔
417

418

419
cdef class _SyclQueue:
420
    """ Barebone data owner class used by SyclQueue.
421
    """
422
    def __dealloc__(self):
423
        if (self._queue_ref):
1✔
424
            DPCTLQueue_Delete(self._queue_ref)
1✔
425
        # self._context is a Python object and will be GC-ed
426
        # self._device is a Python object
427

428

429
cdef class SyclQueue(_SyclQueue):
430
    """
431
    SyclQueue(*args, **kwargs)
432
    Python class representing ``sycl::queue``.
433

434
    There are multiple ways to create a :class:`dpctl.SyclQueue` object:
435

436
    - Invoking the constructor with no arguments creates a context using
437
      the default selector.
438

439
    :Example:
440
        .. code-block:: python
441

442
            import dpctl
443

444
            # Create a default SyclQueue
445
            q = dpctl.SyclQueue()
446
            print(q.sycl_device)
447

448
    - Invoking the constructor with specific filter selector string that
449
      creates a queue for the device corresponding to the filter string.
450

451
    :Example:
452
        .. code-block:: python
453

454
            import dpctl
455

456
            # Create in-order SyclQueue for either gpu, or cpu device
457
            q = dpctl.SyclQueue("gpu,cpu", property="in_order")
458
            print([q.sycl_device.is_gpu, q.sycl_device.is_cpu])
459

460
    - Invoking the constructor with a :class:`dpctl.SyclDevice` object
461
      creates a queue for that device, automatically finding/creating
462
      a :class:`dpctl.SyclContext` for the given device.
463

464
    :Example:
465
        .. code-block:: python
466

467
            import dpctl
468

469
            d = dpctl.SyclDevice("gpu")
470
            q = dpctl.SyclQueue(d)
471
            ctx = q.sycl_context
472
            print(q.sycl_device == d)
473
            print(any([ d == ctx_d for ctx_d in ctx.get_devices()]))
474

475
    - Invoking the constructor with a :class:`dpctl.SyclContext` and a
476
      :class:`dpctl.SyclDevice` creates a queue for given context and
477
      device.
478

479
    :Example:
480
        .. code-block:: python
481

482
            import dpctl
483

484
            # Create a CPU device using the opencl driver
485
            cpu_d = dpctl.SyclDevice("opencl:cpu")
486
            # Partition the CPU device into sub-devices with two cores each.
487
            sub_devices = cpu_d.create_sub_devices(partition=2)
488
            # Create a context common to all the sub-devices.
489
            ctx = dpctl.SyclContext(sub_devices)
490
            # create a queue for each sub-device using the common context
491
            queues = [dpctl.SyclQueue(ctx, sub_d) for sub_d in sub_devices]
492

493
    - Invoking the constructor with a named ``PyCapsule`` with the name
494
      **"SyclQueueRef"** that carries a pointer to a ``sycl::queue``
495
      object. The capsule will be renamed upon successful consumption
496
      to ensure one-time use. A new named capsule can be constructed by
497
      using :func:`dpctl.SyclQueue._get_capsule` method.
498

499
    Args:
500
        ctx (:class:`dpctl.SyclContext`, optional): Sycl context to create
501
            :class:`dpctl.SyclQueue` from. If not specified, a single-device
502
            context will be created from the specified device.
503
        dev (str, :class:`dpctl.SyclDevice`, capsule, optional): Sycl device
504
             to create :class:`dpctl.SyclQueue` from. If not specified, sycl
505
             device selected by ``sycl::default_selector`` is used.
506
             The argument must be explicitly specified if `ctxt` argument is
507
             provided.
508

509
             If `dev` is a named ``PyCapsule`` called **"SyclQueueRef"** and
510
             `ctxt` is not specified, :class:`dpctl.SyclQueue` instance is
511
             created from foreign `sycl::queue` object referenced by the
512
             capsule.
513
        property (str, tuple(str), list(str), optional): Defaults to None.
514
                The argument can be either "default", "in_order",
515
                "enable_profiling", or a tuple containing these.
516

517
    Raises:
518
        SyclQueueCreationError: If the :class:`dpctl.SyclQueue` object
519
                                creation failed.
520
        TypeError: In case of incorrect arguments given to constructors,
521
                   unexpected types of input arguments, or in the case the input
522
                   capsule contained a null pointer or could not be renamed.
523

524
    """
525
    def __cinit__(self, *args, **kwargs):
526
        cdef int len_args
527
        cdef int status = 0
1✔
528
        cdef const char *filter_c_str = NULL
1✔
529
        if len(args) > 2:
1✔
530
            raise TypeError(
1✔
531
                "SyclQueue constructor takes 0, 1, or 2 positinal arguments, "
532
                "but {} were given.".format(len(args))
1✔
533
            )
534
        props = _parse_queue_properties(
1✔
535
            kwargs.pop('property', _queue_property_type._DEFAULT_PROPERTY)
1✔
536
        )
537
        if (kwargs):
1✔
538
            raise TypeError(
1✔
539
                f"Unsupported keyword arguments {kwargs} to "
1✔
540
                "SyclQueue constructor encountered."
541
            )
542
        len_args = len(args)
1✔
543
        if len_args == 0:
1✔
544
            status = self._init_queue_default(props)
1✔
545
        elif len_args == 1:
546
            arg = args[0]
1✔
547
            if type(arg) is str:
1✔
548
                string = bytes(<str>arg, "utf-8")
1✔
549
                filter_c_str = string
1✔
550
                status = self._init_queue_from_filter_string(
1✔
551
                    filter_c_str, props)
552
            elif type(arg) is _SyclQueue:
1✔
553
                status = self._init_queue_from__SyclQueue(<_SyclQueue>arg)
1✔
554
            elif isinstance(arg, SyclDevice):
1✔
555
                status = self._init_queue_from_device(<SyclDevice>arg, props)
1✔
556
            elif pycapsule.PyCapsule_IsValid(arg, "SyclQueueRef"):
1✔
557
                status = self._init_queue_from_capsule(arg)
1✔
558
            else:
559
                raise TypeError(
1✔
560
                    "Positional argument {} is not a filter string or a "
561
                    "SyclDevice".format(arg)
1✔
562
                )
563
        else:
564
            ctx, dev = args
1✔
565
            if not isinstance(ctx, SyclContext):
1✔
566
                raise TypeError(
1✔
567
                    "SyclQueue constructor with two positional arguments "
568
                    "expected SyclContext as its first argument, but got {}."
569
                    .format(type(ctx))
1✔
570
                )
571
            if not isinstance(dev, SyclDevice):
1✔
572
                raise TypeError(
1✔
573
                    "SyclQueue constructor with two positional arguments "
574
                    "expected SyclDevice as its second argument, but got {}."
575
                    .format(type(dev))
1✔
576
                )
577
            status = self._init_queue_from_context_and_device(
1✔
578
                <SyclContext>ctx, <SyclDevice>dev, props
579
            )
580
        if status < 0:
1✔
581
            if status == -1:
1✔
582
                raise SyclQueueCreationError(
1✔
583
                    "Device filter selector string '{}' is not understood."
584
                    .format(arg)
1✔
585
                )
586
            elif status == -2 or status == -8:
1✔
587
                default_dev_error = (
588
                    "Default SYCL Device could not be created."
1✔
589
                )
590
                raise SyclQueueCreationError(
1✔
591
                    default_dev_error if (len_args == 0) else
1✔
592
                    "SYCL Device '{}' could not be created.".format(arg)
1✔
593
                )
UNCOV
594
            elif status == -3 or status == -7:
×
595
                raise SyclQueueCreationError(
×
596
                    "SYCL Context could not be created " +
×
597
                    ("by default constructor" if len_args == 0 else
×
598
                     "from '{}'.".format(arg)
×
599
                    )
600
                )
601
            elif status == -4 or status == -6:
1✔
602
                if len_args == 2:
1✔
603
                    arg = args
1✔
604
                raise SyclQueueCreationError(
1✔
605
                    "SYCL Queue failed to be created from '{}'.".format(arg)
1✔
606
                )
607
            elif status == -5:
UNCOV
608
                raise TypeError(
×
609
                    "Input capsule {} contains a null pointer or could not "
UNCOV
610
                    "be renamed".format(arg)
×
611
                )
612

613
    cdef int _init_queue_from__SyclQueue(self, _SyclQueue other):
1✔
614
        """ Copy data container _SyclQueue fields over.
615
        """
616
        cdef DPCTLSyclQueueRef QRef = DPCTLQueue_Copy(other._queue_ref)
1✔
617
        if (QRef is NULL):
1✔
UNCOV
618
            return -4
×
619
        self._queue_ref = QRef
1✔
620
        self._context = other._context
1✔
621
        self._device = other._device
1✔
622

623
    cdef int _init_queue_from_DPCTLSyclDeviceRef(
1✔
624
        self, DPCTLSyclDeviceRef DRef, int props
625
    ):
626
        """
627
        Initializes self by creating SyclQueue with specified error handler and
628
        specified properties from the given device instance. SyclContext is
629
        looked-up by DPCTL from a cache to avoid repeated construction of new
630
        context for performance reasons.
631

632
        Returns: 0 : normal execution
633
                -3 : Context creation/look-up failed
634
                -4 : queue could not be created from context,device, error
635
                     handler and properties
636
        """
637
        cdef DPCTLSyclContextRef CRef
638
        cdef DPCTLSyclQueueRef QRef
639

640
        CRef = DPCTLDeviceMgr_GetCachedContext(DRef)
1✔
641
        if (CRef is NULL):
1✔
642
            # look-up failed (was not a root device?)
643
            # create a new context
644
            CRef = DPCTLContext_Create(DRef, NULL, 0)
1✔
645
            if (CRef is NULL):
1✔
UNCOV
646
                DPCTLDevice_Delete(DRef)
×
647
                return -3
×
648
        QRef = DPCTLQueue_Create(
1✔
649
            CRef,
650
            DRef,
651
            NULL,
652
            props
653
        )
654
        if QRef is NULL:
1✔
UNCOV
655
            DPCTLContext_Delete(CRef)
×
656
            DPCTLDevice_Delete(DRef)
×
657
            return -4
×
658
        _dev = SyclDevice._create(DRef)
1✔
659
        _ctxt = SyclContext._create(CRef)
1✔
660
        self._device = _dev
1✔
661
        self._context = _ctxt
1✔
662
        self._queue_ref = QRef
1✔
663
        return 0  # normal return
1✔
664

665
    cdef int _init_queue_from_filter_string(self, const char *c_str, int props):
1✔
666
        """
667
        Initializes self from filter string, error handler and properties.
668
        Creates device from device selector, then calls helper function above.
669

670
        Returns:
671
             0 : normal execution
672
            -1 : filter selector could not be created (malformed?)
673
            -2 : Device could not be created from filter selector
674
            -3 : Context creation/look-up failed
675
            -4 : queue could not be created from context,device, error handler
676
                 and properties
677
        """
678
        cdef DPCTLSyclDeviceSelectorRef DSRef = NULL
1✔
679
        cdef DPCTLSyclDeviceRef DRef = NULL
1✔
680
        cdef int ret = 0
1✔
681

682
        DSRef = DPCTLFilterSelector_Create(c_str)
1✔
683
        if DSRef is NULL:
1✔
684
            ret = -1  # Filter selector failed to be created
1✔
685
        else:
686
            DRef = DPCTLDevice_CreateFromSelector(DSRef)
1✔
687
            DPCTLDeviceSelector_Delete(DSRef)
1✔
688
            if (DRef is NULL):
1✔
689
                ret = -2  # Device could not be created
1✔
690
            else:
691
                ret = self._init_queue_from_DPCTLSyclDeviceRef(DRef, props)
1✔
692
        return ret
1✔
693

694
    cdef int _init_queue_from_device(self, SyclDevice dev, int props):
1✔
695
        cdef DPCTLSyclDeviceRef DRef = NULL
1✔
696
        # The DRef will be stored in self._device and freed when self._device
697
        # is garbage collected.
698
        DRef = DPCTLDevice_Copy(dev.get_device_ref())
1✔
699
        if (DRef is NULL):
1✔
UNCOV
700
            return -2  # Device could not be created
×
701
        else:
702
            return self._init_queue_from_DPCTLSyclDeviceRef(DRef, props)
1✔
703

704
    cdef int _init_queue_default(self, int props):
1✔
705
        cdef DPCTLSyclDeviceSelectorRef DSRef = DPCTLDefaultSelector_Create()
1✔
706
        cdef int ret = 0
1✔
707
        # The DRef will be stored in self._device and freed when self._device
708
        # is garbage collected.
709
        DRef = DPCTLDevice_CreateFromSelector(DSRef)
1✔
710
        DPCTLDeviceSelector_Delete(DSRef)
1✔
711
        if (DRef is NULL):
1✔
UNCOV
712
            ret = -2  # Device could not be created
×
713
        else:
714
            ret = self._init_queue_from_DPCTLSyclDeviceRef(DRef, props)
1✔
715
        return ret
1✔
716

717
    cdef int _init_queue_from_context_and_device(
1✔
718
        self, SyclContext ctxt, SyclDevice dev, int props
719
    ):
720
        cdef DPCTLSyclContextRef CRef = NULL
1✔
721
        cdef DPCTLSyclDeviceRef DRef = NULL
1✔
722
        cdef DPCTLSyclQueueRef QRef = NULL
1✔
723
        CRef = ctxt.get_context_ref()
1✔
724
        DRef = dev.get_device_ref()
1✔
725
        QRef = DPCTLQueue_Create(
1✔
726
            CRef,
727
            DRef,
728
            NULL,
729
            props
730
        )
731
        if (QRef is NULL):
1✔
732
            return -4
1✔
733
        self._device = dev
1✔
734
        self._context = ctxt
1✔
735
        self._queue_ref = QRef
1✔
736
        return 0  # normal return
1✔
737

738
    cdef int _init_queue_from_capsule(self, object cap):
1✔
739
        """
740
        For named PyCapsule with name "SyclQueueRef", which carries pointer to
741
        ``sycl::queue`` object, interpreted as ``DPCTLSyclQueueRef``, creates
742
        corresponding :class:`.SyclQueue`.
743
        """
744
        cdef DPCTLSyclContextRef CRef = NULL
1✔
745
        cdef DPCTLSyclDeviceRef DRef = NULL
1✔
746
        cdef DPCTLSyclQueueRef QRef = NULL
1✔
747
        cdef DPCTLSyclQueueRef QRef_copy = NULL
1✔
748
        cdef int ret = 0
1✔
749
        if pycapsule.PyCapsule_IsValid(cap, "SyclQueueRef"):
1✔
750
            QRef = <DPCTLSyclQueueRef> pycapsule.PyCapsule_GetPointer(
1✔
751
                cap, "SyclQueueRef"
752
            )
753
            if (QRef is NULL):
1✔
UNCOV
754
                return -5
×
755
            ret = pycapsule.PyCapsule_SetName(cap, "used_SyclQueueRef")
1✔
756
            if (ret):
1✔
UNCOV
757
                return -5
×
758
            QRef_copy = DPCTLQueue_Copy(QRef)
1✔
759
            if (QRef_copy is NULL):
1✔
UNCOV
760
                return -6
×
761
            CRef = DPCTLQueue_GetContext(QRef_copy)
1✔
762
            if (CRef is NULL):
1✔
UNCOV
763
                DPCTLQueue_Delete(QRef_copy)
×
764
                return -7
×
765
            DRef = DPCTLQueue_GetDevice(QRef_copy)
1✔
766
            if (DRef is NULL):
1✔
UNCOV
767
                DPCTLContext_Delete(CRef)
×
768
                DPCTLQueue_Delete(QRef_copy)
×
769
                return -8
×
770
            self._context = SyclContext._create(CRef)
1✔
771
            self._device = SyclDevice._create(DRef)
1✔
772
            self._queue_ref = QRef_copy
1✔
773
            return 0
1✔
774
        else:
775
            # __cinit__ checks that capsule is valid, so one can be here only
776
            # if call to `_init_queue_from_capsule` was made outside of
777
            # __cinit__ and the capsule was not checked to be valid.
UNCOV
778
            return -128
×
779

780
    @staticmethod
781
    cdef SyclQueue _create(DPCTLSyclQueueRef qref):
1✔
782
        """
783
        This function calls ``DPCTLQueue_Delete(qref)``.
784
        The user of this function must pass a copy to keep the
785
        qref argument alive.
786
        """
787
        if qref is NULL:
1✔
UNCOV
788
            raise SyclQueueCreationError("Queue creation failed.")
×
789
        cdef _SyclQueue ret = _SyclQueue.__new__(_SyclQueue)
1✔
790
        ret._context = SyclContext._create(DPCTLQueue_GetContext(qref))
1✔
791
        ret._device = SyclDevice._create(DPCTLQueue_GetDevice(qref))
1✔
792
        ret._queue_ref = qref
1✔
793
        # ret is a temporary, and will call DPCTLQueue_Delete(qref)
794
        return SyclQueue(ret)
1✔
795

796
    @staticmethod
797
    cdef SyclQueue _create_from_context_and_device(
1✔
798
        SyclContext ctx, SyclDevice dev, int props=0
799
    ):
800
        """
801
        Static factory method to create :class:`dpctl.SyclQueue` instance
802
        from given :class:`dpctl.SyclContext`, :class:`dpctl.SyclDevice`
803
        and optional integer ``props`` encoding the queue properties.
804
        """
805
        cdef _SyclQueue ret = _SyclQueue.__new__(_SyclQueue)
1✔
806
        cdef DPCTLSyclContextRef cref = ctx.get_context_ref()
1✔
807
        cdef DPCTLSyclDeviceRef dref = dev.get_device_ref()
1✔
808
        cdef DPCTLSyclQueueRef qref = NULL
1✔
809

810
        qref = DPCTLQueue_Create(
1✔
811
            cref,
812
            dref,
813
            NULL,
814
            props
815
        )
816
        if qref is NULL:
1✔
UNCOV
817
            raise SyclQueueCreationError("Queue creation failed.")
×
818
        ret._queue_ref = qref
1✔
819
        ret._context = ctx
1✔
820
        ret._device = dev
1✔
821
        return SyclQueue(ret)
1✔
822

823
    cdef int _populate_args(
1✔
824
        self,
825
        list args,
826
        void **kargs,
827
        _arg_data_type *kargty
828
    ):
829
        cdef int ret = 0
1✔
830
        for idx, arg in enumerate(args):
1✔
831
            if isinstance(arg, ctypes.c_char):
1✔
UNCOV
832
                kargs[idx] = <void*><size_t>(ctypes.addressof(arg))
×
833
                kargty[idx] = _arg_data_type._INT8_T
×
834
            elif isinstance(arg, ctypes.c_uint8):
1✔
UNCOV
835
                kargs[idx] = <void*><size_t>(ctypes.addressof(arg))
×
836
                kargty[idx] = _arg_data_type._UINT8_T
×
837
            elif isinstance(arg, ctypes.c_short):
1✔
838
                kargs[idx] = <void*><size_t>(ctypes.addressof(arg))
1✔
839
                kargty[idx] = _arg_data_type._INT16_T
1✔
840
            elif isinstance(arg, ctypes.c_ushort):
1✔
UNCOV
841
                kargs[idx] = <void*><size_t>(ctypes.addressof(arg))
×
842
                kargty[idx] = _arg_data_type._UINT16_T
×
843
            elif isinstance(arg, ctypes.c_int):
1✔
844
                kargs[idx] = <void*><size_t>(ctypes.addressof(arg))
1✔
845
                kargty[idx] = _arg_data_type._INT32_T
1✔
846
            elif isinstance(arg, ctypes.c_uint):
1✔
847
                kargs[idx] = <void*><size_t>(ctypes.addressof(arg))
1✔
848
                kargty[idx] = _arg_data_type._UINT32_T
1✔
849
            elif isinstance(arg, ctypes.c_longlong):
1✔
850
                kargs[idx] = <void*><size_t>(ctypes.addressof(arg))
1✔
851
                kargty[idx] = _arg_data_type._INT64_T
1✔
852
            elif isinstance(arg, ctypes.c_ulonglong):
1✔
853
                kargs[idx] = <void*><size_t>(ctypes.addressof(arg))
1✔
854
                kargty[idx] = _arg_data_type._UINT64_T
1✔
855
            elif isinstance(arg, ctypes.c_float):
1✔
856
                kargs[idx] = <void*><size_t>(ctypes.addressof(arg))
1✔
857
                kargty[idx] = _arg_data_type._FLOAT
1✔
858
            elif isinstance(arg, ctypes.c_double):
1✔
859
                kargs[idx] = <void*><size_t>(ctypes.addressof(arg))
1✔
860
                kargty[idx] = _arg_data_type._DOUBLE
1✔
861
            elif isinstance(arg, _Memory):
1✔
862
                kargs[idx]= <void*>(<size_t>arg._pointer)
1✔
863
                kargty[idx] = _arg_data_type._VOID_PTR
1✔
NEW
UNCOV
864
            elif isinstance(arg, WorkGroupMemory):
×
NEW
865
                kargs[idx] = <void*>(<size_t>arg._ref)
×
NEW
866
                kargty[idx] = _arg_data_type._WORK_GROUP_MEMORY
×
867
            else:
868
                ret = -1
×
869
        return ret
1✔
870

871
    cdef int _populate_range(self, size_t Range[3], list S, size_t nS):
1✔
872

873
        cdef int ret = 0
1✔
874

875
        if nS == 1:
1✔
876
            Range[0] = <size_t>S[0]
1✔
877
            Range[1] = 1
1✔
878
            Range[2] = 1
1✔
879
        elif nS == 2:
880
            Range[0] = <size_t>S[0]
1✔
881
            Range[1] = <size_t>S[1]
1✔
882
            Range[2] = 1
1✔
883
        elif nS == 3:
884
            Range[0] = <size_t>S[0]
1✔
885
            Range[1] = <size_t>S[1]
1✔
886
            Range[2] = <size_t>S[2]
1✔
887
        else:
UNCOV
888
            ret = -1
×
889

890
        return ret
1✔
891

892
    cdef cpp_bool equals(self, SyclQueue q):
1✔
893
        """ Returns true if the :class:`.SyclQueue` argument ``q`` has the
894
            same ``._queue_ref`` attribute as this :class:`.SyclQueue`.
895
        """
896
        return DPCTLQueue_AreEq(self._queue_ref, q.get_queue_ref())
1✔
897

898
    def __eq__(self, other):
899
        """
900
        Returns True if two :class:`dpctl.SyclQueue` compared arguments have
901
        the same underlying ``DPCTLSyclQueueRef`` object.
902

903
        Returns:
904
            bool:
905
                ``True`` if the two :class:`dpctl.SyclQueue` objects
906
                point to the same ``DPCTLSyclQueueRef`` object, otherwise
907
                ``False``.
908
        """
909
        if isinstance(other, SyclQueue):
1✔
910
            return self.equals(<SyclQueue> other)
1✔
911
        else:
912
            return False
1✔
913

914
    @property
915
    def backend(self):
916
        """ Returns the ``backend_type`` enum value for this queue.
917

918
        Returns:
919
            backend_type:
920
                The backend for the queue.
921
        """
922
        cdef _backend_type BE = DPCTLQueue_GetBackend(self._queue_ref)
1✔
923
        if BE == _backend_type._OPENCL:
1✔
924
            return backend_type.opencl
1✔
925
        elif BE == _backend_type._LEVEL_ZERO:
UNCOV
926
            return backend_type.level_zero
×
927
        elif BE == _backend_type._CUDA:
UNCOV
928
            return backend_type.cuda
×
929
        elif BE == _backend_type._HIP:
UNCOV
930
            return backend_type.hip
×
931
        else:
UNCOV
932
            raise ValueError("Unknown backend type.")
×
933

934
    @property
935
    def sycl_context(self):
936
        """
937
        Returns :class:`SyclContext` underlying this queue.
938

939
        Returns:
940
            :class:`SyclContext`
941
                SYCL context underlying this queue
942
        """
943
        return self._context
1✔
944

945
    @property
946
    def sycl_device(self):
947
        """
948
        Returns :class:`.SyclDevice` targeted by this queue.
949

950
        Returns:
951
            :class:`SyclDevice`
952
                SYCL device targeted by this queue
953
        """
954
        return self._device
1✔
955

956
    cpdef SyclContext get_sycl_context(self):
1✔
957
        return self._context
1✔
958

959
    cpdef SyclDevice get_sycl_device(self):
1✔
960
        return self._device
1✔
961

962
    cdef DPCTLSyclQueueRef get_queue_ref(self):
1✔
963
        return self._queue_ref
1✔
964

965
    def addressof_ref(self):
1✔
966
        """
967
        Returns the address of the C API ``DPCTLSyclQueueRef`` pointer as
968
        integral value of type ``size_t``.
969

970
        Returns:
971
            int:
972
                The address of the ``DPCTLSyclQueueRef`` object used to create
973
                this :class:`dpctl.SyclQueue` object cast to ``size_t`` type.
974
        """
975
        return <size_t>self._queue_ref
1✔
976

977

978
    cpdef SyclEvent _submit_keep_args_alive(
1✔
979
        self,
980
        object args,
981
        list dEvents
982
    ):
983
        """ SyclQueue._submit_keep_args_alive(args, events)
984

985
        Keeps objects in ``args`` alive until tasks associated with events
986
        complete.
987

988
        Args:
989
            args(object):
990
                Python object to keep alive.
991
                Typically a tuple with arguments to offloaded tasks
992
            events(Tuple[dpctl.SyclEvent]):
993
                Gating events.
994
                The list or tuple of events associated with tasks
995
                working on Python objects collected in ``args``.
996
        Returns:
997
            dpctl.SyclEvent
998
               The event associated with the submission of host task.
999

1000
        Increments reference count of ``args`` and schedules asynchronous
1001
        ``host_task`` to decrement the count once dependent events are
1002
        complete.
1003

1004
        .. note::
1005
            The ``host_task`` attempts to acquire Python GIL, and it is
1006
            known to be unsafe during interpreter shutdown sequence. It is
1007
            thus strongly advised to ensure that all submitted ``host_task``
1008
            complete before the end of the Python script.
1009
        """
1010
        cdef size_t nDE = len(dEvents)
1✔
1011
        cdef DPCTLSyclEventRef *depEvents = NULL
1✔
1012
        cdef PyObject *args_raw = NULL
1✔
1013
        cdef DPCTLSyclEventRef htERef = NULL
1✔
1014
        cdef int status = -1
1✔
1015

1016
        # Create the array of dependent events if any
1017
        if nDE > 0:
1✔
1018
            depEvents = (
1019
                <DPCTLSyclEventRef*>malloc(nDE*sizeof(DPCTLSyclEventRef))
1✔
1020
            )
1021
            if not depEvents:
1✔
UNCOV
1022
                raise MemoryError()
×
1023
            else:
1024
                for idx, de in enumerate(dEvents):
1✔
1025
                    if isinstance(de, SyclEvent):
1✔
1026
                        depEvents[idx] = (<SyclEvent>de).get_event_ref()
1✔
1027
                    else:
UNCOV
1028
                        free(depEvents)
×
1029
                        raise TypeError(
×
1030
                            "A sequence of dpctl.SyclEvent is expected"
1031
                        )
1032

1033
        # increment reference counts to list of arguments
1034
        Py_INCREF(args)
1✔
1035

1036
        # schedule decrement
1037
        args_raw = <PyObject *>args
1✔
1038

1039
        htERef = async_dec_ref(
1✔
1040
            self.get_queue_ref(),
1✔
1041
            &args_raw, 1,
1042
            depEvents, nDE, &status
1043
        )
1044

1045
        free(depEvents)
1✔
1046
        if (status != 0):
1✔
UNCOV
1047
            with nogil: DPCTLEvent_Wait(htERef)
×
1048
            DPCTLEvent_Delete(htERef)
×
1049
            raise RuntimeError("Could not submit keep_args_alive host_task")
×
1050

1051
        return SyclEvent._create(htERef)
1✔
1052

1053

1054
    cpdef SyclEvent submit_async(
1✔
1055
        self,
1056
        SyclKernel kernel,
1057
        list args,
1058
        list gS,
1059
        list lS=None,
1060
        list dEvents=None
1✔
1061
    ):
1062
        """
1063
        Asynchronously submit :class:`dpctl.program.SyclKernel` for execution.
1064

1065
        Args:
1066
            kernel (dpctl.program.SyclKernel):
1067
                SYCL kernel object
1068
            args (List[object]):
1069
                List of kernel arguments
1070
            gS (List[int]):
1071
                Global iteration range. Must be a list of length 1, 2, or 3.
1072
            lS (List[int], optional):
1073
                Local iteration range. Must be ``None`` or have the same
1074
                length as ``gS`` and each element of ``gS`` must be divisible
1075
                by respective element of ``lS``.
1076
            dEvents (List[dpctl.SyclEvent], optional):
1077
                List of events indicating ordering of this task relative
1078
                to tasks associated with specified events.
1079

1080
        Returns:
1081
            dpctl.SyclEvent:
1082
                An event associated with submission of the kernel.
1083

1084
        .. note::
1085
            One must ensure that the lifetime of all kernel arguments
1086
            extends after the submitted task completes. It is not a concern for
1087
            scalar arguments since they are passed by value, but for
1088
            objects representing USM allocations which are passed to the kernel
1089
            as unified address space pointers.
1090

1091
            One way of accomplishing this is to use
1092
            :meth:`dpctl.SyclQueue._submit_keep_args_alive`.
1093
        """
1094
        cdef void **kargs = NULL
1✔
1095
        cdef _arg_data_type *kargty = NULL
1✔
1096
        cdef DPCTLSyclEventRef *depEvents = NULL
1✔
1097
        cdef DPCTLSyclEventRef Eref = NULL
1✔
1098
        cdef DPCTLSyclEventRef htEref = NULL
1✔
1099
        cdef int ret = 0
1✔
1100
        cdef size_t gRange[3]
1101
        cdef size_t lRange[3]
1102
        cdef size_t nGS = len(gS)
1✔
1103
        cdef size_t nLS = len(lS) if lS is not None else 0
1✔
1104
        cdef size_t nDE = len(dEvents) if dEvents is not None else 0
1✔
1105
        cdef PyObject *args_raw = NULL
1✔
1106
        cdef ssize_t i = 0
1✔
1107

1108
        # Allocate the arrays to be sent to DPCTLQueue_Submit
1109
        kargs = <void**>malloc(len(args) * sizeof(void*))
1✔
1110
        if not kargs:
1✔
UNCOV
1111
            raise MemoryError()
×
1112
        kargty = (
1113
            <_arg_data_type*>malloc(len(args)*sizeof(_arg_data_type))
1✔
1114
        )
1115
        if not kargty:
1✔
UNCOV
1116
            free(kargs)
×
1117
            raise MemoryError()
×
1118
        # Create the array of dependent events if any
1119
        if dEvents is not None and nDE > 0:
1✔
1120
            depEvents = (
1121
                <DPCTLSyclEventRef*>malloc(nDE*sizeof(DPCTLSyclEventRef))
1✔
1122
            )
1123
            if not depEvents:
1✔
UNCOV
1124
                free(kargs)
×
1125
                free(kargty)
×
1126
                raise MemoryError()
×
1127
            else:
1128
                for idx, de in enumerate(dEvents):
1✔
1129
                    if isinstance(de, SyclEvent):
1✔
1130
                        depEvents[idx] = (<SyclEvent>de).get_event_ref()
1✔
1131
                    else:
UNCOV
1132
                        free(kargs)
×
1133
                        free(kargty)
×
1134
                        free(depEvents)
×
1135
                        raise TypeError(
×
1136
                            "A sequence of dpctl.SyclEvent is expected"
1137
                        )
1138

1139
        # populate the args and argstype arrays
1140
        ret = self._populate_args(args, kargs, kargty)
1✔
1141
        if ret == -1:
1✔
UNCOV
1142
            free(kargs)
×
1143
            free(kargty)
×
1144
            free(depEvents)
×
1145
            raise TypeError("Unsupported type for a kernel argument")
×
1146

1147
        if lS is None:
1✔
1148
            ret = self._populate_range(gRange, gS, nGS)
1✔
1149
            if ret == -1:
1✔
UNCOV
1150
                free(kargs)
×
1151
                free(kargty)
×
1152
                free(depEvents)
×
1153
                raise SyclKernelInvalidRangeError(
×
1154
                    "Range with ", nGS, " not allowed. Range can only have "
×
1155
                    "between one and three dimensions."
1156
                )
1157
            Eref = DPCTLQueue_SubmitRange(
1✔
1158
                kernel.get_kernel_ref(),
1✔
1159
                self.get_queue_ref(),
1✔
1160
                kargs,
1161
                kargty,
1162
                len(args),
1✔
1163
                gRange,
1164
                nGS,
1165
                depEvents,
1166
                nDE
1167
            )
1168
        else:
1169
            ret = self._populate_range(gRange, gS, nGS)
1✔
1170
            if ret == -1:
1✔
UNCOV
1171
                free(kargs)
×
1172
                free(kargty)
×
1173
                free(depEvents)
×
1174
                raise SyclKernelInvalidRangeError(
×
1175
                    "Range with ", nGS, " not allowed. Range can only have "
×
1176
                    "between one and three dimensions."
1177
                )
1178
            ret = self._populate_range(lRange, lS, nLS)
1✔
1179
            if ret == -1:
1✔
UNCOV
1180
                free(kargs)
×
1181
                free(kargty)
×
1182
                free(depEvents)
×
1183
                raise SyclKernelInvalidRangeError(
×
1184
                    "Range with ", nLS, " not allowed. Range can only have "
×
1185
                    "between one and three dimensions."
1186
                )
1187
            if nGS != nLS:
1✔
UNCOV
1188
                free(kargs)
×
1189
                free(kargty)
×
1190
                free(depEvents)
×
1191
                raise ValueError(
×
1192
                    "Local and global ranges need to have same "
1193
                    "number of dimensions."
1194
                )
1195
            Eref = DPCTLQueue_SubmitNDRange(
1✔
1196
                kernel.get_kernel_ref(),
1✔
1197
                self.get_queue_ref(),
1✔
1198
                kargs,
1199
                kargty,
1200
                len(args),
1✔
1201
                gRange,
1202
                lRange,
1203
                nGS,
1204
                depEvents,
1205
                nDE
1206
            )
1207
        free(kargs)
1✔
1208
        free(kargty)
1✔
1209
        free(depEvents)
1✔
1210

1211
        if Eref is NULL:
1✔
UNCOV
1212
            raise SyclKernelSubmitError(
×
1213
                "Kernel submission to Sycl queue failed."
1214
            )
1215

1216
        return SyclEvent._create(Eref)
1✔
1217

1218
    cpdef SyclEvent submit(
1✔
1219
        self,
1220
        SyclKernel kernel,
1221
        list args,
1222
        list gS,
1223
        list lS=None,
1224
        list dEvents=None
1✔
1225
    ):
1226
        """
1227
        Submit :class:`dpctl.program.SyclKernel` for execution.
1228

1229
        Args:
1230
            kernel (dpctl.program.SyclKernel):
1231
                SYCL kernel object
1232
            args (List[object]):
1233
                List of kernel arguments
1234
            gS (List[int]):
1235
                Global iteration range. Must be a list of length 1, 2, or 3.
1236
            lS (List[int], optional):
1237
                Local iteration range. Must be ``None`` or have the same
1238
                length as ``gS`` and each element of ``gS`` must be divisible
1239
                by respective element of ``lS``.
1240
            dEvents (List[dpctl.SyclEvent], optional):
1241
                List of events indicating ordering of this task relative
1242
                to tasks associated with specified events.
1243

1244
        Returns:
1245
            dpctl.SyclEvent:
1246
                An event which is always complete. May be ignored.
1247

1248
        .. note::
1249
            :meth:`dpctl.SyclQueue.submit` is a synchronizing method.
1250
            Use :meth:`dpctl.SyclQueue.submit_async` to avoid synchronization.
1251
        """
1252
        cdef SyclEvent e = self.submit_async(kernel, args, gS, lS, dEvents)
1✔
1253
        e.wait()
1✔
1254
        return e
1✔
1255

1256
    cpdef void wait(self):
1✔
1257
        with nogil: DPCTLQueue_Wait(self._queue_ref)
1✔
1258

1259
    cpdef memcpy(self, dest, src, size_t count):
1✔
1260
        """Copy memory from `src` to `dst`"""
1261
        cdef DPCTLSyclEventRef ERef = NULL
1✔
1262

1263
        ERef = _memcpy_impl(<SyclQueue>self, dest, src, count, NULL, 0)
1✔
1264
        if (ERef is NULL):
1✔
UNCOV
1265
            raise RuntimeError(
×
1266
                "SyclQueue.memcpy operation encountered an error"
1267
            )
1268
        with nogil: DPCTLEvent_Wait(ERef)
1✔
1269
        DPCTLEvent_Delete(ERef)
1✔
1270

1271
    cpdef SyclEvent memcpy_async(self, dest, src, size_t count, list dEvents=None):
1✔
1272
        """Copy memory from ``src`` to ``dst``"""
1273
        cdef DPCTLSyclEventRef ERef = NULL
1✔
1274
        cdef DPCTLSyclEventRef *depEvents = NULL
1✔
1275
        cdef size_t nDE = 0
1✔
1276

1277
        if dEvents is None:
1✔
1278
            ERef = _memcpy_impl(<SyclQueue>self, dest, src, count, NULL, 0)
1✔
1279
        else:
1280
            nDE = len(dEvents)
1✔
1281
            depEvents = (
1282
                <DPCTLSyclEventRef*>malloc(nDE*sizeof(DPCTLSyclEventRef))
1✔
1283
            )
1284
            if depEvents is NULL:
1✔
UNCOV
1285
                raise MemoryError()
×
1286
            else:
1287
                for idx, de in enumerate(dEvents):
1✔
1288
                    if isinstance(de, SyclEvent):
1✔
1289
                        depEvents[idx] = (<SyclEvent>de).get_event_ref()
1✔
1290
                    else:
UNCOV
1291
                        free(depEvents)
×
1292
                        raise TypeError(
×
1293
                            "A sequence of dpctl.SyclEvent is expected"
1294
                        )
1295
            ERef = _memcpy_impl(self, dest, src, count, depEvents, nDE)
1✔
1296
            free(depEvents)
1✔
1297

1298
        if (ERef is NULL):
1✔
UNCOV
1299
            raise RuntimeError(
×
1300
                "SyclQueue.memcpy operation encountered an error"
1301
            )
1302

1303
        return SyclEvent._create(ERef)
1✔
1304

1305
    cpdef prefetch(self, mem, size_t count=0):
1✔
1306
        cdef void *ptr
1307
        cdef DPCTLSyclEventRef ERef = NULL
1✔
1308

1309
        if isinstance(mem, _Memory):
1✔
1310
            ptr = <void*>(<_Memory>mem).get_data_ptr()
1✔
1311
        else:
1312
            raise TypeError("Parameter `mem` should have type _Memory")
1✔
1313

1314
        if (count <=0 or count > mem.nbytes):
1✔
UNCOV
1315
            count = mem.nbytes
×
1316

1317
        ERef = DPCTLQueue_Prefetch(self._queue_ref, ptr, count)
1✔
1318
        if (ERef is NULL):
1✔
UNCOV
1319
            raise RuntimeError(
×
1320
                "SyclQueue.prefetch encountered an error"
1321
            )
1322
        with nogil: DPCTLEvent_Wait(ERef)
1✔
1323
        DPCTLEvent_Delete(ERef)
1✔
1324

1325
    cpdef mem_advise(self, mem, size_t count, int advice):
1✔
1326
        cdef void *ptr
1327
        cdef DPCTLSyclEventRef ERef = NULL
1✔
1328

1329
        if isinstance(mem, _Memory):
1✔
1330
            ptr = <void*>(<_Memory>mem).get_data_ptr()
1✔
1331
        else:
1332
            raise TypeError("Parameter `mem` should have type _Memory")
1✔
1333

1334
        if (count <=0 or count > mem.nbytes):
1✔
UNCOV
1335
            count = mem.nbytes
×
1336

1337
        ERef = DPCTLQueue_MemAdvise(self._queue_ref, ptr, count, advice)
1✔
1338
        if (ERef is NULL):
1✔
UNCOV
1339
            raise RuntimeError(
×
1340
                "SyclQueue.mem_advise operation encountered an error"
1341
            )
1342
        with nogil: DPCTLEvent_Wait(ERef)
1✔
1343
        DPCTLEvent_Delete(ERef)
1✔
1344

1345
    @property
1346
    def is_in_order(self):
1347
        """``True`` if :class:`.SyclQueue`` is in-order,
1348
        ``False`` if it is out-of-order.
1349

1350
        :Example:
1351

1352
            ..code-block:: python
1353

1354
                >>> import dpctl
1355
                >>> q = dpctl.SyclQueue("cpu")
1356
                >>> q.is_in_order
1357
                False
1358
                >>> q = dpctl.SyclQueue("cpu", property="in_order")
1359
                >>> q.is_in_order
1360
                True
1361

1362
        Returns:
1363
            bool:
1364
                Indicates whether this :class:`.SyclQueue` was created
1365
                with ``property="in_order"``.
1366

1367
        .. note::
1368
            Unless requested otherwise, :class:`.SyclQueue` is constructed
1369
            to support out-of-order execution.
1370
        """
1371
        return DPCTLQueue_IsInOrder(self._queue_ref)
1✔
1372

1373
    @property
1374
    def has_enable_profiling(self):
1375
        """
1376
        ``True`` if :class:`.SyclQueue` was constructed with
1377
        ``"enabled_profiling"`` property, ``False`` otherwise.
1378

1379
        :Example:
1380

1381
            ..code-block:: python
1382

1383
                >>> import dpctl
1384
                >>> q = dpctl.SyclQueue("cpu")
1385
                >>> q.has_enable_profiling
1386
                False
1387
                >>> q = dpctl.SyclQueue("cpu", property="enable_profiling")
1388
                >>> q.has_enable_profiling
1389
                True
1390

1391
        Returns:
1392
            bool:
1393
                Whether profiling information for tasks submitted
1394
                to this :class:`.SyclQueue` is being collected.
1395

1396
        .. note::
1397
            Profiling information can be accessed using
1398
            properties
1399
            :attr:`dpctl.SyclEvent.profiling_info_submit`,
1400
            :attr:`dpctl.SyclEvent.profiling_info_start`, and
1401
            :attr:`dpctl.SyclEvent.profiling_info_end`. It is
1402
            also necessary for proper working of
1403
            :class:`dpctl.SyclTimer`.
1404

1405
            Collection of profiling information is not enabled
1406
            by default.
1407
        """
1408
        return DPCTLQueue_HasEnableProfiling(self._queue_ref)
1✔
1409

1410
    @property
1411
    def __name__(self):
1412
        "The name of :class:`dpctl.SyclQueue` object"
1413
        return "SyclQueue"
1✔
1414

1415
    def __repr__(self):
1416
        cdef cpp_bool in_order = DPCTLQueue_IsInOrder(self._queue_ref)
1✔
1417
        cdef cpp_bool en_prof = DPCTLQueue_HasEnableProfiling(self._queue_ref)
1✔
1418
        if in_order or en_prof:
1✔
1419
            prop = []
1✔
1420
            if in_order:
1✔
1421
                prop.append("in_order")
1✔
1422
            if en_prof:
1✔
1423
                prop.append("enable_profiling")
1✔
1424
            return (
1✔
1425
                "<dpctl."
1426
                + self.__name__
1✔
1427
                + " at {}, property={}>".format(hex(id(self)), prop)
1✔
1428
            )
1429
        else:
1430
            return "<dpctl." + self.__name__ + " at {}>".format(hex(id(self)))
1✔
1431

1432
    def __hash__(self):
1433
        """
1434
        Returns a hash value by hashing the underlying ``sycl::queue`` object.
1435

1436
        Returns:
1437
            int:
1438
                Hash value of this :class:`.SyclQueue` instance
1439
        """
1440
        return DPCTLQueue_Hash(self._queue_ref)
1✔
1441

1442
    def _get_capsule(self):
1✔
1443
        cdef DPCTLSyclQueueRef QRef = NULL
1✔
1444
        QRef = DPCTLQueue_Copy(self._queue_ref)
1✔
1445
        if (QRef is NULL):
1✔
UNCOV
1446
            raise ValueError("SyclQueue copy failed.")
×
1447
        return pycapsule.PyCapsule_New(
1✔
1448
            <void *>QRef, "SyclQueueRef", &_queue_capsule_deleter
1✔
1449
        )
1450

1451
    cpdef SyclEvent submit_barrier(self, dependent_events=None):
1✔
1452
        """
1453
        Submits a barrier to this queue.
1454

1455
        Args:
1456
            dependent_events:
1457
                List[dpctl.SyclEvent]:
1458
                    List or tuple of events that must complete
1459
                    before this task may begin execution.
1460

1461
        Returns:
1462
            dpctl.SyclEvent:
1463
                Event associated with the submitted task
1464
        """
1465
        cdef DPCTLSyclEventRef *depEvents = NULL
1✔
1466
        cdef DPCTLSyclEventRef ERef = NULL
1✔
1467
        cdef size_t nDE = 0
1✔
1468
        # Create the array of dependent events if any
1469
        if (dependent_events is None or
1✔
1470
            (isinstance(dependent_events, collections.abc.Sequence) and
1✔
1471
             all([type(de) is SyclEvent for de in dependent_events]))):
1✔
1472
            nDE = 0 if dependent_events is None else len(dependent_events)
1✔
1473
        else:
1474
            raise TypeError(
1✔
1475
                "dependent_events must either None, or a sequence of "
1476
                ":class:`dpctl.SyclEvent` objects")
1477
        if nDE > 0:
1✔
1478
            depEvents = (
1479
                <DPCTLSyclEventRef*>malloc(nDE*sizeof(DPCTLSyclEventRef))
1✔
1480
            )
1481
            if not depEvents:
1✔
UNCOV
1482
                raise MemoryError()
×
1483
            else:
1484
                for idx, de in enumerate(dependent_events):
1✔
1485
                    depEvents[idx] = (<SyclEvent>de).get_event_ref()
1✔
1486

1487
        ERef = DPCTLQueue_SubmitBarrierForEvents(
1✔
1488
            self.get_queue_ref(), depEvents, nDE)
1✔
1489
        if (depEvents is not NULL):
1✔
1490
            free(depEvents)
1✔
1491
        if ERef is NULL:
1✔
UNCOV
1492
            raise SyclKernelSubmitError(
×
1493
                "Barrier submission to Sycl queue failed."
1494
            )
1495

1496
        return SyclEvent._create(ERef)
1✔
1497

1498
    @property
1499
    def name(self):
1500
        """Returns the device name for the device
1501
        associated with this queue.
1502

1503
        Returns:
1504
            str:
1505
                The name of the device as a string.
1506
        """
1507
        return self.sycl_device.name
1✔
1508

1509
    @property
1510
    def driver_version(self):
1511
        """Returns the driver version for the device
1512
        associated with this queue.
1513

1514
        Returns:
1515
            str:
1516
                The driver version of the device as a string.
1517
        """
1518
        return self.sycl_device.driver_version
1✔
1519

1520
    def print_device_info(self):
1✔
1521
        """ Print information about the SYCL device
1522
        associated with this queue.
1523
        """
1524
        self.sycl_device.print_device_info()
1✔
1525

1526

1527
cdef api DPCTLSyclQueueRef SyclQueue_GetQueueRef(SyclQueue q):
1✔
1528
    """
1529
    C-API function to get opaque queue reference from
1530
    :class:`dpctl.SyclQueue` instance.
1531
    """
1532
    return q.get_queue_ref()
1✔
1533

1534

1535
cdef api SyclQueue SyclQueue_Make(DPCTLSyclQueueRef QRef):
1✔
1536
    """
1537
    C-API function to create :class:`dpctl.SyclQueue` instance
1538
    from the given opaque queue reference.
1539
    """
1540
    cdef DPCTLSyclQueueRef copied_QRef = DPCTLQueue_Copy(QRef)
1✔
1541
    return SyclQueue._create(copied_QRef)
1✔
1542

1543
cdef class _WorkGroupMemory:
1544
    def __dealloc__(self):
NEW
1545
        if(self._mem_ref):
×
NEW
1546
            DPCTLWorkGroupMemory_Delete(self._mem_ref)
×
1547

1548
cdef class WorkGroupMemory:
1549
    """
1550
    WorkGroupMemory(nbytes)
1551
    Python class representing the ``work_group_memory`` class from the
1552
    Workgroup Memory oneAPI SYCL extension for low-overhead allocation of local
1553
    memory shared by the workitems in a workgroup.
1554

1555
    This is based on a DPC++ SYCL extension and only available in newer
1556
    versions. Use ``is_available()`` to check availability in your build.
1557

1558
    Args:
1559
        nbytes (int)
1560
            number of bytes to allocate in local memory.
1561
            Expected to be positive.
1562
    """
1563
    def __cinit__(self, Py_ssize_t nbytes):
NEW
1564
        if not DPCTLWorkGroupMemory_Available():
×
NEW
1565
            raise RuntimeError("Workgroup memory extension not available")
×
1566

NEW
1567
        self._mem_ref = DPCTLWorkGroupMemory_Create(nbytes)
×
1568

1569
    """Check whether the work_group_memory extension is available"""
1570
    @staticmethod
1✔
1571
    def is_available():
1572
        return DPCTLWorkGroupMemory_Available()
1✔
1573

1574
    property _ref:
1575
        """Returns the address of the C API ``DPCTLWorkGroupMemoryRef``
1576
        pointer as a ``size_t``.
1577
        """
1578
        def __get__(self):
NEW
1579
            return <size_t>self._mem_ref
×
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