Skip to content

Commit

Permalink
Apply C++-only dispatcher branch / PR
Browse files Browse the repository at this point in the history
From PR numba#6349 / the grm-dispatcher-refactor branch.
  • Loading branch information
gmarkall committed Oct 13, 2020
1 parent 88e7016 commit b12426b
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 216 deletions.
8 changes: 4 additions & 4 deletions docs/source/developer/repomap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
145 changes: 90 additions & 55 deletions numba/_dispatcher.c → numba/_dispatcher.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#include "_pymodule.h"

#include <string.h>
#include <time.h>
#include <assert.h>
#include <cstring>
#include <ctime>
#include <cassert>
#include <vector>

#include "_dispatcher.h"
#include "_typeof.h"
#include "frameobject.h"
#include "core/typeconv/typeconv.hpp"

/*
* The following call_trace and call_trace_protected functions
Expand Down Expand Up @@ -97,16 +98,17 @@ else \
} \
}

typedef std::vector<Type> TypeTable;
typedef std::vector<PyObject*> 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 */
Expand All @@ -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; i<argct; ++i) {
overloads.push_back(args[i]);
}
functions.push_back(callable);
}

PyObject* resolve(Type sig[], int &matches, bool allow_unsafe,
bool exact_match_required) const {
const int ovct = functions.size();
int selected;
matches = 0;
if (0 == ovct) {
// No overloads registered
return NULL;
}
if (argct == 0) {
// Nullary function: trivial match on first overload
matches = 1;
selected = 0;
}
else {
matches = tm->selectOverload(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;
Expand All @@ -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<TypeManager*>(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;
}
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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) {
/*
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -797,15 +832,15 @@ 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 */
};


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 */
Expand Down
33 changes: 0 additions & 33 deletions numba/_dispatcher.h

This file was deleted.

0 comments on commit b12426b

Please sign in to comment.