Cython Memoryview作为返回值

时间:2014-01-07 14:20:40

标签: python numpy cython memoryview

考虑这个虚假的Cython代码:

#!python
#cython: boundscheck=False
#cython: wraparound=False
#cython: initializedcheck=False
#cython: cdivision=True
#cython: nonecheck=False

import numpy as np

# iterator function
cdef double[:] f(double[:] data):
    data[0] *= 1.01
    data[1] *= 1.02
    return data

# looping function
cdef double[:] _call_me(int bignumber, double[:] data):
    cdef int ii
    for ii in range(bignumber):
        data = f(data)
    return data

# helper function to allow calls from Python
def call_me(bignumber):
    cdef double[:] data = np.ones(2)
    return _call_me(bignumber, data)

现在,如果我对此进行 cython -a ,它会以黄色显示返回语句。我在一个性能非常关键的程序中做了类似的事情,根据分析,这实际上减慢了我的代码速度。那么,为什么cython需要python用于这些返回语句?带注释的文件提供了一个提示:

PyErr_SetString(PyExc_TypeError,"Memoryview return value is not initialized");

令人惊讶的是,谷歌搜索 cython“Memoryview返回值未初始化”给出零结果。

1 个答案:

答案 0 :(得分:5)

缓慢的部分不是你想象的那样。缓慢的部分是(嗯......主要)

data = f(data)

不是f(data)data =

这会分配struct,其定义为

typedef struct {
  struct __pyx_memoryview_obj *memview;
  char *data;
  Py_ssize_t shape[8];
  Py_ssize_t strides[8];
  Py_ssize_t suboffsets[8];
} __Pyx_memviewslice;

并且提到的作业

__pyx_t_3 = __pyx_f_3cyt_f(__pyx_v_data);

其中__pyx_t_3属于该类型。如果这在循环中大量完成,复制结构比执行函数的普通主体需要更长的时间。我在纯C中做了一个计时,它给出了类似的数字。

编辑备注:分配实际上主要是一个问题,因为它还会导致生成结构和其他副本无法优化。)

然而,整件事看起来很傻。复制结构的唯一原因是,如果某些内容发生了变化,但没有任何变化。记忆指向同一个地方,数据点在同一个地方,形状,步幅和偏移是相同的。

我认为避免struct副本的唯一方法是不更改它引用的任何内容(也称为。始终返回给定的memoryview)。这种情况只有在无论如何返回毫无意义的情况下才有可能,就像这里一样。或者你可以像我一样攻击C,我猜。如果你破坏了什么就不要哭。


另请注意,您可以创建函数nogil,因此它与回归Python无关。


修改

C的优化编译器让我略微偏离。基本上,我删除了一些分配,并删除了其他东西。基本上这条路很慢:

#include<stdio.h>


struct __pyx_memoryview_obj;


typedef struct {
  struct __pyx_memoryview_obj *memview;
  char *data;
  ssize_t shape[8];
  ssize_t strides[8];
  ssize_t suboffsets[8];
} __Pyx_memviewslice;


static __Pyx_memviewslice __pyx_f_3cyt_f(__Pyx_memviewslice __pyx_v_data) {
  __Pyx_memviewslice __pyx_r = { 0, 0, { 0 }, { 0 }, { 0 } };
  __pyx_r = __pyx_v_data;
  return __pyx_r;
}

main() {
    int i;
    __Pyx_memviewslice __pyx_v_data = {0, 0, { 0 }, { 0 }, { 0 }};

    for (i=0; i<10000000; i++) {
        __pyx_v_data = __pyx_f_3cyt_f(__pyx_v_data);
    }
}

(没有优化编译)。我不是C程序员,所以如果我所做的事情在某种程度上与我复制计算机生成的代码的事实没有直接联系,那么道歉。

我知道这不是帮助,但我尽我所能,好吗?