通过pybind11返回numpy数组

By simon at 2018-02-07 • 0人收藏 • 75人看过

我有一个C ++函数计算一个大的张量,我想回到 Python作为NumPy数组[pybind11](https:// github.com/pybind/pybind11)。 从pybind11的文档中,似乎使用[STL uniqueptr ](http://en.cppreference.com/w / cpp / memory / uniqueptr)是可取的。在里面 下面的例子中,注释掉的版本起作用,而给定的版本起作用 编译但在运行时失败(“无法将函数返回值转换为 Python类型!“)。 为什么智能指针版本失败?瓦at是规范的创建方式 并返回一个NumPy数组? PS:由于程序结构和数组的大小,这是可取的不 复制内存,但从给定的指针创建数组。内存所有权应该 被Python采取。

typedef typename py::array_t<double, py::array::c_style | py::array::forcecast> py_cdarray_t;

// py_cd_array_t _test()
std::unique_ptr<py_cdarray_t> _test()
{
    double * memory = new double[3]; memory[0] = 11; memory[1] = 12; memory[2] = 13;
    py::buffer_info bufinfo (
        memory,                                   // pointer to memory buffer
        sizeof(double),                           // size of underlying scalar type
        py::format_descriptor<double>::format(),  // python struct-style format descriptor
        1,                                        // number of dimensions
        { 3 },                                    // buffer dimensions
        { sizeof(double) }                        // strides (in bytes) for each index
    );

    //return py_cdarray_t(bufinfo);
    return std::unique_ptr<py_cdarray_t>( new py_cdarray_t(bufinfo) );
}

2 个回复 | 最后更新于 2018-02-07
2018-02-07   #1

几个意见(然后是一个工作实施)。   * pybind11的C ++对象包装Python类型(如pybind11::object,99999)99999154,在这种情况下,pybind11::array_t<T>)实际上只是一个底层Python对象指针的包装。在这方面已经承担了一个共享的指针包装器的角色,所以没有必要在unique_ptr中包装:直接返回py::array_t<T>对象已经基本上只是返回一个荣耀的指针。   * pybind11::array_t即可直接从数据指针构造,所以你可以跳过py::buffer_info中间步骤,只是给出形状和步幅直接到pybind11::array_t的构造函数。以这种方式构建的numpy数组不会拥有自己的数据,只会引用它(也就是说,numpy owndata标志将被设置为false)。   内存所有权可以绑定到Python对象的生命周期,b但是你仍然处于正确的解除分配状态。 Pybind11提供了一个py::capsule类来帮助你完成这个。你想要做的是使numpy数组依赖于这个胶囊作为它的父类,指定它为

>>> import numpywrap
>>> z = numpywrap.f()
>>> # the python process is now taking up a bit more than 800MB memory
>>> z[1,1,1]
1001001.0
>>> z[0,0,100]
100.0
>>> z[99,999,999]
99999999.0
>>> z[0,0,0] = 3.141592
>>> del z
Element [0] = 3.14159
freeing memory @ 0x7fd769f12010
>>> # python process memory size has dropped back down
34参数array_t.这将使numpy数组引用它,保持活着,只要数组本身是阿里ve,并在不再引用时调用清除函数。   旧版本(2.2之前版本)中的c_style标志只对新数组产生影响,即不传递值指针时。这在2.2版本中得到修复,也影响了a如果只指定形状而不指定步幅,则自动跨步。如果您自己直接指定步幅,则完全没有效果f(正如我在下面的例子中所做的那样)。 所以,把这些代码放在一起,这个代码是一个完整的pybind11模块 演示如何你可以完成你正在寻找的东西(包括一些 C ++输出来证明确实工作正常):
#include <iostream>
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>

namespace py = pybind11;

PYBIND11_PLUGIN(numpywrap) {
    py::module m("numpywrap");
    m.def("f", []() {
        // Allocate and initialize some data; make this big so
        // we can see the impact on the process memory use:
        constexpr size_t size = 100*1000*1000;
        double *foo = new double[size];
        for (size_t i = 0; i < size; i++) {
            foo[i] = (double) i;
        }

        // Create a Python object that will free the allocated
        // memory when destroyed:
        py::capsule free_when_done(foo, [](void *f) {
            double *foo = reinterpret_cast<double *>(f);
            std::cerr << "Element [0] = " << foo[0] << "\n";
            std::cerr << "freeing memory @ " << f << "\n";
            delete[] foo;
        });

        return py::array_t<double>(
            {100, 1000, 1000}, // shape
            {1000*1000*8, 1000*8, 8}, // C-style contiguous strides for double
            foo, // the data pointer
            free_when_done); // numpy array references this parent
    });
    return m.ptr();
}
编译并从Python调用它显示它的工作:
>>> import numpywrap
>>> z = numpywrap.f()
>>> # the python process is now taking up a bit more than 800MB memory
>>> z[1,1,1]
1001001.0
>>> z[0,0,100]
100.0
>>> z[99,999,999]
99999999.0
>>> z[0,0,0] = 3.141592
>>> del z
Element [0] = 3.14159
freeing memory @ 0x7fd769f12010
>>> # python process memory size has dropped back down

2018-02-07   #2

几个意见(然后是一个工作实施)。   * pybind11的C ++对象包装Python类型(如pybind11::object,99999)99999154,在这种情况下,pybind11::array_t<T>)实际上只是一个底层Python对象指针的包装。在这方面已经承担了一个共享的指针包装器的角色,所以没有必要在unique_ptr中包装:直接返回py::array_t<T>对象已经基本上只是返回一个荣耀的指针。   * pybind11::array_t即可直接从数据指针构造,所以你可以跳过py::buffer_info中间步骤,只是给出形状和步幅直接到pybind11::array_t的构造函数。以这种方式构建的numpy数组不会拥有自己的数据,只会引用它(也就是说,numpy owndata标志将被设置为false)。   内存所有权可以绑定到Python对象的生命周期,b但是你仍然处于正确的解除分配状态。 Pybind11提供了一个py::capsule类来帮助你完成这个。你想要做的是使numpy数组依赖于这个胶囊作为它的父类,指定它为

>>> import numpywrap
>>> z = numpywrap.f()
>>> # the python process is now taking up a bit more than 800MB memory
>>> z[1,1,1]
1001001.0
>>> z[0,0,100]
100.0
>>> z[99,999,999]
99999999.0
>>> z[0,0,0] = 3.141592
>>> del z
Element [0] = 3.14159
freeing memory @ 0x7fd769f12010
>>> # python process memory size has dropped back down
34参数array_t.这将使numpy数组引用它,保持活着,只要数组本身是阿里ve,并在不再引用时调用清除函数。   旧版本(2.2之前版本)中的c_style标志只对新数组产生影响,即不传递值指针时。这在2.2版本中得到修复,也影响了a如果只指定形状而不指定步幅,则自动跨步。如果您自己直接指定步幅,则完全没有效果f(正如我在下面的例子中所做的那样)。 所以,把这些代码放在一起,这个代码是一个完整的pybind11模块 演示如何你可以完成你正在寻找的东西(包括一些 C ++输出来证明确实工作正常):
#include <iostream>
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>

namespace py = pybind11;

PYBIND11_PLUGIN(numpywrap) {
    py::module m("numpywrap");
    m.def("f", []() {
        // Allocate and initialize some data; make this big so
        // we can see the impact on the process memory use:
        constexpr size_t size = 100*1000*1000;
        double *foo = new double[size];
        for (size_t i = 0; i < size; i++) {
            foo[i] = (double) i;
        }

        // Create a Python object that will free the allocated
        // memory when destroyed:
        py::capsule free_when_done(foo, [](void *f) {
            double *foo = reinterpret_cast<double *>(f);
            std::cerr << "Element [0] = " << foo[0] << "\n";
            std::cerr << "freeing memory @ " << f << "\n";
            delete[] foo;
        });

        return py::array_t<double>(
            {100, 1000, 1000}, // shape
            {1000*1000*8, 1000*8, 8}, // C-style contiguous strides for double
            foo, // the data pointer
            free_when_done); // numpy array references this parent
    });
    return m.ptr();
}
编译并从Python调用它显示它的工作:
>>> import numpywrap
>>> z = numpywrap.f()
>>> # the python process is now taking up a bit more than 800MB memory
>>> z[1,1,1]
1001001.0
>>> z[0,0,100]
100.0
>>> z[99,999,999]
99999999.0
>>> z[0,0,0] = 3.141592
>>> del z
Element [0] = 3.14159
freeing memory @ 0x7fd769f12010
>>> # python process memory size has dropped back down

登录后方可回帖

Loading...