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中的数据存储到系统本地的简单方法
Apr 11 Python
python 实现批量xls文件转csv文件的方法
Oct 23 Python
python3+PyQt5 使用三种不同的简便项窗口部件显示数据的方法
Jun 17 Python
python opencv 二值化 计算白色像素点的实例
Jul 03 Python
python英语单词测试小程序代码实例
Sep 09 Python
详解python tkinter模块安装过程
Jan 06 Python
python使用梯度下降和牛顿法寻找Rosenbrock函数最小值实例
Apr 02 Python
python小白学习包管理器pip安装
Jun 09 Python
详解基于Scrapy的IP代理池搭建
Sep 29 Python
Python爬虫之Selenium实现窗口截图
Dec 04 Python
用Python自动清理系统垃圾的实现
Jan 18 Python
Python IO文件管理的具体使用
Mar 20 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 curl选项列表(超详细)
2013/07/01 PHP
php curl模拟post请求小实例
2013/11/13 PHP
Smarty最简单实现列表奇偶变色的方法
2015/07/01 PHP
PHP curl使用实例
2015/07/02 PHP
PHP后期静态绑定之self::限制实例分析
2018/12/21 PHP
thinkphp3.2同时连接两个数据库的简单方法
2019/08/13 PHP
解决Laravel5.x的php artisan migrate数据库迁移创建操作报错SQLSTATE[42000]
2020/04/06 PHP
图片上传即时显示缩略图的js代码
2009/05/27 Javascript
jQuery创建自己的插件(自定义插件)的方法
2010/06/10 Javascript
jquery学习笔记 用jquery实现无刷新登录
2011/08/08 Javascript
JS事件Event元素(兼容IE,Firefox,Chorme)
2012/11/01 Javascript
使用js修改客户端注册表的方法
2013/08/09 Javascript
浅谈JavaScript中的String对象常用方法
2015/02/25 Javascript
JQuery实现简单的图片滑动切换特效
2015/11/22 Javascript
jQuery 实现评论等级好评差评特效
2016/05/06 Javascript
AngularJS基础 ng-include 指令示例讲解
2016/08/01 Javascript
jQuery插件HighCharts绘制2D带Label的折线图效果示例【附demo源码下载】
2017/03/08 Javascript
浅谈Angular2 ng-content 指令在组件中嵌入内容
2017/08/18 Javascript
element-ui中select组件绑定值改变,触发change事件方法
2018/08/24 Javascript
解决vue热替换失效的根本原因
2018/09/19 Javascript
react-router 路由切换动画的实现示例
2018/12/03 Javascript
electron踩坑之remote of undefined的解决
2020/10/06 Javascript
简单谈谈python的反射机制
2016/06/28 Python
Python 使用SMTP发送邮件的代码小结
2016/09/21 Python
python+pandas+时间、日期以及时间序列处理方法
2018/07/10 Python
在PyCharm的 Terminal(终端)切换Python版本的方法
2019/08/02 Python
使用jupyter Nodebook查看函数或方法的参数以及使用情况
2020/04/14 Python
Python爬虫HTPP请求方法有哪些
2020/06/03 Python
Python实现LR1文法的完整实例代码
2020/10/25 Python
纯CSS3实现滚动的齿轮动画效果
2014/06/05 HTML / CSS
高等教育学自荐书范文
2014/02/10 职场文书
公安学专业求职信
2014/07/27 职场文书
铁人观后感
2015/06/16 职场文书
学雷锋感言
2015/08/03 职场文书
基于Redis的List实现特价商品列表功能
2021/08/30 Redis
php双向队列实例讲解
2021/11/17 PHP