python - Pybind11 和 std::vector——如何使用胶囊释放数据?

标签 python c++ numpy c++11 pybind11

我有一个返回 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/

相关文章:

python - 如何在Docker群中使用ray

python - 按 0 级索引的最后一个值对 Pandas MultiIndex 进行排序

python - (n,) 在 numpy 和向量的上下文中意味着什么?

python - 为什么没有 OpenBLAS 的 numpy/scipy 更快?

python - 合并 pandas 数据框

python - 如何从文本小部件中单独检索行

c++ - boost 作用域锁(互斥锁)

c++ - 在静态创建方法中从基类调用非静态方法

c++ - 在 C++ 编程中移动到下一行的另一种方法是什么

python - 具有三个 3x3 内核的 6x6 数组的 2D 互相关