迭代cython中的数组,列表比np.array更快吗?
TLDR:在cython中,为什么(或者什么时候?)遍历一个numpy数组比迭代python列表更快?
一般来说:我之前使用过Cython,并且能够在天真的python impl上获得巨大的提速。然而,搞清楚究竟需要做什么似乎并不重要。
考虑sum()函数的以下3个实现。 他们居住在一个名为'cy'的cython文件中(很显然,这里有np.sum(),但除此之外,这是我的观点。)
朴素蟒蛇:
def sum_naive(A):
s = 0
for a in A:
s += a
return s
具有期望python列表的函数的Cython:
def sum_list(A):
cdef unsigned long s = 0
for a in A:
s += a
return s
Cython的函数需要一个numpy数组。
def sum_np(np.ndarray[np.int64_t, ndim=1] A):
cdef unsigned long s = 0
for a in A:
s += a
return s
我认为,就运行时间而言, sum_np <sum_list <sum_naive ,但是,下面的脚本表明了相反的情况(为了完整性,我添加了np.sum())
N = 1000000
v_np = np.array(range(N))
v_list = range(N)
%timeit cy.sum_naive(v_list)
%timeit cy.sum_naive(v_np)
%timeit cy.sum_list(v_list)
%timeit cy.sum_np(v_np)
%timeit v_np.sum()
结果:
In [18]: %timeit cyMatching.sum_naive(v_list)
100 loops, best of 3: 18.7 ms per loop
In [19]: %timeit cyMatching.sum_naive(v_np)
1 loops, best of 3: 389 ms per loop
In [20]: %timeit cyMatching.sum_list(v_list)
10 loops, best of 3: 82.9 ms per loop
In [21]: %timeit cyMatching.sum_np(v_np)
1 loops, best of 3: 1.14 s per loop
In [22]: %timeit v_np.sum()
1000 loops, best of 3: 659 us per loop
这是怎么回事? 为什么cython + numpy很慢?
PS
我确实使用
#cython:boundscheck = False
#cython:wraparound = False
有一种更好的方法可以在cython中实现,至少在我的机器上击败np.sum
是因为它避免了在处理任意数组时通常需要做的类型检查和其他事情:
#cython.wraparound=False
#cython.boundscheck=False
cimport numpy as np
def sum_np(np.ndarray[np.int64_t, ndim=1] A):
cdef unsigned long s = 0
for a in A:
s += a
return s
def sum_np2(np.int64_t[::1] A):
cdef:
unsigned long s = 0
size_t k
for k in range(A.shape[0]):
s += A[k]
return s
然后是时间:
N = 1000000
v_np = np.array(range(N))
v_list = range(N)
%timeit sum(v_list)
%timeit sum_naive(v_list)
%timeit np.sum(v_np)
%timeit sum_np(v_np)
%timeit sum_np2(v_np)
10 loops, best of 3: 19.5 ms per loop
10 loops, best of 3: 64.9 ms per loop
1000 loops, best of 3: 1.62 ms per loop
1 loops, best of 3: 1.7 s per loop
1000 loops, best of 3: 1.42 ms per loop
您不希望通过Python风格遍历numpy数组,而是使用索引访问元素,因为它可以被转换为纯C,而不是依赖于Python API。
a
是无类型的,因此会有很多从Python到C类型和后面的转换。 这些可能会很慢。
JoshAdel正确地指出,不是迭代,而是迭代一个范围。 Cython会将索引转换为C,这很快。
使用cython -a myfile.pyx
会为你突出显示这些东西; 你希望你所有的循环逻辑都是白色的以获得最大速度。
PS:请注意, np.ndarray[np.int64_t, ndim=1]
已过时,并且已被弃用,以支持更快更普遍的long[:]
。
上一篇: Iterating over arrays in cython, is list faster than np.array?
下一篇: Cython either marginally faster or slower than pure Python