由于使用PyArray\u ENABLEFLAG导致Jupyter崩溃

2024-04-26 09:51:01 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图在Cython上转换numpy数组中的C double*,但还没有成功。 我找到了这些有用的链接: Force NumPy ndarray to take ownership of its memory in Cythonhttps://github.com/numpy/numpy/issues/8253

但每次我在Cython中使用以下.pyx文件时,都会导致Jupyter崩溃(死内核):

.c文件:

#include<stdlib.h>
#include "test.h"

double* test1(int n) {
  double* A=(double*)calloc(n,sizeof(double));
  int i;
  for (i=0;i<n;i++) {
    A[i]=i+0.5; }
  return(A); }

我也试着用malloc得到同样的结果。你知道吗

.pyx文件:

cimport c_test
import numpy as np
cimport numpy as np

np.import_array()

ctypedef np.float64_t DTYPE_t

cdef extern from "numpy/arrayobject.h":
    void PyArray_ENABLEFLAGS(np.ndarray arr, int flags)

cdef data_to_numpy_array_with_spec(void * ptr, np.npy_intp N, int t):
    cdef np.ndarray[DTYPE_t, ndim=1] arr = np.PyArray_SimpleNewFromData(1, &N, t, ptr)
    PyArray_ENABLEFLAGS(arr, np.NPY_OWNDATA)
    return arr

def test(n):
    cdef double* t1
    t=data_to_numpy_array_with_spec(c_test.test1(n),n,np.NPY_FLOAT64)
    return(t)

c_fct试验1函数返回一个C double*n double,我想把它转换成一个numpy数组,这个数组拥有数据以避免内存泄漏。如果没有PyArray_ENABLEFLAGS(t, np.NPY_ARRAY_OWNDATA)行,一切都可以正常工作,但是当numpy数组被销毁时,内存不会被释放。你知道吗

jupyter笔记本:

import cy_test as ct
ct.test(1000)

c级_测试.pxd文件:

cdef extern from "test.h":
    double* test1(int n)

我的设置.py文件如下所示:

from setuptools import setup
from setuptools.extension import Extension
from Cython.Distutils import build_ext
from Cython.Build import cythonize
import numpy as np

ext_modules = cythonize([Extension("cy_test", ["cy_test.pyx","test.c"])])


setup(
  name = 'Hello world app',
  cmdclass = {'build_ext': build_ext},
  ext_modules = ext_modules,
  include_dirs=[np.get_include()]
)

我认为我的问题可能来自与github链接相同的原因,但我不知道如何解决它(在我的例子中,C指针已经存在,所以我认为我不能使用相同的解决方案)。你知道吗

我在使用Anaconda 64位的Windows 10上工作,下面是版本的详细信息: 3.7.1(默认,2018年12月10日,22:54:23)[MSC v.1915 64位(AMD64)]


Tags: 文件fromtestimportnumpyincludeasnp
2条回答

正如在链接的Github issue中所解释的,NPY_OWNDATA只有在通过NumPy本身使用的同一分配器分配内存时才是安全的。这个分配器可以通过^{}函数访问。如果您的内存不是来自此分配器,则不能使用NPY_OWNDATA。你知道吗

不要试图强迫一个数组拥有你给它的任意内存。相反,使用^{}将数组的设置为知道如何执行正确清理的对象。capsule可能是一个方便使用的对象。你知道吗

好的,多亏了你,一切都完成了,我所要做的就是修改.pyx文件如下:

cimport c_test
import numpy as np
cimport numpy as np
from libc.stdlib cimport free

np.import_array()

ctypedef void (*PyCapsule_Destructor)(void*)

cdef extern from "numpy/arrayobject.h":
    void* PyCapsule_GetPointer(void* capsule, const char *name)
    void* PyCapsule_New(void *pointer, const char *name, PyCapsule_Destructor destructor)
    int PyArray_SetBaseObject(np.ndarray arr, void* obj)


cdef void capsule_cleanup(void* capsule):
    cdef void *memory = PyCapsule_GetPointer(capsule, NULL)
    free(memory)

def test(n):
  cdef np.ndarray arr
  cdef int nd = 1
  cdef np.npy_intp shape[1]
  shape[0] = <np.npy_intp> n
  cdef double *data = c_test.test1(n)
  arr = np.PyArray_SimpleNewFromData(nd, shape, np.NPY_DOUBLE, data)
  cdef void* capsule = PyCapsule_New(data, NULL, capsule_cleanup)
  PyArray_SetBaseObject( arr, capsule)
  return(arr)

相关问题 更多 >