Python优化技巧之利用ctypes提高执行速度


Posted in Python onSeptember 11, 2016

首先给大家分享一个个人在使用python的ctypes调用c库的时候遇到的一个小坑

这次出问题的地方是一个C函数,返回值是malloc生成的字符串地址。平常使用也没问题,也用了有段时间, 没发现什么异常。

这次在测试中,发现使用这个过程会出现“段错误”,造成程序退出了。

经过排查, 确定问题原因是C函数的返回值问题,ctypes默认的函数返回类型是int类型。

需要在使用中设置返回类型,例如:

func.restype = c_char_p

下面我们就来详细探讨下ctypes的使用小技巧

ctypes 库可以让开发者借助C语言进行开发。这个引入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)

Output

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)

Output

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)

Output

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)

Output

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)

Output

Python version: 0.850172 seconds
C version : 0.27645 seconds

以上都是ctypes的基本技巧,对普通的开发人员来说,基本够用了

更详细的说明请参考:http://docs.python.org/library/ctypes.html

Python 相关文章推荐
python实现巡检系统(solaris)示例
Apr 02 Python
Python对象体系深入分析
Oct 28 Python
11个并不被常用但对开发非常有帮助的Python库
Mar 31 Python
Python的Bottle框架中实现最基本的get和post的方法的教程
Apr 30 Python
初步剖析C语言编程中的结构体
Jan 16 Python
python获取命令行输入参数列表的实例代码
Jun 23 Python
Python读取excel指定列生成指定sql脚本的方法
Nov 28 Python
在python2.7中用numpy.reshape 对图像进行切割的方法
Dec 05 Python
python实现扫描ip地址的小程序
Apr 16 Python
用Python爬取QQ音乐评论并制成词云图的实例
Aug 24 Python
python如何使用Redis构建分布式锁
Jan 16 Python
Python实现投影法分割图像示例(二)
Jan 17 Python
Python 中的with关键字使用详解
Sep 11 #Python
Python冒泡排序注意要点实例详解
Sep 09 #Python
通过5个知识点轻松搞定Python的作用域
Sep 09 #Python
python验证码识别的实例详解
Sep 09 #Python
Python随机数random模块使用指南
Sep 09 #Python
利用ctypes提高Python的执行速度
Sep 09 #Python
python实现批量监控网站
Sep 09 #Python
You might like
PHP通过session id 实现session共享和登录验证的代码
2012/06/03 PHP
基于PHP实现用户注册登录功能
2016/10/14 PHP
php基于数组函数实现关联表的编辑操作示例
2017/07/04 PHP
php实现JWT(json web token)鉴权实例详解
2019/11/05 PHP
jquery如何通过name名称获取当前name的value值
2013/12/20 Javascript
javascript实现点击按钮让DIV层弹性移动的方法
2015/02/24 Javascript
jQuery基于$.ajax设置移动端click超时处理方法
2016/05/14 Javascript
jQuery实现鼠标经过购物车出现下拉框代码(推荐)
2016/07/21 Javascript
AngularJS 工作原理详解
2016/08/18 Javascript
简单谈谈ES6的六个小特性
2016/11/18 Javascript
bootstrap快速制作后台界面
2016/12/05 Javascript
jQuery验证表单格式的使用方法
2017/01/10 Javascript
AngularJS全局警告框实现方法示例
2017/05/18 Javascript
Bootstrap实现的表格合并单元格示例
2018/02/06 Javascript
详解Vue Elememt-UI构建管理后台
2018/02/27 Javascript
vue中本地静态图片路径写法
2018/03/06 Javascript
解决vue-router进行build无法正常显示路由页面的问题
2018/03/06 Javascript
angularJS开发注意事项
2018/05/26 Javascript
在Create React App中启用Sass和Less的方法示例
2019/01/16 Javascript
Vue之Mixins(混入)的使用方法
2019/09/24 Javascript
vue 使用lodash实现对象数组深拷贝操作
2020/09/10 Javascript
bat和python批量重命名文件的实现代码
2016/05/19 Python
Python检测生僻字的实现方法
2016/10/23 Python
windows下cx_Freeze生成Python可执行程序的详细步骤
2018/10/09 Python
Python3中关于cookie的创建与保存
2018/10/21 Python
python多线程实现TCP服务端
2019/09/03 Python
python使用梯度下降算法实现一个多线性回归
2020/03/24 Python
PyQt5 QDockWidget控件应用详解
2020/08/12 Python
python闭包与引用以及需要注意的陷阱
2020/09/18 Python
什么是测试驱动开发(TDD)
2012/02/15 面试题
2014年惩防体系建设工作总结
2014/12/01 职场文书
2015年实习班主任工作总结
2015/04/23 职场文书
李强优秀员工观后感
2015/06/16 职场文书
幼儿园六一儿童节主持词
2015/06/30 职场文书
2016年秋季新学期致辞
2015/07/30 职场文书
最新最全的手机号验证正则表达式
2022/02/24 Javascript