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.- 這不是我需要做的計算,只是一個簡單的例子,顯示了同樣的事情。

时间:原作者:0个回答

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是有用的,因此可以更好地解決問題。

原作者:
...