如何使Cython在将两个数组添加到一起时比Python快得多(没有Numpy)?

如何使Cython在将两个数组添加到一起时比Python快得多(没有Numpy)?,python,arrays,cython,Python,Arrays,Cython,我想使用Cython来减少在不使用Numpy数组的情况下将两个数组添加到一起(元素方面)所需的时间。我发现最快的基本Python方法是使用列表理解,如下所示: def add_arrays(a,b): return [m + n for m,n in zip(a,b)] from array import array from libc.stdlib cimport malloc from cython cimport boundscheck,wraparound @boundsch

我想使用Cython来减少在不使用Numpy数组的情况下将两个数组添加到一起(元素方面)所需的时间。我发现最快的基本Python方法是使用列表理解,如下所示:

def add_arrays(a,b):
    return [m + n for m,n in zip(a,b)]
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
我的Cython方法有点复杂,如下所示:

def add_arrays(a,b):
    return [m + n for m,n in zip(a,b)]
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
>6.33秒

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

T=time.clock()
for i in range(20): add_arrays_Cython(a,b) #Cython approach
print(time.clock() - T)
显然,基于Cython的方法的速度提高了约30%。我预计速度会更接近一个数量级甚至更高(就像Numpy一样)

我能做些什么来进一步加速Cython代码?我的代码中有没有明显的瓶颈?
我是Cython的初学者,所以我可能会误解一些东西。

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

以下是一个优化版本:

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'ing一个临时缓冲区,然后将结果复制到一个数组中。这给了我指针访问的原始速度,也避免了不必要的复制。我还直接返回Cython对象,而不首先将其转换为python对象。总的来说,与最初的Cython实现相比,这给了我70倍的速度

视图
对象转换为列表被证明是很棘手的:如果您只需将return语句更改为
return list(result\u as\u array)
,代码将比最初的实现慢10倍左右。但是,如果您像这样添加一层额外的包装:
返回列表(memoryview(result\u as\u array))
该函数大约比您的版本快5倍。同样,主要的开销是从快速本机对象转移到通用python对象,如果您需要快速代码,则应始终避免这种情况

为了进行比较,我使用numpy运行了代码。numpy版本的运行速度与我的Cython版本一样快。这意味着C编译器能够在我的代码中自动向量化成对求和循环


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

如果应该返回列表,则可以使用C-API构造列表,这比创建中间数组要快。@Stefan感谢您的帮助和教学性回答。事实上,我实现了大约5倍的加速!我希望得到更多,但我想这是一个教训,我应该与视图对象一起工作。@ead感谢您的建议。我尝试了int[::1],但速度没有太大变化。我还尝试使用
array.array('I',memoryview(result\u as\u array))
转换为array.array,但这导致了显著的速度减慢。请确保在使用Python
list
array.array
时,说明清楚
numpy
在很大程度上取代了内置的
array
包。我不知道cython的
实现得有多好。要最大限度地提高速度,请考虑将
数组的
缓冲界面与
cython的
类型化内存视图一起使用。