performance - python Numpy vs Cython速度

  显示原文与译文双语对照的内容
150 3

我有一個分析代碼,它使用numpy做一些繁重的運算。 只是為了好奇心,試圖用cython編譯它,然後我用for重新編寫了它。

令我驚訝的是,基於循環的代碼比( 8x ) 快得多。 我無法發布完整的代碼,但我將一個非常簡單的相關計算組合起來,顯示類似的行為( 雖然時間差異不是那麼大):

版本 1 ( 沒有 cython )


import numpy as np



def _process(array):



 rows = array.shape[0]


 cols = array.shape[1]



 out = np.zeros((rows, cols))



 for row in range(0, rows):


 out[row, :] = np.sum(array - array[row, :], axis=0)



 return out



def main():


 data = np.load('data.npy')


 out = _process(data)


 np.save('vianumpy.npy', out)



版本 2 ( 使用cython構建 MODULE )


import cython


cimport cython



import numpy as np


cimport numpy as np



DTYPE = np.float64


ctypedef np.float64_t DTYPE_t



@cython.boundscheck(False)


@cython.wraparound(False)


@cython.nonecheck(False)


cdef _process(np.ndarray[DTYPE_t, ndim=2] array):



 cdef unsigned int rows = array.shape[0]


 cdef unsigned int cols = array.shape[1]


 cdef unsigned int row


 cdef np.ndarray[DTYPE_t, ndim=2] out = np.zeros((rows, cols))



 for row in range(0, rows):


 out[row, :] = np.sum(array - array[row, :], axis=0)



 return out



def main():


 cdef np.ndarray[DTYPE_t, ndim=2] data


 cdef np.ndarray[DTYPE_t, ndim=2] out


 data = np.load('data.npy')


 out = _process(data)


 np.save('viacynpy.npy', out)



版本 3 ( 使用cython構建 MODULE )


import cython


cimport cython



import numpy as np


cimport numpy as np



DTYPE = np.float64


ctypedef np.float64_t DTYPE_t



@cython.boundscheck(False)


@cython.wraparound(False)


@cython.nonecheck(False)


cdef _process(np.ndarray[DTYPE_t, ndim=2] array):



 cdef unsigned int rows = array.shape[0]


 cdef unsigned int cols = array.shape[1]


 cdef unsigned int row


 cdef np.ndarray[DTYPE_t, ndim=2] out = np.zeros((rows, cols))



 for row in range(0, rows):


 for col in range(0, cols):


 for row2 in range(0, rows):


 out[row, col] += array[row2, col] - array[row, col]



 return out



def main():


 cdef np.ndarray[DTYPE_t, ndim=2] data


 cdef np.ndarray[DTYPE_t, ndim=2] out


 data = np.load('data.npy')


 out = _process(data)


 np.save('vialoop.npy', out)



在 data.npy, 中保存了 10000個x10矩陣,時間是:


$ python -m timeit -c"from version1 import main;main()"


10 loops, best of 3: 4.56 sec per loop



$ python -m timeit -c"from version2 import main;main()"


10 loops, best of 3: 4.57 sec per loop



$ python -m timeit -c"from version3 import main;main()"


10 loops, best of 3: 2.96 sec per loop



是期望還是有我缺少的優化? 版本 1和 2給出的結果是不一樣的,但為什麼版本 3更快?

Ps.- 這不是我需要做的計算,只是一個簡單的例子,顯示了同樣的事情。

时间: 原作者:

142 5

正如它的他答案中提到的,版本 2實際上與版本 1相同,因為cython無法挖掘 array 訪問操作符。 這裡有 2個原因

  • 首先,對numpy函數的每次調用都有一定的開銷,與優化的C 代碼相比。 不過,如果每個操作處理大型數組,那麼這種開銷將變得

  • 第二,中間數組的創建。 如果你考慮更複雜的操作,這樣更清楚,如 out[row, :] = A[row, :] + B[row, :]*C[row, :] 在這種情況下,必須在內存中創建整個 array B*C,然後添加到 A 。 這意味著CPU緩存是 thrashed,因為數據正從內存中讀取和寫入內存,而不是保存在CPU中,直接使用。 如果處理大型數組,這個問題會變得更嚴重。

特別是,你的實際代碼比示例更加複雜,它顯示了一個更加加速的原因。

另外,如果計算足夠簡單,可以通過使用 numexpr 克服這種影響,儘管在許多情況下,cython是有用的,因此可以更好地解決問題。

原作者:
...