利用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下调用pytesseract识别某网站验证码的实现方法
Jun 06 Python
python获取文件真实链接的方法,针对于302返回码
May 14 Python
python中的常量和变量代码详解
Jul 25 Python
python在html中插入简单的代码并加上时间戳的方法
Oct 16 Python
Python3的介绍、安装和命令行的认识(推荐)
Oct 20 Python
Python3内置模块之json编解码方法小结【推荐】
Dec 09 Python
python脚本开机自启的实现方法
Jun 28 Python
Python笔试面试题小结
Sep 07 Python
PyQt使用QPropertyAnimation开发简单动画
Apr 02 Python
工程师必须了解的LRU缓存淘汰算法以及python实现过程
Oct 15 Python
浅谈Python基础之列表那些事儿
May 11 Python
如何用python清洗文件中的数据
Jun 18 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
虹吸壶煮咖啡26个注意事项
2021/03/03 冲泡冲煮
用PHP制作静态网站的模板框架(二)
2006/10/09 PHP
PHP 开发环境配置(Zend Server安装)
2010/04/28 PHP
php生成随机密码的三种方法小结
2010/09/04 PHP
PHP正确配置mysql(apache环境)
2011/08/28 PHP
解析CodeIgniter自定义配置文件
2013/06/18 PHP
PHP大转盘中奖概率算法实例
2014/10/21 PHP
PHP实现生成唯一会员卡号
2015/08/24 PHP
php实现网页端验证码功能
2017/07/11 PHP
如果文字过长,则将过长的部分变成省略号显示
2006/06/26 Javascript
javascript编程起步(第三课)
2007/02/27 Javascript
javascript 读取xml,写入xml 实现代码
2009/07/10 Javascript
js浮点数保留两位小数点示例代码(四舍五入)
2013/12/26 Javascript
一个检测表单数据的JavaScript实例
2014/10/31 Javascript
javaScript和jQuery自动加载简单代码实现方法
2017/11/24 jQuery
Element-ui tree组件自定义节点使用方法代码详解
2018/09/17 Javascript
微信小程序中如何计算距离某个节日还有多少天
2019/07/15 Javascript
为react组件库添加typescript类型提示的方法
2020/06/15 Javascript
python中迭代器(iterator)用法实例分析
2015/04/29 Python
使用Python的Flask框架来搭建第一个Web应用程序
2016/06/04 Python
django轻松使用富文本编辑器CKEditor的方法
2017/03/30 Python
Python 统计位数为偶数的数字代码详解
2020/03/15 Python
Python 炫技操作之合并字典的七种方法
2020/04/10 Python
详解css3 flex弹性盒自动铺满写法
2020/09/17 HTML / CSS
HTML5 通信API 跨域门槛将不再高、数据推送也不再是梦
2013/04/25 HTML / CSS
美国眼镜网:GlassesUSA
2017/09/07 全球购物
英国一家集合了众多有才华设计师品牌的奢侈店:Wolf & Badger
2018/04/18 全球购物
Public Desire美国/加拿大:全球性的在线鞋类品牌
2018/12/17 全球购物
交通专业个人自荐信格式
2013/09/23 职场文书
项目采购员岗位职责
2014/04/15 职场文书
小学亲子活动总结
2014/07/01 职场文书
乡镇党的群众路线教育实践活动制度建设计划
2014/11/03 职场文书
信用卡工作证明范本
2015/06/19 职场文书
致三级跳运动员加油稿
2015/07/21 职场文书
CSS实现章节添加自增序号的方法
2021/06/23 HTML / CSS
Java基础之线程锁相关知识总结
2021/06/30 Java/Android