Python数组到C语言中,在Cython中处理C数组(使用numpy和pytorch)

我正在尝试使用cython来包装C库(fastText,如果它相关). C库类从磁盘加载一个非常大的数组.我的包装器从C库中实例化一个类来加载数组,然后使用cython内存视图和numpy.asarray将数组转换为numpy数组,然后调用torch.from_numpy来创建一个张量.

出现的问题是如何处理为阵列释放内存.

现在,当程序退出时,我得到释放的指针没有被分配.我希望这是因为C代码和numpy / pytorch都试图管理同一块RAM.

我可以简单地在C库中注释掉析构函数,但这感觉就像它将导致我在路上遇到不同的问题.

我该如何处理这个问题?是否有任何关于如何使用C和cython处理内存共享的最佳实践文档?

如果我修改C库以将数组包装在shared_ptr中,cython(和numpy,pytorch等)是否会正确共享shared_ptr?

如果问题是天真的,我道歉; Python垃圾收集对我来说非常神秘.

任何建议表示赞赏.

解决方法:

我可以想到三种合理的方法.我将在下面概述它们(即没有任何代码将完整,但希望它将很清楚如何完成它).

1. C拥有记忆; Cython / Python拥有一个指向C类的共享指针

(这看起来就像你一直在思考的那条线).

首先创建一个包含共享指针的Cython类

from libcpp.memory cimport shared_ptr

cdef class Holder:

cdef shared_ptr[cpp_class] ptr

@staticmethod

cdef make_holder(shared_ptr[cpp_class] ptr):

cdef holder = Holder() # empty class

holder.ptr = ptr

return holder

然后,您需要为Holder定义缓冲区协议.这允许以numpy数组和Cython内存视图都能理解的方式直接访问cpp_class分配的内存.因此,它们持有对Holder实例的引用,这反过来使cpp_class保持活动状态. (使用np.asarray(holder_instance)创建一个使用实例内存的numpy数组)

缓冲协议有点牵扯,但Cython有fairly extensive documentation,你应该可以很大程度上复制和粘贴他们的例子.您需要添加到Holder的两种方法是__getbuffer__和__releasebuffer__.

2. Python拥有内存;您的C类包含指向Python对象的指针

在此版本中,您将内存分配为numpy数组(使用Python C API接口).当你的C类以递减方式销毁数组的引用计数时,但是如果Python保存对该数组的引用,那么该数组可以比C类更长.

#include

#include

class cpp_class {

private:

PyObject* arr;

double* data;

public:

cpp_class() {

arr = PyArray_SimpleNew(...); // details left to be filled in

data = PyArray_DATA(reinterpret_cast(arr));

# fill in the data

}

~cpp_class() {

Py_DECREF(arr); // release our reference to it

}

PyObject* get_np_array() {

Py_INCREF(arr); // Cython expects this to be done before it receives a PyObject

return arr;

}

};

有关如何从C/C++分配numpy数组的详细信息,请参见the numpy documentation.如果定义复制/移动构造函数,请注意引用计数.

Cython包装器然后看起来像:

cdef extern from "some_header.hpp":

cdef cppclass cpp_class:

# whatever constructors you want to allow

object get_np_array()

3. C将数据的所有权转移到Python / Cython

在这个方案中,C分配数组,但Cython / Python负责解除分配.一旦转让所有权,C就无法访问数据.

class cpp_class {

public:

double* data; // for simplicity this is public - you may want to use accessors

cpp_class() :

data(new double[50])

{/* fill the array as needed */}

~cpp_class() {

delete [] data;

}

};

// helper function for Cython

inline void del_cpp_array(double* a) {

delete [] a;

}

然后使用cython.view.array class捕获分配的内存.这有一个用于销毁的回调函数:

from cython cimport view

cdef extern from "some_header.hpp":

cdef cppclass cpp_class:

double* data

# whatever constructors and other functions

void del_cpp_array(double*)

# later

cdef cpp_class cpp_instance # create this however you like

# ...

# modify line below to match your data

arr = view.array(shape=(10, 2), itemsize=sizeof(double), format="d",

mode="C", allocate_buffer=False)

arr.data = cpp_instance.data

cpp_instance.data = None # reset to NULL pointer

arr.callback_free_data = del_cpp_array

然后,arr可以与memoryview或numpy数组一起使用.

你可能不得不在使用void *或者使用del_cpp_array的char *时搞砸一下 – 我不确定Cython接口需要什么类型.

第一个选项可能是大多数要实现的工作,但只需要对C代码进行很少的更改.第二个选项可能需要更改您不想进行的C代码.第三种选择很简单,但意味着C不再能访问数据,这可能是一个缺点.

标签:pytorch,c,numpy,cython,fasttext

来源: https://codeday.me/bug/20190828/1751918.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值