利用ctypes提高Python的执行速度
前言
ctypes是Python的外部函数库。它提供了C兼容的数据类型,并且允许调用动态链接库/共享库中的函数。它可以将这些库包装起来给Python使用。这个引入C语言的接口可以帮助我们做很多事情,比如需要调用C代码的来提高性能的一些小型问题。通过它你可以接入Windows系统上的kernel32.dll和msvcrt.dll动态链接库,以及Linux系统上的libc.so.6库。当然你也可以使用自己的编译好的共享库
我们先来看一个简单的例子我们使用Python求1000000以内素数,重复这个过程10次,并计算运行时间。
importmath fromtimeitimporttimeit defcheck_prime(x): values=xrange(2,int(math.sqrt(x))+1) foriinvalues: ifx%i==0: returnFalse returnTrue defget_prime(n): return[xforxinxrange(2,n)ifcheck_prime(x)] printtimeit(stmt='get_prime(1000000)',setup='from__main__importget_prime', number=10)
输出
42.8259568214
下面用C语言写一个的check_prime函数,然后把它当作共享库(动态链接库)导入
#include<stdio.h>
#include<math.h>
intcheck_prime(inta)
{
intc;
for(c=2;c<=sqrt(a);c++){
if(a%c==0)
return0;
}
return1;
}
使用以下命令生成.so(sharedobject)文件
gcc-shared-oprime.so-fPICprime.c
importctypes
importmath
fromtimeitimporttimeit
check_prime_in_c=ctypes.CDLL('./prime.so').check_prime
defcheck_prime_in_py(x):
values=xrange(2,int(math.sqrt(x))+1)
foriinvalues:
ifx%i==0:
returnFalse
returnTrue
defget_prime_in_c(n):
return[xforxinxrange(2,n)ifcheck_prime_in_c(x)]
defget_prime_in_py(n):
return[xforxinxrange(2,n)ifcheck_prime_in_py(x)]
py_time=timeit(stmt='get_prime_in_py(1000000)',setup='from__main__importget_prime_in_py',
number=10)
c_time=timeit(stmt='get_prime_in_c(1000000)',setup='from__main__importget_prime_in_c',
number=10)
print"Pythonversion:{}seconds".format(py_time)
print"Cversion:{}seconds".format(c_time)
输出
Pythonversion:43.4539749622seconds Cversion:8.56250786781seconds
我们可以看到很明显的性能差距这里有更多的方法去判断一个数是否是素数
再来看一个复杂点的例子快速排序
mylib.c
#include<stdio.h>
typedefstruct_Range{
intstart,end;
}Range;
Rangenew_Range(ints,inte){
Ranger;
r.start=s;
r.end=e;
returnr;
}
voidswap(int*x,int*y){
intt=*x;
*x=*y;
*y=t;
}
voidquick_sort(intarr[],constintlen){
if(len<=0)
return;
Ranger[len];
intp=0;
r[p++]=new_Range(0,len-1);
while(p){
Rangerange=r[--p];
if(range.start>=range.end)
continue;
intmid=arr[range.end];
intleft=range.start,right=range.end-1;
while(left<right){
while(arr[left]<mid&&left<right)
left++;
while(arr[right]>=mid&&left<right)
right--;
swap(&arr[left],&arr[right]);
}
if(arr[left]>=arr[range.end])
swap(&arr[left],&arr[range.end]);
else
left++;
r[p++]=new_Range(range.start,left-1);
r[p++]=new_Range(left+1,range.end);
}
}
gcc-shared-omylib.so-fPICmylib.c
使用ctypes有一个麻烦点的地方是原生的C代码使用的类型可能跟Python不能明确的对应上来。比如这里什么是Python中的数组?列表?还是array模块中的一个数组。所以我们需要进行转换
test.py
importctypes
importtime
importrandom
quick_sort=ctypes.CDLL('./mylib.so').quick_sort
nums=[]
for_inrange(100):
r=[random.randrange(1,100000000)forxinxrange(100000)]
arr=(ctypes.c_int*len(r))(*r)
nums.append((arr,len(r)))
init=time.clock()
foriinrange(100):
quick_sort(nums[i][0],nums[i][1])
print"%s"%(time.clock()-init)
输出
1.874907
与Pythonlist的sort方法进行对比
importctypes
importtime
importrandom
quick_sort=ctypes.CDLL('./mylib.so').quick_sort
nums=[]
for_inrange(100):
nums.append([random.randrange(1,100000000)forxinxrange(100000)])
init=time.clock()
foriinrange(100):
nums[i].sort()
print"%s"%(time.clock()-init)
输出
2.501257
至于结构体,需要定义一个类,包含相应的字段和类型
classPoint(ctypes.Structure):
_fields_=[('x',ctypes.c_double),
('y',ctypes.c_double)]
除了导入我们自己写的C语言扩展文件,我们还可以直接导入系统提供的库文件,比如linux下c标准库的实现glibc
importtime
importrandom
fromctypesimportcdll
libc=cdll.LoadLibrary('libc.so.6')#Linux系统
#libc=cdll.msvcrt#Windows系统
init=time.clock()
randoms=[random.randrange(1,100)forxinxrange(1000000)]
print"Pythonversion:%sseconds"%(time.clock()-init)
init=time.clock()
randoms=[(libc.rand()%100)forxinxrange(1000000)]
print"Cversion:%sseconds"%(time.clock()-init)
输出
Pythonversion:0.850172seconds Cversion:0.27645seconds
总结
以上就是这篇文章的全部内容,希望对大家学习或使用Python能有一定的帮助,如果有疑问大家可以留言交流。