Cython prange с массивом строк - PullRequest
1 голос
/ 16 марта 2019

Я пытаюсь использовать prange для обработки нескольких строк.Поскольку это невозможно сделать с помощью списка Python, я использую массив Numpy.

С массивом чисел с плавающей запятой эта функция работает:

from cython.parallel import prange
cimport numpy as np
from numpy cimport ndarray as ar

cpdef func_float(ar[np.float64_t,cast=True] x, double alpha):
    cdef int i
    for i in prange(x.shape[0], nogil=True):
        x[i] = alpha * x[i]
    return x

Когда я пытаюсь это сделатьпростой:

cpdef func_string(ar[np.str,cast=True] x):
    cdef int i
    for i in prange(x.shape[0], nogil=True):
        x[i] = x[i] + str(i)
    return x

Я получаю это

>> func_string(x = np.array(["apple","pear"],dtype=np.str))
  File "processing.pyx", line 8, in processing.func_string
    cpdef func_string(ar[np.str,cast=True] x):
ValueError: Item size of buffer (20 bytes) does not match size of 'str object' (8 bytes)

Я, вероятно, что-то упускаю и не могу найти альтернативу ул.Есть ли способ правильно использовать prange с массивом строк?

1 Ответ

0 голосов
/ 16 марта 2019

Помимо того факта, что ваш код должен потерпеть неудачу при цитонизации, потому что вы пытаетесь создать Python-объект (т.е. str(i)) без gil, ваш код не делает то, что, как вы думаете, должен делать.

Чтобы проанализировать, что происходит, давайте взглянем на простую версию Cython:

%%cython -2
cimport numpy as np
from numpy cimport ndarray as ar

cpdef func_string(ar[np.str, cast=True] x):
    print(len(x))

Из вашего сообщения об ошибке можно вычесть, что вы используете Python 3, а расширение Cython построено с (по умолчанию) language_level=2, поэтому я использую -2 в %%cython -магическая клетка.

А теперь:

>>> x = np.array(["apple", "pear"], dtype=np.str)
>>> func_string(x)    
ValueError: Item size of buffer (20 bytes) does not match size of 'str object' (8 bytes)

Что происходит?

x это не то, что вы думаете

Сначала давайте взглянем на x:

>>> x.dtype
<U5

Так что x не является коллекцией юникод-объектов. Элемент x состоит из 5 символов юникода, и эти элементы хранятся в памяти последовательно, один за другим. Что важно: та же информация, что и в Unicode-объектах, хранящаяся в другой структуре памяти.

Это одна из странностей numpy, и как работает np.array: каждый элемент в списке преобразуется в объект Unicode, после чего вычисляется максимальный размер элемента и вычисляется dtype (в данном случае <U5). и использовал.

np.str интерпретируется по-разному в коде Cython (ar[np.str] x) (дважды!)

Первое отличие: в вашем Python3-коде np.str для unicode, но в вашем коде Cython, который цифонизирован с language_level=2, np.str для bytes (см. doc ).

Второе отличие: видя np.str, Cython будет интерпретировать его как массив с объектами Python (возможно, это следует рассматривать как ошибку Cython) - это почти то же самое, как если бы dtype было np.object - на самом деле единственное отличие от np.object - немного отличающиеся сообщения об ошибках.

С помощью этой информации мы можем понять сообщение об ошибке. Во время выполнения проверяется массив ввода (перед выполнением первой строки функции!):

  1. ожидается, что это массив с python-объектами, то есть 8-байтовые указатели, то есть массив с размером элемента 8 байтов
  2. получен массив с размером элемента 5 * 4 = 20 байт (один юникод-символ 4 байта)

Таким образом, приведение не может быть выполнено, и выдается наблюдаемое исключение.

вы не можете изменить размер элемента в <U.. -numpy-array :

Теперь давайте посмотрим на следующее:

>>> x = np.array(["apple", b"pear"], dtype=np.str)
>>> x[0] = x[0]+str(0)
>>> x[0]
'apple'

элемент не изменился, потому что строка x[0]+str(0) была усечена при записи обратно в x -array: есть место только для 5 символов! Это работало бы (до некоторой степени, пока получающаяся строка не больше чем 5 символов) с "pear", хотя:

>>> x[1] = x[1]+str(1)
>>> x[1]
'pear0' 

Куда это все тебя приведет?

  • Вы, вероятно, хотите использовать bytes, а не unicodes (т.е. dtype=np.bytes_)
  • учитывая, что вы не знаете размер элемента вашего numpy-массива в типе компиляции, вы должны объявить входной массив x как ar x в сигнатуре и развернуть проверки во время выполнения, как это было сделано в Cyber's "dericated" numpy-tutorial .
  • если изменения должны быть сделаны на месте, элементы во входном массиве должны быть достаточно большими для результирующих строк.

Все вышеперечисленное не имеет ничего общего с prange. Чтобы использовать prange, вы не можете использовать str(i), потому что он работает с python-объектами.

...