首页 文章

Cython Memoryview作为返回值

提问于
浏览
11

考虑这个虚拟的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 return value is not initialized" 给出的结果为零 .

1 回答

  • 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中做了一个计时,它给出了相似的数字 .

    (编辑注释:分配实际上主要是一个问题,因为它还会导致结构和其他副本的生成不被优化 . )

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

    我看到避免 struct 副本的唯一方法是不更改它引用的任何内容(也就是说 . 总是返回给定的 memoryview ) . 如果你打破了某些东西,那就哭了 .


    另请注意,您可以将函数设置为 nogil ,因此它与回溯到Python无关 .


    EDIT

    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程序员,所以如果我所做的事情在某种程度上与我复制计算机生成的代码的事实没有直接联系,那么道歉 .

    我知道这没有用,但我尽了最大努力,好吗?

相关问题