Python Cython Memoryview作为返回值
考虑这个虚拟Cython代码: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
#!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);
}
}