您的代码中有两个问题(使用选项
-a使其可见):
- numpy数组的索引效率不高
- 你忘
int
了cdef sum=0
考虑到这一点,我们得到:
cpdef int f(np.ndarray[np.int_t] f): ##HERE assert f.dtype == np.int cdef int array_length = f.shape[0] cdef int sum = 0 ##HERE cdef int k for k in range(array_length): sum += f[k] return sum
对于循环,以下代码:
int __pyx_t_5;int __pyx_t_6;Py_ssize_t __pyx_t_7;....__pyx_t_5 = __pyx_v_array_length;for (__pyx_t_6 = 0; __pyx_t_6 < __pyx_t_5; __pyx_t_6+=1) { __pyx_v_k = __pyx_t_6; __pyx_t_7 = __pyx_v_k; __pyx_v_sum = (__pyx_v_sum + (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_int_t *, __pyx_pybuffernd_f.rcbuffer->pybuffer.buf, __pyx_t_7, __pyx_pybuffernd_f.diminfo[0].strides)));}
这样做还不错,但是对于优化器而言,却不如人类编写的普通代码那么容易。正如您已经指出的那样,
__pyx_pybuffernd_f.diminfo[0].strides在编译时未知,这会阻止矢量化。
但是,使用类型化的内存视图时,您会得到更好的结果,即:
cpdef int mf(int[::1] f): cdef int array_length = len(f)...
这导致了不透明的C代码-至少是我的编译器可以更好地优化:
__pyx_t_2 = __pyx_v_array_length; for (__pyx_t_3 = 0; __pyx_t_3 < __pyx_t_2; __pyx_t_3+=1) { __pyx_v_k = __pyx_t_3; __pyx_t_4 = __pyx_v_k; __pyx_v_sum = (__pyx_v_sum + (*((int *) ( ((char *) (((int *) __pyx_v_f.data) + __pyx_t_4)) )))); }这里最关键的是,我们要让cython清楚,内存是连续的,即
int[::1]与
int[:]numpy数组所看到的相比,
stride!=1必须考虑可能的情况。
在这种情况下,用Cython生成的C代码导致同一汇编作为代码我会写。正如crisb指出的那样,相加
-march=native会导致向量化,但是在这种情况下,两个函数的汇编器将再次略有不同。
但是,以我的经验,编译器通常会遇到一些问题,无法优化由cython创建的循环,并且/或者容易遗漏一个细节,从而阻止生成真正好的C代码。因此,我处理工作循环的策略是用纯C语言编写它们,并使用cython来包装/访问它们-
通常会更快一些,因为也可以使用专用的编译器标志来捕获此代码而不会影响整个Cython-模块。



