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 pickle模块用法实例分析
May 27 Python
python 获取网页编码方式实现代码
Mar 11 Python
一百行python代码将图片转成字符画
Feb 19 Python
浅谈pandas筛选出表中满足另一个表所有条件的数据方法
Feb 08 Python
python做反被爬保护的方法
Jul 01 Python
pytorch使用 to 进行类型转换方式
Jan 08 Python
Pytest参数化parametrize使用代码实例
Feb 22 Python
Python如何定义接口和抽象类
Jul 28 Python
Python 读取位于包中的数据文件
Aug 07 Python
Python3如何实现Win10桌面自动切换
Aug 11 Python
详解Python中openpyxl模块基本用法
Feb 23 Python
如何用六步教会你使用python爬虫爬取数据
Apr 06 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
Ajax+PHP 边学边练 之二 实例
2009/11/24 PHP
php生出随机字符串
2017/07/06 PHP
Nginx下ThinkPHP5的配置方法详解
2017/08/01 PHP
PHP7基于curl实现的上传图片功能
2018/05/11 PHP
2007/12/23更新创意无限,简单实用(javascript log)
2007/12/24 Javascript
js的闭包的一个示例说明
2008/11/18 Javascript
Dojo 学习笔记入门篇 First Dojo Example
2009/11/15 Javascript
jQuery 添加/移除CSS类实现代码
2010/02/11 Javascript
cument.execCommand()用法深入理解
2012/12/04 Javascript
javascript跑马灯悬停放大效果实现代码
2012/12/12 Javascript
jQuery实现 注册时选择阅读条款 左右移动
2013/04/11 Javascript
JavaScript在IE和FF下的兼容性问题
2014/05/19 Javascript
JavaScript常用的弹出广告及背投广告实现方法
2015/02/06 Javascript
JavaScript 里的类数组对象
2015/04/08 Javascript
JavaScript中Cookies的相关使用教程
2015/06/04 Javascript
vue2.0 自定义日期时间过滤器
2017/06/07 Javascript
Mui使用jquery并且使用点击跳转新窗口的实例
2017/08/19 jQuery
尝试自己动手用react来写一个分页组件(小结)
2018/02/09 Javascript
js装饰设计模式学习心得
2018/02/17 Javascript
express + jwt + postMan验证实现持久化登录
2019/06/05 Javascript
JavaScript位置参数实现原理及过程解析
2020/09/14 Javascript
ant design的table组件实现全选功能以及自定义分页
2020/11/17 Javascript
Python3 适合初学者学习的银行账户登录系统实例
2017/08/08 Python
浅谈Python中的zip()与*zip()函数详解
2018/02/24 Python
实例分析python3实现并发访问水平切分表
2018/09/29 Python
发现世界上最好的珠宝设计师:JewelStreet
2017/12/17 全球购物
美国一家运动专业鞋类零售商:Warehouse Shoe Sale(WSS)
2018/03/28 全球购物
称象教学反思
2014/02/03 职场文书
会计电算化应届生自荐信
2014/02/25 职场文书
大学生2014全国两会学习心得体会
2014/03/10 职场文书
竞选学生会主席演讲稿
2014/04/24 职场文书
专题组织生活会方案
2014/06/15 职场文书
2015年社区文体活动总结
2015/03/25 职场文书
2015教师个人师德工作总结
2015/10/23 职场文书
三好学生主要事迹怎么写
2015/11/03 职场文书
导游词之长城八达岭
2019/09/24 职场文书