возвращая несколько py :: array без копирования в pybind11 - PullRequest
0 голосов
/ 26 сентября 2019

Я пытаюсь собрать модуль Python в C ++, используя pybind11.У меня есть следующий код:

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/numpy.h>

namespace py = pybind11;

struct ContainerElement
{
    uint8_t i;
    double d;
    double d2;
};

class Container
{
private:
    std::vector<uint8_t> ints;
    std::vector<double> doubles;
    std::vector<double> doubles2;

public:

    std::vector<uint8_t>& getInts() { return ints; }
    std::vector<double>& getDoubles() { return doubles; }
    std::vector<double>& getDoubles2() { return doubles2; }

    void addElement(ContainerElement element)
    {
        ints.emplace_back(element.i);
        doubles.emplace_back(element.d);
        doubles2.emplace_back(element.d2);
    }
};

void fillContainer(Container& container)
{
    for (int i = 0; i < 1e6; ++i)
    {
        container.addElement({(uint8_t)i, (double)i,(double)i });
    }
}

PYBIND11_MODULE(containerInterface, m) {
    py::class_<Container>(m, "Container")
        .def(py::init<>())
        .def("getInts", [](Container& container)
        {
            return py::array_t<uint8_t>(
                    { container.getInts().size() },
                    { sizeof(uint8_t) },
                    container.getInts().data());
        })
        .def("getDoubles", [](Container& container)
        {
            return py::array_t<double>(
                    { container.getDoubles().size() },
                    { sizeof(double) },
                    container.getDoubles().data());
        })
        .def("getDoubles2", [](Container& container)
        {
            return py::array_t<double>(
                    { container.getDoubles2().size() },
                    { sizeof(double) },
                    container.getDoubles2().data());
        });

    m.def("fillContainer", &fillContainer);
}

Когда я вызываю этот код на python:

import containerInterface

container = containerInterface.Container()

containerInterface.fillContainer(container)

i = container.getInts()
d = container.getDoubles()
d2 = container.getDoubles2()

Это работает, однако, когда я проверяю использование памяти программой (используя psutil.Process(os.getpid()).memory_info().rss) кажется, что я делаю копию, когда я вызываю функции getInts, getDoubles и getDoubles2.Есть ли способ избежать этого?

Я пытался использовать np.array(container.getInts(), copy=False), но он все равно делает копию.Также я попытался использовать py::buffer_protocol() для класса Container, как упомянуто здесь: https://pybind11.readthedocs.io/en/stable/advanced/pycpp/numpy.html.Однако я могу сделать это только для вектора Ints или векторов Doubles, но не для всех одновременно.

PYBIND11_MODULE(containerInterface, m) {
    py::class_<Container>(m, "Container", py::buffer_protocol())
        .def(py::init<>())
        .def("getInts", &Container::getInts)
        .def("getDoubles", &Container::getDoubles)
        .def_buffer([](Container& container) -> py::buffer_info {
            return py::buffer_info(
                container.getInts().data(),
                sizeof(uint8_t),
                py::format_descriptor<uint8_t>::format(),
                1,
                { container.getInts().size() },
                { sizeof(uint8_t) * container.getInts().size() }
        );
        });
m.def("fillContainer", &fillContainer);

Тогда я могу использовать i = np.array(container, copy=False), без копирования.Однако, как я уже сказал, он работает только для вектора Ints.

1 Ответ

1 голос
/ 26 сентября 2019

Я предполагаю, что вы должны указать, что функции доступа возвращают ссылки вместо копии, что, вероятно, по умолчанию.Я не знаю, как вы делаете это с pybind, но я сделал это с boost :: python и Ponder .

Т.е. вам нужно указать политику возврата .

...