# 1.11。使用`@stencil`裝飾器
> 原文: [http://numba.pydata.org/numba-doc/latest/user/stencil.html](http://numba.pydata.org/numba-doc/latest/user/stencil.html)
模板是一種常見的計算模式,其中數組元素根據稱為模板內核的某種固定模式進行更新。 Numba 提供`@stencil`裝飾器,以便用戶可以輕松指定模板內核,然后 Numba 生成將該內核應用于某個輸入數組所需的循環代碼。因此,模板裝飾器允許更清晰,更簡潔的代碼,并且與[一起,并行 jit 選項](jit.html#parallel-jit-option)通過模板執行的并行化實現更高的性能。
## 1.11.1。基本用法
`@stencil`裝飾器的示例用法:
```py
from numba import stencil
@stencil
def kernel1(a):
return 0.25 * (a[0, 1] + a[1, 0] + a[0, -1] + a[-1, 0])
```
模板內核由看起來像標準 Python 函數定義的內容指定,但是在數組索引方面有不同的語義。模板生成與輸入數組具有相同大小和形狀的輸出數組,盡管取決于內核定義可能具有不同的類型。從概念上講,模板內核對輸出數組中的每個元素運行一次。模板內核的返回值是寫入該特定元素的輸出數組的值。
參數`a`表示應用內核的輸入數組。索引到此數組是針對正在處理的輸出數組的當前元素進行的。例如,如果正在處理元素`(x, y)`,則模板內核中的`a[0, 0]`對應于輸入數組中的`a[x + 0, y + 0]`。類似地,模板內核中的`a[-1, 1]`對應于輸入數組中的`a[x - 1, y + 1]`。
根據指定的內核,內核可能不適用于輸出數組的邊界,因為這可能導致輸入數組被越界訪問。模板裝飾器處理這種情況的方式取決于選擇 [func_or_mode](#stencil-mode) 。默認模式是模板修飾器將輸出數組的邊框元素設置為零。
要在輸入數組上調用模板,請將模板調用為常規函數,并將輸入數組作為參數傳遞。例如,使用上面定義的內核:
```py
>>> import numpy as np
>>> input_arr = np.arange(100).reshape((10, 10))
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
[30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
[50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
[80, 81, 82, 83, 84, 85, 86, 87, 88, 89],
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]])
>>> output_arr = kernel1(input_arr)
array([[ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 11., 12., 13., 14., 15., 16., 17., 18., 0.],
[ 0., 21., 22., 23., 24., 25., 26., 27., 28., 0.],
[ 0., 31., 32., 33., 34., 35., 36., 37., 38., 0.],
[ 0., 41., 42., 43., 44., 45., 46., 47., 48., 0.],
[ 0., 51., 52., 53., 54., 55., 56., 57., 58., 0.],
[ 0., 61., 62., 63., 64., 65., 66., 67., 68., 0.],
[ 0., 71., 72., 73., 74., 75., 76., 77., 78., 0.],
[ 0., 81., 82., 83., 84., 85., 86., 87., 88., 0.],
[ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
>>> input_arr.dtype
dtype('int64')
>>> output_arr.dtype
dtype('float64')
```
請注意,模板裝飾器已確定指定的模板內核的輸出類型為`float64`,因此將輸出數組創建為`float64`,而輸入數組的類型為`int64`。
## 1.11.2。模板參數
模板內核定義可以采用以下規定的任意數量的參數。第一個參數必須是數組。輸出數組的大小和形狀將與第一個參數的大小和形狀相同。其他參數可以是標量或數組。對于數組參數,這些數組必須至少與每個維度中的第一個參數(數組)一樣大。所有此類輸入數組參數的數組索引都是相對的。
## 1.11.3。內核形狀推理和邊界處理
在上面的示例中,在大多數情況下,模板內核中的數組索引將專門使用`Integer`文字。在這種情況下,模板裝飾器能夠分析模板內核以確定其大小。在上面的例子中,模板裝飾器確定內核是`3 x 3`的形狀,因為索引`-1`到`1`用于第一和第二維度。請注意,模板裝飾器也可以正確處理非對稱和非方形模板內核。
根據模板內核的大小,模板裝飾器能夠計算輸出數組中邊框的大小。如果將內核應用于輸入數組的某個元素會導致索引超出范圍,那么該元素屬于輸出數組的邊界。在上面的示例中,在每個維度中訪問點`-1`和`+1`,因此輸出數組在所有維度中具有大小為 1 的邊界。
如果可能,并行模式能夠從簡單表達式推斷內核索引作為常量。例如:
```py
@njit(parallel=True)
def stencil_test(A):
c = 2
B = stencil(
lambda a, c: 0.3 * (a[-c+1] + a[0] + a[c-1]))(A, c)
return B
```
## 1.11.4。模板裝飾器選項
注意
模板裝飾器可以在將來增強,以提供用于邊界處理的附加機制。目前,只實施了一種行為,`"constant"`(詳見下文`func_or_mode`)。
### 1.11.4.1。 `neighborhood`
有時用`Integer`文字專門編寫模板內核可能不方便。例如,假設我們想計算一組時間序列數據的 30 天移動平均值。可以編寫`(a[-29] + a[-28] + ... + a[-1] + a[0]) / 30`,但模板裝飾器使用`neighborhood`選項提供更簡潔的形式:
```py
@stencil(neighborhood = ((-29, 0),))
def kernel2(a):
cumul = 0
for i in range(-29, 1):
cumul += a[i]
return cumul / 30
```
鄰域選項是元組的元組。外元組的長度等于輸入數組的維數。內元組的長度始終為 2,因為外元組的每個元素對應于相應維度中使用的最小和最大索引偏移量。
如果用戶指定了鄰域但內核訪問了指定鄰域之外的元素,則**行為未定義。**
### 1.11.4.2。 `func_or_mode`
可選的`func_or_mode`參數控制如何處理輸出數組的邊框。目前,只有一個受支持的值,`"constant"`。在`constant`模式下,在內核訪問輸入數組有效范圍之外的元素的情況下,不應用模板內核。在這種情況下,輸出數組中的那些元素被賦值為常量值,由`cval`參數指定。
### 1.11.4.3。 `cval`
可選的 cval 參數默認為零,但可以設置為任何所需的值,如果`func_or_mode`參數設置為`constant`,則該值用于輸出數組的邊界。在所有其他模式中忽略 cval 參數。 cval 參數的類型必須與模板內核的返回類型匹配。如果用戶希望輸出數組是從特定類型構造的,那么他們應該確保模板內核返回該類型。
### 1.11.4.4。 `standard_indexing`
默認情況下,模板內核中的所有數組訪問都作為相對索引處理,如上所述。然而,有時將輔助數組(例如權重數組)傳遞給模板內核并使該數組使用標準 Python 索引而不是相對索引可能是有利的。為此,有一個模板裝飾器選項`standard_indexing`,其值是一個字符串的集合,其名稱與模板函數的參數相匹配,這些參數將使用標準 Python 索引而不是相對索引來訪問:
```py
@stencil(standard_indexing=("b",))
def kernel3(a, b):
return a[-1] * b[0] + a[0] + b[1]
```
## 1.11.5。 `StencilFunc`
模板裝飾器返回`StencilFunc`類型的可調用對象。 `StencilFunc`對象包含許多屬性,但用戶可能感興趣的唯一屬性是`neighborhood`屬性。如果將`neighborhood`選項傳遞給模板裝飾器,則提供的鄰域將存儲在此屬性中。否則,在首次執行或編譯時,系統如上所述計算鄰域,然后將計算的鄰域存儲到該屬性中。然后,如果用戶希望驗證所計算的鄰域是正確的,則用戶可以檢查該屬性。
## 1.11.6。模板調用選項
在內部,模板裝飾器將指定的模板內核轉換為常規的 Python 函數。此函數將具有與模板內核定義中指定的參數相同的參數,但也將包含以下可選參數。
### 1.11.6.1。 `out`
可選的`out`參數被添加到 Numba 生成的每個模板函數中。如果指定,`out`參數告訴 Numba 用戶正在提供他們自己的預分配數組以用于模板的輸出。在這種情況下,模板函數不會分配自己的輸出數組。用戶應確保模板內核的返回類型可以安全地轉換為 [Numpy ufunc 強制轉換規則](http://docs.scipy.org/doc/numpy/reference/ufuncs.html#casting-rules)之后的用戶指定輸出數組的元素類型。
示例用法如下所示:
```py
>>> import numpy as np
>>> input_arr = np.arange(100).reshape((10, 10))
>>> output_arr = np.full(input_arr.shape, 0.0)
>>> kernel1(input_arr, out=output_arr)
```
- 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. 術語表