可以在nopython-numba中使用Cython的
cpdef
/
cdef
函数(但不能使用
def
函数):
- 步骤:在Cython代码中,
cdef
/cpdef
函数必须标记为api
。
- 步骤:
numba.extending.get_cython_function_address
可用于获取cpdef函数的地址。
- 步骤:可以使用
ctypes
从cpdef函数的地址创建CFunction
,该函数可在numba-nopython代码中使用。
阅读以下内容以获取更详细的解释。
即使内置函数(
PyCFunction
,与Cython的
def
函数相同)是用C编写的,它们也没有一个可供nopython-numba代码使用的签名。
例如,来自
math
模块的
acos
函数没有签名。
`double acos(double)`
正如人们所预料的那样,但它的签名是
static PyObject * math_acos(PyObject *self, PyObject *args)
基本上,为了调用这个函数,numba需要从手头的C-float构建一个Python-float,但是这被
nopython=True
禁止了。
然而,Cython的
cpdef
函数有些不同:它是一个小包装器,包装了一个真正的
cdef
函数,其参数是原始的C类型,如
double
、
int
等。如果已知其地址,numba可以使用这个
cdef
函数。
Cython提供了一种在可移植的方式中查找
cdef
函数地址的方法:这些地址可以在cythonized模块的
__pyx_capi__
属性中找到。
然而,并不是所有的
cdef
和
cpdef
函数都以这种方式公开,只有那些显式标记为
C-api declarations或通过
pxd
文件共享的函数才会被公开。
一旦foomodule
的函数foo
被标记为api
:
cpdef api double foo(double x):
return x*x
cpdef函数foo
的地址可以在foomodule.__pyx_capi__
字典中找到:
import foomodule
foomodule.__pyx_capi
# {'foo': <capsule object "double (double)" at 0x7fe0a46f0360>}
从Python的PyCapsule
中提取地址非常困难。一种可能的方法是使用ctypes.pythonapi
,另一种(可能更容易的方法)是利用Cython访问Python的C-API:
%%cython
from cpython.pycapsule cimport PyCapsule_GetPointer, PyCapsule_GetName
def address_from_capsule(object capsule):
name = PyCapsule_GetName(capsule)
return <unsigned long long int> PyCapsule_GetPointer(capsule, name)
可以用作:
addr = address_from_capsule(foomodule.__pyx_capi__['foo'])
然而,numba提供了一个类似的功能,可以直接使用 -
get_cython_function_address
:
from numba.extending import get_cython_function_address
addr = get_cython_function_address("foomodule", "foo")
一旦我们获得了C函数的地址,我们就可以构建一个ctypes
函数:
import ctypes
foo_functype = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double)
foo_for_numba = foo_functype(addr)
这个函数可以像下面这样从nopython-numba中使用:
from numba import njit
@njit
def use_foo(x):
return foo_for_numba(x)
现在:
use_foo(5)
产生预期结果。
ctype函数的参数可以在这里找到,numba可以直接识别这些参数。
_FROM_CTYPES = {
ctypes.c_bool: types.boolean,
ctypes.c_int8: types.int8,
ctypes.c_int16: types.int16,
ctypes.c_int32: types.int32,
ctypes.c_int64: types.int64,
ctypes.c_uint8: types.uint8,
ctypes.c_uint16: types.uint16,
ctypes.c_uint32: types.uint32,
ctypes.c_uint64: types.uint64,
ctypes.c_float: types.float32,
ctypes.c_double: types.float64,
ctypes.c_void_p: types.voidptr,
ctypes.py_object: types.ffi_forced_object,
}
foo
函数的参数或返回值为 numpy 的 ndarrays? - ibarrond