[英]how to pass list of numpy arrays to c++ via cython
我想將2d numpy數組的列表傳遞給c ++函數。 我的第一個想法是使用std::vector<float *>
接收數組列表,但是我找不到傳遞列表的方法。
c ++函數如下所示:
double cpp_func(const std::vector<const float*>& vec) {
return 0.0;
}
Cython函數如下所示:
cpdef py_func(list list_of_array):
cdef vector[float*] vec
cdef size_t i
cdef size_t n = len(list_of_array)
for i in range(n):
vec.push_back(&list_of_array[i][0][0]) # error: Cannot take address of Python object
return cpp_func(vec)
我曾嘗試使用list[float[:,:]]
聲明list_of_array
,但也無法正常工作。
我將稍微更改您的函數的簽名:
double *
而不是float *
因為這對應於默認的np.float
-type。 但這可以根據您的需要進行調整。 這導致以下c ++接口/代碼(為方便起見,我將Cy - thon> = 0.28使用C-verbatim-code功能):
%%cython --cplus -c=-std=c++11
from libcpp.vector cimport vector
cdef extern from *:
"""
struct Numpy1DArray{
double *ptr;
int size;
};
static double cpp_func(const std::vector<Numpy1DArray> &vec){
// Fill with life to see, that it really works:
double res = 0.0;
for(const auto &a : vec){
if(a.size>0)
res+=a.ptr[0];
}
return res;
}
"""
cdef struct Numpy1DArray:
double *ptr
int size
double cpp_func(const vector[Numpy1DArray] &vec)
...
struct Numpy1DArray
只是捆綁了一個np數組所需的信息,因為這不僅僅是指向連續數據的指針。
現在,編寫包裝函數非常簡單:
%%cython --cplus -c=-std=c++11
....
def call_cpp_func(list_of_arrays):
cdef Numpy1DArray ar_descr
cdef vector[Numpy1DArray] vec
cdef double[::1] ar
for ar in list_of_arrays: # coerse elements to double[::1]
ar_descr.size = ar.size
if ar.size > 0:
ar_descr.ptr = &ar[0]
else:
ar_descr.ptr = NULL # set to nullptr
vec.push_back(ar_descr)
return cpp_func(vec)
有一些值得注意的事情:
&ar[0]
顯然將不起作用,因為Cython希望ar[0]
是Python對象。 順便說一句,這就是您所錯過的。 double[::1]
)作為強制目標。 與np.ndarray
相比,優點在於它還可以與array.array
一起array.array
,並且還可以自動檢查數據是否連續(即::1
的含義)。 ar[0]
以獲得空的ndarray
此訪問必須受到保護。 cimport array
以便使代碼與array.array
一起array.array
。 最后,這是一個測試代碼是否有效的測試(列表中還有一個array.array
可以說明這一點):
import array
import numpy as np
lst = (np.full(3, 1.0), np.full(0, 2.0), array.array('d', [2.0]))
call_cpp_func(lst) # 3.0 as expected!
上面的代碼也可以用線程安全的方式編寫。 可能的問題是:
list_of_arrays.clear()
觸發numpy-array的刪除-之后,周圍將不再有數組的引用,它們將被刪除。 這意味着只要使用指針,就需要保留對每個輸入數組的引用。 __getbuffer__
鎖定緩沖區,因此一旦完成計算,就不能使它無效並通過__releasebuffer__
釋放緩沖區。 Cython的內存視圖可用於鎖定緩沖區並保持輸入數組周圍的引用:
%%cython --cplus -c=-std=c++11
....
def call_cpp_func_safe(list_of_arrays):
cdef Numpy1DArray ar_descr
cdef vector[Numpy1DArray] vec
cdef double[::1] ar
cdef list stay_alive = []
for ar in list_of_arrays: # coerse elements to double[::1]
stay_alive.append(ar) # keep arrays alive and locked
ar_descr.size = ar.size
if ar.size > 0:
ar_descr.ptr = &ar[0]
else:
ar_descr.ptr = NULL # set to nullptr
vec.push_back(ar_descr)
return cpp_func(vec)
開銷很小:將內存視圖添加到列表中-安全性的代價。
最后一項改進:可以在計算cpp_fun
時釋放gil,這意味着我們必須將cpp_func
導入為nogil並釋放它,為什么調用該函數:
%%cython --cplus -c=-std=c++11
from libcpp.vector cimport vector
cdef extern from *:
....
double cpp_func(const vector[Numpy1DArray] &vec) nogil
...
def call_cpp_func(list_of_arrays):
...
with nogil:
result = cpp_func(vec)
return result
Cython會發現, result
是double類型的,因此可以在調用cpp_func
同時釋放gil。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.