利用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 相关文章推荐
python3.6+opencv3.4实现鼠标交互查看图片像素
Feb 26 Python
python实现日常记账本小程序
Mar 10 Python
使用NumPy和pandas对CSV文件进行写操作的实例
Jun 14 Python
Django model反向关联名称的方法
Dec 15 Python
Python实现获取系统临时目录及临时文件的方法示例
Jun 26 Python
python中pygame安装过程(超级详细)
Aug 04 Python
python excel转换csv代码实例
Aug 26 Python
python文件处理fileinput使用方法详解
Jan 02 Python
Python要求O(n)复杂度求无序列表中第K的大元素实例
Apr 02 Python
Python类中的装饰器在当前类中的声明与调用详解
Apr 15 Python
Python特殊属性property原理及使用方法解析
Oct 09 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 获取客户端真实IP地址多种方法小结
2010/05/15 PHP
php模拟用户自动在qq空间发表文章的方法
2015/01/07 PHP
php构造方法中析构方法在继承中的表现
2016/04/12 PHP
详解Laravel设置多态关系模型别名的方式
2019/10/17 PHP
JQUERY 浏览器判断实现函数
2009/08/20 Javascript
javascript 最常用的10个自定义函数[推荐]
2009/12/26 Javascript
关于Mozilla浏览器不支持innerText的解决办法
2011/01/01 Javascript
浅谈JS原型对象和原型链
2016/03/02 Javascript
完美的js div拖拽实例代码
2016/09/24 Javascript
jquery.multiselect多选下拉框实现代码
2016/11/11 Javascript
详解微信小程序Radio选中样式切换
2017/07/06 Javascript
nodejs body-parser 解析post数据实例
2017/07/26 NodeJs
Vue.js通用应用框架-Nuxt.js的上手教程
2017/12/25 Javascript
node内置调试方法总结
2018/02/22 Javascript
Angular5中调用第三方库及jQuery的添加的方法
2018/06/07 jQuery
浅析Vue项目中使用keep-Alive步骤
2018/07/27 Javascript
Vue自定义弹窗指令的实现代码
2018/08/13 Javascript
JavaScript偏函数与柯里化实例详解
2019/03/27 Javascript
Vue中的循环及修改差值表达式的方法
2019/08/29 Javascript
vue实现文字加密功能
2019/09/27 Javascript
Angular封装表单控件及思想总结
2019/12/11 Javascript
Python httplib,smtplib使用方法
2008/09/06 Python
pygame学习笔记(6):完成一个简单的游戏
2015/04/15 Python
python with提前退出遇到的坑与解决方案
2018/01/05 Python
关于numpy数组轴的使用详解
2019/12/05 Python
Python实现i人事自动打卡的示例代码
2020/01/09 Python
Python如何使用ConfigParser读取配置文件
2020/11/12 Python
HTML5使用drawImage()方法绘制图像
2014/06/23 HTML / CSS
大韩航空官方网站:Korean Air
2017/10/25 全球购物
C++:memset ,memcpy和strcpy的根本区别
2013/04/27 面试题
广州喜创信息技术有限公司JAVA软件工程师笔试题
2012/10/17 面试题
在求职信中如何凸显个人优势
2013/10/30 职场文书
领导干部作风建设自查报告
2014/10/23 职场文书
2015年思想品德教学工作总结
2015/07/22 职场文书
HTML+CSS制作心跳特效的实现
2021/05/26 HTML / CSS
Vue Mint UI mt-swipe的使用方式
2022/06/05 Vue.js