2016-03-09 57 views
0

我遇到了一个Cython的错误,我发现很难解决。 我有一个struct ret_val,它有一个叫做last_visited的long [:]字段。 我想设置这个,但得到以下运行时错误:Cython memoryview错误 - 致命的Python错误:获取计数是

Fatal Python error: Acquisition count is -1753032536 (line 5052)

以下是负责任的C文件在上面一行的摘录:

/* "cymain.pyx":196 
*  last = np.array([1,1,1], dtype=np.int64) 
*  ret_val.last_visited = last    # <<<<<<<<<<<<<< 
*/ 

__pyx_t_9 = __Pyx_PyObject_to_MemoryviewSlice_ds_long(__pyx_v_last); 

if (unlikely(!__pyx_t_9.memview)) { 
    __pyx_filename = __pyx_f[0]; 
    __pyx_lineno = 196; 
    __pyx_clineno = __LINE__; 
    goto __pyx_L1_error; 
} 
__PYX_XDEC_MEMVIEW(&__pyx_v_ret_val->last_visited, 0); 
__pyx_v_ret_val->last_visited = __pyx_t_9; 
__pyx_t_9.memview = NULL; 
__pyx_t_9.data = NULL; 

我试图使一个最小的例子来重现错误,但它没有发生。然后,我重写了使用最小示例绘制的函数,并再次失败。

这里有一个小例子,不产生错误,但是,据我了解,在功能上等同于错误导致代码:

cdef struct baz: 
    long[:] lv 
    othermodule.something* cd 

cdef baz* initialise_baz(dict req): 
    cdef: 
     baz* ret_val = <baz *> malloc(sizeof(baz)) 
     long nlevels = 3 

    ret_val.cd = NULL 

    lv = req["key"] 
    lv = np.array(lv, dtype=np.int64) 
    ret_val.lv = lv 

    return ret_val 

def test_memview_error(req): 
    cdef baz* foo 
    foo = initialise_baz(req) 
    print "foo.lv[2]", foo.lv[2] 

然后调用

import cymodule 
cymodule.test_memview_error({"key":np.array([1,2,3])}) 
+0

你能提供Python/Cython代码,而不是自动生成的输出吗?问题出在那里;总是假定编译器是正确的,而且你的代码是错误的,直到你排除了你自己代码中的所有错误(即使这样,它仍然通常是代码中的问题)。 – ShadowRanger

+0

您提供的代码中没有足够的信息来诊断问题。如果你不能创造[mcve],可悲的现实是没有人能够帮助你。 – Kevin

+0

如果我不得不猜测,我会说这是由于内存视图被初始化为随机的东西而引起的,所以当它被分配时,它会尝试释放“旧内存视图”(基于初始化的内存)并命中该错误。尝试将malloc更改为calloc ... – DavidW

回答

1

我相信这个问题与未初始化的内存有关(正如我在评论中所说的那样)。纵观生成的C代码从简单的例子:

/* "code.pyx":5 
* import numpy as np 
* 
* cdef struct baz:    # <<<<<<<<<<<<<< 
*  long[:] lv 
*  #othermodule.something* cd 
*/ 
struct __pyx_t_4code_baz { 
    __Pyx_memviewslice lv; 
}; 

(请注意,我为简单起见注释掉othermodule.something)。 __Pyx_memviewslice被定义为

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; 

initialise_baz一些相关的代码(我在这里省略了一些小位)

__pyx_v_ret_val = ((struct __pyx_t_4code_baz *)malloc((sizeof(struct __pyx_t_4code_baz)))); 

注意malloc(一定)为零的内存。因此,lv(关键是指向memview的指针)的内容被设置为任意的内容(可能是之前内存中的内容 - 这显然取决于您之前运行的其他代码)。如果您使用calloc而不是malloc它会使内存为零。 initialise_baz继续:

/* "code.pyx":18 
*  lv = req["key"] 
*  lv = np.array(lv, dtype=np.int64) 
*  ret_val.lv = lv    # <<<<<<<<<<<<<< 
* 
*  return ret_val 
*/ 
    __pyx_t_6 = __Pyx_PyObject_to_MemoryviewSlice_ds_long(__pyx_v_lv); 
    if (unlikely(!__pyx_t_6.memview)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;} 
    __PYX_XDEC_MEMVIEW(&__pyx_v_ret_val->lv, 0); 
    __pyx_v_ret_val->lv = __pyx_t_6; 
    __pyx_t_6.memview = NULL; 
    __pyx_t_6.data = NULL; 

重点线是__PYX_XDEC_MEMVIEW被称为上的lv以前(任意!)内容。这是事情出错的地方。 lv.memview指向任意位置,从中读取它认为是acquisition_count的内容。

表面修复是使用calloc而不是malloc。但是,即使在这种情况下,当您释放baz时,lv永远不会正确解除分配,这可能会导致内存泄漏。我认为确实认为使用memoryviews作为C结构的一部分是没有意义的。你可以使用它们作为cdef class的一部分,而不是正确地处理所有事情?

+0

谢谢你这很有趣。我想我可能需要用C数组来重写,因为这个结构的位会传递到更基本的C函数中。 – conjectures

+0

也许你也可以查看[公共扩展类型](http://docs.cython.org/src/userguide/extension_types.html#public-and-external-extension-types),这样你可以使用'cdef类'作为C中的一个结构体 – DavidW