> 原文: [http://docs.cython.org/en/latest/src/userguide/parallelism.html](http://docs.cython.org/en/latest/src/userguide/parallelism.html)
# 使用并行性
Cython 通過 [`cython.parallel`](#module-cython.parallel "cython.parallel") 模塊支持原生并行性。要使用這種并行性,必須釋放 GIL(參見 [釋放 GIL](external_C_code.html#nogil))。它目前支持 OpenMP,但稍后可能會支持更多后端。
注意
由于 OpenMP 限制,此模塊中的功能僅可用于主線程或并行區域。
`cython.parallel.``prange`(_[start,] stop[, step][, nogil=False][, schedule=None[, chunksize=None]][, num_threads=None]_)
此函數可用于并行循環。 OpenMP 自動啟動線程池并根據使用的計劃分配工作。
線程局部性和減少量是自動推斷的變量。
如果分配給 prange 塊中的變量,它將變為 lastprivate,這意味著變量將包含上次迭代的值。如果對變量使用 inplace 運算符,它將變為減少,這意味著變量的線程局部副本的值將隨操作符一起減少,并在循環后分配給原始變量。索引變量始終是 lastprivate。與塊并行分配的變量在塊之后將是私有的并且不可用,因為沒有順序最后值的概念。
<colgroup><col class="field-name"> <col class="field-body"></colgroup>
| 參數: |
* **start** - 指示循環開始的索引(與范圍中的起始參數相同)。
* **stop** - 指示何時停止循環的索引(與范圍中的停止參數相同)。
* **步** - 給出序列步驟的整數(與范圍中的步驟參數相同)。它不能為 0。
* **nogil** - 此功能只能與發布的 GIL 一起使用。如果`nogil`為真,則循環將包裹在 nogil 部分中。
* **schedule** –
`schedule`傳遞給 OpenMP,可以是以下之一:
static:
If a chunksize is provided, iterations are distributed to all threads ahead of time in blocks of the given chunksize. If no chunksize is given, the iteration space is divided into chunks that are approximately equal in size, and at most one chunk is assigned to each thread in advance.
當調度開銷很重要并且可以將問題簡化為已知具有大致相同運行時的相同大小的塊時,這是最合適的。
dynamic:
The iterations are distributed to threads as they request them, with a default chunk size of 1.
當每個塊的運行時間不同并且事先不知道時,這是合適的,因此使用更多數量的較小塊來保持所有線程忙。
guided:
As with dynamic scheduling, the iterations are distributed to threads as they request them, but with decreasing chunk size. The size of each chunk is proportional to the number of unassigned iterations divided by the number of participating threads, decreasing to 1 (or the chunksize if provided).
這比純動態調度有一個優勢,當事實證明最后一個塊比預期花費更多時間或者其他方式被錯誤調度時,所以大多數線程開始運行空閑而最后一個塊只由較少數量的線程處理。
runtime:
The schedule and chunk size are taken from the runtime scheduling variable, which can be set through the `openmp.omp_set_schedule()` function call, or the OMP_SCHEDULE environment variable. Note that this essentially disables any static compile time optimisations of the scheduling code itself and may therefore show a slightly worse performance than when the same scheduling policy is statically configured at compile time. The default schedule is implementation defined. For more information consult the OpenMP specification [[1]](#id2).
* **num_threads** - `num_threads`參數表示團隊應該包含多少個線程。如果沒有給出,OpenMP 將決定使用多少線程。通常,這是計算機上可用的核心數。但是,這可以通過`omp_set_num_threads()`功能或`OMP_NUM_THREADS`環境變量來控制。
* **chunksize** - `chunksize`參數指示用于在線程之間劃分迭代的塊大小。這僅對`static`,`dynamic`和`guided`調度有效,并且是可選的。根據日程安排,它提供的負載平衡,調度開銷和錯誤共享的數量(如果有的話),不同的組塊可以提供顯著不同的性能結果。
|
| --- | --- |
減少的例子:
```py
from cython.parallel import prange
cdef int i
cdef int n = 30
cdef int sum = 0
for i in prange(n, nogil=True):
sum += i
print(sum)
```
帶有類型化內存視圖的示例(例如 NumPy 數組):
```py
from cython.parallel import prange
def func(double[:] x, double alpha):
cdef Py_ssize_t i
for i in prange(x.shape[0]):
x[i] = alpha * x[i]
```
`cython.parallel.``parallel`(_num_threads=None_)
該指令可用作`with`語句的一部分,以并行執行代碼序列。這對于設置 prange 使用的線程局部緩沖區非常有用。包含的 prange 將是一個不平行的工作共享循環,因此在并行部分中分配給的任何變量對于 prange 也是私有的。在并行塊之后,并行塊中的私有變量不可用。
線程局部緩沖區的示例:
```py
from cython.parallel import parallel, prange
from libc.stdlib cimport abort, malloc, free
cdef Py_ssize_t idx, i, n = 100
cdef int * local_buf
cdef size_t size = 10
with nogil, parallel():
local_buf = <int *> malloc(sizeof(int) * size)
if local_buf is NULL:
abort()
# populate our local buffer in a sequential loop
for i in xrange(size):
local_buf[i] = i * 2
# share the work using the thread-local buffer(s)
for i in prange(n, schedule='guided'):
func(local_buf)
free(local_buf)
```
稍后可能會在并行塊中支持部分,以在線程之間分配工作的代碼部分。
`cython.parallel.``threadid`()
返回線程的 id。對于 n 個線程,id 的范圍從 0 到 n-1。
## 編譯
要實際使用 OpenMP 支持,您需要告訴 C 或 C ++編譯器啟用 OpenMP。對于 gcc,這可以在 setup.py 中完成如下:
```py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
ext_modules = [
Extension(
"hello",
["hello.pyx"],
extra_compile_args=['-fopenmp'],
extra_link_args=['-fopenmp'],
)
]
setup(
name='hello-parallel-world',
ext_modules=cythonize(ext_modules),
)
```
對于 Microsoft Visual C ++編譯器,請使用`'/openmp'`而不是`'-fopenmp'`。
## 打破循環
并行和 prange 塊支持語句中斷,繼續并以 nogil 模式返回。此外,在這些塊中使用`with gil`塊是有效的,并且具有從它們傳播的異常。但是,因為塊使用 OpenMP,所以不能只留下它們,因此現有的過程是最好的努力。對于 prange(),這意味著在任何線程中的任何后續迭代的第一次中斷,返回或異常之后跳過循環體。如果可能返回多個不同的值,則返回哪個值是未定義的,因為迭代沒有特定的順序:
```py
from cython.parallel import prange
cdef int func(Py_ssize_t n):
cdef Py_ssize_t i
for i in prange(n, nogil=True):
if i == 8:
with gil:
raise Exception()
elif i == 4:
break
elif i == 2:
return i
```
在上面的例子中,未定義是否應該引發異常,它是否會簡單地中斷或者它是否會返回 2。
## 使用 OpenMP 函數
可以通過 cimporting `openmp`使用 OpenMP 函數:
```py
# tag: openmp
# You can ignore the previous line.
# It's for internal testing of the Cython documentation.
from cython.parallel cimport parallel
cimport openmp
cdef int num_threads
openmp.omp_set_dynamic(1)
with nogil, parallel():
num_threads = openmp.omp_get_num_threads()
# ...
```
參考
<colgroup><col class="label"><col></colgroup>
| [[1]](#id1) | [https://www.openmp.org/mp-documents/spec30.pdf](https://www.openmp.org/mp-documents/spec30.pdf) |
- Cython 3.0 中文文檔
- 入門
- Cython - 概述
- 安裝 Cython
- 構建 Cython 代碼
- 通過靜態類型更快的代碼
- Tutorials
- 基礎教程
- 調用 C 函數
- 使用 C 庫
- 擴展類型(又名.cdef 類)
- pxd 文件
- Caveats
- Profiling
- Unicode 和傳遞字符串
- 內存分配
- 純 Python 模式
- 使用 NumPy
- 使用 Python 數組
- 進一步閱讀
- 相關工作
- 附錄:在 Windows 上安裝 MinGW
- 用戶指南
- 語言基礎
- 擴展類型
- 擴展類型的特殊方法
- 在 Cython 模塊之間共享聲明
- 與外部 C 代碼連接
- 源文件和編譯
- 早期綁定速度
- 在 Cython 中使用 C ++
- 融合類型(模板)
- 將 Cython 代碼移植到 PyPy
- Limitations
- Cython 和 Pyrex 之間的區別
- 鍵入的內存視圖
- 實現緩沖協議
- 使用并行性
- 調試你的 Cython 程序
- 用于 NumPy 用戶的 Cython
- Pythran 作為 Numpy 后端