Итерация по списку параллельно с Cython

Как выполняется параллельная итерация по списку (Python) в Cython?

рассмотрим следующую простую функцию:

def sumList():
    cdef int n = 1000
    cdef int sum = 0

    ls = [i for i in range(n)]

    cdef Py_ssize_t i
    for i in prange(n, nogil=True):
        sum += ls[i]

    return sum

это дает много ошибок компилятора, потому что параллельный раздел без GIL, по-видимому, не может работать с любым объектом Python:

Error compiling Cython file:
------------------------------------------------------------
...

    ls = [i for i in range(n)]

    cdef Py_ssize_t i
    for i in prange(n, nogil=True):
        sum += ls[i]
     ^
------------------------------------------------------------

src/parallel.pyx:42:6: Coercion from Python not allowed without the GIL

Error compiling Cython file:
------------------------------------------------------------
...

    ls = [i for i in range(n)]

    cdef Py_ssize_t i
    for i in prange(n, nogil=True):
        sum += ls[i]
     ^
------------------------------------------------------------

src/parallel.pyx:42:6: Operation not allowed without gil

Error compiling Cython file:
------------------------------------------------------------
...

    ls = [i for i in range(n)]

    cdef Py_ssize_t i
    for i in prange(n, nogil=True):
        sum += ls[i]
     ^
------------------------------------------------------------

src/parallel.pyx:42:6: Converting to Python object not allowed without gil

Error compiling Cython file:
------------------------------------------------------------
...

    ls = [i for i in range(n)]

    cdef Py_ssize_t i
    for i in prange(n, nogil=True):
        sum += ls[i]
          ^
------------------------------------------------------------

src/parallel.pyx:42:11: Indexing Python object not allowed without gil

2 ответов


Я не знаю, как это сделать. Список-это объект Python, поэтому используйте его __getitem__ метод требует GIL. Если вы можете использовать массив NumPy в этом случае, он будет работать. Например, если вы хотите выполнить итерацию по массиву A значений с плавающей запятой двойной точности вы можете сделать что-то вроде этого:

cimport cython
from numpy cimport ndarray as ar
from cython.parallel import prange
@cython.boundscheck(False)
@cython.wraparound(False)
cpdef cysumpar(ar[double] A):
    cdef double tot=0.
    cdef int i, n=A.size
    for i in prange(n, nogil=True):
        tot += A[i]
    return tot

на моей машине для этого конкретного случая prange не делает его быстрее обычного цикла, но в других случаях он может работать лучше. Для большего как использовать prange см. документацию по адресуhttp://docs.cython.org/src/userguide/parallelism.html

можно ли использовать массив или нет, зависит от того, насколько вы изменяете размер массива. Если вам нужно много гибкости с размером, массив не будет работать. Вы также можете попробовать взаимодействовать с vector класс на C++. Я никогда не делал этого сам, но здесь есть краткое описание того, как это сделать: http://docs.cython.org/src/userguide/wrapping_CPlusPlus.html#nested-class-declarations


преобразуйте свой список в массив, если вам нужно какое-либо числовое значение, или bytearray, если значения ограничены от 0 до 255. Если вы храните что-либо кроме числовых значений, попробуйте numpy или используйте dtypes напрямую. Например, с байтами:

cdef int[::1] gen = array.array('i',[1, 2, 3, 4])

и если вы хотите использовать C типа:

ctypedef unsigned char uint8_t