From b12426b2efab45de6ce5bc4339acffa2532046db Mon Sep 17 00:00:00 2001 From: Graham Markall Date: Tue, 13 Oct 2020 15:24:23 +0100 Subject: [PATCH] Apply C++-only dispatcher branch / PR From PR #6349 / the grm-dispatcher-refactor branch. --- docs/source/developer/repomap.rst | 8 +- numba/{_dispatcher.c => _dispatcher.cpp} | 145 ++++++++++++++--------- numba/_dispatcher.h | 33 ------ numba/_dispatcherimpl.cpp | 108 ----------------- numba/_typeof.h | 7 ++ setup.py | 23 ++-- 6 files changed, 108 insertions(+), 216 deletions(-) rename numba/{_dispatcher.c => _dispatcher.cpp} (89%) delete mode 100644 numba/_dispatcher.h delete mode 100644 numba/_dispatcherimpl.cpp diff --git a/docs/source/developer/repomap.rst b/docs/source/developer/repomap.rst index 0dea8a5b033..0b09d14a63e 100644 --- a/docs/source/developer/repomap.rst +++ b/docs/source/developer/repomap.rst @@ -101,10 +101,8 @@ Dispatching - :ghfile:`numba/core/dispatcher.py` - Dispatcher objects are compiled functions produced by ``@jit``. A dispatcher has different implementations for different type signatures. -- :ghfile:`numba/_dispatcher.{h,c}` - C interface to C++ dispatcher - implementation -- :ghfile:`numba/_dispatcherimpl.cpp` - C++ dispatcher implementation (for - speed on common data types) +- :ghfile:`numba/_dispatcher.cpp` - C++ dispatcher implementation (for speed on + common data types) Compiler Pipeline @@ -358,6 +356,8 @@ that must be matched during type inference. - :ghfile:`numba/core/typing/cffi_utils.py` - Typing of CFFI objects - :ghfile:`numba/core/typing/typeof.py` - Implementation of typeof operations (maps Python object to Numba type) +- :ghfile:`numba/core/typing/asnumbatype.py` - Implementation of + ``as_numba_type`` operations (maps Python types to Numba type) - :ghfile:`numba/core/typing/npdatetime.py` - Datetime dtype support for NumPy arrays diff --git a/numba/_dispatcher.c b/numba/_dispatcher.cpp similarity index 89% rename from numba/_dispatcher.c rename to numba/_dispatcher.cpp index 3e0a5240bce..2858ec6ead5 100644 --- a/numba/_dispatcher.c +++ b/numba/_dispatcher.cpp @@ -1,12 +1,13 @@ #include "_pymodule.h" -#include -#include -#include +#include +#include +#include +#include -#include "_dispatcher.h" #include "_typeof.h" #include "frameobject.h" +#include "core/typeconv/typeconv.hpp" /* * The following call_trace and call_trace_protected functions @@ -97,16 +98,17 @@ else \ } \ } +typedef std::vector TypeTable; +typedef std::vector Functions; -typedef struct DispatcherObject{ +class Dispatcher { +public: PyObject_HEAD - /* Holds borrowed references to PyCFunction objects */ - dispatcher_t *dispatcher; char can_compile; /* Can auto compile */ char can_fallback; /* Can fallback */ char exact_match_required; - /* Borrowed references */ - PyObject *firstdef, *fallbackdef, *interpdef; + /* Borrowed reference */ + PyObject *fallbackdef; /* Whether to fold named arguments and default values (false for lifted loops)*/ int fold_args; /* Whether the last positional argument is a stararg */ @@ -115,28 +117,76 @@ typedef struct DispatcherObject{ PyObject *argnames; /* Tuple of default values */ PyObject *defargs; -} DispatcherObject; + /* Number of arguments to function */ + int argct; + /* Used for selecting overloaded function implementations */ + TypeManager *tm; + /* An array of overloads */ + Functions functions; + /* A flattened array of argument types to all overloads + * (invariant: sizeof(overloads) == argct * sizeof(functions)) */ + TypeTable overloads; + + void addDefinition(Type args[], PyObject *callable) { + overloads.reserve(argct + overloads.size()); + for (int i=0; iselectOverload(sig, &overloads[0], selected, argct, + ovct, allow_unsafe, + exact_match_required); + } + if (matches == 1) { + return functions[selected]; + } + return NULL; + } + + void clear() { + functions.clear(); + overloads.clear(); + } + +}; static int -Dispatcher_traverse(DispatcherObject *self, visitproc visit, void *arg) +Dispatcher_traverse(Dispatcher *self, visitproc visit, void *arg) { Py_VISIT(self->defargs); return 0; } static void -Dispatcher_dealloc(DispatcherObject *self) +Dispatcher_dealloc(Dispatcher *self) { Py_XDECREF(self->argnames); Py_XDECREF(self->defargs); - dispatcher_del(self->dispatcher); + self->clear(); Py_TYPE(self)->tp_free((PyObject*)self); } static int -Dispatcher_init(DispatcherObject *self, PyObject *args, PyObject *kwds) +Dispatcher_init(Dispatcher *self, PyObject *args, PyObject *kwds) { PyObject *tmaddrobj; void *tmaddr; @@ -158,71 +208,59 @@ Dispatcher_init(DispatcherObject *self, PyObject *args, PyObject *kwds) Py_INCREF(self->argnames); Py_INCREF(self->defargs); tmaddr = PyLong_AsVoidPtr(tmaddrobj); - self->dispatcher = dispatcher_new(tmaddr, argct); + self->tm = static_cast(tmaddr); + self->argct = argct; self->can_compile = 1; self->can_fallback = can_fallback; - self->firstdef = NULL; self->fallbackdef = NULL; - self->interpdef = NULL; self->has_stararg = has_stararg; self->exact_match_required = exact_match_required; return 0; } static PyObject * -Dispatcher_clear(DispatcherObject *self, PyObject *args) +Dispatcher_clear(Dispatcher *self, PyObject *args) { - dispatcher_clear(self->dispatcher); + self->clear(); Py_RETURN_NONE; } static PyObject* -Dispatcher_Insert(DispatcherObject *self, PyObject *args) +Dispatcher_Insert(Dispatcher *self, PyObject *args) { PyObject *sigtup, *cfunc; int i, sigsz; int *sig; int objectmode = 0; - int interpmode = 0; - if (!PyArg_ParseTuple(args, "OO|ii", &sigtup, - &cfunc, &objectmode, &interpmode)) { + if (!PyArg_ParseTuple(args, "OO|i", &sigtup, + &cfunc, &objectmode)) { return NULL; } - if (!interpmode && !PyObject_TypeCheck(cfunc, &PyCFunction_Type) ) { + if (!PyObject_TypeCheck(cfunc, &PyCFunction_Type) ) { PyErr_SetString(PyExc_TypeError, "must be builtin_function_or_method"); return NULL; } sigsz = PySequence_Fast_GET_SIZE(sigtup); - sig = malloc(sigsz * sizeof(int)); + sig = new int[sigsz]; for (i = 0; i < sigsz; ++i) { sig[i] = PyLong_AsLong(PySequence_Fast_GET_ITEM(sigtup, i)); } - if (!interpmode) { - /* The reference to cfunc is borrowed; this only works because the - derived Python class also stores an (owned) reference to cfunc. */ - dispatcher_add_defn(self->dispatcher, sig, (void*) cfunc); + /* The reference to cfunc is borrowed; this only works because the + derived Python class also stores an (owned) reference to cfunc. */ + self->addDefinition(sig, cfunc); - /* Add first definition */ - if (!self->firstdef) { - self->firstdef = cfunc; - } - } /* Add pure python fallback */ if (!self->fallbackdef && objectmode){ self->fallbackdef = cfunc; } - /* Add interpreter fallback */ - if (!self->interpdef && interpmode) { - self->interpdef = cfunc; - } - free(sig); + delete[] sig; Py_RETURN_NONE; } @@ -340,13 +378,13 @@ int search_new_conversions(PyObject *dispatcher, PyObject *args, PyObject *kws) /* A custom, fast, inlinable version of PyCFunction_Call() */ static PyObject * -call_cfunc(DispatcherObject *self, PyObject *cfunc, PyObject *args, PyObject *kws, PyObject *locals) +call_cfunc(Dispatcher *self, PyObject *cfunc, PyObject *args, PyObject *kws, PyObject *locals) { PyCFunctionWithKeywords fn; PyThreadState *tstate; assert(PyCFunction_Check(cfunc)); - assert(PyCFunction_GET_FLAGS(cfunc) == (METH_VARARGS | METH_KEYWORDS)); + assert(PyCFunction_GET_FLAGS(cfunc) == METH_VARARGS | METH_KEYWORDS); fn = (PyCFunctionWithKeywords) PyCFunction_GET_FUNCTION(cfunc); tstate = PyThreadState_GET(); @@ -405,7 +443,7 @@ call_cfunc(DispatcherObject *self, PyObject *cfunc, PyObject *args, PyObject *kw static PyObject* -compile_and_invoke(DispatcherObject *self, PyObject *args, PyObject *kws, PyObject *locals) +compile_and_invoke(Dispatcher *self, PyObject *args, PyObject *kws, PyObject *locals) { /* Compile a new one */ PyObject *cfa, *cfunc, *retval; @@ -449,9 +487,8 @@ compile_only(DispatcherObject *self, PyObject *args, PyObject *kws, PyObject *lo return cfunc; } - static int -find_named_args(DispatcherObject *self, PyObject **pargs, PyObject **pkws) +find_named_args(Dispatcher *self, PyObject **pargs, PyObject **pkws) { PyObject *oldargs = *pargs, *newargs; PyObject *kws = *pkws; @@ -565,7 +602,7 @@ find_named_args(DispatcherObject *self, PyObject **pargs, PyObject **pkws) } static PyObject* -Dispatcher_call(DispatcherObject *self, PyObject *args, PyObject *kws) +Dispatcher_call(Dispatcher *self, PyObject *args, PyObject *kws) { PyObject *tmptype, *retval = NULL; int *tys = NULL; @@ -595,7 +632,7 @@ Dispatcher_call(DispatcherObject *self, PyObject *args, PyObject *kws) if (argct < (Py_ssize_t) (sizeof(prealloc) / sizeof(int))) tys = prealloc; else - tys = malloc(argct * sizeof(int)); + tys = new int[argct]; for (i = 0; i < argct; ++i) { tmptype = PySequence_Fast_GET_ITEM(args, i); @@ -616,8 +653,8 @@ Dispatcher_call(DispatcherObject *self, PyObject *args, PyObject *kws) /* We only allow unsafe conversions if compilation of new specializations has been disabled. */ - cfunc = dispatcher_resolve(self->dispatcher, tys, &matches, - !self->can_compile, self->exact_match_required); + cfunc = self->resolve(tys, matches, !self->can_compile, + self->exact_match_required); if (matches == 0 && !self->can_compile) { /* @@ -632,9 +669,8 @@ Dispatcher_call(DispatcherObject *self, PyObject *args, PyObject *kws) } if (res > 0) { /* Retry with the newly registered conversions */ - cfunc = dispatcher_resolve(self->dispatcher, tys, &matches, - !self->can_compile, - self->exact_match_required); + cfunc = self->resolve(tys, matches, !self->can_compile, + self->exact_match_required); } } @@ -664,7 +700,7 @@ Dispatcher_call(DispatcherObject *self, PyObject *args, PyObject *kws) CLEANUP: if (tys != prealloc) - free(tys); + delete[] tys; Py_DECREF(args); return retval; @@ -782,7 +818,6 @@ Dispatcher_cuda_call(DispatcherObject *self, PyObject *args, PyObject *kws) return retval; } - static PyMethodDef Dispatcher_methods[] = { { "_clear", (PyCFunction)Dispatcher_clear, METH_NOARGS, NULL }, { "_insert", (PyCFunction)Dispatcher_Insert, METH_VARARGS, @@ -797,7 +832,7 @@ static PyMethodDef Dispatcher_methods[] = { }; static PyMemberDef Dispatcher_members[] = { - {"_can_compile", T_BOOL, offsetof(DispatcherObject, can_compile), 0, NULL }, + {(char*)"_can_compile", T_BOOL, offsetof(Dispatcher, can_compile), 0, NULL }, {NULL} /* Sentinel */ }; @@ -805,7 +840,7 @@ static PyMemberDef Dispatcher_members[] = { static PyTypeObject DispatcherType = { PyVarObject_HEAD_INIT(NULL, 0) "_dispatcher.Dispatcher", /* tp_name */ - sizeof(DispatcherObject), /* tp_basicsize */ + sizeof(Dispatcher), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Dispatcher_dealloc, /* tp_dealloc */ 0, /* tp_print */ diff --git a/numba/_dispatcher.h b/numba/_dispatcher.h deleted file mode 100644 index 3517775e1af..00000000000 --- a/numba/_dispatcher.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef NUMBA_DISPATCHER_H_ -#define NUMBA_DISPATCHER_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -typedef struct _opaque_dispatcher dispatcher_t; - -dispatcher_t * -dispatcher_new(void *tm, int argct); - -void -dispatcher_clear(dispatcher_t *obj); - -void -dispatcher_del(dispatcher_t *obj); - -void -dispatcher_add_defn(dispatcher_t *obj, int tys[], void* callable); - -void* -dispatcher_resolve(dispatcher_t *obj, int sig[], int *matches, - int allow_unsafe, int exact_match_required); - -int -dispatcher_count(dispatcher_t *obj); - -#ifdef __cplusplus - } -#endif - -#endif /* NUMBA_DISPATCHER_H_ */ diff --git a/numba/_dispatcherimpl.cpp b/numba/_dispatcherimpl.cpp deleted file mode 100644 index 67ea1351116..00000000000 --- a/numba/_dispatcherimpl.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include "core/typeconv/typeconv.hpp" -#include -#include - -typedef std::vector TypeTable; -typedef std::vector Functions; - -struct _opaque_dispatcher {}; - -class Dispatcher: public _opaque_dispatcher { -public: - Dispatcher(TypeManager *tm, int argct): argct(argct), tm(tm) { } - - void addDefinition(Type args[], void *callable) { - overloads.reserve(argct + overloads.size()); - for (int i=0; iselectOverload(sig, &overloads[0], selected, argct, - ovct, allow_unsafe, - exact_match_required); - } - if (matches == 1) { - return functions[selected]; - } - return NULL; - } - - int count() const { return functions.size(); } - - void clear() { - functions.clear(); - overloads.clear(); - } - -private: - const int argct; - TypeManager *tm; - // An array of overloads - Functions functions; - // A flattened array of argument types to all overloads - // (invariant: sizeof(overloads) == argct * sizeof(functions)) - TypeTable overloads; -}; - - -#include "_dispatcher.h" - -dispatcher_t * -dispatcher_new(void *tm, int argct){ - return new Dispatcher(static_cast(tm), argct); -} - -void -dispatcher_clear(dispatcher_t *obj) { - Dispatcher *disp = static_cast(obj); - disp->clear(); -} - -void -dispatcher_del(dispatcher_t *obj) { - Dispatcher *disp = static_cast(obj); - delete disp; -} - -void -dispatcher_add_defn(dispatcher_t *obj, int tys[], void* callable) { - assert(sizeof(int) == sizeof(Type) && - "Type should be representable by an int"); - - Dispatcher *disp = static_cast(obj); - Type *args = reinterpret_cast(tys); - disp->addDefinition(args, callable); -} - -void* -dispatcher_resolve(dispatcher_t *obj, int sig[], int *count, int allow_unsafe, - int exact_match_required) { - Dispatcher *disp = static_cast(obj); - Type *args = reinterpret_cast(sig); - void *callable = disp->resolve(args, *count, (bool) allow_unsafe, - (bool) exact_match_required); - return callable; -} - -int -dispatcher_count(dispatcher_t *obj) { - Dispatcher *disp = static_cast(obj); - return disp->count(); -} diff --git a/numba/_typeof.h b/numba/_typeof.h index dfd345589fb..dd4110e6105 100644 --- a/numba/_typeof.h +++ b/numba/_typeof.h @@ -1,6 +1,9 @@ #ifndef NUMBA_TYPEOF_H_ #define NUMBA_TYPEOF_H_ +#ifdef __cplusplus + extern "C" { +#endif extern PyObject *typeof_init(PyObject *self, PyObject *args); extern int typeof_typecode(PyObject *dispatcher, PyObject *val); @@ -8,4 +11,8 @@ extern PyObject *typeof_compute_fingerprint(PyObject *val); extern void typeof_set_devicendarraybase(PyObject *val); +#ifdef __cplusplus + } +#endif + #endif /* NUMBA_TYPEOF_H_ */ diff --git a/setup.py b/setup.py index 658b309ea14..3ccbc1fb138 100644 --- a/setup.py +++ b/setup.py @@ -57,24 +57,17 @@ class NumbaBuildExt(build_ext): def initialize_options(self): super().initialize_options() - # We build with -Werror and -Wall by default on Linux x86, to provide - # some level of checking without the burden of maintaining it for all - # platforms. - is_linux_x86 = (sys.platform == 'linux' and platform.machine() - in ('i386', 'x86_64')) - self.werror = is_linux_x86 - self.wall = is_linux_x86 - # Building with debug symbols should generally work on Linux and OS X. - self.debug = sys.platform in ('linux', 'darwin') - # Optional build without optimization, to make debugging extensions - # easier. + self.werror = 0 + self.wall = 0 self.noopt = 0 def run(self): extra_compile_args = [] if self.noopt: - # Debugging is easier with an unoptimized binary - extra_compile_args.append('-O0') + if sys.platform == 'win32': + extra_compile_args.append('/Od') + else: + extra_compile_args.append('-O0') if self.werror: extra_compile_args.append('-Werror') if self.wall: @@ -135,13 +128,11 @@ def get_ext_modules(): 'numba/_dynfunc.c']) ext_dispatcher = Extension(name="numba._dispatcher", - sources=['numba/_dispatcher.c', + sources=['numba/_dispatcher.cpp', 'numba/_typeof.c', 'numba/_hashtable.c', - 'numba/_dispatcherimpl.cpp', 'numba/core/typeconv/typeconv.cpp'], depends=["numba/_pymodule.h", - "numba/_dispatcher.h", "numba/_typeof.h", "numba/_hashtable.h"], **np_compile_args)