Подтвердить что ты не робот

Как передать массив 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] не работает. Почему бы и нет?
4b9b3361

Ответ 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)