cfunc.rst 5.2 KB
Newer Older
dugupeiwen's avatar
dugupeiwen committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
============================
NBEP 4: Defining C callbacks
============================

:Author: Antoine Pitrou
:Date: April 2016
:Status: Draft


Interfacing with some native libraries (for example written in C
or C++) can necessitate writing native callbacks to provide business logic
to the library.  Some Python-facing libraries may also provide the
alternative of passing a ctypes-wrapped native callback instead of a
Python callback for better performance.  A simple example is the
``scipy.integrate`` package where the user passes the function to be
integrated as a callback.

Users of those libraries may want to benefit from the performance advantage
of running purely native code, while writing their code in Python.
This proposal outlines a scheme to provide such a functionality in
Numba.


Basic usage
===========

We propose adding a new decorator, ``@cfunc``, importable from the main
package.  This decorator allows defining a callback as in the following
example::

   from numba import cfunc
   from numba.types import float64

   # A callback with the C signature `double(double)`

   @cfunc(float64(float64), nopython=True)
   def integrand(x):
       return 1 / x


The ``@cfunc`` decorator returns a "C function" object holding the
resources necessary to run the given compiled function (for example its
LLVM module).  This object has several attributes and methods:

* the ``ctypes`` attribute is a ctypes function object representing
  the native function.

* the ``address`` attribute is the address of the native function code, as
  an integer (note this can also be computed from the ``ctypes`` attribute).

* the ``native_name`` attribute is the symbol under which the function
  can be looked up inside the current process.

* the ``inspect_llvm()`` method returns the IR for the LLVM module
  in which the function is compiled.  It is expected that the ``native_name``
  attribute corresponds to the function's name in the LLVM IR.

The general signature of the decorator is ``cfunc(signature, **options)``.

The ``signature`` must specify the argument types and return type of the
function using Numba types.  In contrary to ``@jit``, the return type cannot
be omitted.

The ``options`` are keyword-only parameters specifying compilation options.
We are expecting that the standard ``@jit`` options (``nopython``,
``forceobj``, ``cache``) can be made to work with ``@cfunc``.


Calling from Numba-compiled functions
-------------------------------------

While the intended use is to pass a callback's address to foreign C
code expecting a function pointer, it should be made possible to call
the C callback from a Numba-compiled function.


Passing array data
==================

Native platform ABIs as used by C or C++ don't have the notion of a shaped
array as in Numpy.  One common solution is to pass a raw data pointer and
one or several size arguments (depending on dimensionality).  Numba must
provide a way to rebuild an array view of this data inside the callback.

::

   from numba import cfunc, carray
   from numba.types import float64, CPointer, void, intp

   # A callback with the C signature `void(double *, double *, size_t)`

   @cfunc(void(CPointer(float64), CPointer(float64), intp))
   def invert(in_ptr, out_ptr, n):
       in_ = carray(in_ptr, (n,))
       out = carray(out_ptr, (n,))
       for i in range(n):
           out[i] = 1 / in_[i]


The ``carray`` function takes ``(pointer, shape, dtype)`` arguments
(``dtype`` being optional) and returns a C-layout array view over the
data *pointer*, with the given *shape* and *dtype*.  *pointer* must
be a ctypes pointer object (not a Python integer).  The array's
dimensionality corresponds to the *shape* tuple's length.  If *dtype*
is not given, the array's dtype corresponds to the *pointer*'s pointee
type.

The ``farray`` function is similar except that it returns a F-layout
array view.


Error handling
==============

There is no standard mechanism in C for error reporting.  Unfortunately,
Numba currently doesn't handle ``try..except`` blocks, which makes it more
difficult for the user to implement the required error reporting scheme.
The current stance of this proposal is to let users guard against invalid
arguments where necessary, and do whatever is required to inform the caller
of the error.

Based on user feedback, we can later add support for some error reporting
schemes, such as returning an integer error code depending on whether an
exception was raised, or setting ``errno``.


Deferred topics
===============

Ahead-of-Time compilation
-------------------------

This proposal doesn't make any provision for AOT compilation of C callbacks.
It would probably necessitate a separate API (a new method on the
``numba.pycc.CC`` object), and the implementation would require exposing
a subset of the C function object's functionality from the compiled C
extension module.

Opaque data pointers
--------------------

Some libraries allow passing an opaque data pointer (``void *``) to a
user-provided callback, to provide any required context for execution
of the callback.  Taking advantage of this functionality would require
adding specific support in Numba, for example the ability to do generic
conversion from ``types.voidptr`` and to take the address of a
Python-facing ``jitclass`` instance.