# 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. 術語表