利用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服务器端收发请求的实现代码
Sep 29 Python
python中import reload __import__的区别详解
Oct 16 Python
Python简单定义与使用二叉树示例
May 11 Python
解决安装pycharm后不能执行python脚本的问题
Jan 19 Python
Python字典推导式将cookie字符串转化为字典解析
Aug 10 Python
python实现代码统计器
Sep 19 Python
Python版中国省市经纬度
Feb 11 Python
Python爬虫requests库多种用法实例
May 28 Python
Python如何读写二进制数组数据
Aug 01 Python
Python如何测试stdout输出
Aug 10 Python
Django Model层F,Q对象和聚合函数原理解析
Nov 12 Python
实例讲解Python中sys.argv[]的用法
Jun 03 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
smarty模板引擎之分配数据类型
2015/03/30 PHP
在WordPress中实现评论头像的自定义默认和延迟加载
2015/11/24 PHP
你真的了解JavaScript吗?
2007/02/24 Javascript
javascript iframe内的函数调用实现方法
2009/07/19 Javascript
用RadioButten或CheckBox实现div的显示与隐藏
2013/09/21 Javascript
thinkphp 表名 大小写 窍门
2015/02/01 Javascript
javascript实现数独解法
2015/03/14 Javascript
学习JavaScript编程语言的8张思维导图分享
2015/03/27 Javascript
原生JS和jQuery操作DOM对比总结
2017/01/19 Javascript
微信小程序如何获知用户运行小程序的场景教程
2017/05/17 Javascript
浅谈angular4 ng-content 中隐藏的内容
2017/08/18 Javascript
Vue利用canvas实现移动端手写板的方法
2018/05/03 Javascript
React全家桶环境搭建过程详解
2018/05/18 Javascript
详解Angularjs 自定义指令中的数据绑定
2018/07/19 Javascript
clipboard在vue中的使用的方法示例
2018/10/19 Javascript
基于layui table返回的值的多级嵌套的解决方法
2019/09/19 Javascript
vue element-ui实现动态面包屑导航
2019/12/23 Javascript
详解vue中在循环中使用@mouseenter 和 @mouseleave事件闪烁问题解决方法
2020/04/07 Javascript
Vant+postcss-pxtorem 实现浏览器适配功能
2021/02/05 Javascript
[03:59]5分钟带你了解什么是DOTA2(第二期)
2017/02/07 DOTA
在SAE上部署Python的Django框架的一些问题汇总
2015/05/30 Python
Python使用正则表达式过滤或替换HTML标签的方法详解
2017/09/25 Python
python使用response.read()接收json数据的实例
2018/12/19 Python
python自动发送测试报告邮件功能的实现
2019/01/22 Python
django celery redis使用具体实践
2019/04/08 Python
详解Python3网络爬虫(二):利用urllib.urlopen向有道翻译发送数据获得翻译结果
2019/05/07 Python
python2和python3实现在图片上加汉字的方法
2019/08/22 Python
python PIL/cv2/base64相互转换实例
2020/01/09 Python
python + selenium 刷B站播放量的实例代码
2020/06/12 Python
Expedia印度:您的一站式在线旅游网站
2017/08/24 全球购物
文明宿舍获奖感言
2014/02/07 职场文书
人身损害赔偿协议书范本
2014/09/27 职场文书
教师批评与自我批评总结
2014/10/16 职场文书
健康教育主题班会
2015/08/14 职场文书
2019朋友新婚祝福语精选
2019/10/10 职场文书
Oracle笔记
2021/04/05 Oracle