使用cython添加数组比numpy慢



我刚刚开始学习python,所以请原谅我的无知。cython可以改进numpy简单地将两个数组加在一起吗?我非常糟糕的尝试添加两个数组a + b来给出一个新的数组c:

import numpy as np
cimport numpy as np
DTYPE = np.int
ctypedef np.int_t DTYPE_t
def add_arrays(np.ndarray[DTYPE_t, ndim=2] a, np.ndarray[DTYPE_t, ndim=2] b, np.ndarray[DTYPE_t, ndim=2] c):
    cdef int x = a.shape[0]
    cdef int y = a.shape[1]
    cdef int val_a
    cdef int val_b
    for j in range(x):
        for k in range(y):
            val_a = a[j][k]
            val_b = b[j][k]
            c[j][k] = val_a + val_b    
    return c

然而,当传递这些数组时,这个版本比numpy慢700倍(*edit:比numpy慢):

n = 1000 
a = np.ones((n, n), dtype=np.int)
b = np.ones((n, n), dtype=np.int)
c = np.zeros((n, n), dtype=np.int)

我显然错过了一些非常大的东西。

问题是您正在索引像c[j][k]这样的二维数组,而实际上您应该做c[j,k],否则Cython正在使用buf=c[j]的中间缓冲区,它将采取buf[k],导致速度减慢。你应该使用这个适当的索引加上@XavierCombelle指定的cdef声明。

你可以检查这个中间缓冲区是否导致了慢速:

np.ndarray[DTYPE_t, ndim=1] buf

,然后在循环内:

buf = c[j]
buf[k] = val_a + val_b

这个声明的缓冲区应该提供与

相同的速度(或接近):
c[j,k] = val_a + val_b

我想你错过了

cdef int j
cdef int k

所以你的变量循环是python对象而不是c对象

下面是两个例子:

The numpy way

%%timeit
table1 = np.ones((10,10))
table2 = np.ones((10,10))
result = np.zeros((10,10))
table1 + table2 
100000 loops, best of 3: 14.5 µs per loop

循环遍历索引方式

%%timeit
def add_arrays(ar1, ar2):
    for j in range(len(ar1)):
        for k in range(len(ar2)):
            val_a = ar1[j][k]
            val_b = ar2[j][k]
            result[j][k] = val_a + val_b    
    return result
add_arrays(table1, table2)
1000 loops, best of 3: 307 µs per loop

同样,快了20倍。

我知道我没有完全回答你的问题,但也许这能给你一个更好的比较视角?

[edit]对于1000x1000个表,时差更明显;我怀疑这是由于构建表的开销的摊销。

former code: 100 loops, best of 3: 13.1 ms per loop
latter code: 1 loops, best of 3: 2.78 s per loop

是200倍

最新更新