Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sycl event #1134

Merged
merged 1 commit into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
ZzEeKkAa marked this conversation as resolved.
Show resolved Hide resolved

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