繁体   English   中英

返回向量从 C++ 到 Python 而不使用 SWIG 复制

[英]Return vector from C++ to Python without copying using SWIG

我正在尝试使用 SWIG 在 C++ 中构建 python 模块。 我有以下代码:

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 });
    }
}

还有一个接口文件container.i:

%module container

%{
#include "container.h"
%}

%include container.h
%include "std_vector.i"
%include "stdint.i"

%template(DoubleVector) std::vector<double>;
%template(IntVector) std::vector<uint8_t>;

当我在 python 中调用此代码时:

import psutil
import os
import container
import numpy as np

print(psutil.Process(os.getpid()).memory_info().rss)

cont = container.Container()
container.fillContainer(cont)

print(psutil.Process(os.getpid()).memory_info().rss)

a = np.array(cont.getInts(), copy=False)
b = np.array(cont.getDoubles(), copy=False)
c = np.array(cont.getDoubles2(), copy=False)

print(psutil.Process(os.getpid()).memory_info().rss)

这可行,但是当我检查程序的 memory 使用情况(使用psutil.Process(os.getpid()).memory_info().rss )时,当我调用函数getInts, getDoublesgetDoubles2时,它似乎会进行复制。 有没有办法避免这种情况?

Numpy 不能对向量 object 做任何事情:它需要底层 memory。 最简单的方法是调用向量的 data(),但 SWIG 不会公开 function。 仅供参考,这里是 cppyy ( http://cppyy.org ),它确实暴露了 data() 以表明它可以正常工作,即问题不在于 numpy:

import cppyy
cppyy.include("container.h")
import psutil
import os
import numpy as np

print(psutil.Process(os.getpid()).memory_info().rss)

cont = cppyy.gbl.Container()
cppyy.gbl.fillContainer(cont) 

print(psutil.Process(os.getpid()).memory_info().rss)

a = np.array(cont.getInts().data(), copy=False)
b = np.array(cont.getDoubles().data(), copy=False)
c = np.array(cont.getDoubles2().data(), copy=False)

print(psutil.Process(os.getpid()).memory_info().rss)

(当然 cppyy 需要更多的 memory 直接的 bat,b/c 它加载 LLVM;但是它会逐渐获胜。)

因此,首先您需要通过 SWIG 公开该 data() 调用,然后我认为最简单的方法是在其上添加 python 分层,以使客户端代码的使用干净(需要注意的是,如果原始向量被删除或调整大小,但这对于任何不复制的代码来说总是一个问题)。

首先修改您的容器代码:

// include the numpy header; find its location with:
//    python -c 'import numpy; print(numpy.get_include())'
#include "numpy/arrayobject.h"

// ... rest of your container code here ...

// data to numpy helpers
PyObject* vec_data_uint8_t(std::vector<uint8_t>& d) {
    import_array();
    npy_intp dims[] = {(npy_intp)d.size()};
    npy_intp strides[] = {sizeof(std::vector<uint8_t>::value_type)};
    return PyArray_NewFromDescr(&PyArray_Type, PyArray_DescrFromType(NPY_UINT8),
        1, dims, strides, d.data(), NPY_ARRAY_C_CONTIGUOUS, nullptr);
}

PyObject* vec_data_double(std::vector<double>& d) {
    import_array();
    npy_intp dims[] = {(npy_intp)d.size()};
    npy_intp strides[] = {sizeof(std::vector<double>::value_type)};
    return PyArray_NewFromDescr(&PyArray_Type, PyArray_DescrFromType(NPY_DOUBLE),
        1, dims, strides, d.data(), NPY_ARRAY_C_CONTIGUOUS, nullptr);
}

SWIG 会自动拾取这些,所以 container.i 很好。 现在添加一个 mycontainer.py 辅助模块,如下所示:

import numpy
from container import *

IntVector.__array__ = vec_data_uint8_t
DoubleVector.__array__ = vec_data_double

并在您原来的 python 代码中,将import container替换为:

import mycontainer as container

现在您可以像往常一样使用您的向量,但是当交给 numpy 数组时,自定义__array__ function 将被调用,因为它可以访问内部数据并且如果copy=False则不会复制。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM