Как передать массив Numpy в функцию cffi и как получить обратно?
Я разрабатываю аудио-алгоритм с использованием Python и Numpy. Теперь я хочу ускорить этот алгоритм, выполнив часть его в C. В прошлом Я сделал это с помощью cython. Теперь я хочу сделать то же самое, используя новый cffi.
В целях тестирования я написал тривиальную функцию C:
void copy(float *in, float *out, int len) {
for (int i=0; i<len; i++) {
out[i] = in[i];
}
}
Теперь я хочу создать два массива numpy и обработать их этой функцией.
Я понял способ сделать это:
import numpy as np
from cffi import FFI
ffi = FFI()
ffi.cdef("void copy(float *in, float *out, int len);")
C = ffi.dlopen("/path/to/copy.dll")
float_in = ffi.new("float[16]")
float_out = ffi.new("float[16]")
arr_in = 42*np.ones(16, dtype=np.float32)
float_in[0:16] = arr_in[0:16]
C.copy(float_in, float_out, 16)
arr_out = np.frombuffer(ffi.buffer(float_out, 16*4), dtype=np.float32)
Однако я хотел бы улучшить этот код:
- Есть ли способ прямого доступа к базовым буферам с плавающей точкой массивов numpy без их копирования?
-
ffi.buffer
очень удобен для быстрого преобразования в содержимое массива C массиву Numpy. Существует ли эквивалентный способ быстрого преобразования массива numpy в массив C без копирования отдельных элементов?
- Для некоторых приложений
float_in[0:16] = arr_in[0:16]
- удобный способ доступа к данным. Противоположность, arr_out[0:16] = float_out[0:16]
не работает. Почему бы и нет?
Ответы
Ответ 1
Атрибут ctypes
ndarray может взаимодействовать с модулем ctypes, например, ndarray.ctypes.data
- это адрес данных массива, вы можете направить его на указатель float *
,
а затем передать указатель на функцию C.
import numpy as np
from cffi import FFI
ffi = FFI()
ffi.cdef("void copy(float *in, float *out, int len);")
C = ffi.dlopen("ccode.dll")
a = 42*np.ones(16, dtype=np.float32)
b = np.zeros_like(a)
pa = ffi.cast("float *", a.ctypes.data)
pb = ffi.cast("float *", b.ctypes.data)
C.copy(pa, pb, len(a))
print b
По вашему вопросу 3:
Я думаю, что массив ffi не предоставляет numpy необходимую информацию для доступа к внутреннему буферу. Поэтому numpy попытается преобразовать его в число с плавающей точкой, которое не удалось.
Лучшее решение, которое я могу думать, это преобразовать его в список первым:
float_in[0:16] = list(arr_in[0:16])
Ответ 2
данные в массиве numpy могут быть доступны через интерфейс массива:
import numpy as np
import cffi
ffi = cffi.FFI()
a = np.zeros(42)
data = a.__array_interface__['data'][0]
cptr = ffi.cast ( "double*" , data )
теперь у вас есть тип указателя cffi, который вы можете передать в свою программу копирования. обратите внимание, что это базовый подход; массивы numpy могут не содержать свои данные в плоской памяти, поэтому, если ваш ndarray структурирован, вам придется рассмотреть его форму и шаги. Если все это плоское, этого достаточно.
Ответ 3
Обновление для этого: современные версии CFFI имеют ffi.from_buffer()
, который превращает любой объект буфера (например, массив numpy) в указатель char *
FFI. Теперь вы можете сделать прямо:
cptr = ffi.cast("float *", ffi.from_buffer(my_np_array))
или непосредственно в качестве аргументов для вызова (char *
автоматически добавляется к float *
):
C.copy(ffi.from_buffer(arr_in), ffi.from_buffer(arr_out), 16)