python调用C函数时的数组传递

python调用C函数时的数组传递

2019-4-5

引言:最近需要对一个算法进行并行加速,最初使用python实现的,也尝试了用python中的多线程进行加速,后来才发现,python中的threading受制于GIL,同时只能使用一个核进行运算,所以搞了半天最后发现多线程和非并行算法在运行时间上无差别。当然我也尝试了multiprocessing模块,但我那个算法不适合用多进程的方法加速,反而会导致程序运行时间变长,所以我就只好退而求其次,需要加速的部分用C语言重写,并通过C的多线程来进行加速。所以就涉及到了python调用C函数的问题。因为需要将python中的numpy数组传递到C函数中,为了实现这一部分也花了两个晚上才折腾明白,所以记录下来,我自己也是刚学这一部分,可能无法写的特别精致到位,只是把我自己觉得有用的部分记录。

1. ctypes

ctypes是python提供的一个用来调用C函数的模块。在网上看到很多人都建议用ctypes的方法在python中调用C的动态链接库。具体关于ctypes的介绍可以去官网上看一下。

from ctypes import *
2. C函数

2.1 编写C函数
由于ctypes的功劳,c函数部分按照正常写c的方法写即可。下面是我测试时写的一个c文件:

//ctype_test.c

#include 

void onedemiarr(const int * a, int n, int * b){
    puts("onedemiarr starts!\n");
    int i;
    
    for(i = 0; i < n; i++){
        b[i] = a[i] * 2; 
        } 
         
    puts("onedemiarr ends!");
    return;
}

void twodemiarr(const int (*a)[3], int m, int n, int (*b)[3]){
    puts("twodemiarr starts!\n");
    int i, j;

    for(i = 0; i < m; i++){
        for(j = 0; j < n; j++){
            b[i][j] = a[i][j] * 2; 
        } 
    }
    puts("twodemiarr ends!\n");
    return;
}

函数onedemiarr, twodemiarr均为将数组a的内容乘以2放入数组b中。

在这里想说的是,写完C代码后,一定要都测试一下自己的C函数写的是否正确,是否可以通过main函数来正常调用,不然不经测试即在python中使用,很可能出错了也没办法发现是C函数本身的错(我自己就是这么被自己坑过来的)。

2.2 编译动态库
使用gcc命令将c函数编译为动态链接库,因为我是在windows下写的,所以编译成.dll格式,如果是linux下就是.so格式了。

gcc -shared ctype_test.c -o ctest.dll   //-shared说明这是一个动态库

需要注意的是,生成的动态链接库如果是32位的(根据电脑上的MinGW位数而定),则只能被32位的python调用,如果用64位的python调用32位的动态链接库会报错。

3. python部分

当我们编译好动态链接库后,主要的工作就在python部分了。python中需要做的事情有:引入动态链接库,定义参数,申明动态链接库中函数的参数格式,调用C函数。

#ctype_test.py

from ctypes import *
import numpy as np
import numpy.ctypeslib as npct

if __name__ == '__main__':
    arr_1 = np.array([1,2,3,4], dtype = np.int)
    arr_2 = np.ndarray((4,), dtype = np.int)
    arr_3 = np.array([[1,2,3],[4,5,6]], dtype = np.int)
    arr_4 = np.ndarray((2,3), dtype = np.int)
    
    lib = npct.load_library("ctest",".")  #引入动态链接库,load_library的用法可参考官网文档
    
    '''
    申明函数onedemiarr, twodemiarr的传入参数类型;ndpointer为numpy.ctypeslib扩展库中提供的函数,
    可将numpy数组转为指针形式被C函数识别, 其中ndim表示数组维数,还有一个shape参数(可选)表示数组格式,
    flags = "C_CONTIGUOUS" 表示是和C语言相似的数组存放方式。
    '''
    lib.onedemiarr.argtypes = [npct.ndpointer(dtype = np.int, ndim = 1, flags="C_CONTIGUOUS"),
    c_int, npct.ndpointer(dtype = np.int, ndim = 1, flags="C_CONTIGUOUS")]
    
    lib.twodemiarr.argtypes = [npct.ndpointer(dtype = np.int, ndim = 2, shape = (2, 3), flags="C_CONTIGUOUS"),
    c_int, c_int, npct.ndpointer(dtype = np.int, ndim = 2, shape = (2,3), flags="C_CONTIGUOUS")]
    
    lib.onedemiarr(arr_1, c_int(4), arr_2)    #函数调用
    print(arr_1,arr_2) #将数组内容打印出来,就会发现此时arr_2数组的内容已经被改变。
    print("**----------------**")
    lib.twodemiarr(arr_3, c_int(2), c_int(3), arr_4)
    print(arr_3, "\n",arr_4)

确认python代码无误了,就可以执行python程序了。

python ctype_test.py

以上内容均已测试无误,测试平台为:Windows10, MinGW32位,python32位。

你可能感兴趣的:(python,C/C++,C/C++总结,Python学习笔记)