简体   繁体   English

如何将 Numpy 数组传递给 cffi 函数以及如何将其取出?

[英]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:但是,我想改进此代码:

  1. Is there a way to directly access the underlying float buffers of the numpy arrays without copying them?有没有办法直接访问 numpy 数组的底层浮点缓冲区而不复制它们?
  2. 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?是否有一种等效的方法可以在不复制单个元素的情况下将 numpy 数组快速转换为 C 数组?
  3. For some applications, 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.

相关问题 如何使用 CFFI 将多维 Numpy 数组传递给 C 函数? - How to pass multidimensional Numpy arrays to C function using CFFI? 如何将Numpy ND数组转换为CFFI C ++数组并再次返回? - How do I convert a numpy ND-array to a CFFI C++ array and back again? 如何创建 unicode 字符串的 numpy 数组的有效 CFFI 表示? - How to create a valid CFFI representation of a numpy array of unicode strings? 如何使用 CFFI 将指针传递给 function 更改指针 - How to pass a pointer to function which changes the pointer, using CFFI 如何使用Python的CFFI将指针传递给C函数? - How do I pass a pointer to a C function with Python's CFFI? 如何正确地将numpy数组传递给Cython函数? - how to pass numpy array to Cython function correctly? 如何在Python中获取CFFI函数的`const`修饰符? - How can I get the `const` modifier of a CFFI function in Python? 如何在递归循环中继续附加到 numpy 数组,然后打印出最终结果/将其传递给另一个 function? - How to keep appending to a numpy array inside a recurive loop and then print out the final result/pass it to another function? 如何在 numpy 中循环回数组的开头以获取越界索引? - How to loop back to beginning of the array for out of bounds index in numpy? 我如何正确地将字符串数据传递给numpy记录数组(并将其取出) - How do I properly pass string data to a numpy record array (and get it out)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM