NBEP 4: 定义C回调
- 作者:
安托万·皮特鲁
- 日期:
2016年4月
- 状态:
草稿
与一些原生库(例如用C或C++编写的库)接口时,可能需要编写原生回调以向库提供业务逻辑。一些面向Python的库也可能提供传递ctypes包装的原生回调而不是Python回调的替代方案,以获得更好的性能。一个简单的例子是``scipy.integrate``包,用户在其中将需要积分的函数作为回调传递。
这些库的用户可能希望在编写Python代码的同时,利用运行纯原生代码的性能优势。本提案概述了在Numba中提供这种功能的一个方案。
基本用法
我们建议添加一个新的装饰器,@cfunc
,可以从主包中导入。这个装饰器允许定义回调,如下例所示:
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
@cfunc
装饰器返回一个“C 函数”对象,该对象持有运行给定编译函数所需的资源(例如其 LLVM 模块)。这个对象有几个属性和方法:
ctypes
属性是一个 ctypes 函数对象,表示本地函数。address
属性是本地函数代码的地址,作为一个整数(注意这也可以从ctypes
属性计算得出)。native_name
属性是函数在当前进程中可以被查找的符号。inspect_llvm()
方法返回函数编译后的 LLVM 模块的 IR。预期native_name
属性对应于 LLVM IR 中函数的名称。
装饰器的一般签名是 cfunc(signature, **options)
。
signature
必须使用 Numba 类型指定函数的参数类型和返回类型。与 @jit
相反,返回类型不能省略。
options
是仅限关键字的参数,用于指定编译选项。我们预期标准的 @jit
选项(nopython
、forceobj
、cache
)可以与 @cfunc
一起使用。
从 Numba 编译的函数中调用
虽然其预期用途是将回调地址传递给期望函数指针的外部C代码,但应允许从Numba编译的函数中调用C回调。
传递数组数据
C 或 C++ 使用的原生平台 ABI 没有 Numpy 中形状数组的概念。一个常见的解决方案是传递一个原始数据指针和一到多个大小参数(取决于维度)。Numba 必须提供一种方法,在回调内部重建此数据的数组视图。
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]
carray
函数接受 (pointer, shape, dtype)
参数(dtype
是可选的),并返回一个覆盖数据 pointer 的 C 布局数组视图,具有给定的 shape 和 dtype。pointer 必须是一个 ctypes 指针对象(而不是 Python 整数)。数组的维度对应于 shape 元组的长度。如果未提供 dtype,则数组的 dtype 对应于 pointer 的指针类型。
farray
函数类似,只不过它返回一个 F-布局的数组视图。
错误处理
C 语言中没有标准的错误报告机制。不幸的是,Numba 目前不处理 try..except
块,这使得用户更难实现所需的错误报告方案。当前的提案立场是让用户在必要时防范无效参数,并采取任何必要措施来通知调用者错误。
根据用户反馈,我们可以在之后添加对某些错误报告方案的支持,例如根据是否引发异常返回整数错误代码,或设置 errno
。
延迟主题
预先编译
此提案没有为C回调的AOT编译提供任何规定。这可能需要一个单独的API(在 numba.pycc.CC
对象上的一个新方法),并且实现将需要从编译的C扩展模块中暴露C函数对象功能的一个子集。
不透明数据指针
一些库允许将不透明的数据指针 (void *
) 传递给用户提供的回调函数,以提供执行回调所需的任何上下文。利用此功能需要在 Numba 中添加特定支持,例如能够从 types.voidptr
进行通用转换,以及获取面向 Python 的 jitclass
实例的地址。