简体   繁体   中英

Cython iterate over list of numpy arrays without the gil

I would like to iterate over a list of numpy arrays with different dimensions, and pass them in to a cython function that does not require the GIL:

# a has T1 rows and M columns
a = np.array([[0.0, 0.1, 0.3, 0.7],
              [0.1, 0.2, 0.1, 0.6],
              [0.1, 0.2, 0.1, 0.6]])

# b has T2 rows and M columns
b = np.array([[1.0, 0.0, 0.0, 0.0],
              [0.1, 0.2, 0.1, 0.6]])

# c has T3 rows and M columns
c = np.array([[0.1, 0.0, 0.3, 0.6],
              [0.5, 0.2, 0.3, 0.0],
              [0.0, 1.0, 0.0, 0.0],
              [0.0, 0.0, 0.1, 0.0]])

array_list = [a, b, c]
N = len(array_list)

# this function works
@cython.boundscheck(False)
@cython.wraparound(False)
cdef void function_not_requiring_the_gil(double[:, ::1] arr) nogil:
    cdef int T = arr.shape[0]
    cdef int M = arr.shape[1]
    cdef int i, t

    for t in range(T):
        for i in range(M):
            # do some arbitrary thing to the array in-place
            arr[t, i] += 0.001

# issue is with this function
def function_with_loop_over_arrays(array_list, int N):
    cdef int i

    with nogil:
        for i in range(N):
            function_not_requiring_the_gil(array_list[i])

When I compile the Cython code, I get the following error:

Error compiling Cython file:
------------------------------------------------------------
...
def function_with_loop_over_arrays(array_list, int N):
    cdef int i

    with nogil:
        for i in range(N):
            function_not_requiring_the_gil(array_list[i])                                                    ^
------------------------------------------------------------

my_file.pyx:312:53: Indexing Python object not allowed without gil

Is there another type of data structure that I can use instead of a Python list to store these numpy arrays so that I can iterate over them without the gil? I'm open to suggestions involving malloc C pointers/Cython memoryviews/other types I'm not aware of.

Note that each numpy array has a different number of rows, but the length of the list of arrays is known.

You can pass two arrays of shape (3,) to function_with_loop_over_arrays : one ( array_starts ) which contains pointers to the first element of a , b and c , and one ( arrays_rows ) which contains T1 , T2 and T3 .

Then modify function_not_requiring_the_gil so that it receives a pointer on the first element of the array and its number of rows, and you will be able to call it in function_with_loop_over_arrays with:

for i in range(N):  # N is 3 and should be passed to function_with_loop_over_arrays
    function_not_requiring_the_gil(array_starts[i], array_rows[i])  

As the error message says, you can't index a Python list without the gil, and there aren't really any obvious alternative data-structures that fill the same role. You just need to move the nogil to take the indexing outside it

def function_with_loop_over_arrays(array_list, int N):
    cdef int i
    cdef double[:, ::1] tmp_mview

    for i in range(N):
        tmp_mview = array_list[i]
        with nogil:        
            function_not_requiring_the_gil(tmp_mview)

(Equivalently you can put the indexing inside a with gil: block instead.)

There's a small cost to getting and releasing the gil but provided your function_not_requiring_the_gil does a decent amount of work compared to the indexing it should be insignificant.

nogil features are often easy to control from numba :

import numba
@numba.jit(nogil=True) 
def function_not_requiring_the_gil(arr):
    T,N = arr.shape
    for t in range(T):
        for i in range(N):
        # do some arbitrary thing to the array in-place
        arr[t, i] += 0.001

def function_with_loop_over_arrays(array_list)
        for a in array_list:
            function_not_requiring_the_gil(a)

will give you the same (efficient) result.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM