2011-03-24 92 views
5

我使用SWIG將一些C++代碼粘合到Python(2.6),並且該粘合的一部分包含一段代碼,用於將大型數據字段(數百萬個值)從C++端到Numpy數組。我可以拿出實現對類的迭代器,然後最好的方法提供了Python的方法:將C/C++向量快速轉換爲Numpy數組

def __array__(self, dtype=float): 
    return np.fromiter(self, dtype, self.size()) 

的問題是,每個迭代next通話是非常昂貴的,因爲它要經過三四SWIG包裝。這需要很長的時間。我可以保證C++數據是連續存儲的(因爲它們生活在一個std :: vector中),它只是覺得Numpy應該能夠將數據的開始和它所包含的值的數目一起指向數據的開始,直接閱讀。

有沒有辦法通過指向internal_data_[0]的指針和值internal_data_.size() numpy,以便它可以直接訪問或複製數據而沒有所有的Python開銷?

回答

0

所以它看起來像唯一真正的解決方案是基於pybuffer.i可以從C++複製到現有緩衝區的東西。如果您添加到痛飲包括文件:

%insert("python") %{ 
import numpy as np 
%} 

/*! Templated function to copy contents of a container to an allocated memory 
* buffer 
*/ 
%inline %{ 
//==== ADDED BY numpy.i 
#include <algorithm> 

template < typename Container_T > 
void copy_to_buffer(
     const Container_T& field, 
     typename Container_T::value_type* buffer, 
     typename Container_T::size_type length 
     ) 
{ 
// ValidateUserInput(length == field.size(), 
//   "Destination buffer is the wrong size"); 
    // put your own assertion here or BAD THINGS CAN HAPPEN 

    if (length == field.size()) { 
     std::copy(field.begin(), field.end(), buffer); 
    } 
} 
//==== 

%} 

%define TYPEMAP_COPY_TO_BUFFER(CLASS...) 
%typemap(in) (CLASS::value_type* buffer, CLASS::size_type length) 
(int res = 0, Py_ssize_t size_ = 0, void *buffer_ = 0) { 

    res = PyObject_AsWriteBuffer($input, &buffer_, &size_); 
    if (res < 0) { 
     PyErr_Clear(); 
     %argument_fail(res, "(CLASS::value_type*, CLASS::size_type length)", 
       $symname, $argnum); 
    } 
    $1 = ($1_ltype) buffer_; 
    $2 = ($2_ltype) (size_/sizeof($*1_type)); 
} 
%enddef 


%define ADD_NUMPY_ARRAY_INTERFACE(PYVALUE, PYCLASS, CLASS...) 

TYPEMAP_COPY_TO_BUFFER(CLASS) 

%template(_copy_to_buffer_ ## PYCLASS) copy_to_buffer<CLASS>; 

%extend CLASS { 
%insert("python") %{ 
def __array__(self): 
    """Enable access to this data as a numpy array""" 
    a = np.ndarray(shape=(len(self),), dtype=PYVALUE) 
    _copy_to_buffer_ ## PYCLASS(self, a) 
    return a 
%} 
} 

%enddef 

那麼你可以做一個容器 「numpy的」 -able與

%template(DumbVectorFloat) DumbVector<double>; 
ADD_NUMPY_ARRAY_INTERFACE(float, DumbVectorFloat, DumbVector<double>); 

然後在Python,只是做:

# dvf is an instance of DumbVectorFloat 
import numpy as np 
my_numpy_array = np.asarray(dvf) 

這隻有一個Python < - > C++翻譯調用的開銷,而不是由典型長度爲N的數組產生的N.

此代碼的稍微更加完整的版本是我的PyTRT project at github的一部分。

2

你會想要定義__array_interface__() instead。這會讓你直接傳回指針和形狀信息。

+0

你能否提供一些實際實施的更多細節?有沒有辦法做到這一點,而不必根據Numpy頭文件編譯我的項目?謝謝。 – 2011-03-24 19:21:25

+0

它也說這是一個傳統的界面。 – 2011-03-24 19:48:16

+0

'__array_interface__'只是一個普通的字典,裏面有簡單的類型。無需使用任何Numpy頭文件進行編譯。忽略將其稱爲「傳統」的註釋。我以爲我已經刪除了。如果你喜歡,你可以實現PEP 3118緩衝接口,但這很容易。 – 2011-05-20 23:39:57

0

如果您將向量包裹在實現Pythons Buffer Interface的對象中,可以將其傳遞給numpy數組進行初始化(請參閱docs,第三個參數)。我敢打賭,這初始化是更快,因爲它可以只使用memcpy來複制數據。

+0

感謝您的提示。你有沒有在SWIG中使用'pybuffer_mutable_binary'或其他接口來實現'__buffer__'接口的例子,例如浮點數? – 2011-03-24 19:57:30

+0

@Seth:對不起,我幫不了你。 – 2011-03-24 20:03:48

+0

所以看起來我必須從頭開始爲這個類實現整個緩衝區接口。 SWIG僅提供*讀取其他緩衝區*的能力,而不是導出緩衝區功能。 – 2011-03-24 21:36:59

1

也許有可能使用f2py而不是swig。儘管它的名字,它能夠連接Python和C以及Fortran。請參閱http://www.scipy.org/Cookbook/f2py_and_NumPy

其優點是它可自動處理向numpy數組的轉換。兩個注意事項:如果你還不知道Fortran,你可能會發現f2py有點奇怪;如果你還不知道Fortran,你可能會發現f2py有點奇怪;如果你還不知道Fortran,我不知道它與C++有多好。

+0

感謝您的回覆。我知道一些FORTRAN,但我在代碼中使用了很多'C++'-y特性:模板,typedefs等等。我寧願不引入另一個依賴。 – 2011-03-25 12:13:14

+0

足夠公平的重新C++。你可能不得不編寫中間的純C包裝,這可能會很痛苦。另一方面,它不是真正的另一個依賴項,因爲f2py是你已經使用的numpy的一部分。你不需要Fortran編譯器。 – deprecated 2011-03-25 16:15:06