利用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 检查数组元素是否存在类似PHP isset()方法
Oct 14 Python
深入理解Python中命名空间的查找规则LEGB
Aug 06 Python
Python处理XML格式数据的方法详解
Mar 21 Python
使用Python实现简单的服务器功能
Aug 25 Python
利用Python写一个爬妹子的爬虫
Jun 08 Python
Python简单获取二维数组行列数的方法示例
Dec 21 Python
python实现祝福弹窗效果
Apr 07 Python
使用python PIL库实现简单验证码的去噪方法步骤
May 10 Python
Python3内置模块之base64编解码方法详解
Jul 13 Python
python多维数组分位数的求取方式
Mar 03 Python
Python爬虫之Selenium实现关闭浏览器
Dec 04 Python
opencv读取视频并保存图像的方法
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
PHP中的strtr函数使用介绍(str_replace)
2011/10/20 PHP
PHP中使用GD库绘制折线图 折线统计图的绘制方法
2015/11/09 PHP
php实现在线考试系统【附源码】
2018/09/18 PHP
Thinkphp 5.0实现微信企业付款到零钱
2018/09/30 PHP
window.showModalDialog参数传递中含有特殊字符的处理方法
2013/06/06 Javascript
js call方法详细介绍(js 的继承)
2013/11/18 Javascript
Get中文乱码IE浏览器Get中文乱码解决方案
2013/12/26 Javascript
input:checkbox多选框实现单选效果跟radio一样
2014/06/16 Javascript
Javascript动画的实现原理浅析
2015/03/02 Javascript
jquery实现的代替传统checkbox样式插件
2015/06/19 Javascript
JavaScript实现将文本框的值插入指定位置的方法
2015/08/13 Javascript
jQuery form插件之ajaxForm()和ajaxSubmit()的可选参数项对象
2016/01/23 Javascript
关于JavaScript作用域你想知道的一切
2016/02/04 Javascript
jQuery选择器及jquery案例详解(必看)
2016/05/20 Javascript
关于vuejs中v-if和v-show的区别及v-show不起作用问题
2018/03/26 Javascript
JS实现的哈夫曼编码示例【原始版与修改版】
2018/04/22 Javascript
js实现坦克移动小游戏
2019/10/28 Javascript
使用Vue Composition API写出清晰、可扩展的表单实现
2020/06/10 Javascript
JS将指定的某个字符全部转换为其他字符实例代码
2020/10/13 Javascript
vue 图片裁剪上传组件的实现
2020/11/12 Javascript
Python中的生成器和yield详细介绍
2015/01/09 Python
Python实现线程池代码分享
2015/06/21 Python
Python使用multiprocessing实现一个最简单的分布式作业调度系统
2016/03/14 Python
django之常用命令详解
2016/06/30 Python
Python 两个列表的差集、并集和交集实现代码
2016/09/21 Python
python多进程中的内存复制(实例讲解)
2018/01/05 Python
详谈在flask中使用jsonify和json.dumps的区别
2018/03/26 Python
Python基于opencv的图像压缩算法实例分析
2018/05/03 Python
Python操作MySQL数据库的方法
2018/06/20 Python
Python OpenCV 使用滑动条来调整函数参数的方法
2019/07/08 Python
简单了解python shutil模块原理及使用方法
2020/04/28 Python
Christys’ Hats官网:英国帽子制造商
2018/11/28 全球购物
捐款倡议书
2014/04/14 职场文书
2015年挂职锻炼个人总结
2015/10/22 职场文书
幼儿教师三分钟演讲稿
2019/06/21 职场文书
js前端图片加载异常兜底方案
2022/06/21 Javascript