Python Cython Memoryview作为返回值

Python Cython Memoryview作为返回值,python,numpy,cython,memoryview,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

考虑这个虚拟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)
现在,如果我对它执行acython-a,它将以黄色显示返回语句。我在一个性能非常关键的程序中做了类似的事情,根据分析,这确实会减慢我的代码速度。那么,为什么cython需要python来处理这些返回语句呢?带注释的文件给出了一个提示:

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

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

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

不是
f(数据)
数据=

这将分配一个结构,其定义如下

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);
其中
\uuupyx\ut\u3
属于该类型。如果这是在一个循环中大量完成的,那么复制结构所需的时间要比复制普通函数体所需的时间长得多。我用纯C做了一个计时,它给出了类似的数字

(编辑说明:分配实际上主要是一个问题,因为它还会导致结构和其他副本的生成未得到优化。)

然而,整件事似乎很愚蠢。复制结构的唯一原因是,如果某些内容发生了更改,但什么都没有发生。记忆点在同一个地方,数据点在同一个地方,形状、步幅和偏移是相同的

我所看到的避免
结构
复制的唯一方法是不更改它引用的任何内容(即,始终返回中给出的
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);
    }
}
#包括
结构uu pyx_memoryview_obj;
类型定义结构{
结构uu pyx_memoryview_obj*memview;
字符*数据;
ssize_t形状[8];
大踏步[8];
ssize_t子补偿[8];
}_uuPyx_memviewslice;
静态uuPyx_memviewslice uuPyx_f_3cyt_f(uuuPyx_memviewslice uuPyx_v_数据){
__Pyx_memviewslice_uupyx_r={0,0,{0},{0},{0};
__pyx_r=uu pyx_v_数据;
返回uupyx_r;
}
main(){
int i;
__Pyx_memviewslice(Pyx_v_data={0,0,{0},{0},{0});

对于(i=0;iCython版本0.19.2在您的实际代码中,您需要返回memoryview还是可以像这里这样修改它?这样做会使我的速度提高40倍。我不确定是否有方法关闭该检查…实际代码迭代求解常微分方程,所以是的,我确实需要返回它。嗯,让我们看看cython是否izard知道一种快速返回小内存视图的方法。作为一种解决方法,
f
可以被重写以接受数据输入和数据输出缓冲区,而不是返回它。+1表示这比我想象的要复杂,以及nogil提示。
#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);
    }
}