[英]How to pass a Numpy array into a cffi function and how to get one back out?
I am developing an audio algorithm using Python and Numpy.我正在使用 Python 和 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 .
现在我想通过在 C 中实现它的一部分来加速该算法。过去,我使用 cython 完成了这个。 Now I want to do the same thing using the new cffi .
现在我想使用新的cffi做同样的事情。
For testing purposes, I wrote a trivial C function:出于测试目的,我编写了一个简单的 C 函数:
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.现在我想创建两个 numpy 数组并让它们由这个函数处理。 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:但是,我想改进此代码:
ffi.buffer
is very convenient for quickly converting to contents of a C array to a Numpy array. ffi.buffer
对于快速将 C 数组的内容转换为 Numpy 数组非常方便。 Is there an equivalent way for quickly converting a numpy array into a C array without copying the individual elements?float_in[0:16] = arr_in[0:16]
is a convenient way of accessing data.float_in[0:16] = arr_in[0:16]
是一种访问数据的便捷方式。 The opposite, arr_out[0:16] = float_out[0:16]
does not work however.arr_out[0:16] = float_out[0:16]
但是不起作用。 Why not?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. ndarray 的
ctypes
属性可以和 ctypes 模块交互,比如ndarray.ctypes.data
就是数组的数据地址,可以将其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
For your question 3:对于您的问题 3:
I think ffi array doesn't provide numpy the necessary information to access it's inner buffer.我认为 ffi 数组没有提供 numpy 访问其内部缓冲区的必要信息。 So numpy try to convert it to a float number which failed.
因此 numpy 尝试将其转换为失败的浮点数。
The best solution I can thinks is convert it to list first:我能想到的最佳解决方案是先将其转换为列表:
float_in[0:16] = list(arr_in[0:16])
the data in a numpy array can be accessed via it's array interface:可以通过它的数组接口访问 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 )
now you have a cffi pointer type, which you can pass to your copy routine.现在您有一个 cffi 指针类型,您可以将其传递给您的复制例程。 note that this is a basic approach;
请注意,这是一种基本方法; numpy arrays may not contain their data in flat memory, so if your ndarray is structured, you will have to consider it's shape and strides.
numpy 数组可能不会在平面内存中包含它们的数据,所以如果你的 ndarray 是结构化的,你将不得不考虑它的形状和步幅。 If it's all flat, though, this is sufficient.
不过,如果它是平的,这就足够了。
An update to this: modern versions of CFFI have ffi.from_buffer()
, which turns any buffer object (like a numpy array) to a char *
FFI pointer.对此的更新:CFFI 的现代版本具有
ffi.from_buffer()
,它将任何缓冲区对象(如 numpy 数组)转换为char *
FFI 指针。 You can now do directly:您现在可以直接执行以下操作:
cptr = ffi.cast("float *", ffi.from_buffer(my_np_array))
or directly as arguments to the call (the char *
is casted automatically to float *
):或直接作为调用的参数(
char *
自动转换为float *
):
C.copy(ffi.from_buffer(arr_in), ffi.from_buffer(arr_out), 16)
After you got a flat result array from cffi, you also could reshape the array with given strides via numpy like this:从 cffi 获得平面结果数组后,您还可以通过 numpy 以给定的步幅重塑数组,如下所示:
a=np.ones(24); a.shape = (2, 3, 4)
or要么
a=np.ones(24); b = a.reshape(2, 3, 4)
This is for example helpful if you want to have nested lists for further python processing (like in blenders sverchok addon)例如,如果您想要嵌套列表以进行进一步的 Python 处理(例如在 Blenders sverchok 插件中),这将很有帮助
More complex example:更复杂的例子:
Say you want to have a list of lists of vertices with 3 floats each, and have created a cdata float array like this:假设您想要一个包含 3 个浮点数的顶点列表的列表,并创建了一个 cdata 浮点数数组,如下所示:
cverts = ffi.new("float [][3]", nverts * num)
as output parameter for a function like:作为函数的输出参数,如:
lib.myfunction(... other input...., num, nverts, cverts)
Cutting this list of verts into num sub-lists of nverts verts each you could do as follows then:将此verts列表切割为nverts verts 的num个子列表,然后您可以执行以下操作:
flat_size = 4 * 3 * nverts * num
verts = np.frombuffer(ffi.buffer(cverts, flat_size), dtype=np.float32)
verts.shape = (num, nverts, 3)
verts = verts.tolist()
verts should look for example like [[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]]
then. verts 应该看起来像
[[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]]
然后。
Since CFFI version 1.12, you can create an appropriately typed pointer to a NumPy array with a single call to FFI.from_buffer
:从 CFFI 1.12 版开始,您可以通过对
FFI.from_buffer
的一次调用来创建一个适当类型的指向 NumPy 数组的指针:
array = np.zeros(16, dtype=np.float32)
pointer = ffi.from_buffer("float[]", array)
C code that writes to the array behind this pointer will directly mutate the original NumPy array.写入此指针后面的数组的 C 代码将直接改变原始 NumPy 数组。 There is no need to "get the result out".
没有必要“得出结果”。
You may want to call numpy.ascontiguousarray
before passing it to the buffer if there is a chance the array does not have a C_CONTIGUOUS
memory layout.如果数组可能没有
C_CONTIGUOUS
内存布局,您可能希望在将其传递到缓冲区之前调用numpy.ascontiguousarray
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.