2016-03-09 24 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