利用ctypes提高Python的执行速度


Posted in Python onSeptember 09, 2016

前言

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.27645 seconds

总结

以上就是这篇文章的全部内容,希望对大家学习或使用Python能有一定的帮助,如果有疑问大家可以留言交流。

Python 相关文章推荐
使用python绘制人人网好友关系图示例
Apr 01 Python
python正则表达式re模块详细介绍
May 29 Python
使用Python的Twisted框架构建非阻塞下载程序的实例教程
May 25 Python
谈谈如何手动释放Python的内存
Dec 17 Python
Python中创建字典的几种方法总结(推荐)
Apr 27 Python
Python实现将SQLite中的数据直接输出为CVS的方法示例
Jul 13 Python
详解Django的model查询操作与查询性能优化
Oct 16 Python
Python子类继承父类构造函数详解
Feb 19 Python
Python 实现数据结构中的的栈队列
May 16 Python
Python 使用多属性来进行排序
Sep 01 Python
用Python的绘图库(matplotlib)绘制小波能量谱
Apr 17 Python
pytorch训练神经网络爆内存的解决方案
May 22 Python
python实现批量监控网站
Sep 09 #Python
利用python批量检查网站的可用性
Sep 09 #Python
Python如何判断数独是否合法
Sep 08 #Python
python框架django基础指南
Sep 08 #Python
python中星号变量的几种特殊用法
Sep 07 #Python
Python 实现 贪吃蛇大作战 代码分享
Sep 07 #Python
python 转换 Javascript %u 字符串为python unicode的代码
Sep 06 #Python
You might like
TMDPHP 模板引擎使用教程
2012/03/13 PHP
php使用gd2绘制基本图形示例(直线、圆、正方形)
2017/02/15 PHP
老生常谈ThinkPHP中的行为扩展和插件(推荐)
2017/05/05 PHP
PHP+MySQL实现模糊查询员工信息功能示例
2018/06/01 PHP
PHP-FPM 设置多pool及配置文件重写操作示例
2019/10/02 PHP
Avengerls vs KG BO3 第三场2.18
2021/03/10 DOTA
JavaScript打字小游戏代码
2011/12/26 Javascript
JS实现DIV容器赋值的方法
2015/12/14 Javascript
js实现搜索框关键字智能匹配代码
2020/03/26 Javascript
AngularJs基于角色的前端访问控制的实现
2016/11/07 Javascript
Bootstrap3 内联单选和多选框
2016/12/29 Javascript
Bootstrap栅格系统使用方法及页面调整变形的解决方法
2017/03/10 Javascript
vue和iview实现Scroll 数据无限滚动功能
2019/10/31 Javascript
vue实现将数据存入vuex中以及从vuex中取出数据
2019/11/08 Javascript
这样回答继承可能面试官更满意
2019/12/10 Javascript
python操作xml文件详细介绍
2014/06/09 Python
Python实现简单的可逆加密程序实例
2015/03/05 Python
python文件选择对话框的操作方法
2019/06/27 Python
python实现人像动漫化的示例代码
2020/05/17 Python
使用OpenCV实现道路车辆计数的使用方法
2020/07/15 Python
Python 如何测试文件是否存在
2020/07/31 Python
Python getsizeof()和getsize()区分详解
2020/11/20 Python
DRF使用simple JWT身份验证的实现
2021/01/14 Python
理肤泉加拿大官网:La Roche-Posay加拿大
2018/07/06 全球购物
Derek Rose官网:英国高档睡衣、家居服和内衣品牌
2020/01/18 全球购物
size?荷兰官方网站:英国高级运动鞋精品店
2020/07/24 全球购物
Perfume’s Club澳大利亚官网:西班牙领先的在线美容店
2021/02/01 全球购物
小学运动会广播稿200字(十二篇)
2014/01/14 职场文书
个人求职信范文分享
2014/01/31 职场文书
实习推荐信
2014/05/10 职场文书
2014年党员自我评议(5篇)
2014/09/12 职场文书
党员四风问题对照检查材料思想汇报
2014/09/16 职场文书
教学改革问题查摆整改措施
2014/09/27 职场文书
女人创业励志语录,句句蕴含能量,激发你的潜能
2019/08/20 职场文书
python基础详解之if循环语句
2021/04/24 Python
大脑的记忆过程在做数据压缩,不同图形也有共同的记忆格式
2022/04/29 数码科技