How to pass a Numpy array into a cffi function and how to get one back out?

bastibe picture bastibe · Apr 29, 2013 · Viewed 9.5k times · Source

I am developing an audio algorithm using Python and Numpy. Now I want to speed up that algorithm by implementing a part of it in C. In the past, I have done this using cython. Now I want to do the same thing using the new cffi.

For testing purposes, I wrote a trivial C function:

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

Now I want to create two numpy arrays and have those be processed by this function. I figured out a way to do that:

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)

However, I would like to improve this code:

  1. Is there a way to directly access the underlying float buffers of the numpy arrays without copying them?
  2. ffi.buffer is very convenient for quickly converting to contents of a C array to a Numpy array. Is there an equivalent way for quickly converting a numpy array into a C array without copying the individual elements?
  3. For some applications, float_in[0:16] = arr_in[0:16] is a convenient way of accessing data. The opposite, arr_out[0:16] = float_out[0:16] does not work however. Why not?

Answer

HYRY picture HYRY · Apr 30, 2013

The ctypes attribute of ndarray can interact with the ctypes module, for example, ndarray.ctypes.data is the data address of the array, you can cast it to a float * pointer, and then pass the pointer to the C function.

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

For your question 3:

I think ffi array doesn't provide numpy the necessary information to access it's inner buffer. So numpy try to convert it to a float number which failed.

The best solution I can thinks is convert it to list first:

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