Cython 数组索引

5 投票
1 回答
3330 浏览
提问于 2025-04-17 02:26

我正在尝试用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.arrayorder参数为默认值,使用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);
  }

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 

撰写回答