Skip to content

Commit

Permalink
Add sycl event
Browse files Browse the repository at this point in the history
  • Loading branch information
ZzEeKkAa committed Sep 29, 2023
1 parent 4910970 commit 95f35ef
Show file tree
Hide file tree
Showing 11 changed files with 409 additions and 5 deletions.
38 changes: 37 additions & 1 deletion numba_dpex/core/datamodel/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@
from numba_dpex.core.exceptions import UnreachableError
from numba_dpex.utils import address_space

from ..types import Array, DpctlSyclQueue, DpnpNdArray, USMNdArray
from ..types import (
Array,
DpctlSyclEvent,
DpctlSyclQueue,
DpnpNdArray,
USMNdArray,
)


class GenericPointerModel(PrimitiveModel):
Expand Down Expand Up @@ -162,6 +168,33 @@ def __init__(self, dmm, fe_type):
super(SyclQueueModel, self).__init__(dmm, fe_type, members)


class SyclEventModel(StructModel):
"""Represents the native data model for a dpctl.SyclEvent PyObject.
Numba-dpex uses a C struct as defined in
numba_dpex/core/runtime._eventstruct.h to store the required attributes for
a ``dpctl.SyclEvent`` Python object.
- ``event_ref``: An opaque C pointer to an actual SYCL event C++ object.
- ``parent``: A PyObject* that stores a reference back to the original
``dpctl.SyclEvent`` PyObject if the native struct is
created by unboxing the PyObject.
"""

def __init__(self, dmm, fe_type):
members = [
(
"parent",
types.CPointer(types.int8),
),
(
"event_ref",
types.CPointer(types.int8),
),
]
super(SyclEventModel, self).__init__(dmm, fe_type, members)


def _init_data_model_manager() -> datamodel.DataModelManager:
"""Initializes a DpexKernelTarget-specific data model manager.
Expand Down Expand Up @@ -213,3 +246,6 @@ def _init_data_model_manager() -> datamodel.DataModelManager:

# Register the DpctlSyclQueue type
register_model(DpctlSyclQueue)(SyclQueueModel)

# Register the DpctlSyclEvent type
register_model(DpctlSyclEvent)(SyclEventModel)
3 changes: 3 additions & 0 deletions numba_dpex/core/kernel_interface/dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,9 @@ def __call__(self, *args):
# Make sure the kernel launch range/nd_range are sane
self._check_ranges(exec_queue.sycl_device)

# TODO: return event that calls wait if no reference to the object if
# it is possible
# event = exec_queue.submit(
exec_queue.submit(
sycl_kernel,
packer.unpacked_args,
Expand Down
102 changes: 102 additions & 0 deletions numba_dpex/core/runtime/_dpexrt_python.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "_nrt_python_helper.h"

#include "_dbg_printer.h"
#include "_eventstruct.h"
#include "_queuestruct.h"
#include "_usmarraystruct.h"

Expand Down Expand Up @@ -63,6 +64,8 @@ DPEXRT_sycl_usm_ndarray_to_python_acqref(usmarystruct_t *arystruct,
PyArray_Descr *descr);
static int DPEXRT_sycl_queue_from_python(PyObject *obj,
queuestruct_t *queue_struct);
static int DPEXRT_sycl_event_from_python(PyObject *obj,
eventstruct_t *event_struct);
static PyObject *DPEXRT_sycl_queue_to_python(queuestruct_t *queuestruct);

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

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

DPEXRT_DEBUG(drt_debug_print(
Expand Down Expand Up @@ -1269,6 +1273,97 @@ static PyObject *DPEXRT_sycl_queue_to_python(queuestruct_t *queuestruct)
return orig_queue;
}

/*----------------------------------------------------------------------------*/
/*--------------------- Box-unbox helpers for dpctl.SyclEvent ----------*/
/*----------------------------------------------------------------------------*/

/*!
* @brief Helper to unbox a Python dpctl.SyclEvent object to a Numba-native
* eventstruct_t instance.
*
* @param obj A dpctl.SyclEvent Python object
* @param event_struct An instance of the struct numba-dpex uses to
* represent a dpctl.SyclEvent inside Numba.
* @return {return} Return code indicating success (0) or failure (-1).
*/
static int DPEXRT_sycl_event_from_python(PyObject *obj,
eventstruct_t *event_struct)
{

struct PySyclEventObject *event_obj = NULL;
DPCTLSyclEventRef event_ref = NULL;
PyGILState_STATE gstate;

// We are unconditionally casting obj to a struct PySyclEventObject*. If
// the obj is not a struct PySyclEventObject* then the SyclEvent_GetEventRef
// will error out.
event_obj = (struct PySyclEventObject *)obj;

DPEXRT_DEBUG(
drt_debug_print("DPEXRT-DEBUG: In DPEXRT_sycl_event_from_python.\n"););

if (!(event_ref = SyclEvent_GetEventRef(event_obj))) {
DPEXRT_DEBUG(drt_debug_print(
"DPEXRT-ERROR: SyclEvent_GetEventRef returned NULL at "
"%s, line %d.\n",
__FILE__, __LINE__));
goto error;
}

event_struct->parent = obj;
event_struct->event_ref = event_ref;

return 0;

error:
// If the check failed then decrement the refcount and return an error
// code of -1.
DPEXRT_DEBUG(drt_debug_print(
"DPEXRT-ERROR: Failed to unbox dpctl SyclEvent into a Numba "
"eventstruct at %s, line %d\n",
__FILE__, __LINE__));

return -1;
}

/*!
* @brief A helper function that boxes a Numba-dpex eventstruct_t object into a
* dctl.SyclEvent PyObject using the eventstruct_t's parent attribute.
*
* If there is no parent pointer stored in the eventstruct, then an error will
* be raised.
*
* @param eventstruct A Numba-dpex eventstruct object.
* @return {return} A PyObject created from the eventstruct->parent, if
* the PyObject could not be created return NULL.
*/
static PyObject *DPEXRT_sycl_event_to_python(eventstruct_t *eventstruct)
{
PyObject *orig_event = NULL;
PyGILState_STATE gstate;

orig_event = eventstruct->parent;
// FIXME: Better error checking is needed to enforce the boxing of the event
// object. For now, only the minimal is done as the returning of SyclEvent
// from a dpjit function should not be a used often and the dpctl C API for
// type checking etc. is not ready.
if (orig_event == NULL) {
PyErr_Format(PyExc_ValueError,
"In 'box_from_eventstruct_parent', "
"failed to create a new dpctl.SyclEvent object.");
return NULL;
}

DPEXRT_DEBUG(
drt_debug_print("DPEXRT-DEBUG: In DPEXRT_sycl_event_to_python.\n"););

// We need to increase reference count because we are returning new
// reference to the same event.
Py_INCREF(orig_event);

return orig_event;
}

/*----------------------------------------------------------------------------*/
/*--------------------- The _dpexrt_python Python extension module -- -------*/
/*----------------------------------------------------------------------------*/
Expand Down Expand Up @@ -1306,6 +1401,9 @@ static PyObject *build_c_helpers_dict(void)
_declpointer("DPEXRT_sycl_queue_from_python",
&DPEXRT_sycl_queue_from_python);
_declpointer("DPEXRT_sycl_queue_to_python", &DPEXRT_sycl_queue_to_python);
_declpointer("DPEXRT_sycl_event_from_python",
&DPEXRT_sycl_event_from_python);
_declpointer("DPEXRT_sycl_event_to_python", &DPEXRT_sycl_event_to_python);

#undef _declpointer
return dct;
Expand Down Expand Up @@ -1357,6 +1455,10 @@ MOD_INIT(_dpexrt_python)
PyLong_FromVoidPtr(&DPEXRT_sycl_queue_from_python));
PyModule_AddObject(m, "DPEXRT_sycl_queue_to_python",
PyLong_FromVoidPtr(&DPEXRT_sycl_queue_to_python));
PyModule_AddObject(m, "DPEXRT_sycl_event_from_python",
PyLong_FromVoidPtr(&DPEXRT_sycl_event_from_python));
PyModule_AddObject(m, "DPEXRT_sycl_event_to_python",
PyLong_FromVoidPtr(&DPEXRT_sycl_event_to_python));

PyModule_AddObject(m, "DPEXRTQueue_CreateFromFilterString",
PyLong_FromVoidPtr(&DPEXRTQueue_CreateFromFilterString));
Expand Down
20 changes: 20 additions & 0 deletions numba_dpex/core/runtime/_eventstruct.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: 2020 - 2023 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0

//===----------------------------------------------------------------------===//
///
/// \file
/// Defines the numba-dpex native representation for a dpctl.SyclEvent
///
//===----------------------------------------------------------------------===//

#pragma once

#include <Python.h>

typedef struct
{
PyObject *parent;
void *event_ref;
} eventstruct_t;
26 changes: 26 additions & 0 deletions numba_dpex/core/runtime/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,32 @@ def queuestruct_to_python(self, pyapi, val):

return self.error

def eventstruct_from_python(self, pyapi, obj, ptr):
"""Calls the c function DPEXRT_sycl_event_from_python"""
fnty = llvmir.FunctionType(
llvmir.IntType(32), [pyapi.pyobj, pyapi.voidptr]
)

fn = pyapi._get_function(fnty, "DPEXRT_sycl_event_from_python")
fn.args[0].add_attribute("nocapture")
fn.args[1].add_attribute("nocapture")

self.error = pyapi.builder.call(fn, (obj, ptr))
return self.error

def eventstruct_to_python(self, pyapi, val):
"""Calls the c function DPEXRT_sycl_event_to_python"""

fnty = llvmir.FunctionType(pyapi.pyobj, [pyapi.voidptr])

fn = pyapi._get_function(fnty, "DPEXRT_sycl_event_to_python")
fn.args[0].add_attribute("nocapture")
qptr = cgutils.alloca_once_value(pyapi.builder, val)
ptr = pyapi.builder.bitcast(qptr, pyapi.voidptr)
self.error = pyapi.builder.call(fn, [ptr])

return self.error

def usm_ndarray_to_python_acqref(self, pyapi, aryty, ary, dtypeptr):
"""Boxes a DpnpNdArray native object into a Python dpnp.ndarray.
Expand Down
3 changes: 2 additions & 1 deletion numba_dpex/core/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# SPDX-License-Identifier: Apache-2.0

from .array_type import Array
from .dpctl_types import DpctlSyclQueue
from .dpctl_types import DpctlSyclEvent, DpctlSyclQueue
from .dpnp_ndarray_type import DpnpNdArray
from .numba_types_short_names import (
b1,
Expand Down Expand Up @@ -33,6 +33,7 @@
__all__ = [
"Array",
"DpctlSyclQueue",
"DpctlSyclEvent",
"DpnpNdArray",
"USMNdArray",
"none",
Expand Down
75 changes: 74 additions & 1 deletion numba_dpex/core/types/dpctl_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import random

from dpctl import SyclQueue
from dpctl import SyclEvent, SyclQueue
from numba import types
from numba.core import cgutils
from numba.extending import NativeValue, box, unbox
Expand Down Expand Up @@ -118,3 +118,76 @@ def box_sycl_queue(typ, val, c):
return queue
else:
raise UnreachableError


class DpctlSyclEvent(types.Type):
"""A Numba type to represent a dpctl.SyclEvent PyObject."""

def __init__(self, sycl_event):
if not isinstance(sycl_event, SyclEvent):
raise TypeError("The argument sycl_event is not of type SyclEvent.")

super(DpctlSyclEvent, self).__init__(name="DpctlSyclEvent")

@property
def box_type(self):
return SyclEvent


@unbox(DpctlSyclEvent)
def unbox_sycl_event(typ, obj, c):
"""
Convert a SyclEvent object to a native structure.
"""

qstruct = cgutils.create_struct_proxy(typ)(c.context, c.builder)
qptr = qstruct._getpointer()
ptr = c.builder.bitcast(qptr, c.pyapi.voidptr)

dpexrtCtx = dpexrt.DpexRTContext(c.context)
errcode = dpexrtCtx.eventstruct_from_python(c.pyapi, obj, ptr)
is_error = cgutils.is_not_null(c.builder, errcode)

# Handle error
with c.builder.if_then(is_error, likely=False):
c.pyapi.err_set_string(
"PyExc_TypeError",
"can't unbox dpctl.SyclEvent from PyObject into a Numba "
"native value. The object maybe of a different type",
)

return NativeValue(c.builder.load(qptr), is_error=is_error)


@box(DpctlSyclEvent)
def box_sycl_event(typ, val, c):
"""Boxes a NativeValue representation of DpctlSyclEvent type into a
dpctl.SyclEvent PyObject
At this point numba-dpex does not support creating a dpctl.SyclEvent inside
a dpjit decorated function. For this reason, boxing is only returns the
original parent object stored in DpctlSyclEvent's data model.
Args:
typ: The representation of the dpctl.SyclEvent type.
val: A native representation of a Numba DpctlSyclEvent type object.
c: The boxing context.
Returns: A Pyobject for a dpctl.SyclEvent boxed from the Numba native value.
"""

if not c.context.enable_nrt:
raise UnreachableError

print("boxing...")

dpexrtCtx = dpexrt.DpexRTContext(c.context)
event = dpexrtCtx.eventstruct_to_python(c.pyapi, val)

if not event:
c.pyapi.err_set_string(
"PyExc_TypeError",
"could not box native sycl queue into a dpctl.SyclEvent"
" PyObject.",
)
return event
Loading

0 comments on commit 95f35ef

Please sign in to comment.