利用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使用xauth方式登录饭否网然后发消息
Apr 11 Python
python 按照固定长度分割字符串的方法小结
Apr 30 Python
pandas将numpy数组写入到csv的实例
Jul 04 Python
解决python通过cx_Oracle模块连接Oracle乱码的问题
Oct 18 Python
利用Python将文本中的中英文分离方法
Oct 31 Python
Python 虚拟空间的使用代码详解
Jun 10 Python
在Qt5和PyQt5中设置支持高分辨率屏幕自适应的方法
Jun 18 Python
pytorch 求网络模型参数实例
Dec 30 Python
Pytorch 实现计算分类器准确率(总分类及子分类)
Jan 18 Python
Python基于wordcloud及jieba实现中国地图词云图
Jun 09 Python
Python如何实现自带HTTP文件传输服务
Jul 08 Python
解决selenium+Headless Chrome实现不弹出浏览器自动化登录的问题
Jan 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自动跳转中英文页面
2008/07/29 PHP
PHP 魔术函数使用说明
2010/05/14 PHP
一个经典的PHP验证码类分享
2014/11/18 PHP
javascript类继承机制的原理分析
2009/09/12 Javascript
web基于浏览器的本地存储方法应用
2012/11/27 Javascript
jquery实现marquee效果(文字或者图片的水平垂直滚动)
2013/01/07 Javascript
jqueryUI里拖拽排序示例分析
2015/02/26 Javascript
Angularjs手动解析表达式($parse)
2016/10/12 Javascript
自己封装的一个简单的倒计时功能实例
2016/11/23 Javascript
微信小程序 radio单选框组件详解及实例代码
2017/01/10 Javascript
从零开始学习Node.js系列教程一:http get和post用法分析
2017/04/13 Javascript
vue-cli3.0 特性解读
2018/04/22 Javascript
微信小程序使用wxParse解析html的方法教程
2018/07/06 Javascript
Node.js爬虫如何获取天气和每日问候详解
2019/08/26 Javascript
JS实现“全选”和&quot;全不选&quot;功能代码实例
2020/02/06 Javascript
Python中使用pprint函数进行格式化输出的教程
2015/04/07 Python
python计算方程式根的方法
2015/05/07 Python
python类和函数中使用静态变量的方法
2015/05/09 Python
python比较两个列表是否相等的方法
2015/07/28 Python
Python 出现错误TypeError: ‘NoneType’ object is not iterable解决办法
2017/01/12 Python
Python实现二维数组按照某行或列排序的方法【numpy lexsort】
2017/09/22 Python
Python实现的排列组合、破解密码算法示例
2019/04/12 Python
python3实现从kafka获取数据,并解析为json格式,写入到mysql中
2019/12/23 Python
python定义类的简单用法
2020/07/24 Python
猎人靴英国官网:Hunter Boots
2017/02/02 全球购物
英国家电购物网站:Sonic Direct
2019/03/26 全球购物
小学老师寄语大全
2014/04/04 职场文书
年度安全生产目标责任书
2014/07/23 职场文书
生物学专业求职信
2014/07/23 职场文书
学习党的群众路线剖析材料
2014/10/09 职场文书
优秀党员推荐材料
2014/12/18 职场文书
南极大冒险观后感
2015/06/05 职场文书
2016情人节宣传语
2015/07/14 职场文书
2016师德师风学习心得体会
2016/01/12 职场文书
七年级之开学家长寄语35句
2019/09/05 职场文书
Python使用random模块实现掷骰子游戏的示例代码
2021/04/29 Python