Cython numpy数组索引

5
我正在尝试使用Cython加速一些Python代码,并利用Cython的-a选项查看我可以改进哪些地方。据我了解,在生成的HTML文件中,高亮显示的行是调用Python函数的行-是这样吗?
在下面的简单函数中,我使用缓冲语法声明了numpy数组参数arr。我认为这允许索引操作纯粹在C中进行,而无需调用Python函数。然而,cython -a(版本0.15)突出显示了我设置arr元素值的行,但没有突出显示读取其元素之一的行。为什么会发生这种情况?有更有效的访问numpy数组元素的方法吗?
import numpy
cimport numpy

def foo(numpy.ndarray[double, ndim=1] arr not None):
    cdef int i
    cdef double elem
    for i in xrange(10):
      elem = arr[i]          #not highlighted
      arr[i] = 1.0 + elem    #highlighted

编辑: 此外,mode缓冲区参数如何与numpy交互? 假设我没有改变numpy.array的默认order参数,是否总是安全使用mode ='c'? 这对性能有实际影响吗?

在delnan的评论之后进行编辑:arr[i] + = 1也会被突出显示(这就是我首先分开它的原因,以查看操作的哪个部分引起了问题)。 如果我关闭边界检查以简化事情(这不会影响到什么被突出显示),所生成的C代码为:

  /* "ct.pyx":11
 *   cdef int i
 *   cdef double elem
 *   for i in xrange(10):             # <<<<<<<<<<<<<<
 *     elem = arr[i]
 *     arr[i] = 1.0 + elem
 */
  for (__pyx_t_1 = 0; __pyx_t_1 < 10; __pyx_t_1+=1) {
    __pyx_v_i = __pyx_t_1;

    /* "ct.pyx":12
 *   cdef double elem
 *   for i in xrange(10):
 *     elem = arr[i]             # <<<<<<<<<<<<<<
 *     arr[i] = 1.0 + elem
 */
    __pyx_t_2 = __pyx_v_i;
    __pyx_v_elem = (*__Pyx_BufPtrStrided1d(double *, __pyx_bstruct_arr.buf, __pyx_t_2, __pyx_bstride_0_arr));

    /* "ct.pyx":13
 *   for i in xrange(10):
 *     elem = arr[i]
 *     arr[i] = 1.0 + elem             # <<<<<<<<<<<<<<
 */
    __pyx_t_3 = __pyx_v_i;
    *__Pyx_BufPtrStrided1d(double *, __pyx_bstruct_arr.buf, __pyx_t_3, __pyx_bstride_0_arr) = (1.0 + __pyx_v_elem);
  }

arr[i] += 1 是否被突出显示?此外,我相信您可以在 cython -a 生成的 HTML 文档中直接查看该特定行生成的代码,这可能有助于了解该行发生了哪些 Python API 调用。 - user395760
1个回答

5
答案是,荧光笔欺骗了读者。 我编译了您的代码,高亮显示下生成的说明是处理错误情况和返回值所需的内容,它们与数组赋值无关。 实际上,如果您将代码更改为以下内容:
def foo(numpy.ndarray[double, ndim=1] arr not None):
    cdef int i
    cdef double elem
    for i in xrange(10):
      elem = arr[i]
      arr[i] = 1.0 + elem
    return # + add this

重点应放在最后一行,而不是分配位置。

您可以通过使用@cython.boundscheck来进一步加快代码速度:

import numpy
cimport numpy
cimport cython

@cython.boundscheck(False)
def foo(numpy.ndarray[double, ndim=1] arr not None):
    cdef int i
    cdef double elem
    for i in xrange(10):
      elem = arr[i]
      arr[i] = 1.0 + elem
    return 

提供的C代码没有边界检查,也没有环绕索引计算。因此,我认为提问者在他的setup.py中将它们禁用了。 - rocksportrocker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接