我有一个返回 std::vector
的 C++ 函数,并且使用 Pybind11,我想将该 vector 的内容作为 Numpy 数组返回,而不必复制 vector 转换成原始数据数组。
当前尝试
在this well-written SO answer作者演示了如何确保在 Numpy 数组的引用计数为零时适当释放用 C++ 创建的原始数据数组。我尝试使用 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);
}
观察到的问题
但是,如果我从 Python 调用它,我会观察到两件事:(1) 输出数组中的数据是垃圾,(2) 当我手动删除 Numpy 数组时,我收到以下错误 (SIGABRT):
python3(91198,0x7fff9f2c73c0) malloc: *** error for object 0x7f8816561550: pointer being freed was not allocated
我的猜测是这个问题与行“delete[] foo
”有关,大概是在 foo
设置为 result 的情况下被调用的。数据()
。这不是释放 std::vector
的方法。
可能的解决方案
一个可能的解决方案是创建一个 T *ptr = new T[result.size()]
并将 result
的内容复制到这个原始数据数组。但是,我遇到结果可能很大的情况,我想避免花费所有时间来分配和复制。 (但也许没有我想象的那么长。)
另外,我对std::allocator
知之甚少|但也许有一种方法可以在 compute_something_returns_vector()
函数调用 之外分配输出 vector 所需的原始数据数组,然后丢弃 std::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.
问题很简单:result
是堆栈分配的,需要堆分配,以便 free_when_done
可以取得所有权。下面是一个修复示例:
{
// ... 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);
}
我还能够使用 std::unique_ptr
实现一个不需要使用 free_when_done
函数的解决方案。但是,我无法使用任何一种解决方案运行 Valgrind,因此我不能 100% 确定 vector 持有的内存是否已适当释放。 (Valgrind + Python 对我来说是个谜。)为了完整起见,下面是 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
}
但是,我能够检查在 Python 和 C++ 代码中分配的 vector 地址,并确认没有复制 compute_something_returns_vector()
的输出。
关于python - Pybind11 和 std::vector——如何使用胶囊释放数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54876346/