我需要实现一个函数来对具有可变节长度的数组元素求和。 所以,
a = np.arange(10)
section_lengths = np.array([3, 2, 4])
out = accumulate(a, section_lengths)
print out
array([ 3., 7., 35.])
我在cython
尝试了一个实现:
https://gist.github.com/2784725
表现我在与section_lengths完全相同的情况下与纯numpy
解决方案进行比较:
LEN = 10000
b = np.ones(LEN, dtype=np.int) * 2000
a = np.arange(np.sum(b), dtype=np.double)
out = np.zeros(LEN, dtype=np.double)
%timeit np.sum(a.reshape(-1,2000), axis=1)
10 loops, best of 3: 25.1 ms per loop
%timeit accumulate.accumulate(a, b, out)
10 loops, best of 3: 64.6 ms per loop
你对改善表现有什么建议吗?
答案 0 :(得分:2)
您可以尝试以下某些操作:
除了@cython.boundscheck(False)
编译器指令外,还可以尝试添加@cython.wraparound(False)
在setup.py
脚本中,尝试添加一些优化标记:
ext_modules = [Extension("accumulate", ["accumulate.pyx"], extra_compile_args=["-O3",])]
查看cython -a accumulate.pyx
生成的.html文件,看看是否有部分缺少静态类型或严重依赖Python C-API调用:
http://docs.cython.org/src/quickstart/cythonize.html#determining-where-to-add-types
在方法的末尾添加return
语句。目前,它正在i_el += 1
的紧密循环中进行一系列不必要的错误检查。
不确定它是否有所作为,但我倾向于制作循环计数器cdef unsigned int
,而不仅仅是int
当section_lengths
不相等时,您也可以将代码与numpy进行比较,因为它可能需要的不仅仅是一个简单的sum
。
答案 1 :(得分:1)
在for循环更新out[i_bas]
中,您可以创建一个临时变量来执行准确,并在nest for循环完成时更新out[i_bas]
。以下代码将与numpy版本一样快:
import numpy as np
cimport numpy as np
ctypedef np.int_t DTYPE_int_t
ctypedef np.double_t DTYPE_double_t
cimport cython
@cython.boundscheck(False)
@cython.wraparound(False)
def accumulate(
np.ndarray[DTYPE_double_t, ndim=1] a not None,
np.ndarray[DTYPE_int_t, ndim=1] section_lengths not None,
np.ndarray[DTYPE_double_t, ndim=1] out not None,
):
cdef int i_el, i_bas, sec_length, lenout
cdef double tmp
lenout = out.shape[0]
i_el = 0
for i_bas in range(lenout):
tmp = 0
for sec_length in range(section_lengths[i_bas]):
tmp += a[i_el]
i_el+=1
out[i_bas] = tmp