利用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 相关文章推荐
wxpython 学习笔记 第一天
Feb 09 Python
python如何实现远程控制电脑(结合微信)
Dec 21 Python
python中enumerate函数遍历元素用法分析
Mar 11 Python
python 中if else 语句的作用及示例代码
Mar 05 Python
CentOS7下python3.7.0安装教程
Jul 30 Python
Python创建一个空的dataframe,并循环赋值的方法
Nov 08 Python
python爬虫 urllib模块url编码处理详解
Aug 20 Python
Python scipy的二维图像卷积运算与图像模糊处理操作示例
Sep 06 Python
在tensorflow中设置保存checkpoint的最大数量实例
Jan 21 Python
python列表的逆序遍历实现
Apr 20 Python
Python pandas对excel的操作实现示例
Jul 21 Python
拒绝盗图!教你怎么用python给图片加水印
Jun 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
WHOIS类的修改版
2006/10/09 PHP
落伍首发 php+mysql 采用ajax技术的 省 市 地 3级联动无刷新菜单 源码
2006/12/16 PHP
PHP连接SQLSERVER 注意事项(附dll文件下载)
2012/06/28 PHP
PHP实现简单实用的验证码类
2015/07/29 PHP
php 获取xml接口数据的处理方法
2018/05/31 PHP
laravel实现于语言包的完美切换方法
2019/09/29 PHP
laravel框架与其他框架的详细对比
2019/10/23 PHP
javascript编程起步(第七课)
2007/01/10 Javascript
JavaScript 浏览器验证代码(来自discuz)
2010/07/17 Javascript
js网页侧边随页面滚动广告效果实现
2011/04/14 Javascript
IE中jquery.form中ajax提交没反应解决方法分享
2012/09/11 Javascript
jquery单行文字向上滚动效果示例
2014/03/06 Javascript
JavaScript关闭当前页面(窗口)不带任何提示
2014/03/26 Javascript
javascript框架设计读书笔记之种子模块
2014/12/02 Javascript
jQuery 和 CSS 的文本特效插件集锦
2014/12/12 Javascript
vue+mousemove实现鼠标拖动功能(拖动过快失效问题解决方法)
2018/08/24 Javascript
node.js处理前端提交的GET请求
2019/08/30 Javascript
记录Django开发心得
2014/07/16 Python
复习Python中的字符串知识点
2015/04/14 Python
python解决js文件utf-8编码乱码问题(推荐)
2018/05/02 Python
Python简单获取二维数组行列数的方法示例
2018/12/21 Python
canvas简易绘图的实现(海绵宝宝篇)
2018/07/04 HTML / CSS
Canvas中设置width与height的问题浅析
2018/11/01 HTML / CSS
iPad和Surface Pro蓝牙键盘:Brydge
2018/11/10 全球购物
定制iPhone和Macbook保护壳:Slick Case
2018/11/21 全球购物
Abbacino官网:包、钱包和女士配饰
2019/04/15 全球购物
大学生实习自我鉴定
2013/12/11 职场文书
财务主管的岗位职责
2013/12/30 职场文书
五年级英语教学反思
2014/01/31 职场文书
个人培训自我鉴定
2014/03/28 职场文书
骨干教师培训方案
2014/05/06 职场文书
小学班级口号
2014/06/09 职场文书
2014年社区妇联工作总结
2014/12/02 职场文书
课外活动总结
2015/02/04 职场文书
2016年“世界环境日”校园广播稿
2015/12/18 职场文书
Python可变与不可变数据和深拷贝与浅拷贝
2022/04/06 Python