【问题标题】:Cython Memoryview as return valueCython Memoryview 作为返回值
【发布时间】:2014-01-07 14:20:40
【问题描述】:

考虑一下这个虚拟的 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" 结果为零。

【问题讨论】:

  • Cython 版本 0.19.2
  • 在你的真实代码中,你需要返回内存视图还是可以像这里一样修改它?做这些改变给了我 40 倍的加速。我不确定是否有办法关闭该检查...
  • 真实代码迭代求解常微分方程,所以是的,我确实需要返回它。
  • 嗯,让我们看看 cython 向导是否知道快速返回小内存视图的方法。作为一种解决方法,可以重写 f 以接受 data_in 和 data_out 缓冲区而不是返回它。

标签: python numpy cython memoryview


【解决方案1】:

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

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 程序员,所以如果我所做的事情在某种程度上与我复制计算机生成的代码没有直接关系,我深表歉意。

我知道这没有帮助,但我已经尽力了,好吗?

【讨论】:

  • +1 表示这比我想象的要复杂,以及 nogil 提示。
猜你喜欢
  • 2016-09-07
  • 1970-01-01
  • 1970-01-01
  • 2019-05-18
  • 2012-09-29
  • 1970-01-01
  • 1970-01-01
  • 2019-06-21
  • 1970-01-01
相关资源
最近更新 更多