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

Feature/remove numpy array kernelargs #1049

Merged
merged 7 commits into from
May 22, 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
71 changes: 4 additions & 67 deletions numba_dpex/core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def __init__(self, kernel_name, dim, work_groups, work_items) -> None:
super().__init__(self.message)


class ComputeFollowsDataInferenceError(Exception):
class ExecutionQueueInferenceError(Exception):
"""Exception raised when an execution queue for a given array expression or
a kernel function could not be deduced using the compute-follows-data
programming model.
Expand All @@ -194,34 +194,20 @@ class ComputeFollowsDataInferenceError(Exception):
which the array operands were allocated. Computation is required to occur
on the same device where the arrays currently reside.

A ComputeFollowsDataInferenceError is raised when the execution queue using
A ExecutionQueueInferenceError is raised when the execution queue using
compute-follows-data rules could not be deduced. It may happen when arrays
that have a device attribute such as ``dpctl.tensor.usm_ndarray`` are mixed
with host arrays such as ``numpy.ndarray``. The error may also be raised if
the array operands are allocated on different devices.

Args:
kernel_name : Name of the kernel function for which the error occurred.
ndarray_argnum_list: The list of ``numpy.ndarray`` arguments identified
by the argument position that caused the error.
usmarray_argnum_list: The list of ``dpctl.tensor.usm_ndarray`` arguments
identified by the argument position that caused the error.
"""

def __init__(
self, kernel_name, ndarray_argnum_list=None, *, usmarray_argnum_list
) -> None:
if ndarray_argnum_list and usmarray_argnum_list:
ndarray_args = ",".join([str(i) for i in ndarray_argnum_list])
usmarray_args = ",".join([str(i) for i in usmarray_argnum_list])
self.message = (
f'Kernel "{kernel_name}" has arguments of both usm_ndarray and '
"non-usm_ndarray types. Mixing of arguments of different "
"array types is disallowed. "
f"Arguments {ndarray_args} are non-usm arrays, "
f"and arguments {usmarray_args} are usm arrays."
)
elif usmarray_argnum_list is not None:
def __init__(self, kernel_name, *, usmarray_argnum_list) -> None:
if usmarray_argnum_list is not None:
usmarray_args = ",".join([str(i) for i in usmarray_argnum_list])
self.message = (
f'Execution queue for kernel "{kernel_name}" could '
Expand All @@ -232,32 +218,6 @@ def __init__(
super().__init__(self.message)


class ExecutionQueueInferenceError(Exception):
"""Exception raised when an execution queue could not be deduced for NumPy
ndarray kernel arguments.

Args:
kernel_name (str): Name of kernel where the error was raised.

.. deprecated:: 0.19
"""

def __init__(self, kernel_name) -> None:
warn(
"The ExecutionQueueInferenceError class is deprecated, and will "
+ "be removed once support for NumPy ndarrays as kernel arguments "
+ "is removed.",
DeprecationWarning,
stacklevel=2,
)
self.message = (
f'Kernel "{kernel_name}" was called with NumPy ndarray arguments '
"outside a dpctl.device_context. The execution queue to be used "
"could not be deduced."
)
super().__init__(self.message)


class UnsupportedBackendError(Exception):
"""Exception raised when the target device is not supported by dpex.

Expand Down Expand Up @@ -357,29 +317,6 @@ def __init__(self, kernel_name, arg) -> None:
super().__init__(self.message)


class UnsupportedAccessQualifierError(Exception):
"""Exception raised when an illegal access specifier value is specified for
a NumPy array argument passed to a kernel.

Args:
kernel_name (str): Name of kernel where the error was raised.
array_val: name of the array argument with the illegal access specifier.
illegal_access_type (str): The illegal access specifier string.
legal_access_list (str): Joined string for the legal access specifiers.
"""

def __init__(
self, kernel_name, array_val, illegal_access_type, legal_access_list
) -> None:
self.message = (
f"Invalid access type {illegal_access_type} applied to "
f'array {array_val} argument passed to kernel "{kernel_name}". '
f"Legal access specifiers are {legal_access_list}."
)

super().__init__(self.message)


class UnsupportedCompilationModeError(Exception):
def __init__(self) -> None:
self.message = (
Expand Down
173 changes: 21 additions & 152 deletions numba_dpex/core/kernel_interface/arg_pack_unpacker.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@
from numba.core import types

import numba_dpex.utils as utils
from numba_dpex.core.exceptions import (
UnsupportedAccessQualifierError,
UnsupportedKernelArgumentError,
)
from numba_dpex.core.exceptions import UnsupportedKernelArgumentError
from numba_dpex.core.types import USMNdArray
from numba_dpex.core.utils import get_info_from_suai

Expand All @@ -28,42 +25,31 @@ def __init__(self, usm_mem, orig_val, packed_val, packed) -> None:

class Packer:
"""Implements the functionality to unpack a Python object passed as an
argument to a numba_dpex kernel fucntion into corresponding ctype object.
argument to a numba_dpex kernel function into corresponding ctype object.
"""

# TODO: Remove after NumPy support is removed
_access_types = ("read_only", "write_only", "read_write")

def _check_for_invalid_access_type(self, array_val, access_type):
if access_type and access_type not in Packer._access_types:
raise UnsupportedAccessQualifierError(
self._pyfunc_name,
array_val,
access_type,
",".join(Packer._access_types),
)

def _unpack_array_helper(self, size, itemsize, buf, shape, strides, ndim):
"""
Implements the unpacking logic for array arguments.
def _unpack_usm_array(self, val):
"""Flattens an object of USMNdArray type into ctypes objects to be
passed as kernel arguments.

Args:
size: Total number of elements in the array.
itemsize: Size in bytes of each element in the array.
buf: The pointer to the memory.
shape: The shape of the array.
ndim: Number of dimension.
val : An object of dpctl.types.UsmNdArray type.

Returns:
A list a ctype value for each array attribute argument
list: A list of ctype objects representing the flattened usm_ndarray
"""
unpacked_array_attrs = []

# meminfo (FIXME: should be removed and the USMNdArray type modified
# once NumPy support is removed)
suai_attrs = get_info_from_suai(val)
size = suai_attrs.size
itemsize = suai_attrs.itemsize
buf = suai_attrs.data
shape = suai_attrs.shape
strides = suai_attrs.strides
ndim = suai_attrs.dimensions

# meminfo
unpacked_array_attrs.append(ctypes.c_size_t(0))
# parent (FIXME: Evaluate if the attribute should be removed and the
# USMNdArray type modified once NumPy support is removed)
# parent
unpacked_array_attrs.append(ctypes.c_size_t(0))
unpacked_array_attrs.append(ctypes.c_longlong(size))
unpacked_array_attrs.append(ctypes.c_longlong(itemsize))
Expand All @@ -75,90 +61,7 @@ def _unpack_array_helper(self, size, itemsize, buf, shape, strides, ndim):

return unpacked_array_attrs

def _unpack_usm_array(self, val):
"""Flattens an object of USMNdArray type into ctypes objects to be
passed as kernel arguments.

Args:
val : An object of dpctl.types.UsmNdArray type.

Returns:
list: A list of ctype objects representing the flattened usm_ndarray
"""
suai_attrs = get_info_from_suai(val)

return self._unpack_array_helper(
size=suai_attrs.size,
itemsize=suai_attrs.itemsize,
buf=suai_attrs.data,
shape=suai_attrs.shape,
strides=suai_attrs.strides,
ndim=suai_attrs.dimensions,
)

def _unpack_array(self, val, access_type):
"""Deprecated to be removed once NumPy array support in kernels is
removed.
"""
packed_val = val
# Check if the NumPy array is backed by USM memory
usm_mem = utils.has_usm_memory(val)

# If the NumPy array is not USM backed, then copy to a USM memory
# object. Add an entry to the repack_map so that on exit from kernel
# the data from the USM object can be copied back into the NumPy array.
if usm_mem is None:
self._check_for_invalid_access_type(val, access_type)
usm_mem = utils.as_usm_obj(val, queue=self._queue, copy=False)

orig_val = val
packed = False
if not val.flags.c_contiguous:
# If the numpy.ndarray is not C-contiguous
# we pack the strided array into a packed array.
# This allows us to treat the data from here on as C-contiguous.
# While packing we treat the data as C-contiguous.
# We store the reference of both (strided and packed)
# array and during unpacking we use numpy.copyto() to copy
# the data back from the packed temporary array to the
# original strided array.
packed_val = val.flatten(order="C")
packed = True

if access_type == "read_only":
utils.copy_from_numpy_to_usm_obj(usm_mem, packed_val)
elif access_type == "read_write":
utils.copy_from_numpy_to_usm_obj(usm_mem, packed_val)
# Store to the repack map
self._repack_list.append(
_NumPyArrayPackerPayload(
usm_mem, orig_val, packed_val, packed
)
)
elif access_type == "write_only":
self._repack_list.append(
_NumPyArrayPackerPayload(
usm_mem, orig_val, packed_val, packed
)
)
else:
utils.copy_from_numpy_to_usm_obj(usm_mem, packed_val)
self._repack_list.append(
_NumPyArrayPackerPayload(
usm_mem, orig_val, packed_val, packed
)
)

return self._unpack_array_helper(
packed_val.size,
packed_val.dtype.itemsize,
usm_mem,
packed_val.shape,
packed_val.strides,
packed_val.ndim,
)

def _unpack_argument(self, ty, val, access_specifier):
def _unpack_argument(self, ty, val):
"""
Unpack a Python object into one or more ctype values using Numba's
type-inference machinery.
Expand All @@ -176,8 +79,6 @@ def _unpack_argument(self, ty, val, access_specifier):

if isinstance(ty, USMNdArray):
return self._unpack_usm_array(val)
elif isinstance(ty, types.Array):
return self._unpack_array(val, access_specifier)
elif ty == types.int64:
return ctypes.c_longlong(val)
elif ty == types.uint64:
Expand All @@ -199,48 +100,22 @@ def _unpack_argument(self, ty, val, access_specifier):
else:
raise UnsupportedKernelArgumentError(ty, val, self._pyfunc_name)

def _pack_array(self):
"""
Deprecated to be removed once NumPy array support in kernels is
removed.
"""
for obj in self._repack_list:
utils.copy_to_numpy_from_usm_obj(obj._usm_mem, obj._packed_val)
if obj._packed:
np.copyto(obj._orig_val, obj._packed_val)

def __init__(
self, kernel_name, arg_list, argty_list, access_specifiers_list, queue
) -> None:
def __init__(self, kernel_name, arg_list, argty_list, queue) -> None:
"""Initializes new Packer object and unpacks the input argument list.

Args:
kernel_name (str): The kernel function name.
arg_list (list): A list of arguments to be unpacked
argty_list (list): A list of Numba inferred types for each argument.
access_specifiers_list(list): A list of access specifiers for
NumPy arrays to optimize host to device memory copy.
[Deprecated: can be removed along with NumPy array support]
queue (dpctl.SyclQueue): The SYCL queue where the kernel is to be
executed. The queue is required to allocate USM memory for NumPy
arrays.
[Deprecated: can be removed along with NumPy array support]
"""
self._pyfunc_name = kernel_name
self._arg_list = arg_list
self._argty_list = argty_list
self._queue = queue
# Create a list to store the numpy arrays that need to be
# repacked beoe returning from a kernel.
self._repack_list = []

# loop over the arg_list and generate the kernelargs list
self._unpacked_args = []
for i, val in enumerate(arg_list):
arg = self._unpack_argument(
ty=argty_list[i],
val=val,
access_specifier=access_specifiers_list[i],
)
arg = self._unpack_argument(ty=argty_list[i], val=val)
if type(arg) == list:
self._unpacked_args.extend(arg)
else:
Expand All @@ -250,9 +125,3 @@ def __init__(
def unpacked_args(self):
"""Returns the list of unpacked arguments created by a Packer object."""
return self._unpacked_args

@property
def repacked_args(self):
"""Returns the list of NumPy"""
self._pack_array()
return self._repack_list
Loading