![](/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.