簡體   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