pybind11和std :: vector-如何使用胶囊释放数据

Pybind11 and std::vector -- How to free data using capsules?

本文关键字:释放 数据 何使用 std vector- pybind11      更新时间:2023-10-16

i具有返回 std::vector的C 函数原始数据阵列。

当前尝试

在此井井有条中,因此回答作者演示了如何确保在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.data()的行为。这不是分配std::vector的方法。

可能的解决方案

一种可能的解决方案是创建T *ptr = new T[result.size()]并将result的内容复制到此原始数据数组。但是,我有结果可能很大,我想避免将所有时间分配和复制。(但也许不如我认为的那样长。)

另外,我对std::allocator了解不多,但是也许有一种方法可以分配输出矢量外部所需的原始数据阵列 compute_something_returns_vector()函数调用,然后丢弃std::vector,然后保留,保留基础原始数据数组?

最终选项是重写compute_something_returns_vector

在与同事进行离线讨论之后,我解决了问题。我不想犯下如此人造的pas,所以我不会接受自己的答案。但是,为了将其作为信息目录,我想在此处为他人提供答案。

问题很简单: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);
}

我还能够使用不需要使用free_when_done函数的std::unique_ptr实现解决方案。但是,我无法使用任何一种解决方案运行Valgrind,因此我不能100%确定矢量持有的内存已适当释放。(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 代码中分配的向量的地址,并确认没有进行compute_something_returns_vector()的输出副本。