# 1.8。使用`@cfunc` 创建 C 回调
> 原文: [http://numba.pydata.org/numba-doc/latest/user/cfunc.html](http://numba.pydata.org/numba-doc/latest/user/cfunc.html)
与某些本机库(例如,用 C 或 C ++编写)连接可能需要编写本机回调以向库提供业务逻辑。 [`numba.cfunc()`](../reference/jit-compilation.html#numba.cfunc "numba.cfunc") 装饰器使用您选择的签名创建可从外部 C 代码调用的编译函数。
## 1.8.1。基本用法
`@cfunc`装饰器与`@jit`具有相似的用法,但有一个重要区别:传递单个签名是强制性的。它确定 C 回调的可见签名:
```py
from numba import cfunc
@cfunc("float64(float64, float64)")
def add(x, y):
return x + y
```
C 函数对象将已编译的 C 回调的地址公开为 [`address`](../reference/jit-compilation.html#CFunc.address "CFunc.address") 属性,以便您可以将其传递给任何外部 C 或 C ++库。它还暴露了指向该回调的 [`ctypes`](https://docs.python.org/3/library/ctypes.html#module-ctypes "(in Python v3.7)") 回调对象;该对象也可以从 Python 调用,从而可以轻松检查已编译的代码:
```py
@cfunc("float64(float64, float64)")
def add(x, y):
return x + y
print(add.ctypes(4.0, 5.0)) # prints "9.0"
```
## 1.8.2。示例
在这个例子中,我们将使用`scipy.integrate.quad`函数。该函数接受常规 Python 回调或包含在 [`ctypes`](https://docs.python.org/3/library/ctypes.html#module-ctypes "(in Python v3.7)") 回调对象中的 C 回调。
让我们定义一个纯 Python 的 integrand 并将其编译为 C 回调:
```py
>>> import numpy as np
>>> from numba import cfunc
>>> def integrand(t):
return np.exp(-t) / t**2
...:
>>> nb_integrand = cfunc("float64(float64)")(integrand)
```
我们可以将`nb_integrand`对象的 [`ctypes`](https://docs.python.org/3/library/ctypes.html#module-ctypes "(in Python v3.7)") 回调传递给`scipy.integrate.quad`,并检查结果是否与纯 Python 函数相同:
```py
>>> import scipy.integrate as si
>>> def do_integrate(func):
"""
Integrate the given function from 1.0 to +inf.
"""
return si.quad(func, 1, np.inf)
...:
>>> do_integrate(integrand)
(0.14849550677592208, 3.8736750296130505e-10)
>>> do_integrate(nb_integrand.ctypes)
(0.14849550677592208, 3.8736750296130505e-10)
```
使用已编译的回调,集成函数在每次评估被积函数时都不会调用 Python 解释器。在我们的例子中,集成速度提高了 18 倍:
```py
>>> %timeit do_integrate(integrand)
1000 loops, best of 3: 242 µs per loop
>>> %timeit do_integrate(nb_integrand.ctypes)
100000 loops, best of 3: 13.5 µs per loop
```
## 1.8.3。处理指针和数组内存
C 回调的一个不太重要的用例涉及对调用者传递的某些数据数组进行操作。由于 C 没有类似于 Numpy 数组的高级抽象,C 回调的签名将传递低级指针和大小参数。然而,回调的 Python 代码将期望利用 Numpy 数组的强大功能和表现力。
在下面的示例中,C 回调预计将在 2-d 数组上运行,签名为`void(double *input, double *output, int m, int n)`。你可以这样实现这样的回调:
```py
from numba import cfunc, types, carray
c_sig = types.void(types.CPointer(types.double),
types.CPointer(types.double),
types.intc, types.intc)
@cfunc(c_sig)
def my_callback(in_, out, m, n):
in_array = carray(in_, (m, n))
out_array = carray(out, (m, n))
for i in range(m):
for j in range(n):
out_array[i, j] = 2 * in_array[i, j]
```
[`numba.carray()`](../reference/utils.html#numba.carray "numba.carray") 函数将数据指针和形状作为输入,并返回给定形状的数组视图。假设数据按 C 顺序排列。如果数据以 Fortran 顺序排列,则应使用 [`numba.farray()`](../reference/utils.html#numba.farray "numba.farray") 。
## 1.8.4。处理 C 结构
### 1.8.4.1。用 CFFI
对于具有大量状态的应用程序,在 C 结构中传递数据很有用。为了简化与 C 代码的互操作性,numba 可以使用`numba.cffi_support.map_type`将`cffi`类型转换为 numba `Record`类型:
```py
from numba import cffi_support
nbtype = cffi_support.map_type(cffi_type, use_record_dtype=True)
```
注意
**use_record_dtype = True** 是必需的,否则指向 C 结构的指针将作为 void 指针返回。
例如:
```py
from cffi import FFI
src = """
/* Define the C struct */
typedef struct my_struct {
int i1;
float f2;
double d3;
float af4[7]; // arrays are supported
} my_struct;
/* Define a callback function */
typedef double (*my_func)(my_struct*, size_t);
"""
ffi = FFI()
ffi.cdef(src)
# Get the function signature from *my_func*
sig = cffi_support.map_type(ffi.typeof('my_func'), use_record_dtype=True)
# Make the cfunc
from numba import cfunc, carray
@cfunc(sig)
def foo(ptr, n):
base = carray(ptr, n) # view pointer as an array of my_struct
tmp = 0
for i in range(n):
tmp += base[i].i1 * base[i].f2 / base[i].d3
tmp += base[i].af4.sum() # nested arrays are like normal numpy array
return tmp
```
### 1.8.4.2。用`numba.types.Record.make_c_struct`
可以手动创建`numba.types.Record`类型以遵循 C 结构的布局。为此,请使用`Record.make_c_struct`,例如:
```py
my_struct = types.Record.make_c_struct([
# Provides a sequence of 2-tuples i.e. (name:str, type:Type)
('i1', types.int32),
('f2', types.float32),
('d3', types.float64),
('af4', types.NestedArray(dtype=types.float32, shape=(7,))),
])
```
由于 ABI 限制,应使用`types.CPointer(my_struct)`作为参数类型将结构作为指针传递。在`cfunc`体内,可以使用`carray`访问`my_struct*`。
### 1.8.4.3。完整示例
请参阅`examples/notebooks/Accessing C Struct Data.ipynb`中的完整示例。
## 1.8.5。签名规范
显式`@cfunc`签名可以使用任何 [Numba 类型](../reference/types.html#numba-types),但只有它们的一个子集对 C 回调有意义。您通常应将自己限制为标量类型(例如`int8`或`float64`),指向它们的指针(例如`types.CPointer(types.int8)`)或指向`Record`类型的指针。
## 1.8.6。编译选项
可以将许多仅关键字参数传递给`@cfunc`装饰器:`nopython`和`cache`。它们的含义类似于`@jit`装饰器中的含义。
- 1. 用户手册
- 1.1。 Numba 的约 5 分钟指南
- 1.2。概述
- 1.3。安装
- 1.4。使用@jit 编译 Python 代码
- 1.5。使用@generated_jit 进行灵活的专业化
- 1.6。创建 Numpy 通用函数
- 1.7。用@jitclass 编译 python 类
- 1.8。使用@cfunc 创建 C 回调
- 1.9。提前编译代码
- 1.10。使用@jit 自动并行化
- 1.11。使用@stencil装饰器
- 1.12。从 JIT 代码 中回调到 Python 解释器
- 1.13。性能提示
- 1.14。线程层
- 1.15。故障排除和提示
- 1.16。常见问题
- 1.17。示例
- 1.18。会谈和教程
- 2. 参考手册
- 2.1。类型和签名
- 2.2。即时编译
- 2.3。提前编译
- 2.4。公用事业
- 2.5。环境变量
- 2.6。支持的 Python 功能
- 2.7。支持的 NumPy 功能
- 2.8。与 Python 语义的偏差
- 2.9。浮点陷阱
- 2.10。 Python 2.7 寿命终止计划
- 3. 用于 CUDA GPU 的 Numba
- 3.1。概述
- 3.2。编写 CUDA 内核
- 3.3。内存管理
- 3.4。编写设备功能
- 3.5。 CUDA Python 中支持的 Python 功能
- 3.6。支持的原子操作
- 3.7。随机数生成
- 3.8。设备管理
- 3.10。示例
- 3.11。使用 CUDA 模拟器 调试 CUDA Python
- 3.12。 GPU 减少
- 3.13。 CUDA Ufuncs 和广义 Ufuncs
- 3.14。共享 CUDA 内存
- 3.15。 CUDA 阵列接口
- 3.16。 CUDA 常见问题
- 4. CUDA Python 参考
- 4.1。 CUDA 主机 API
- 4.2。 CUDA 内核 API
- 4.3。内存管理
- 5. 用于 AMD ROC GPU 的 Numba
- 5.1。概述
- 5.2。编写 HSA 内核
- 5.3。内存管理
- 5.4。编写设备功能
- 5.5。支持的原子操作
- 5.6。代理商
- 5.7。 ROC Ufuncs 和广义 Ufuncs
- 5.8。示例
- 6. 扩展 Numba
- 6.1。高级扩展 API
- 6.2。低级扩展 API
- 6.3。示例:间隔类型
- 7. 开发者手册
- 7.1。贡献给 Numba
- 7.2。 Numba 建筑
- 7.3。多态调度
- 7.4。关于发电机的注意事项
- 7.5。关于 Numba Runtime 的注意事项
- 7.6。使用 Numba Rewrite Pass 获得乐趣和优化
- 7.7。实时变量分析
- 7.8。上市
- 7.9。模板注释
- 7.10。关于自定义管道的注意事项
- 7.11。环境对象
- 7.12。哈希 的注意事项
- 7.13。 Numba 项目路线图
- 8. Numba 增强建议
- 9. 术语表