cimport libc.stdio
from libc.stdlib cimport malloc, calloc, free
from cpython.mem cimport PyMem_Malloc, PyMem_Free
from libc.string cimport strncmp
cimport numpy as np
import numpy as np
from cpython cimport Py_INCREF, PyNumber_Index
from cpython.object cimport Py_EQ, Py_NE
def api_version():
# major, minor, py
return (gpuarray_api_major, gpuarray_api_minor, 0)
np.import_array()
# to export the numeric value
SIZE = GA_SIZE
SSIZE = GA_SSIZE
# Numpy API steals dtype references and this breaks cython
cdef object PyArray_Empty(int a, np.npy_intp *b, np.dtype c, int d):
Py_INCREF(c)
return _PyArray_Empty(a, b, c, d)
cdef bytes _s(s):
if isinstance(s, unicode):
return (s).encode('ascii')
if isinstance(s, bytes):
return s
raise TypeError("Expected a string")
def cl_wrap_ctx(size_t ptr):
"""
cl_wrap_ctx(ptr)
Wrap an existing OpenCL context (the cl_context struct) into a
GpuContext class.
"""
cdef gpucontext *(*cl_make_ctx)(void *, int)
cdef GpuContext res
cl_make_ctx = gpuarray_get_extension("cl_make_ctx")
if cl_make_ctx == NULL:
raise RuntimeError, "cl_make_ctx extension is absent"
res = GpuContext.__new__(GpuContext)
res.ctx = cl_make_ctx(ptr, 0)
if res.ctx == NULL:
raise RuntimeError, "cl_make_ctx call failed"
return res
def cuda_wrap_ctx(size_t ptr, bint own):
"""
cuda_wrap_ctx(ptr)
Wrap an existing CUDA driver context (CUcontext) into a GpuContext
class.
If `own` is true, libgpuarray is now reponsible for the context and
it will be destroyed once there are no references to it.
Otherwise, the context will not be destroyed and it is the calling
code's reponsability.
"""
cdef gpucontext *(*cuda_make_ctx)(void *, int)
cdef int flags
cdef GpuContext res
cuda_make_ctx = gpuarray_get_extension("cuda_make_ctx")
if cuda_make_ctx == NULL:
raise RuntimeError, "cuda_make_ctx extension is absent"
res = GpuContext.__new__(GpuContext)
flags = 0
if not own:
flags |= GPUARRAY_CUDA_CTX_NOFREE
res.ctx = cuda_make_ctx(ptr, flags)
if res.ctx == NULL:
raise RuntimeError, "cuda_make_ctx call failed"
return res
import numpy
cdef dict NP_TO_TYPE = {
np.dtype('bool'): GA_BOOL,
np.dtype('int8'): GA_BYTE,
np.dtype('uint8'): GA_UBYTE,
np.dtype('int16'): GA_SHORT,
np.dtype('uint16'): GA_USHORT,
np.dtype('int32'): GA_INT,
np.dtype('uint32'): GA_UINT,
np.dtype('int64'): GA_LONG,
np.dtype('uint64'): GA_ULONG,
np.dtype('float32'): GA_FLOAT,
np.dtype('float64'): GA_DOUBLE,
np.dtype('complex64'): GA_CFLOAT,
np.dtype('complex128'): GA_CDOUBLE,
np.dtype('float16'): GA_HALF,
}
cdef dict TYPE_TO_NP = dict((v, k) for k, v in NP_TO_TYPE.iteritems())
def register_dtype(np.dtype dtype, cname):
"""
register_dtype(dtype, cname)
Make a new type known to the cluda machinery.
This function return the associted internal typecode for the new
type.
:param dtype: new type
:type dtype: numpy.dtype
:param cname: C name for the type declarations
:type cname: string
:rtype: int
"""
cdef gpuarray_type *t
cdef int typecode
cdef char *tmp
t = malloc(sizeof(gpuarray_type))
if t == NULL:
raise MemoryError, "Can't allocate new type"
tmp = malloc(len(cname)+1)
if tmp == NULL:
free(t)
raise MemoryError
memcpy(tmp, cname, len(cname)+1)
t.size = dtype.itemsize
t.align = dtype.alignment
t.cluda_name = tmp
typecode = gpuarray_register_type(t, NULL)
if typecode == -1:
free(tmp)
free(t)
raise RuntimeError, "Could not register type"
NP_TO_TYPE[dtype] = typecode
TYPE_TO_NP[typecode] = dtype
cdef np.dtype typecode_to_dtype(int typecode):
res = TYPE_TO_NP.get(typecode, None)
if res is not None:
return res
else:
raise NotImplementedError, "TODO"
# This function takes a flexible dtype as accepted by the functions of
# this module and ensures it becomes a numpy dtype.
cdef np.dtype dtype_to_npdtype(dtype):
if dtype is None:
return None
if isinstance(dtype, int):
return typecode_to_dtype(dtype)
try:
return np.dtype(dtype)
except TypeError:
pass
if isinstance(dtype, np.dtype):
return dtype
raise ValueError("data type not understood", dtype)
# This is a stupid wrapper to avoid the extra argument introduced by having
# dtype_to_typecode declared 'cpdef'.
cdef int get_typecode(dtype) except -1:
return dtype_to_typecode(dtype)
cpdef int dtype_to_typecode(dtype) except -1:
"""
dtype_to_typecode(dtype)
Get the internal typecode for a type.
:param dtype: type to get the code for
:type dtype: numpy.dtype
:rtype: int
"""
if isinstance(dtype, int):
return dtype
try:
dtype = np.dtype(dtype)
except TypeError:
pass
if isinstance(dtype, np.dtype):
res = NP_TO_TYPE.get(dtype, None)
if res is not None:
return res
raise ValueError, "don't know how to convert to dtype: %s"%(dtype,)
def dtype_to_ctype(dtype):
"""
dtype_to_ctype(dtype)
Return the C name for a type.
:param dtype: type to get the name for
:type dtype: numpy.dtype
:rtype: string
"""
cdef int typecode = dtype_to_typecode(dtype)
cdef const gpuarray_type *t = gpuarray_get_type(typecode)
cdef bytes res
if t.cluda_name == NULL:
raise ValueError, "No mapping for %s"%(dtype,)
res = t.cluda_name
return res.decode('ascii')
cdef ga_order to_ga_order(ord) except -2:
if ord == "C" or ord == "c":
return GA_C_ORDER
elif ord == "A" or ord == "a" or ord is None:
return GA_ANY_ORDER
elif ord == "F" or ord == "f":
return GA_F_ORDER
else:
raise ValueError, "Valid orders are: 'A' (any), 'C' (C), 'F' (Fortran)"
class GpuArrayException(Exception):
"""
Exception used for most errors related to libgpuarray.
"""
class UnsupportedException(GpuArrayException):
pass
cdef type get_exc(int errcode):
if errcode == GA_VALUE_ERROR:
return ValueError
if errcode == GA_DEVSUP_ERROR:
return UnsupportedException
else:
return GpuArrayException
cdef bint py_CHKFLAGS(GpuArray a, int flags):
return GpuArray_CHKFLAGS(&a.ga, flags)
cdef bint py_ISONESEGMENT(GpuArray a):
return GpuArray_ISONESEGMENT(&a.ga)
cdef int array_empty(GpuArray a, gpucontext *ctx,
int typecode, unsigned int nd, const size_t *dims,
ga_order ord) except -1:
cdef int err
err = GpuArray_empty(&a.ga, ctx, typecode, nd, dims, ord)
if err != GA_NO_ERROR:
raise get_exc(err), gpucontext_error(ctx, err)
cdef int array_fromdata(GpuArray a,
gpudata *data, size_t offset, int typecode,
unsigned int nd, const size_t *dims,
const ssize_t *strides, int writeable) except -1:
cdef int err
err = GpuArray_fromdata(&a.ga, data, offset, typecode, nd, dims,
strides, writeable)
if err != GA_NO_ERROR:
raise get_exc(err), gpucontext_error(gpudata_context(data), err)
cdef int array_copy_from_host(GpuArray a,
gpucontext *ctx, void *buf, int typecode,
unsigned int nd, const size_t *dims,
const ssize_t *strides) except -1:
cdef int err
with nogil:
err = GpuArray_copy_from_host(&a.ga, ctx, buf, typecode, nd, dims,
strides);
if err != GA_NO_ERROR:
raise get_exc(err), gpucontext_error(ctx, err)
cdef int array_view(GpuArray v, GpuArray a) except -1:
cdef int err
err = GpuArray_view(&v.ga, &a.ga)
if err != GA_NO_ERROR:
raise get_exc(err), GpuArray_error(&a.ga, err)
cdef int array_sync(GpuArray a) except -1:
cdef int err
with nogil:
err = GpuArray_sync(&a.ga)
if err != GA_NO_ERROR:
raise get_exc(err), GpuArray_error(&a.ga, err)
cdef int array_index(GpuArray r, GpuArray a, const ssize_t *starts,
const ssize_t *stops, const ssize_t *steps) except -1:
cdef int err
err = GpuArray_index(&r.ga, &a.ga, starts, stops, steps)
if err != GA_NO_ERROR:
raise get_exc(err), GpuArray_error(&a.ga, err)
cdef int array_take1(GpuArray r, GpuArray a, GpuArray i,
int check_err) except -1:
cdef int err
err = GpuArray_take1(&r.ga, &a.ga, &i.ga, check_err)
if err != GA_NO_ERROR:
if err == GA_VALUE_ERROR:
raise IndexError, "Index out of bounds"
raise get_exc(err), GpuArray_error(&r.ga, err)
cdef int array_setarray(GpuArray v, GpuArray a) except -1:
cdef int err
err = GpuArray_setarray(&v.ga, &a.ga)
if err != GA_NO_ERROR:
raise get_exc(err), GpuArray_error(&v.ga, err)
cdef int array_reshape(GpuArray res, GpuArray a, unsigned int nd,
const size_t *newdims, ga_order ord,
bint nocopy) except -1:
cdef int err
err = GpuArray_reshape(&res.ga, &a.ga, nd, newdims, ord, nocopy)
if err != GA_NO_ERROR:
raise get_exc(err), GpuArray_error(&a.ga, err)
cdef int array_transpose(GpuArray res, GpuArray a,
const unsigned int *new_axes) except -1:
cdef int err
err = GpuArray_transpose(&res.ga, &a.ga, new_axes)
if err != GA_NO_ERROR:
raise get_exc(err), GpuArray_error(&a.ga, err)
cdef int array_clear(GpuArray a) except -1:
GpuArray_clear(&a.ga)
cdef bint array_share(GpuArray a, GpuArray b):
return GpuArray_share(&a.ga, &b.ga)
cdef gpucontext *array_context(GpuArray a) except NULL:
cdef gpucontext *res
res = GpuArray_context(&a.ga)
if res is NULL:
raise GpuArrayException, "Invalid array or destroyed context"
return res
cdef int array_move(GpuArray a, GpuArray src) except -1:
cdef int err
err = GpuArray_move(&a.ga, &src.ga)
if err != GA_NO_ERROR:
raise get_exc(err), GpuArray_error(&a.ga, err)
cdef int array_write(GpuArray a, void *src, size_t sz) except -1:
cdef int err
with nogil:
err = GpuArray_write(&a.ga, src, sz)
if err != GA_NO_ERROR:
raise get_exc(err), GpuArray_error(&a.ga, err)
cdef int array_read(void *dst, size_t sz, GpuArray src) except -1:
cdef int err
with nogil:
err = GpuArray_read(dst, sz, &src.ga)
if err != GA_NO_ERROR:
raise get_exc(err), GpuArray_error(&src.ga, err)
cdef int array_memset(GpuArray a, int data) except -1:
cdef int err
err = GpuArray_memset(&a.ga, data)
if err != GA_NO_ERROR:
raise get_exc(err), GpuArray_error(&a.ga, err)
cdef int array_copy(GpuArray res, GpuArray a, ga_order order) except -1:
cdef int err
err = GpuArray_copy(&res.ga, &a.ga, order)
if err != GA_NO_ERROR:
raise get_exc(err), GpuArray_error(&a.ga, err)
cdef int array_transfer(GpuArray res, GpuArray a) except -1:
cdef int err
with nogil:
err = GpuArray_transfer(&res.ga, &a.ga)
if err != GA_NO_ERROR:
raise get_exc(err), GpuArray_error(&a.ga, err)
cdef int array_split(_GpuArray **res, GpuArray a, size_t n, size_t *p,
unsigned int axis) except -1:
cdef int err
err = GpuArray_split(res, &a.ga, n, p, axis)
if err != GA_NO_ERROR:
raise get_exc(err), GpuArray_error(&a.ga, err)
cdef int array_concatenate(GpuArray r, const _GpuArray **a, size_t n,
unsigned int axis, int restype) except -1:
cdef int err
err = GpuArray_concatenate(&r.ga, a, n, axis, restype)
if err != GA_NO_ERROR:
raise get_exc(err), GpuArray_error(a[0], err)
cdef const char *kernel_error(GpuKernel k, int err) except NULL:
return gpucontext_error(gpukernel_context(k.k.k), err)
cdef int kernel_init(GpuKernel k, gpucontext *ctx,
unsigned int count, const char **strs, const size_t *len,
const char *name, unsigned int argcount, const int *types,
int flags) except -1:
cdef int err
cdef char *err_str = NULL
err = GpuKernel_init(&k.k, ctx, count, strs, len, name, argcount,
types, flags, &err_str)
if err != GA_NO_ERROR:
if err_str != NULL:
try:
py_err_str = err_str.decode('UTF-8')
finally:
free(err_str)
raise get_exc(err), py_err_str
raise get_exc(err), gpucontext_error(ctx, err)
cdef int kernel_clear(GpuKernel k) except -1:
GpuKernel_clear(&k.k)
cdef gpucontext *kernel_context(GpuKernel k) except NULL:
cdef gpucontext *res
res = GpuKernel_context(&k.k)
if res is NULL:
raise GpuArrayException, "Invalid kernel or destroyed context"
return res
cdef int kernel_sched(GpuKernel k, size_t n, size_t *ls, size_t *gs) except -1:
cdef int err
err = GpuKernel_sched(&k.k, n, ls, gs)
if err != GA_NO_ERROR:
raise get_exc(err), kernel_error(k, err)
cdef int kernel_call(GpuKernel k, unsigned int n, const size_t *ls,
const size_t *gs, size_t shared, void **args) except -1:
cdef int err
err = GpuKernel_call(&k.k, n, ls, gs, shared, args)
if err != GA_NO_ERROR:
raise get_exc(err), kernel_error(k, err)
cdef int kernel_binary(GpuKernel k, size_t *sz, void **bin) except -1:
cdef int err
err = GpuKernel_binary(&k.k, sz, bin)
if err != GA_NO_ERROR:
raise get_exc(err), kernel_error(k, err)
cdef int kernel_property(GpuKernel k, int prop_id, void *res) except -1:
cdef int err
err = gpukernel_property(k.k.k, prop_id, res)
if err != GA_NO_ERROR:
raise get_exc(err), kernel_error(k, err)
cdef GpuContext pygpu_default_context():
return default_context
cdef GpuContext default_context = None
cdef int ctx_property(GpuContext c, int prop_id, void *res) except -1:
cdef int err
err = gpucontext_property(c.ctx, prop_id, res)
if err != GA_NO_ERROR:
raise get_exc(err), gpucontext_error(c.ctx, err)
def set_default_context(GpuContext ctx):
"""
set_default_context(ctx)
Set the default context for the module.
:param ctx: default context
:type ctx: GpuContext
:rtype: None
The provided context will be used as a default value for all the
other functions in this module which take a context as parameter.
Call with `None` to clear the default value.
If you don't call this function the context of all other functions
is a mandatory argument.
This can be helpful to reduce clutter when working with only one
context. It is strongly discouraged to use this function when
working with multiple contexts at once.
"""
global default_context
default_context = ctx
def get_default_context():
"""
get_default_context()
Return the currently defined default context (or `None`).
"""
return default_context
cdef GpuContext ensure_context(GpuContext c):
global default_context
if c is None:
if default_context is None:
raise TypeError, "No context specified."
return default_context
return c
cdef bint pygpu_GpuArray_Check(object o):
return isinstance(o, GpuArray)
def count_platforms(kind):
"""Return number of host's platforms compatible with `kind`.
"""
cdef unsigned int platcount
cdef int err
err = gpu_get_platform_count(_s(kind), &platcount)
if err != GA_NO_ERROR:
raise get_exc(err), gpucontext_error(NULL, err)
return platcount
def count_devices(kind, unsigned int platform):
"""Returns number of devices in host's `platform` compatible with `kind`.
"""
cdef unsigned int devcount
cdef int err
err = gpu_get_device_count(_s(kind), platform, &devcount)
if err != GA_NO_ERROR:
raise get_exc(err), gpucontext_error(NULL, err)
return devcount
cdef GpuContext pygpu_init(dev, int flags):
if dev.startswith('cuda'):
kind = b"cuda"
if dev[4:] == '':
devnum = -1
else:
devnum = int(dev[4:])
elif dev.startswith('opencl'):
kind = b"opencl"
devspec = dev[6:].split(':')
if len(devspec) < 2:
raise ValueError, "OpenCL name incorrect. Should be opencl: instead got: " + dev
if not devspec[0].isdigit() or not devspec[1].isdigit():
raise ValueError, "OpenCL name incorrect. Should be opencl: instead got: " + dev
else:
devnum = int(devspec[0])