[英]Allocate intermediate multidimensional arrays in Cython without acquiring the GIL
我正在嘗試使用Cython並行化涉及生成中間多維數組的昂貴操作。
以下非常簡化的代碼說明了我正在嘗試做的事情:
import numpy as np
cimport cython
cimport numpy as np
from cython.parallel cimport prange
from libc.stdlib cimport malloc, free
@cython.boundscheck(False)
@cython.wraparound(False)
def embarrasingly_parallel_example(char[:, :] A):
cdef unsigned int m = A.shape[0]
cdef unsigned int n = A.shape[1]
cdef np.ndarray[np.float64_t, ndim = 2] out = np.empty((m, m), np.float64)
cdef unsigned int ii, jj
cdef double[:, :] tmp
for ii in prange(m, nogil=True):
for jj in range(m):
# allocate a temporary array to hold the result of
# expensive_function_1
tmp_carray = <double * > malloc((n ** 2) * sizeof(double))
# a 2D typed memoryview onto tmp_carray
tmp = <double[:n, :n] > tmp_carray
# shove the intermediate result in tmp
expensive_function_1(A[ii, :], A[jj, :], tmp)
# get the final (scalar) output for this ii, jj
out[ii, jj] = expensive_function_2(tmp)
# free the intermediate array
free(tmp_carray)
return out
# some silly examples - the actual operation I'm performing is a lot more
# involved
# ------------------------------------------------------------------------
@cython.boundscheck(False)
@cython.wraparound(False)
cdef void expensive_function_1(char[:] x, char[:] y, double[:, :] tmp):
cdef unsigned int m = tmp.shape[0]
cdef unsigned int n = x.shape[0]
cdef unsigned int ii, jj
for ii in range(m):
for jj in range(m):
tmp[ii, jj] = 0
for kk in range(n):
tmp[ii, jj] += (x[kk] + y[kk]) * (ii - jj)
@cython.boundscheck(False)
@cython.wraparound(False)
cdef double expensive_function_2(double[:, :] tmp):
cdef unsigned int m = tmp.shape[0]
cdef unsigned int ii, jj
cdef double result = 0
for ii in range(m):
for jj in range(m):
result += tmp[ii, jj]
return result
似乎無法編譯的原因至少有兩個:
基於cython -a
的輸出, cython -a
創建類型化的內存視圖:
cdef double[:, :] tmp = <double[:n, :n] > tmp_carray
似乎涉及Python API調用,因此我無法釋放GIL以允許外部循環並行運行。
我給人的印象是,鍵入的內存視圖不是Python對象,因此子進程應該能夠在不先獲取GIL的情況下創建它們。 是這樣嗎
2.即使我將 prange(m, nogil=True)
替換為正常的range(m)
,Cython仍然似乎不喜歡在內部循環中存在cdef
:
Error compiling Cython file:
------------------------------------------------------------
...
# allocate a temporary array to hold the result of
# expensive_function_1
tmp_carray = <double*> malloc((n ** 2) * sizeof(double))
# a 2D typed memoryview onto tmp_carray
cdef double[:, :] tmp = <double[:n, :n]> tmp_carray
^
------------------------------------------------------------
parallel_allocate.pyx:26:17: cdef statement not allowed here
事實證明,第二個問題很容易通過移動解決
cdef double[:, :] tmp
在for
循環之外,僅分配
tmp = <double[:n, :n] > tmp_carray
在循環內。 不過,我仍然不完全理解為什么這樣做是必要的。
現在,如果我嘗試使用prange
遇到以下編譯錯誤:
Error compiling Cython file:
------------------------------------------------------------
...
# allocate a temporary array to hold the result of
# expensive_function_1
tmp_carray = <double*> malloc((n ** 2) * sizeof(double))
# a 2D typed memoryview onto tmp_carray
tmp = <double[:n, :n]> tmp_carray
^
------------------------------------------------------------
parallel_allocate.pyx:28:16: Memoryview slices can only be shared in parallel sections
免責聲明:這里的一切都應與一粒鹽一起服用。 我更是在猜測那個知道。 您當然應該在Cython-User上提問 。 他們總是友好而快速地回答。
我同意Cython的文檔不是很清楚:
內存視圖通常不需要GIL:
cpdef int sum3d(int [:,:,:] arr)nogil:...
特別是,您不需要GIL進行memoryview索引,切片或轉置。 對於復制方法(C和Fortran連續副本),或者當dtype為object並且讀取或寫入object元素時,Memoryviews需要GIL。
我認為這意味着傳遞內存視圖參數或將其用於切片或轉置不需要Python GIL。 但是, 創建一個內存視圖或復制一個內存視圖需要GIL。
支持此功能的另一個參數是Cython函數可能向Python返回一個內存視圖。
from cython.view cimport array as cvarray
import numpy as np
def bla():
narr = np.arange(27, dtype=np.dtype("i")).reshape((3, 3, 3))
cdef int [:, :, :] narr_view = narr
return narr_view
給出:
>>> import hello
>>> hello.bla()
<MemoryView of 'ndarray' at 0x1b03380>
這意味着memoryview是在Python的GC管理的內存中分配的,因此需要創建GIL。 因此, 您無法在nogil部分中創建memoryview
現在,關於錯誤消息的問題
Memoryview切片只能在並行部分中共享
我認為您應該將其閱讀為“您不能擁有線程專用memoryview切片。它必須是線程共享memoryview切片”。
http://docs.cython.org/src/userguide/external_C_code.html#releasing-the-gil
“”
釋放GIL
您可以使用with nogil語句在部分代碼周圍釋放GIL:
with nogil:
<code to be executed with the GIL released> Code in the body of the statement must not manipulate Python objects in any way, and must
在不重新獲取GIL的情況下,不要調用任何可操縱Python對象的東西。 Cython當前不檢查。
“”
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.