[如何使Cython与Python(没有Numpy)相比,将两个数组加在一起要快得多?

问题描述 投票:1回答:1

我想用Cython减少使用Numpy数组将两个数组加在一起(按元素的方式)without所花费的时间。我发现最快的基本Python方法是使用列表理解,如下所示:

def add_arrays(a,b):
    return [m + n for m,n in zip(a,b)]

我的Cython方法稍微复杂一点,看起来如下:

from array import array
from libc.stdlib cimport malloc
from cython cimport boundscheck,wraparound

@boundscheck(False)
@wraparound(False)
cpdef add_arrays_Cython(int[:] Aarr, int[:] Barr):
    cdef size_t i, I
    I = Aarr.shape[0]
    cdef int *Carr = <int *> malloc(640000 * sizeof(int))
    for i in range(I):
        Carr[i] = Aarr[i]+Barr[i]
    result_as_array  = array('i',[e for e in Carr[:640000]])
    return result_as_array

请注意,我使用@boundscheck(False)@wraparound(False)使速度更快。另外,我担心一个非常大的数组(大小为640000),发现如果我仅使用cdef int Carr[640000],它就会崩溃,所以我使用了malloc(),这解决了该问题。最后,我将数据结构返回为整数类型的Python数组。

为了分析代码,我运行了以下代码:

a = array.array('i', range(640000)) #create integer array
b = a[:] #array to add

T=time.clock()
for i in range(20): add_arrays(a,b) #Python list comprehension approach
print(time.clock() - T)

> 6.33秒

T=time.clock()
for i in range(20): add_arrays_Cython(a,b) #Cython approach
print(time.clock() - T)

> 4.54秒

显然,基于Cython的方法可将速度提高约30%。我希望速度会接近一个数量级甚至更高(就像Numpy一样)。

我该怎么做才能进一步加快Cython代码的速度?我的代码中是否有明显的瓶颈?我是Cython的初学者,所以我可能会误会某些东西。

python arrays cython
1个回答
0
投票

最大的瓶颈是将结果指针转换回数组。

这里是优化版本:

from cython cimport boundscheck,wraparound
from cython cimport view

@boundscheck(False)
@wraparound(False)
cpdef add_arrays_Cython(int[:] Aarr, int[:] Barr):
    cdef size_t i, I
    I = Aarr.shape[0]
    result_as_array = view.array(shape=(I,), itemsize=sizeof(int), format='i')
    cdef int[:] Carr = result_as_array
    for i in range(I):
        Carr[i] = Aarr[i]+Barr[i]
    return result_as_array

这里没有什么要注意的-我创建了cython.view.array并将其转换为int[:],而不是malloc分配临时缓冲区,然后将结果复制到数组。这为我提供了指针访问的原始速度,还避免了不必要的复制。我还直接返回了Cython对象,而无需先将其转换为python对象。与原始的Cython实施相比,总的来说,这使我的速度提高了70倍。

view对象转换为列表被证明是棘手的:如果仅将return语句更改为return list(result_as_array),则代码将比初始实现大10倍slower。但是,如果您像这样添加额外的包装层:return list(memoryview(result_as_array)),该功能比您的版本快5倍。因此,再次,主要的开销是从快速的本地对象到通用的python对象,如果需要快速的代码,应始终避免这种情况。

旁注:您需要在free()的指针上调用malloc(),否则会泄漏内存。

© www.soinside.com 2019 - 2024. All rights reserved.