2013-04-29 11 views
21

Sto sviluppando un algoritmo audio usando Python e Numpy. Ora voglio accelerare tale algoritmo implementando una parte di esso in C. In passato, I have done this using cython. Ora voglio fare la stessa cosa usando il nuovo cffi.Come passare un array Numpy in una funzione cffi e come recuperarne uno?

A scopo di verifica, ho scritto una funzione C banale:

void copy(float *in, float *out, int len) { 
    for (int i=0; i<len; i++) { 
     out[i] = in[i]; 
    } 
} 

Ora voglio creare due array NumPy ed avere quelli essere effettuati anche da questa funzione. Ho trovato un modo per farlo:

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) 

Tuttavia, vorrei migliorare questo codice:

  1. c'è un modo per accedere direttamente i buffer galleggiante sottostanti degli array numpy senza copiare loro?
  2. ffi.buffer è molto conveniente per la conversione rapida di contenuti di un array C in un array Numpy. Esiste un modo equivalente per convertire rapidamente un array numerico in un array C senza copiare i singoli elementi?
  3. Per alcune applicazioni, float_in[0:16] = arr_in[0:16] è un modo conveniente per accedere ai dati. L'opposto, arr_out[0:16] = float_out[0:16], tuttavia, non funziona. Perchè no?

risposta

19

L'attributo ctypes di ndarray può interagire con il modulo ctypes, per esempio, ndarray.ctypes.data è l'indirizzo di dati della matrice, è possibile lanciare a un puntatore float *, e quindi passare il puntatore alla funzione 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 

per la tua domanda 3:

Penso serie ffi non fornisce numpy le informazioni necessarie per accedere è di buffer interno. Così numpy prova a convertirlo in un numero float che ha fallito.

La soluzione migliore che posso pensa è convertirlo in un elenco prima:

float_in[0:16] = list(arr_in[0:16]) 
12

i dati in un array NumPy si può accedere tramite la sua interfaccia matrice:

import numpy as np 
import cffi 
ffi = cffi.FFI() 

a = np.zeros(42) 
data = a.__array_interface__['data'][0] 
cptr = ffi.cast ("double*" , data) 

Ora avete un CFFI tipo di puntatore, che puoi passare alla tua routine di copia. si noti che questo è un approccio di base; gli array di numpy potrebbero non contenere i loro dati nella memoria piatta, quindi se il tuo narray è strutturato, dovrai considerare la sua forma e le sue andature. Se è tutto piatto, però, questo è sufficiente.

+0

+1 per citare passi – Matthias

6

Un aggiornamento a questo: le versioni moderne di CFFI hanno ffi.from_buffer(), che trasforma qualsiasi oggetto buffer (come un array numpy) in un puntatore FFI char *. Ora è possibile fare direttamente:

cptr = ffi.cast("float *", ffi.from_buffer(my_np_array)) 

o direttamente come argomenti alla chiamata (la char * viene colato automaticamente a float *):

C.copy(ffi.from_buffer(arr_in), ffi.from_buffer(arr_out), 16)