[英]Pybind11 and std::vector — How to free data using capsules?
I have a C++ function that returns a std::vector
and, using Pybind11, I would like to return the contents of that vector as a Numpy array without having to copy the underlying data of the vector into a raw data array. 我有一个C ++函数,它返回一个std::vector
并且使用Pybind11,我想将该向量的内容作为Numpy数组返回,而不必将向量的基础数据复制到原始数据数组中。
Current Attempt 当前尝试
In this well-written SO answer the author demonstrates how to ensure that a raw data array created in C++ is appropriately freed when the Numpy array has zero reference count. 在这个写得很好的SO答案中 ,作者演示了当Numpy数组的引用计数为零时,如何确保适当释放在C ++中创建的原始数据数组。 I tried to write a version of this using std::vector
instead: 我尝试使用std::vector
来编写此版本:
// aside - I made a templated version of the wrapper with which
// I create specific instances of in the PYBIND11_MODULE definitions:
//
// m.def("my_func", &wrapper<int>, ...)
// m.def("my_func", &wrapper<float>, ...)
//
template <typename T>
py::array_t<T> wrapper(py::array_t<T> input) {
auto proxy = input.template unchecked<1>();
std::vector<T> result = compute_something_returns_vector(proxy);
// give memory cleanup responsibility to the Numpy array
py::capsule free_when_done(result.data(), [](void *f) {
auto foo = reinterpret_cast<T *>(f);
delete[] foo;
});
return py::array_t<T>({result.size()}, // shape
{sizeof(T)}, // stride
result.data(), // data pointer
free_when_done);
}
Observed Issues 观察到的问题
However, if I call this from Python I observe two things: (1) the data in the output array is garbage and (2) when I manually delete the Numpy array I receive the following error (SIGABRT): 但是,如果我从Python调用它,我会观察到两件事:(1)输出数组中的数据是垃圾,(2)手动删除Numpy数组时,我收到以下错误(SIGABRT):
python3(91198,0x7fff9f2c73c0) malloc: *** error for object 0x7f8816561550: pointer being freed was not allocated
My guess is that this issue has to do with the line " delete[] foo
", which presumably is being called with foo
set to result.data()
. 我的猜测是,此问题与“ delete[] foo
”行有关,大概是通过将foo
设置为result.data()
来result.data()
。 This is not the way to deallocate a std::vector
. 这不是释放std::vector
。
Possible Solutions 可能的解决方案
One possible solution is to create a T *ptr = new T[result.size()]
and copy the contents of result
to this raw data array. 一种可能的解决方案是创建一个T *ptr = new T[result.size()]
并将result
内容复制到此原始数据数组。 However, I have cases where the results might be large and I want to avoid taking all of that time to allocate and copy. 但是,在某些情况下,结果可能会很大,因此我想避免花费所有时间来分配和复制。 (But perhaps it's not as long as I think it would be.) (但是也许没有我想的那么长。)
Also, I don't know much about std::allocator
but perhaps there is a way to allocate the raw data array needed by the output vector outside the compute_something_returns_vector()
function call and then discard the std::vector
afterwards, retaining the underlying raw data array? 另外,我不是很了解std::allocator
,但也许有分配由输出载体所需要的原始数据阵外的方式compute_something_returns_vector()
函数调用,然后丢弃std::vector
事后,保留底层原始数据数组?
The final option is to rewrite compute_something_returns_vector
. 最后的选择是重写compute_something_returns_vector
。
After an offline discussion with a colleague I resolved my problem. 与同事进行离线讨论后,我解决了我的问题。 I do not want to commit an SO faux pas so I won't accept my own answer. 我不想冒充别人,所以我不会接受自己的回答。 However, for the sake of using SO as a catalog of information I want to provide the answer here for others. 但是,为了将SO用作信息目录,我想在此为其他人提供答案。
The problem was simple: result
was stack-allocated and needed to be heap-allocated so that free_when_done
can take ownership. 问题很简单: result
是堆栈分配的,需要进行堆分配,以便free_when_done
可以拥有所有权。 Below is an example fix: 以下是修复示例:
{
// ... snip ...
std::vector<T> *result = new std::vector<T>(compute_something_returns_vector(proxy));
py::capsule free_when_done(result, [](void *f) {
auto foo = reinterpret_cast<std::vector<T> *>(f);
delete foo;
});
return py::array_t<T>({result->size()}, // shape
{sizeof(T)}, // stride
result->data(), // data pointer
free_when_done);
}
I was also able to implement a solution using std::unique_ptr
that doesn't require the use of a free_when_done
function. 我还能够使用std::unique_ptr
实现解决方案,而无需使用free_when_done
函数。 However, I wasn't able to run Valgrind with either solution so I'm not 100% sure that the memory held by the vector was appropriately freed. 但是,我无法使用任何一种解决方案运行Valgrind,因此我不能100%确定该向量所保存的内存是否已适当释放。 (Valgrind + Python is a mystery to me.) For completeness, below is the std::unique_ptr
approach: (Valgrind + Python对我来说是个谜。)为完整std::unique_ptr
,以下是std::unique_ptr
方法:
{
// ... snip ...
std::unique_ptr<std::vector<T>> result =
std::make_unique<std::vector<T>>(compute_something_returns_vector(proxy));
return py::array_t<T>({result->size()}, // shape
{sizeof(T)}, // stride
result->data()); // data pointer
}
I was, however, able to inspect the addresses of the vectors allocated in both the Python and C++ code and confirmed that no copies of the output of compute_something_returns_vector()
were made. 但是,我能够检查在Python和C ++代码中分配的向量的地址,并确认没有对compute_something_returns_vector()
的输出进行任何复制。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.