我知道调用另一个jitted函数的Numba-jitted函数会识别这个并自动使用快速C调用约定而不是通过Python对象层,因此避免了高Python函数调用开销:
import numba
@numba.jit
def foo(x):
return x**2
@numba.jit
def bar(x):
return 4 * foo(x) # this will be a fast function call
我的问题是,如果我从Numba调用Cython函数,是否也是如此。所以,让我们说我有一个Cython模块,foo.pyx
:
cpdef double foo(double x):
return x**2
以及标准Python模块bar.py
:
import numba
import foo
@numba.jit
def bar(x):
return 4 * foo.foo(x) # will this be a fast function call?
Numba会自动将foo.foo
识别为C可调用函数,还是需要通过设置CFFI包装来手动告诉它?
编辑:经过进一步的反思,Cython功能只是标准的内置"从Python解释器的角度来看。因此,问题可以更加通用:Numba是否优化对内置函数和方法的调用以绕过Python调用开销?
答案 0 :(得分:5)
可以在nopython-numba中使用Cython的cpdef
/ cdef
函数(但不能使用def
函数)
cdef
/ cpdef
函数必须在Cython代码中为marked as api
。numba.extending.get_cython_function_address
可用于获取cpdef函数的地址。ctypes
可用于从cpdef函数的地址创建CFunction
,该地址可用于numba-nopython代码。继续阅读,以获得更详细的解释。
即使内置函数(PyCFunction
与Cython的def
函数)都是用C编写的,它们也没有可被nopython-numba代码使用的签名
例如,acos
模块中的math
函数没有签名
`double acos(double)`
正如人们所期望的,但是its signature是
static PyObject * math_acos(PyObject *self, PyObject *args)
因此,基本上,为了调用此函数,numba需要从手头的C浮点构建一个Python浮点,但是nopython=True
禁止这样做。
但是,Cythons cpdef
函数有点不同:它是围绕实cdef
函数的小包装,其实参为double
,{ {1}},依此类推。仅在知道地址的情况下,numba才能使用此int
函数。
Cython提供了一种以可移植的方式找出cdef
函数的地址的方法:这些地址可以在cythonized模块的属性cdef
中找到。
但是,并非所有__pyx_capi__
和cdef
函数都以这种方式公开,而是只有那些显式标记为C-api declarations或通过cpdef
共享隐式公开的函数。文件。
一旦pxd
的功能foo
被标记为foomodule
:
api
cpdef函数cpdef api double foo(double x):
return x*x
的地址可以在foo
-字典中找到:
foomodule.__pyx_capi__
在Python中,很难从PyCapsule
中提取地址。一种可能是使用ctypes.pythonapi
,另一种(可能更简单)是利用Cython访问Python的C-API:
import foomodule
foomodule.__pyx_capi
# {'foo': <capsule object "double (double)" at 0x7fe0a46f0360>}
可以用作:
%%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)
但是,numba提供了类似的开箱即用功能-get_cython_function_address
:
addr = address_from_capsule(foomodule.__pyx_capi__['foo'])
一旦获得了c函数的地址,就可以构造一个from numba.extending import get_cython_function_address
addr = get_cython_function_address("foomodule", "foo")
函数:
ctypes
例如,可以从nopython-numba中使用此功能,例如:
import ctypes
foo_functype = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double)
foo_for_numba = foo_functype(addr)
现在:
from numba import njit
@njit
def use_foo(x):
return foo_for_numba(x)
产生预期的结果。
答案 1 :(得分:4)
有一组有限的内置函数(来自python标准库和numpy),numba知道如何转换为本机代码:
http://numba.pydata.org/numba-doc/latest/reference/pysupported.html http://numba.pydata.org/numba-doc/latest/reference/numpysupported.html
Numba无法在nopython
模式下进行任何其他操作,因此使用速度慢得多的objectmode
。
没有直接的方法可以将cython函数传递给Numba并在nopython
模式下识别它。 Numba确实有cffi的钩子:
http://numba.pydata.org/numba-doc/latest/reference/pysupported.html#cffi
可以用来调用外部C代码,如果你可以在C级创建一个低级别的包装器,你可以装备调用cython;我不能100%确定这是否可行。我写过这样做是为了从Numba调用RMath函数:
https://www.continuum.io/blog/developer-blog/calling-c-libraries-numba-using-cffi
如果你走这条路,这可能对你有所帮助。