Skip to content

Commit 26cba6c

Browse files
authored
Merge pull request #1134 from IntelPython/feature/add_sycl_event
Add sycl event
2 parents 4910970 + 95f35ef commit 26cba6c

File tree

11 files changed

+409
-5
lines changed

11 files changed

+409
-5
lines changed

numba_dpex/core/datamodel/models.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@
99
from numba_dpex.core.exceptions import UnreachableError
1010
from numba_dpex.utils import address_space
1111

12-
from ..types import Array, DpctlSyclQueue, DpnpNdArray, USMNdArray
12+
from ..types import (
13+
Array,
14+
DpctlSyclEvent,
15+
DpctlSyclQueue,
16+
DpnpNdArray,
17+
USMNdArray,
18+
)
1319

1420

1521
class GenericPointerModel(PrimitiveModel):
@@ -162,6 +168,33 @@ def __init__(self, dmm, fe_type):
162168
super(SyclQueueModel, self).__init__(dmm, fe_type, members)
163169

164170

171+
class SyclEventModel(StructModel):
172+
"""Represents the native data model for a dpctl.SyclEvent PyObject.
173+
174+
Numba-dpex uses a C struct as defined in
175+
numba_dpex/core/runtime._eventstruct.h to store the required attributes for
176+
a ``dpctl.SyclEvent`` Python object.
177+
178+
- ``event_ref``: An opaque C pointer to an actual SYCL event C++ object.
179+
- ``parent``: A PyObject* that stores a reference back to the original
180+
``dpctl.SyclEvent`` PyObject if the native struct is
181+
created by unboxing the PyObject.
182+
"""
183+
184+
def __init__(self, dmm, fe_type):
185+
members = [
186+
(
187+
"parent",
188+
types.CPointer(types.int8),
189+
),
190+
(
191+
"event_ref",
192+
types.CPointer(types.int8),
193+
),
194+
]
195+
super(SyclEventModel, self).__init__(dmm, fe_type, members)
196+
197+
165198
def _init_data_model_manager() -> datamodel.DataModelManager:
166199
"""Initializes a DpexKernelTarget-specific data model manager.
167200
@@ -213,3 +246,6 @@ def _init_data_model_manager() -> datamodel.DataModelManager:
213246

214247
# Register the DpctlSyclQueue type
215248
register_model(DpctlSyclQueue)(SyclQueueModel)
249+
250+
# Register the DpctlSyclEvent type
251+
register_model(DpctlSyclEvent)(SyclEventModel)

numba_dpex/core/kernel_interface/dispatcher.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,9 @@ def __call__(self, *args):
486486
# Make sure the kernel launch range/nd_range are sane
487487
self._check_ranges(exec_queue.sycl_device)
488488

489+
# TODO: return event that calls wait if no reference to the object if
490+
# it is possible
491+
# event = exec_queue.submit(
489492
exec_queue.submit(
490493
sycl_kernel,
491494
packer.unpacked_args,

numba_dpex/core/runtime/_dpexrt_python.c

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "_nrt_python_helper.h"
2121

2222
#include "_dbg_printer.h"
23+
#include "_eventstruct.h"
2324
#include "_queuestruct.h"
2425
#include "_usmarraystruct.h"
2526

@@ -63,6 +64,8 @@ DPEXRT_sycl_usm_ndarray_to_python_acqref(usmarystruct_t *arystruct,
6364
PyArray_Descr *descr);
6465
static int DPEXRT_sycl_queue_from_python(PyObject *obj,
6566
queuestruct_t *queue_struct);
67+
static int DPEXRT_sycl_event_from_python(PyObject *obj,
68+
eventstruct_t *event_struct);
6669
static PyObject *DPEXRT_sycl_queue_to_python(queuestruct_t *queuestruct);
6770

6871
/** An NRT_external_malloc_func implementation using DPCTLmalloc_device.
@@ -783,6 +786,7 @@ static int DPEXRT_sycl_usm_ndarray_from_python(PyObject *obj,
783786

784787
// Increment the ref count on obj to prevent CPython from garbage
785788
// collecting the array.
789+
// TODO: add extra description why do we need this
786790
Py_IncRef(obj);
787791

788792
DPEXRT_DEBUG(drt_debug_print(
@@ -1269,6 +1273,97 @@ static PyObject *DPEXRT_sycl_queue_to_python(queuestruct_t *queuestruct)
12691273
return orig_queue;
12701274
}
12711275

1276+
/*----------------------------------------------------------------------------*/
1277+
/*--------------------- Box-unbox helpers for dpctl.SyclEvent ----------*/
1278+
/*----------------------------------------------------------------------------*/
1279+
1280+
/*!
1281+
* @brief Helper to unbox a Python dpctl.SyclEvent object to a Numba-native
1282+
* eventstruct_t instance.
1283+
*
1284+
* @param obj A dpctl.SyclEvent Python object
1285+
* @param event_struct An instance of the struct numba-dpex uses to
1286+
* represent a dpctl.SyclEvent inside Numba.
1287+
* @return {return} Return code indicating success (0) or failure (-1).
1288+
*/
1289+
static int DPEXRT_sycl_event_from_python(PyObject *obj,
1290+
eventstruct_t *event_struct)
1291+
{
1292+
1293+
struct PySyclEventObject *event_obj = NULL;
1294+
DPCTLSyclEventRef event_ref = NULL;
1295+
PyGILState_STATE gstate;
1296+
1297+
// We are unconditionally casting obj to a struct PySyclEventObject*. If
1298+
// the obj is not a struct PySyclEventObject* then the SyclEvent_GetEventRef
1299+
// will error out.
1300+
event_obj = (struct PySyclEventObject *)obj;
1301+
1302+
DPEXRT_DEBUG(
1303+
drt_debug_print("DPEXRT-DEBUG: In DPEXRT_sycl_event_from_python.\n"););
1304+
1305+
if (!(event_ref = SyclEvent_GetEventRef(event_obj))) {
1306+
DPEXRT_DEBUG(drt_debug_print(
1307+
"DPEXRT-ERROR: SyclEvent_GetEventRef returned NULL at "
1308+
"%s, line %d.\n",
1309+
__FILE__, __LINE__));
1310+
goto error;
1311+
}
1312+
1313+
event_struct->parent = obj;
1314+
event_struct->event_ref = event_ref;
1315+
1316+
return 0;
1317+
1318+
error:
1319+
// If the check failed then decrement the refcount and return an error
1320+
// code of -1.
1321+
DPEXRT_DEBUG(drt_debug_print(
1322+
"DPEXRT-ERROR: Failed to unbox dpctl SyclEvent into a Numba "
1323+
"eventstruct at %s, line %d\n",
1324+
__FILE__, __LINE__));
1325+
1326+
return -1;
1327+
}
1328+
1329+
/*!
1330+
* @brief A helper function that boxes a Numba-dpex eventstruct_t object into a
1331+
* dctl.SyclEvent PyObject using the eventstruct_t's parent attribute.
1332+
*
1333+
* If there is no parent pointer stored in the eventstruct, then an error will
1334+
* be raised.
1335+
*
1336+
* @param eventstruct A Numba-dpex eventstruct object.
1337+
* @return {return} A PyObject created from the eventstruct->parent, if
1338+
* the PyObject could not be created return NULL.
1339+
*/
1340+
static PyObject *DPEXRT_sycl_event_to_python(eventstruct_t *eventstruct)
1341+
{
1342+
PyObject *orig_event = NULL;
1343+
PyGILState_STATE gstate;
1344+
1345+
orig_event = eventstruct->parent;
1346+
// FIXME: Better error checking is needed to enforce the boxing of the event
1347+
// object. For now, only the minimal is done as the returning of SyclEvent
1348+
// from a dpjit function should not be a used often and the dpctl C API for
1349+
// type checking etc. is not ready.
1350+
if (orig_event == NULL) {
1351+
PyErr_Format(PyExc_ValueError,
1352+
"In 'box_from_eventstruct_parent', "
1353+
"failed to create a new dpctl.SyclEvent object.");
1354+
return NULL;
1355+
}
1356+
1357+
DPEXRT_DEBUG(
1358+
drt_debug_print("DPEXRT-DEBUG: In DPEXRT_sycl_event_to_python.\n"););
1359+
1360+
// We need to increase reference count because we are returning new
1361+
// reference to the same event.
1362+
Py_INCREF(orig_event);
1363+
1364+
return orig_event;
1365+
}
1366+
12721367
/*----------------------------------------------------------------------------*/
12731368
/*--------------------- The _dpexrt_python Python extension module -- -------*/
12741369
/*----------------------------------------------------------------------------*/
@@ -1306,6 +1401,9 @@ static PyObject *build_c_helpers_dict(void)
13061401
_declpointer("DPEXRT_sycl_queue_from_python",
13071402
&DPEXRT_sycl_queue_from_python);
13081403
_declpointer("DPEXRT_sycl_queue_to_python", &DPEXRT_sycl_queue_to_python);
1404+
_declpointer("DPEXRT_sycl_event_from_python",
1405+
&DPEXRT_sycl_event_from_python);
1406+
_declpointer("DPEXRT_sycl_event_to_python", &DPEXRT_sycl_event_to_python);
13091407

13101408
#undef _declpointer
13111409
return dct;
@@ -1357,6 +1455,10 @@ MOD_INIT(_dpexrt_python)
13571455
PyLong_FromVoidPtr(&DPEXRT_sycl_queue_from_python));
13581456
PyModule_AddObject(m, "DPEXRT_sycl_queue_to_python",
13591457
PyLong_FromVoidPtr(&DPEXRT_sycl_queue_to_python));
1458+
PyModule_AddObject(m, "DPEXRT_sycl_event_from_python",
1459+
PyLong_FromVoidPtr(&DPEXRT_sycl_event_from_python));
1460+
PyModule_AddObject(m, "DPEXRT_sycl_event_to_python",
1461+
PyLong_FromVoidPtr(&DPEXRT_sycl_event_to_python));
13601462

13611463
PyModule_AddObject(m, "DPEXRTQueue_CreateFromFilterString",
13621464
PyLong_FromVoidPtr(&DPEXRTQueue_CreateFromFilterString));
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// SPDX-FileCopyrightText: 2020 - 2023 Intel Corporation
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
//===----------------------------------------------------------------------===//
6+
///
7+
/// \file
8+
/// Defines the numba-dpex native representation for a dpctl.SyclEvent
9+
///
10+
//===----------------------------------------------------------------------===//
11+
12+
#pragma once
13+
14+
#include <Python.h>
15+
16+
typedef struct
17+
{
18+
PyObject *parent;
19+
void *event_ref;
20+
} eventstruct_t;

numba_dpex/core/runtime/context.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,32 @@ def queuestruct_to_python(self, pyapi, val):
203203

204204
return self.error
205205

206+
def eventstruct_from_python(self, pyapi, obj, ptr):
207+
"""Calls the c function DPEXRT_sycl_event_from_python"""
208+
fnty = llvmir.FunctionType(
209+
llvmir.IntType(32), [pyapi.pyobj, pyapi.voidptr]
210+
)
211+
212+
fn = pyapi._get_function(fnty, "DPEXRT_sycl_event_from_python")
213+
fn.args[0].add_attribute("nocapture")
214+
fn.args[1].add_attribute("nocapture")
215+
216+
self.error = pyapi.builder.call(fn, (obj, ptr))
217+
return self.error
218+
219+
def eventstruct_to_python(self, pyapi, val):
220+
"""Calls the c function DPEXRT_sycl_event_to_python"""
221+
222+
fnty = llvmir.FunctionType(pyapi.pyobj, [pyapi.voidptr])
223+
224+
fn = pyapi._get_function(fnty, "DPEXRT_sycl_event_to_python")
225+
fn.args[0].add_attribute("nocapture")
226+
qptr = cgutils.alloca_once_value(pyapi.builder, val)
227+
ptr = pyapi.builder.bitcast(qptr, pyapi.voidptr)
228+
self.error = pyapi.builder.call(fn, [ptr])
229+
230+
return self.error
231+
206232
def usm_ndarray_to_python_acqref(self, pyapi, aryty, ary, dtypeptr):
207233
"""Boxes a DpnpNdArray native object into a Python dpnp.ndarray.
208234

numba_dpex/core/types/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# SPDX-License-Identifier: Apache-2.0
44

55
from .array_type import Array
6-
from .dpctl_types import DpctlSyclQueue
6+
from .dpctl_types import DpctlSyclEvent, DpctlSyclQueue
77
from .dpnp_ndarray_type import DpnpNdArray
88
from .numba_types_short_names import (
99
b1,
@@ -33,6 +33,7 @@
3333
__all__ = [
3434
"Array",
3535
"DpctlSyclQueue",
36+
"DpctlSyclEvent",
3637
"DpnpNdArray",
3738
"USMNdArray",
3839
"none",

numba_dpex/core/types/dpctl_types.py

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import random
66

7-
from dpctl import SyclQueue
7+
from dpctl import SyclEvent, SyclQueue
88
from numba import types
99
from numba.core import cgutils
1010
from numba.extending import NativeValue, box, unbox
@@ -118,3 +118,76 @@ def box_sycl_queue(typ, val, c):
118118
return queue
119119
else:
120120
raise UnreachableError
121+
122+
123+
class DpctlSyclEvent(types.Type):
124+
"""A Numba type to represent a dpctl.SyclEvent PyObject."""
125+
126+
def __init__(self, sycl_event):
127+
if not isinstance(sycl_event, SyclEvent):
128+
raise TypeError("The argument sycl_event is not of type SyclEvent.")
129+
130+
super(DpctlSyclEvent, self).__init__(name="DpctlSyclEvent")
131+
132+
@property
133+
def box_type(self):
134+
return SyclEvent
135+
136+
137+
@unbox(DpctlSyclEvent)
138+
def unbox_sycl_event(typ, obj, c):
139+
"""
140+
Convert a SyclEvent object to a native structure.
141+
"""
142+
143+
qstruct = cgutils.create_struct_proxy(typ)(c.context, c.builder)
144+
qptr = qstruct._getpointer()
145+
ptr = c.builder.bitcast(qptr, c.pyapi.voidptr)
146+
147+
dpexrtCtx = dpexrt.DpexRTContext(c.context)
148+
errcode = dpexrtCtx.eventstruct_from_python(c.pyapi, obj, ptr)
149+
is_error = cgutils.is_not_null(c.builder, errcode)
150+
151+
# Handle error
152+
with c.builder.if_then(is_error, likely=False):
153+
c.pyapi.err_set_string(
154+
"PyExc_TypeError",
155+
"can't unbox dpctl.SyclEvent from PyObject into a Numba "
156+
"native value. The object maybe of a different type",
157+
)
158+
159+
return NativeValue(c.builder.load(qptr), is_error=is_error)
160+
161+
162+
@box(DpctlSyclEvent)
163+
def box_sycl_event(typ, val, c):
164+
"""Boxes a NativeValue representation of DpctlSyclEvent type into a
165+
dpctl.SyclEvent PyObject
166+
167+
At this point numba-dpex does not support creating a dpctl.SyclEvent inside
168+
a dpjit decorated function. For this reason, boxing is only returns the
169+
original parent object stored in DpctlSyclEvent's data model.
170+
171+
Args:
172+
typ: The representation of the dpctl.SyclEvent type.
173+
val: A native representation of a Numba DpctlSyclEvent type object.
174+
c: The boxing context.
175+
176+
Returns: A Pyobject for a dpctl.SyclEvent boxed from the Numba native value.
177+
"""
178+
179+
if not c.context.enable_nrt:
180+
raise UnreachableError
181+
182+
print("boxing...")
183+
184+
dpexrtCtx = dpexrt.DpexRTContext(c.context)
185+
event = dpexrtCtx.eventstruct_to_python(c.pyapi, val)
186+
187+
if not event:
188+
c.pyapi.err_set_string(
189+
"PyExc_TypeError",
190+
"could not box native sycl queue into a dpctl.SyclEvent"
191+
" PyObject.",
192+
)
193+
return event

0 commit comments

Comments
 (0)