利用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 28 Python
Python字符串拼接的几种方法整理
Aug 02 Python
python2.7 json 转换日期的处理的示例
Mar 07 Python
解决Matplotlib图表不能在Pycharm中显示的问题
May 24 Python
Python DataFrame.groupby()聚合函数,分组级运算
Sep 18 Python
python logging添加filter教程
Dec 24 Python
pytorch 状态字典:state_dict使用详解
Jan 17 Python
使用python创建生成动态链接库dll的方法
May 09 Python
pycharm 实现本地写代码,服务器运行的操作
Jun 08 Python
Python自定义sorted排序实现方法详解
Sep 18 Python
Docker如何部署Python项目的实现详解
Oct 26 Python
Python制作动态字符画的源码
Aug 04 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 上一篇,下一篇文章实现代码与原理说明
2010/05/09 PHP
分享一下贝贝成长进度的php代码
2012/09/14 PHP
laravel 解决强制跳转 https的问题
2019/10/22 PHP
JavaScript 类似flash效果的立体图片浏览器
2010/02/08 Javascript
JS Excel读取和写入操作(模板操作)实现代码
2010/04/11 Javascript
计算世界完全对称日的js代码,粗糙版
2011/11/04 Javascript
Javacript实现颜色梯度变化和渐变的效果代码
2013/05/31 Javascript
javascript中比较字符串是否相等的方法
2013/07/23 Javascript
jQuery处理xml格式的返回数据(实例解析)
2013/11/28 Javascript
jQuery zTree加载树形菜单功能
2016/02/25 Javascript
boostrapTable的refresh和refreshOptions区别浅析
2017/01/22 Javascript
canvas仿iwatch时钟效果
2017/03/06 Javascript
纯JS实现图片验证码功能并兼容IE6-8(推荐)
2017/04/19 Javascript
jquery submit()不能提交表单的解决方法
2017/04/24 jQuery
Swiper实现轮播图效果
2017/07/03 Javascript
js防刷新的倒计时代码 js倒计时代码
2017/09/06 Javascript
Node.js连接Sql Server 2008及数据层封装详解
2018/08/27 Javascript
浅谈vue.use()方法从源码到使用
2019/05/12 Javascript
vue-cli脚手架引入弹出层layer插件的几种方法
2019/06/24 Javascript
jQuery实现异步上传一个或多个文件
2020/08/17 jQuery
如何使用jQuery操作Cookies方法解析
2020/09/08 jQuery
Python简单实现子网掩码转换的方法
2016/04/13 Python
通过Python 获取Android设备信息的轻量级框架
2017/12/18 Python
Python实现的连接mssql数据库操作示例
2018/08/17 Python
Python日期时间模块datetime详解与Python 日期时间的比较,计算实例代码
2018/09/14 Python
深入理解Django-Signals信号量
2019/02/19 Python
pyqt 实现QlineEdit 输入密码显示成圆点的方法
2019/06/24 Python
详解Python中的format格式化函数的使用方法
2019/11/20 Python
python代码打印100-999之间的回文数示例
2019/11/24 Python
Python2和Python3中@abstractmethod使用方法
2020/02/04 Python
html5 Canvas绘制线条 closePath()实例代码
2012/05/10 HTML / CSS
俄罗斯眼镜网: optikaworld
2016/07/31 全球购物
香港优质食材和美酒专门店:FoodWise
2017/09/01 全球购物
机械系大学毕业生推荐信
2013/11/27 职场文书
介绍信怎么写
2015/05/05 职场文书
能用CSS实现的就不要麻烦JavaScript了
2021/10/05 HTML / CSS