簡體   English   中英

如何使 Cython 比 Python(沒有 Numpy)快得多以將兩個數組相加?

[英]How to make Cython much faster than Python (without Numpy) for adding two arrays together?

我想使用 Cython 來減少在使用 Numpy 數組的情況下將兩個數組相加(逐元素)所需的時間。 我發現最快的基本 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 的初學者,所以我可能會誤解一些東西。

最大的瓶頸是將結果指針轉換回數組。

這是一個優化版本:

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

這里有幾件事需要注意 - 我不是 malloc'ing 臨時緩沖區,然后將結果復制到數組,而是創建cython.view.array並將其轉換為int[:] 這給了我指針訪問的原始速度,也避免了不必要的復制。 我也直接返回了 Cython 對象,而不是先將其轉換為 python 對象。 總的來說,與您最初的 Cython 實現相比,這使我的速度提高了 70 倍。

view對象轉換為列表被證明是棘手的:如果您簡單地將 return 語句更改為return list(result_as_array) ,代碼將比您的初始實現10 倍。 但是,如果您像這樣添加額外的包裝層: return list(memoryview(result_as_array))該函數比您的版本快約 5 倍。 因此,主要開銷是從快速的本機對象到通用的 Python 對象,如果您需要快速代碼,則應始終避免這種情況。

為了進行比較,我用 numpy 運行了代碼。 numpy 版本的執行速度與我的 Cython 版本完全一樣。 這意味着 C 編譯器能夠在我的代碼中自動矢量化成對求和循環。

旁注:你需要在malloc()的指針上調用free() ,否則你會泄漏內存。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM