利用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利用pyHook实现监听用户鼠标与键盘事件
Aug 21 Python
使用Python标准库中的wave模块绘制乐谱的简单教程
Mar 30 Python
python3抓取中文网页的方法
Jul 28 Python
python用装饰器自动注册Tornado路由详解
Feb 14 Python
Python3实战之爬虫抓取网易云音乐的热门评论
Oct 09 Python
PyQt5每天必学之布局管理
Apr 19 Python
python通过配置文件共享全局变量的实例
Jan 11 Python
python中的数组赋值与拷贝的区别详解
Nov 26 Python
Python 实现向word(docx)中输出
Feb 13 Python
python GUI库图形界面开发之PyQt5信号与槽多窗口数据传递详细使用方法与实例
Mar 08 Python
Python 线性回归分析以及评价指标详解
Apr 02 Python
Python 创建守护进程的示例
Sep 29 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
最令PHP初学者们头痛的十四个问题
2007/01/15 PHP
Optimizer与Debugger兼容性问题的解决方法
2008/12/01 PHP
PHP删除HTMl标签的三种解决方法
2013/06/30 PHP
thinkphp中session和cookie无效的解决方法
2014/12/19 PHP
详解WordPress开发中wp_title()函数的用法
2016/01/07 PHP
Apache无法自动跳转却显示目录的解决方法
2020/11/30 PHP
PHP面相对象中的重载与重写
2017/02/13 PHP
jQuery 核心函数以及jQuery对象
2010/03/23 Javascript
url地址自动加#号问题说明
2010/08/21 Javascript
JavaScript自定义DateDiff函数(兼容所有浏览器)
2012/03/01 Javascript
JavaScript动态创建div属性和样式示例代码
2013/10/09 Javascript
Web开发必知Javascript技巧大全
2016/02/23 Javascript
AngularJS入门教程之过滤器详解
2016/08/19 Javascript
EasyUI中的dataGrid的行内编辑
2017/06/22 Javascript
jQuery表单设置值的方法
2017/06/30 jQuery
基于jQuery的$.getScript方法去加载javaScript文档解析
2017/11/08 jQuery
详解python单例模式与metaclass
2016/01/15 Python
TF-IDF与余弦相似性的应用(一) 自动提取关键词
2017/12/21 Python
python 爬虫 批量获取代理ip的实例代码
2018/05/22 Python
使用Python进行目录的对比方法
2018/11/01 Python
Python jieba库用法及实例解析
2019/11/04 Python
python 中值滤波,椒盐去噪,图片增强实例
2019/12/18 Python
python 动态渲染 mysql 配置文件的示例
2020/11/20 Python
利用HTML5绘制点线面组成的3D图形的示例
2015/05/12 HTML / CSS
解析HTML5的存储功能和web SQL的相关操作方法
2016/02/19 HTML / CSS
linux面试题参考答案(3)
2012/09/13 面试题
夜大毕业生自我鉴定
2013/10/31 职场文书
医药代表个人求职信范本
2013/12/19 职场文书
高一化学教学反思
2014/02/05 职场文书
职位说明书范文
2014/05/07 职场文书
普通党员整改措施
2014/10/24 职场文书
2014年城管个人工作总结
2014/12/08 职场文书
教师工作表现自我评价
2015/03/05 职场文书
党员转正申请报告
2015/05/15 职场文书
2015年企业员工工作总结范文
2015/05/21 职场文书
深入理解margin塌陷和margin合并的解决方案
2021/06/26 HTML / CSS