视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
利用ctypes提高Python的执行速度方法介绍
2020-11-27 14:24:47 责编:小采
文档


这篇文章给大家介绍了如何利用ctypes提高Python的执行速度,对大家学习使用python具有一定的参考借鉴价值。有需要的朋友们一起来看看吧。

">

前言

ctypes是Python的外部函数库。它提供了C兼容的数据类型,并且允许调用动态链接库/共享库中的函数。它可以将这些库包装起来给Python使用。这个引入C语言的接口可以帮助我们做很多事情,比如需要调用C代码的来提高性能的一些小型问题。通过它你可以接入Windows系统上的 kernel32.dll 和 msvcrt.dll 动态链接库,以及Linux系统上的 libc.so.6 库。当然你也可以使用自己的编译好的共享库

我们先来看一个简单的例子 我们使用 Python 求 1000000 以内素数,重复这个过程10次,并计算运行时间。

import math
from timeit import timeit
def check_prime(x):
values = xrange(2, int(math.sqrt(x)) + 1)
for i in values:
if x % i == 0:
return False
return True
def get_prime(n):
return [x for x in xrange(2, n) if check_prime(x)]
print timeit(stmt='get_prime(1000000)', setup='from __main__ import get_prime',
number=10)

输出

42.8259568214

下面用C语言写一个的 check_prime 函数,然后把它当作共享库(动态链接库)导入

#include <stdio.h>
#include <math.h>
int check_prime(int a)
{
int c;
for ( c = 2 ; c <= sqrt(a) ; c++ ) {
if ( a%c == 0 )
return 0;
}
return 1;
}

使用以下命令生成 .so (shared object)文件

gcc -shared -o prime.so -fPIC prime.c
import ctypes
import math
from timeit import timeit
check_prime_in_c = ctypes.CDLL('./prime.so').check_prime
def check_prime_in_py(x):
values = xrange(2, int(math.sqrt(x)) + 1)
for i in values:
if x % i == 0:
return False
return True
def get_prime_in_c(n):
return [x for x in xrange(2, n) if check_prime_in_c(x)]
def get_prime_in_py(n):
return [x for x in xrange(2, n) if check_prime_in_py(x)]
py_time = timeit(stmt='get_prime_in_py(1000000)', setup='from __main__ import get_prime_in_py',
number=10)
c_time = timeit(stmt='get_prime_in_c(1000000)', setup='from __main__ import get_prime_in_c',
number=10)
print "Python version: {} seconds".format(py_time)
print "C version: {} seconds".format(c_time)

输出

Python version: 43.4539749622 seconds
C version: 8.56250786781 seconds

我们可以看到很明显的性能差距 这里有更多的方法去判断一个数是否是素数

再来看一个复杂点的例子 快速排序

mylib.c

#include <stdio.h>
typedef struct _Range {
int start, end;
} Range;
Range new_Range(int s, int e) {
Range r;
r.start = s;
r.end = e;
return r;
}
void swap(int *x, int *y) {
int t = *x;
*x = *y;
*y = t;
}
void quick_sort(int arr[], const int len) {
if (len <= 0)
return;
Range r[len];
int p = 0;
r[p++] = new_Range(0, len - 1);
while (p) {
Range range = r[--p];
if (range.start >= range.end)
continue;
int mid = arr[range.end];
int left = 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 -o mylib.so -fPIC mylib.c

使用ctypes有一个麻烦点的地方是原生的C代码使用的类型可能跟Python不能明确的对应上来。比如这里什么是Python中的数组?列表?还是 array 模块中的一个数组。所以我们需要进行转换

test.py

import ctypes
import time
import random
quick_sort = ctypes.CDLL('./mylib.so').quick_sort
nums = []
for _ in range(100):
r = [random.randrange(1, 100000000) for x in xrange(100000)]
arr = (ctypes.c_int * len(r))(*r)
nums.append((arr, len(r)))
init = time.clock()
for i in range(100):
quick_sort(nums[i][0], nums[i][1])
print "%s" % (time.clock() - init)

输出

1.874907

与Python list 的 sort 方法进行对比

import ctypes
import time
import random
quick_sort = ctypes.CDLL('./mylib.so').quick_sort
nums = []
for _ in range(100):
nums.append([random.randrange(1, 100000000) for x in xrange(100000)])
init = time.clock()
for i in range(100):
nums[i].sort()
print "%s" % (time.clock() - init)

输出

2.501257

至于结构体,需要定义一个类,包含相应的字段和类型

class Point(ctypes.Structure):
_fields_ = [('x', ctypes.c_double),
('y', ctypes.c_double)]

除了导入我们自己写的C语言扩展文件,我们还可以直接导入系统提供的库文件,比如linux下c标准库的实现 glibc

import time
import random
from ctypes import cdll
libc = cdll.LoadLibrary('libc.so.6') # Linux系统
# libc = cdll.msvcrt # Windows系统
init = time.clock()
randoms = [random.randrange(1, 100) for x in xrange(1000000)]
print "Python version: %s seconds" % (time.clock() - init)
init = time.clock()
randoms = [(libc.rand() % 100) for x in xrange(1000000)]
print "C version : %s seconds" % (time.clock() - init)

输出

Python version: 0.850172 seconds
C version : 0.275 seconds

总结

下载本文
显示全文
专题