Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/file/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 强制NumPy Ndaray拥有Cython中的内存_Python_Arrays_Numpy_Cython - Fatal编程技术网

Python 强制NumPy Ndaray拥有Cython中的内存

Python 强制NumPy Ndaray拥有Cython中的内存,python,arrays,numpy,cython,Python,Arrays,Numpy,Cython,接下来,我尝试通过Cython的NumPy包装器使用Python C API函数PyArray_ENABLEFLAGS,发现它没有公开 以下尝试手动公开它(这只是再现故障的最低示例) 来自libc.stdlib cimport malloc 将numpy作为np导入 cimport numpy作为np np.import_数组() ctypedef np.int32\u t数据类型 “numpy/ndarraytypes.h”中的cdef外部: void PyArray_ENABLEFLAGS(

接下来,我尝试通过Cython的NumPy包装器使用Python C API函数
PyArray_ENABLEFLAGS
,发现它没有公开

以下尝试手动公开它(这只是再现故障的最低示例)

来自libc.stdlib cimport malloc
将numpy作为np导入
cimport numpy作为np
np.import_数组()
ctypedef np.int32\u t数据类型
“numpy/ndarraytypes.h”中的cdef外部:
void PyArray_ENABLEFLAGS(np.PyArrayObject*arr,int标志)
def test():
cdef int N=1000
cdef DTYPE_t*data=malloc(N*sizeof(DTYPE_t))
cdef np.ndarray[DTYPE_t,ndim=1]arr=np.PyArray_SimpleNewFromData(1,&N,np.NPY_INT32,数据)
PyArray_ENABLEFLAGS(arr,np.NPY_数组_OWNDATA)
失败,出现编译错误:

Error compiling Cython file:
------------------------------------------------------------
...
def test():
    cdef int N = 1000

    cdef DTYPE_t *data = <DTYPE_t *>malloc(N * sizeof(DTYPE_t))
    cdef np.ndarray[DTYPE_t, ndim=1] arr = np.PyArray_SimpleNewFromData(1, &N, np.NPY_INT32, data)
    PyArray_ENABLEFLAGS(arr, np.NPY_ARRAY_OWNDATA)
                          ^
------------------------------------------------------------

/tmp/test.pyx:19:27: Cannot convert Python object to 'PyArrayObject *'
编译Cython文件时出错: ------------------------------------------------------------ ... def test(): cdef int N=1000 cdef DTYPE_t*data=malloc(N*sizeof(DTYPE_t)) cdef np.ndarray[DTYPE_t,ndim=1]arr=np.PyArray_SimpleNewFromData(1,&N,np.NPY_INT32,数据) PyArray_ENABLEFLAGS(arr,np.NPY_数组_OWNDATA) ^ ------------------------------------------------------------ /tmp/test.pyx:19:27:无法将Python对象转换为“PyArrayObject*”
我的问题是:在这种情况下,这是正确的方法吗?如果是,我做错了什么?如果不是,我如何强制NumPy拥有Cython的所有权,而不必使用C扩展模块?

您只是在接口定义中有一些小错误。以下几点对我很有用:

from libc.stdlib cimport malloc
import numpy as np
cimport numpy as np

np.import_array()

ctypedef np.int32_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 = 1000

    cdef DTYPE_t *data = <DTYPE_t *>malloc(N * sizeof(DTYPE_t))
    arr = data_to_numpy_array_with_spec(data, N, np.NPY_INT32)
    return arr
使用
python setup.py Build\u ext--inplace构建
。然后验证数据是否实际拥有:

import _owndata
arr = _owndata.test()
print arr.flags
除此之外,您应该看到
OWNDATA:True

是的,这绝对是解决这个问题的正确方法,因为
numpy.pxd
将所有其他函数导出到Cython时做了完全相同的事情。

适用于大多数场景,但有些脆弱。Numpy用于内存管理,这是一个实现细节,这些调用映射到通常的+一些内存跟踪(我不知道Stefan的解决方案对内存跟踪有什么影响,至少它似乎没有崩溃)

还有一些更为深奥的情况,其中numpy库中的
free
与cython代码中的
malloc
使用的内存分配器不同(例如在本例或本例中,针对不同的运行时间链接)

传递/管理数据所有权的正确工具是。

首先,我们需要一个python对象,它负责释放内存。我在这里使用一个自制的cdef类(主要是因为日志记录/演示),但显然还有其他可能性:

%%cython
from libc.stdlib cimport free

cdef class MemoryNanny:
    cdef void* ptr # set to NULL by "constructor"
    def __dealloc__(self):
        print("freeing ptr=", <unsigned long long>(self.ptr)) #just for debugging
        free(self.ptr)
        
    @staticmethod
    cdef create(void* ptr):
        cdef MemoryNanny result = MemoryNanny()
        result.ptr = ptr
        print("nanny for ptr=", <unsigned long long>(result.ptr)) #just for debugging
        return result

 ...
下面是一个示例,如何调用此功能:

%%cython
...
from libc.stdlib cimport malloc
def create():
    cdef double *ptr=<double*>malloc(sizeof(double)*8);
    ptr[0]=42.0
    return array_from_ptr(ptr, 8, np.NPY_FLOAT64)
结果/输出符合预期

注意:结果数组并不真正拥有数据(即,标志返回
OWNDATA:False
),因为内存是由内存保姆拥有的,但结果是一样的:一旦数组被删除,内存就会被释放(因为再也没有人持有对保姆的引用)


memorynny
不必保护原始C指针。它可以是其他任何东西,例如,也可以是
std::vector

%%cython -+
from libcpp.vector cimport vector
cdef class VectorNanny:
    #automatically default initialized/destructed by Cython:
    cdef vector[double] vec 
    @staticmethod
    cdef create(vector[double]& vec):
        cdef VectorNanny result = VectorNanny()
        result.vec.swap(vec) # swap and not copy
        return result
   
# for testing:
def create_vector(int N):
    cdef vector[double] vec;
    vec.resize(N, 2.0)
    return VectorNanny.create(vec)
以下测试表明,保姆工作正常:

nanny=create_vector(10**8) # top shows additional 800MB memory are used
del nanny                  # top shows, this additional memory is no longer used.

最新的Cython版本允许您使用最少的语法,尽管比建议的较低级别解决方案稍微多一些开销

numpy\u数组=np.asarray(我的指针)

仅此一点并不能传递所有权

值得注意的是,通过调用生成Cython数组

这将生成一个
cython.array
,而不分配内存。默认情况下,
cython.array
使用
stdlib.h
,因此您也应该使用默认的malloc,而不是任何特殊的CPython/Numpy分配器

仅当为此
cython.array
设置了所有权时,才会调用
free
,默认情况下,仅当它分配数据时才会调用该属性。对于我们的情况,我们可以通过以下方式手动设置:

my\u cyarr.free\u data=True


因此,要返回1D数组,它将非常简单:

从cython.view cimport数组作为cvarray
# ...
cdef cvarray cvarr=数据
cvarr.free_数据=真
返回np.asarray(cvarr)

我的答案对你有用吗?确实有用,谢谢!这对我不管用。它编译得很好,但是导入模块会导致链接错误,并抱怨PyArray_ENABLEFLAGS。numpy 1.9.1就是这样。这个解决方案从numpy 1.7开始就可以工作。旧版本缺少PyArray_ENABLEFLAGS此操作失败,原因是导入错误:./_owndata.so:未定义符号:PyArray_ENABLEFLAGS@Korem您是否使用了“numpy/arrayobject.h”中的
cdef extern
?也许您需要检查include路径中是否有“numpy”。如果数据类型是用户定义的,比如
my\u dtype=np.dtype([('t1',np.float32),('t2',np.uint16)])
?我意识到它有一个
类型_num
,但无法在Cython中找到它。它被定义为
cdef np.ndarray[mytype_t]arr
mytype_t
具有压缩浮点和uint16。
%%cython
...
from libc.stdlib cimport malloc
def create():
    cdef double *ptr=<double*>malloc(sizeof(double)*8);
    ptr[0]=42.0
    return array_from_ptr(ptr, 8, np.NPY_FLOAT64)
>>> m =  create()
nanny for ptr= 94339864945184
>>> m.flags
...
OWNDATA : False
...
>>> m[0]
42.0
>>> del m
freeing ptr= 94339864945184
%%cython -+
from libcpp.vector cimport vector
cdef class VectorNanny:
    #automatically default initialized/destructed by Cython:
    cdef vector[double] vec 
    @staticmethod
    cdef create(vector[double]& vec):
        cdef VectorNanny result = VectorNanny()
        result.vec.swap(vec) # swap and not copy
        return result
   
# for testing:
def create_vector(int N):
    cdef vector[double] vec;
    vec.resize(N, 2.0)
    return VectorNanny.create(vec)
nanny=create_vector(10**8) # top shows additional 800MB memory are used
del nanny                  # top shows, this additional memory is no longer used.