Cython prange与字符串数组

时间:2019-03-15 21:17:05

标签: python parallel-processing cython

我正在尝试使用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)

我可能缺少一些东西,找不到str的替代品。 有没有办法正确地将prange与字符串数组一起使用?

1 个答案:

答案 0 :(得分:0)

除了事实,因为您尝试创建不带gil的Python对象(即str(i))时,您的代码在被cython化后也会失败。

>

为了分析正在发生的事情,让我们看一个非常简单的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构建的(仍然默认),因此我在{{1}中使用了-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 不是Unicode对象的集合。 x的一个元素由5个unicode字符组成,并且这些元素连续存储在内存中。重要的是:与存储在不同内存布局中的Unicode对象中的信息相同。

这是numpy的怪癖之一,以及x的工作方式:列表中的每个元素都将转换为unicode对象,然后计算该元素的最大大小并加上dtype(在这种情况下,{{1} })进行计算和使用。

np.array在cython代码(<U5)中的解释不同(两次!)

第一个区别:您的Python3代码np.str用于ar[np.str] x,但是在您的cython代码(被np.str进行了cythonized的情况下,unicode则用于{{1 }}(请参阅doc)。

第二个区别:看到language_level=2,Cython会将其解释为带有Python对象的数组(也许应该视为Cython-bug)-几乎与np.str是{ {1}}-实际上与bytes的唯一区别是错误消息略有不同。

使用此信息,我们可以了解错误消息。在运行时,将检查输入数组(在执行函数的第一行之前!):

  1. 期望的是一个带有python对象的数组,即8个字节的指针,即元素大小为8个字节的数组
  2. 接收到一个元素大小为5 * 4 = 20字节的数组(一个Unicode字符为4字节)

因此无法进行强制转换,并且抛出观察到的异常。

您无法更改np.str-numpy-array 中元素的大小:

现在让我们看一下以下内容:

dtype

该元素没有改变,因为字符串np.object在写回到np.object数组时被截断了:只有5个字符的位置!尽管可以使用<U..(在某种程度上,只要结果字符串不超过5个字符)就可以了:

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

这一切都会离开你吗?

  • 您可能想使用x[0]+str(0)而不是x(即"pear"
  • 如果您不知道编译类型下的numpy数组的元素大小,则应在签名中将输入数组>>> x[1] = x[1]+str(1) >>> x[1] 'pear0' 声明为bytes并推出运行时检查,与Cython的"depricated" numpy-tutorial中的操作类似。
  • 如果应就地进行更改,则输入数组中的元素应足够大以容纳生成的字符串。

以上所有与unicodes无关。要使用dtype=np.bytes_,您不能使用x,因为它可以在python对象上运行。