利用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实现Const详解
Jan 27 Python
深入浅析python定时杀进程
Jun 06 Python
Python Socket实现简单TCP Server/client功能示例
Aug 05 Python
利用Opencv中Houghline方法实现直线检测
Feb 11 Python
python实现字符串中字符分类及个数统计
Sep 28 Python
使用Py2Exe for Python3创建自己的exe程序示例
Oct 31 Python
Python修改列表值问题解决方案
Mar 06 Python
调整Jupyter notebook的启动目录操作
Apr 10 Python
Keras构建神经网络踩坑(解决model.predict预测值全为0.0的问题)
Jul 07 Python
Python机器学习工具scikit-learn的使用笔记
Jan 28 Python
python爬虫之爬取笔趣阁小说
Apr 22 Python
 分享一个Python 遇到数据库超好用的模块
Apr 06 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
编写漂亮的代码 - 将后台程序与前端程序分开
2008/04/23 PHP
php单例模式实现(对象只被创建一次)
2012/12/05 PHP
避免Smarty与CSS语法冲突的方法
2015/03/02 PHP
如何写php守护进程(Daemon)
2015/12/30 PHP
php制作圆形用户头像的实例_自定义封装类源代码
2017/09/18 PHP
laravel使用Faker数据填充的实现方法
2019/04/12 PHP
laravel config文件配置全局变量的例子
2019/10/13 PHP
jquery easyui滚动条部分设置介绍
2013/09/12 Javascript
js仿百度贴吧验证码特效实例代码
2014/01/16 Javascript
node.js中的console.warn方法使用说明
2014/12/09 Javascript
基于jQuery实现点击弹出层实例代码
2016/01/01 Javascript
用JavaScript获取页面文档内容的实现代码
2016/06/10 Javascript
Vue2.0实现组件数据的双向绑定问题
2018/03/06 Javascript
vue路由拦截及页面跳转的设置方法
2018/05/24 Javascript
详解Vue中使用Echarts的两种方式
2018/07/03 Javascript
Angularjs 根据一个select的值去设置另一个select的值方法
2018/08/13 Javascript
解决vue热替换失效的根本原因
2018/09/19 Javascript
使用javascript做时间倒数读秒功能的实例
2019/01/23 Javascript
原生JS实现烟花效果
2020/03/10 Javascript
React+EggJs实现断点续传的示例代码
2020/07/07 Javascript
[05:08]第一届“网鱼杯”DOTA2比赛精彩集锦
2014/09/05 DOTA
pandas值替换方法
2018/07/10 Python
django-rest-swagger的优化使用方法
2019/08/29 Python
文件上传服务器-jupyter 中python解压及压缩方式
2020/04/22 Python
Python pathlib模块使用方法及实例解析
2020/10/05 Python
使用HTML5的链接预取功能(link prefetching)给网站提速
2012/12/13 HTML / CSS
HTML5 Canvas 实现圆形进度条并显示数字百分比效果示例
2017/08/18 HTML / CSS
Theflamel意大利:女士奢华服装、鞋子和配件
2020/01/11 全球购物
C/C++程序员常见面试题一
2012/12/08 面试题
高校毕业生登记表自我鉴定
2013/11/03 职场文书
最热门的自我评价
2013/12/30 职场文书
仓管员岗位责任制
2014/02/19 职场文书
法学专业毕业实习自我鉴定2014
2014/09/27 职场文书
学生保证书格式
2015/02/27 职场文书
《将心比心》教学反思
2016/02/23 职场文书
学校2016年全国助残日活动总结
2016/04/01 职场文书