![](/img/trans.png)
[英]Return vector<pair<int,int>> & from c++ method to python list of tuples using swig typemap
[英]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, getDoubles
和getDoubles2
时,它似乎会进行复制。 有没有办法避免这种情况?
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.