将一组NumPy数组传递给C函数以进行输入和输出

时间:2021-09-02 17:01:48

Let's assume we have a C function that takes a set of one or more input arrays, processes them, and writes its output into a set of output arrays. The signature looks as follows (with count representing the number of array elements to be processed):

假设我们有一个C函数,它接受一组一个或多个输入数组,处理它们,并将其输出写入一组输出数组。签名如下所示(计数表示要处理的数组元素的数量):

void compute (int count, float** input, float** output)

I want to call this function from Python via ctypes and use it to apply a transformation to a set of NumPy arrays. For a one-input/one-output function defined as

我想通过ctypes从Python调用此函数,并使用它将转换应用于一组NumPy数组。对于定义为的单输入/单输出功能

void compute (int count, float* input, float* output)

the following works:

以下作品:

import ctypes
import numpy

from numpy.ctypeslib import ndpointer

lib = ctypes.cdll.LoadLibrary('./block.so')
fun = lib.compute
fun.restype = None
fun.argtypes = [ctypes.c_int,
                ndpointer(ctypes.c_float),
                ndpointer(ctypes.c_float)]

data = numpy.ones(1000).astype(numpy.float32)
output = numpy.zeros(1000).astype(numpy.float32)
fun(1000, data, output)

However, I have no clue how to create the corresponding pointer array for multiple inputs (and/or outputs). Any ideas?

但是,我不知道如何为多个输入(和/或输出)创建相应的指针数组。有任何想法吗?

Edit: So people have been wondering how compute knows how many array pointers to expect (as count refers to the number of elements per array). This is, in fact, hard-coded; a given compute knows precisely how many inputs and outputs to expect. It's the caller's job to verify that input and output point to the right number of inputs and outputs. Here's an example compute taking 2 inputs and writing to 1 output array:

编辑:所以人们一直想知道计算器如何知道预期的数组指针数(因为计数指的是每个数组的元素数)。事实上,这是硬编码的;给定的计算精确地知道预期的输入和输出数量。验证输入和输出指向正确数量的输入和输出是调用者的工作。这是一个计算2个输入并写入1个输出数组的示例:

virtual void compute (int count, float** input, float** output) {
    float* input0 = input[0];
    float* input1 = input[1];
    float* output0 = output[0];
    for (int i=0; i<count; i++) {
        float fTemp0 = (float)input1[i];
        fRec0[0] = ((0.09090909090909091f * fTemp0) + (0.9090909090909091f * fRec0[1]));
        float fTemp1 = (float)input0[i];
        fRec1[0] = ((0.09090909090909091f * fTemp1) + (0.9090909090909091f * fRec1[1]));
        output0[i] = (float)((fTemp0 * fRec1[0]) - (fTemp1 * fRec0[0]));
        // post processing
        fRec1[1] = fRec1[0];
        fRec0[1] = fRec0[0];
    }
}

I have no way of influencing the signature and implementation of compute. I can verify (from Python!) how many inputs and outputs are required. Key problem is how to give the correct argtypes for the function, and how to produce appropriate data structures in NumPy (an array of pointers to NumPy arrays).

我无法影响计算的签名和实现。我可以验证(来自Python!)需要多少输入和输出。关键问题是如何为函数提供正确的argtypes,以及如何在NumPy(指向NumPy数组的指针数组)中生成适当的数据结构。

2 个解决方案

#1


7  

To do this specifically with Numpy arrays, you could use:

要使用Numpy数组专门执行此操作,您可以使用:

import numpy as np
import ctypes

count = 5
size = 1000

#create some arrays
arrays = [np.arange(size,dtype="float32") for ii in range(count)] 

#get ctypes handles
ctypes_arrays = [np.ctypeslib.as_ctypes(array) for array in arrays]

#Pack into pointer array
pointer_ar = (ctypes.POINTER(C.c_float) * count)(*ctypes_arrays)

ctypes.CDLL("./libfoo.so").foo(ctypes.c_int(count), pointer_ar, ctypes.c_int(size))

Where the C side of things might look like:

事物的C面可能是这样的:

# function to multiply all arrays by 2
void foo(int count, float** array, int size)
{
   int ii,jj;
   for (ii=0;ii<count;ii++){
      for (jj=0;jj<size;jj++)
         array[ii][jj] *= 2;    
   }

}

#2


2  

In C, float** points to first element in a table/array of float* pointers.

在C中,float **指向float *指针的表/数组中的第一个元素。

Presumably each of those float* points to first element in a table/array of float values.

据推测,每个float *都指向浮点值的表/数组中的第一个元素。

Your function declaration has 1 count, however it's not clear what this count applies to:

您的函数声明有1个计数,但是不清楚这个计数适用于什么:

void compute (int count, float** input, float** output)
  • 2D matrix count x count in size?
  • 2D矩阵计数x计数大小?
  • count -sized array of float* each somehow terminated, e.g. with nan?
  • count -sized float of float *以某种方式终止,例如与南?
  • null-terminated array of float* each of count elements (reasonable assumption)?
  • 以null为终点的float *每个count元素数组(合理的假设)?

Please clarify your question and I will clarify my answer :-)

请澄清你的问题,我会澄清我的答案:-)

Assuming the last API interpretation, here's my sample compute function:

假设最后的API解释,这是我的示例计算函数:

/* null-terminated array of float*, each points to count-sized array
*/
extern void compute(int count, float** in, float** out)
{
    while (*in)
    {
        for (int i=0; i<count; i++)
        {
            (*out)[i] = (*in)[i]*42;
        }
        in++; out++;
    }
}

Test code for the sample compute function:

测试计算函数的测试代码:

#include <stdio.h>
extern void compute(int count, float** in, float** out);

int main(int argc, char** argv)
{
#define COUNT 3
    float ina[COUNT] = { 1.5, 0.5, 3.0 };
    float inb[COUNT] = { 0.1, -0.2, -10.0 };
    float outa[COUNT];
    float outb[COUNT];
    float* in[] = {ina, inb, (float*)0};
    float* out[] = {outa, outb, (float*)0};

    compute(COUNT, in, out);

    for (int row=0; row<2; row++)
        for (int c=0; c<COUNT; c++)
            printf("%d %d %f %f\n", row, c, in[row][c], out[row][c]);
    return 0;
}

And how you use same via ctypes in Python for count == 10 float subarrays and size 2 float* array, containing 1 real subarray and NULL terminator:

以及如何通过Python中的ctypes使用相同的count == 10浮点子数组和大小为2的float *数组,包含1个真正的子数组和NULL终结符:

import ctypes

innertype = ctypes.ARRAY(ctypes.c_float, 10)
outertype = ctypes.ARRAY(ctypes.POINTER(ctypes.c_float), 2)

in1 = innertype(*range(10))
in_ = outertype(in1, None)
out1 = innertype(*range(10))
out = outertype(out1, None)

ctypes.CDLL("./compute.so").compute(10, in_, out)

for i in range(10): print in_[0][i], out[0][i]

Numpy interface to ctypes is covered here http://www.scipy.org/Cookbook/Ctypes#head-4ee0c35d45f89ef959a7d77b94c1c973101a562f, arr.ctypes.shape[:] arr.ctypes.strides[:] and arr.ctypes.data are what you need; you might be able to feed that directly to your compute.

这里介绍了ctypes的Numpy接口http://www.scipy.org/Cookbook/Ctypes#head-4ee0c35d45f89ef959a7d77b94c1c973101a562f,arr.ctypes.shape [:] arr.ctypes.strides [:]和arr.ctypes.data就是你的意思需要;您可能可以直接将其提供给您的计算机。

Here's an example:

这是一个例子:

In [55]: a = numpy.array([[0.0]*10]*2, dtype=numpy.float32)

In [56]: ctypes.cast(a.ctypes.data, ctypes.POINTER(ctypes.c_float))[0]
Out[56]: 0.0

In [57]: ctypes.cast(a.ctypes.data, ctypes.POINTER(ctypes.c_float))[0] = 1234

In [58]: a
Out[58]: 
array([[ 1234.,     0.,     0.,     0.,     0.,     0.,     0.,     0.,
            0.,     0.],
       [    0.,     0.,     0.,     0.,     0.,     0.,     0.,     0.,
            0.,     0.]], dtype=float32)

#1


7  

To do this specifically with Numpy arrays, you could use:

要使用Numpy数组专门执行此操作,您可以使用:

import numpy as np
import ctypes

count = 5
size = 1000

#create some arrays
arrays = [np.arange(size,dtype="float32") for ii in range(count)] 

#get ctypes handles
ctypes_arrays = [np.ctypeslib.as_ctypes(array) for array in arrays]

#Pack into pointer array
pointer_ar = (ctypes.POINTER(C.c_float) * count)(*ctypes_arrays)

ctypes.CDLL("./libfoo.so").foo(ctypes.c_int(count), pointer_ar, ctypes.c_int(size))

Where the C side of things might look like:

事物的C面可能是这样的:

# function to multiply all arrays by 2
void foo(int count, float** array, int size)
{
   int ii,jj;
   for (ii=0;ii<count;ii++){
      for (jj=0;jj<size;jj++)
         array[ii][jj] *= 2;    
   }

}

#2


2  

In C, float** points to first element in a table/array of float* pointers.

在C中,float **指向float *指针的表/数组中的第一个元素。

Presumably each of those float* points to first element in a table/array of float values.

据推测,每个float *都指向浮点值的表/数组中的第一个元素。

Your function declaration has 1 count, however it's not clear what this count applies to:

您的函数声明有1个计数,但是不清楚这个计数适用于什么:

void compute (int count, float** input, float** output)
  • 2D matrix count x count in size?
  • 2D矩阵计数x计数大小?
  • count -sized array of float* each somehow terminated, e.g. with nan?
  • count -sized float of float *以某种方式终止,例如与南?
  • null-terminated array of float* each of count elements (reasonable assumption)?
  • 以null为终点的float *每个count元素数组(合理的假设)?

Please clarify your question and I will clarify my answer :-)

请澄清你的问题,我会澄清我的答案:-)

Assuming the last API interpretation, here's my sample compute function:

假设最后的API解释,这是我的示例计算函数:

/* null-terminated array of float*, each points to count-sized array
*/
extern void compute(int count, float** in, float** out)
{
    while (*in)
    {
        for (int i=0; i<count; i++)
        {
            (*out)[i] = (*in)[i]*42;
        }
        in++; out++;
    }
}

Test code for the sample compute function:

测试计算函数的测试代码:

#include <stdio.h>
extern void compute(int count, float** in, float** out);

int main(int argc, char** argv)
{
#define COUNT 3
    float ina[COUNT] = { 1.5, 0.5, 3.0 };
    float inb[COUNT] = { 0.1, -0.2, -10.0 };
    float outa[COUNT];
    float outb[COUNT];
    float* in[] = {ina, inb, (float*)0};
    float* out[] = {outa, outb, (float*)0};

    compute(COUNT, in, out);

    for (int row=0; row<2; row++)
        for (int c=0; c<COUNT; c++)
            printf("%d %d %f %f\n", row, c, in[row][c], out[row][c]);
    return 0;
}

And how you use same via ctypes in Python for count == 10 float subarrays and size 2 float* array, containing 1 real subarray and NULL terminator:

以及如何通过Python中的ctypes使用相同的count == 10浮点子数组和大小为2的float *数组,包含1个真正的子数组和NULL终结符:

import ctypes

innertype = ctypes.ARRAY(ctypes.c_float, 10)
outertype = ctypes.ARRAY(ctypes.POINTER(ctypes.c_float), 2)

in1 = innertype(*range(10))
in_ = outertype(in1, None)
out1 = innertype(*range(10))
out = outertype(out1, None)

ctypes.CDLL("./compute.so").compute(10, in_, out)

for i in range(10): print in_[0][i], out[0][i]

Numpy interface to ctypes is covered here http://www.scipy.org/Cookbook/Ctypes#head-4ee0c35d45f89ef959a7d77b94c1c973101a562f, arr.ctypes.shape[:] arr.ctypes.strides[:] and arr.ctypes.data are what you need; you might be able to feed that directly to your compute.

这里介绍了ctypes的Numpy接口http://www.scipy.org/Cookbook/Ctypes#head-4ee0c35d45f89ef959a7d77b94c1c973101a562f,arr.ctypes.shape [:] arr.ctypes.strides [:]和arr.ctypes.data就是你的意思需要;您可能可以直接将其提供给您的计算机。

Here's an example:

这是一个例子:

In [55]: a = numpy.array([[0.0]*10]*2, dtype=numpy.float32)

In [56]: ctypes.cast(a.ctypes.data, ctypes.POINTER(ctypes.c_float))[0]
Out[56]: 0.0

In [57]: ctypes.cast(a.ctypes.data, ctypes.POINTER(ctypes.c_float))[0] = 1234

In [58]: a
Out[58]: 
array([[ 1234.,     0.,     0.,     0.,     0.,     0.,     0.,     0.,
            0.,     0.],
       [    0.,     0.,     0.,     0.,     0.,     0.,     0.,     0.,
            0.,     0.]], dtype=float32)