利用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 相关文章推荐
用yum安装MySQLdb模块的步骤方法
Dec 15 Python
python 把文件中的每一行以数组的元素放入数组中的方法
Apr 29 Python
python调用接口的4种方式代码实例
Nov 19 Python
Python enumerate函数遍历数据对象组合过程解析
Dec 11 Python
Python中sorted()排序与字母大小写的问题
Jan 14 Python
Tensorflow累加的实现案例
Feb 05 Python
如何验证python安装成功
Jul 06 Python
pycharm 配置svn的图文教程(手把手教你)
Jan 15 Python
Python爬虫之爬取二手房信息
Apr 27 Python
python基础学习之生成器与文件系统知识总结
May 25 Python
python tqdm用法及实例详解
Jun 16 Python
pytest实现多进程与多线程运行超好用的插件
Jul 15 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 8小时时间差的解决方法小结
2009/12/22 PHP
3款值得推荐的微信开发开源框架
2014/10/28 PHP
PHP截取指定图片大小的方法
2014/12/10 PHP
php第一次无法获取cookie问题处理
2014/12/15 PHP
Codeigniter通过SimpleXML将xml转换成对象的方法
2015/03/19 PHP
Yii2.0多文件上传实例说明
2017/07/24 PHP
PHP获取文件扩展名的常用方法小结【五种方式】
2018/04/27 PHP
escape、encodeURI 和 encodeURIComponent 的区别
2009/03/02 Javascript
js动画(animate)简单引擎代码示例
2012/12/04 Javascript
javascript操作css属性
2013/12/30 Javascript
jQuery中offset()方法用法实例
2015/01/16 Javascript
jQuery数据类型小结(14个)
2016/01/08 Javascript
jQuery实现简单滚动动画效果
2016/04/07 Javascript
原生JS实现轮播效果+学前端的感受(防止走火入魔)
2016/08/21 Javascript
AngularJs实现聊天列表实时刷新功能
2017/06/15 Javascript
nodejs之koa2请求示例(GET,POST)
2018/08/07 NodeJs
如何在JavaScript中优雅的提取循环内数据详解
2019/03/04 Javascript
Vue如何循环提取对象数组中的值
2020/11/18 Vue.js
JavaScript实现HTML导航栏下拉菜单
2020/11/25 Javascript
[04:31]2016国际邀请赛中国区预选赛妖精采访
2016/06/27 DOTA
python实现自动登录人人网并采集信息的方法
2015/06/28 Python
python批量修改文件编码格式的方法
2018/05/31 Python
python中的字符串内部换行方法
2018/07/19 Python
浅谈Python3中strip()、lstrip()、rstrip()用法详解
2019/04/29 Python
python for循环remove同一个list过程解析
2019/08/14 Python
树莓派3 搭建 django 服务器的实例
2019/08/29 Python
python实现简单的购物程序代码实例
2020/03/03 Python
python获取linux系统信息的三种方法
2020/10/14 Python
西班牙拥有最佳品牌的动物商店:Animalear.com
2018/01/05 全球购物
美国睫毛、眉毛精华液领导品牌:RevitaLash Cosmetics
2018/03/26 全球购物
俄罗斯卫浴采暖及维修用品超级市场:Dkrussia
2020/05/12 全球购物
民事诉讼代理授权委托书范本
2014/10/08 职场文书
庆祝教师节标语
2014/10/09 职场文书
西柏坡导游词
2015/02/05 职场文书
团队合作精神学习心得体会
2016/01/19 职场文书
Python List remove()实例用法详解
2021/08/02 Python