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 选项(nopythonforceobjcache)可以与 @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 布局数组视图,具有给定的 shapedtypepointer 必须是一个 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 实例的地址。