我正在尝试使用boost :: python :: numpy :: ndarray在C ++中创建一个多维数组并将其传递给python。问题是如何做到这一点而不必自己管理C ++中与ndarray相关的内存。
我正在尝试使用boost :: python :: numpy :: from_data函数在C ++中创建numpy数组。我的基本理解是,在没有适当的函数所有者参数的情况下,管理与数组关联的内存的责任落在了我身上。
我最初的假设是,不必根据boost page来传递owner参数,它是关于owner的:“数据的所有者,以防它不是ndarray本身。”
但是,我读过一些似乎相反的帖子。例如,link说,“如果将object()作为所有者参数传递,则该数组肯定应该拥有其数据(因此报告OWNDATA = True)...”,link说该对象必须与显式析构函数相关联。
我想知道什么是正确的方法。还是这不是boost :: python :: numpy的预期用例?
答案 0 :(得分:1)
是的,文档说如果您传递object(),则数组将在释放数据时释放数据,但这并不能像宣传的那样起作用。
以下是我在github https://github.com/boostorg/python/issues/97#issuecomment-519679003上的帖子的一些摘录,这是OP链接的相同问题,但以前没有答案。答案不是来自我,而是答案的演示以及为什么object()
演示无法正常工作。
解决方案是创建一个拥有原始指针的对象(胶囊)并将其传递给boost::python::numpy::ndarray::from_data()
函数。胶囊是管理指针的Python对象:
PyObject* PyCapsule_New(void *pointer, const char *name, PyCapsule_Destructor destructor)
这是一个创建大型数组的示例。我将在Python while True:
循环中重复调用此函数。使用此功能,您可以整天进行循环。这是因为在循环中,我将返回值分配给相同的变量,因此在每次迭代中,先前返回的ndarray
的refcount变为零,并且内存被释放。
typedef long int my_data_type;
inline void destroyManagerCObject(PyObject* self) {
auto * b = reinterpret_cast<my_data_type*>( PyCapsule_GetPointer(self, NULL) );
std::cout << "C++ : " << __PRETTY_FUNCTION__ << " delete [] " << b << std::endl;
delete [] b;
}
boost::python::numpy::ndarray get_array_that_owns_through_capsule()
{
// Change this to see how the adresses change.
unsigned int last_dim = 6000;
boost::python::object shape = boost::python::make_tuple(4, 5, last_dim);
boost::python::numpy::dtype dt = boost::python::numpy::dtype::get_builtin<my_data_type>();
auto * const data_ptr = new my_data_type[4*5*last_dim];
const size_t s = sizeof(my_data_type);
boost::python::object strides = boost::python::make_tuple(5*last_dim*s, last_dim*s, s);
for(int i = 1; i <= 4*5*last_dim; ++i){ data_ptr[i-1] = i; }
// This sets up a python object whose destruction will free data_ptr
PyObject *capsule = ::PyCapsule_New((void *)data_ptr, NULL, (PyCapsule_Destructor)&destroyManagerCObject);
boost::python::handle<> h_capsule{capsule};
boost::python::object owner_capsule{h_capsule};
std::cout << "C++ : " << __PRETTY_FUNCTION__ << "data_ptr = " << data_ptr << std::endl;
return boost::python::numpy::from_data( data_ptr, dt, shape, strides, owner_capsule);
}
BOOST_PYTHON_MODULE(interface){
.def("get_array_that_owns_through_capsule", get_array_that_owns_through_capsule)
然后在while
循环中,我可以整天调用此函数,并且
import interface
import psutil
def get_process_memory_usage():
process = psutil.Process(os.getpid())
return process.memory_info().rss
hundred_mb = 100000000
MEMORY_MAX = 100 * one_mb
i = 0
while True:
print("PYTHON : ---------------- While iteration ------------------- ({})".format(i))
print("PYTHON : BEFORE calling test_capsule_way()")
arr = interface.get_array_that_owns_through_default_object()
print("PYTHON : AFTER calling test_capsule_way()")
i += 1
if i % 1000 == 0:
print("PYTHON : Nb arrays created (and pretty sure not destroyed) : {}".format(i))
mem = get_process_memory_usage()
if mem > MEMORY_MAX:
print("PYTHON : Bro chill with the memory, you're using {}MB over here!".format(mem/one_mb))
quit()
print("PYTHON : ----------- End while iteration\n")
print("PYTHON : SCRIPT END")
前几次迭代的输出是
PYTHON : ---------------- While iteration ------------------- (0)
PYTHON : BEFORE calling test_capsule_way()
C++ : boost::python::numpy::ndarray get_array_that_owns_through_capsule()data_ptr = 0x7fb7c9831010
PYTHON : AFTER calling test_capsule_way()
PYTHON : ----------- End while iteration
PYTHON : ---------------- While iteration ------------------- (1)
PYTHON : BEFORE calling test_capsule_way()
C++ : boost::python::numpy::ndarray get_array_that_owns_through_capsule()data_ptr = 0x7fb7c9746010
C++ : void destroyManagerCObject(PyObject*) delete [] 0x7fb7c9831010
PYTHON : AFTER calling test_capsule_way()
PYTHON : ----------- End while iteration
PYTHON : ---------------- While iteration ------------------- (2)
PYTHON : BEFORE calling test_capsule_way()
C++ : boost::python::numpy::ndarray get_array_that_owns_through_capsule()data_ptr = 0x14c9f20
C++ : void destroyManagerCObject(PyObject*) delete [] 0x7fb7c9746010
PYTHON : AFTER calling test_capsule_way()
PYTHON : ----------- End while iteration
PYTHON : ---------------- While iteration ------------------- (3)
在本期中,我还演示了如何执行此操作,而不是使用胶囊,而是将boost::python::object()
作为owner
参数传递,不释放内存,并且python循环将由于检查过程存储器而停止:https://github.com/boostorg/python/issues/97#issuecomment-520555403