代码中有两个问题(使用选项-a使其可见):numpy数组的索引不是efficient
您在^{中忘记了int
考虑到这一点,我们得到: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
对于循环,以下代码:
^{pr2}$
}
这并不是那么糟糕,但是对于优化器来说并不像人类编写的普通代码那么容易。正如您已经指出的那样,__pyx_pybuffernd_f.diminfo[0].strides在编译时是未知的,这会阻止向量化。在
但是,当使用typed memory views时,您将获得更好的结果,即: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 *) ( /* dim=0 */ ((char *) (((int *) __pyx_v_f.data) + __pyx_t_4)) ))));
}
这里最关键的一点是,我们要让cython明白,内存是连续的,即int[::1]与{}相比是连续的,对于numpy数组,必须考虑一个可能的stride!=1。在
在本例中,cython生成的C代码的结果是same assembleras the code我本应编写的。正如crisb所指出的,添加-march=native将导致向量化,但在这种情况下,这两个函数的汇编器将再次略有不同。在
然而,根据我的经验,编译器在优化由cython创建的循环时经常会遇到一些问题,并且/或者更容易遗漏一个细节,这会妨碍生成真正优秀的C代码。所以我处理horse循环的策略是用纯C语言编写它们,并使用cython来包装/访问它们——通常这会更快一些,因为我们还可以为这个被截取的代码使用专用的编译器标志,而不会影响整个cython模块。在