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基础教程之数字处理(math)模块详解
Mar 25 Python
python中子类继承父类的__init__方法实例
Dec 15 Python
利用Python脚本生成sitemap.xml的实现方法
Jan 31 Python
Python数据结构与算法之完全树与最小堆实例
Dec 13 Python
火车票抢票python代码公开揭秘!
Mar 08 Python
python topN 取最大的N个数或最小的N个数方法
Jun 04 Python
使用Python编写Prometheus监控的方法
Oct 15 Python
Python图像处理实现两幅图像合成一幅图像的方法【测试可用】
Jan 04 Python
python实现QQ邮箱/163邮箱的邮件发送
Jan 22 Python
Python 绘制酷炫的三维图步骤详解
Jul 12 Python
python画图常规设置方式
Mar 05 Python
Django高并发负载均衡实现原理详解
Apr 04 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)
2010/09/04 PHP
PHP中-&gt;和=&gt;的含义及使用示例解析
2020/08/06 PHP
jQuery解决iframe高度自适应代码
2009/12/20 Javascript
仿中关村在线首页弹出式广告插件(jQuery版)
2012/05/03 Javascript
js控制表单操作的常用代码小结
2013/08/15 Javascript
node.js中的http.response.addTrailers方法使用说明
2014/12/14 Javascript
使用jquery 简单实现下拉菜单
2015/01/14 Javascript
JS+CSS实现经典的左侧竖向滑动菜单效果
2015/09/23 Javascript
使用postMesssage()实现跨域iframe页面间的信息传递方法
2016/03/29 Javascript
使用jQuery实现WordPress中的Ctrl+Enter和@评论回复
2016/05/21 Javascript
Javascript 对cookie操作详解及实例
2016/12/29 Javascript
js代码延迟一定时间后执行一个函数的实例
2017/02/15 Javascript
js 实现复选框只能选择一项的示例代码
2018/01/23 Javascript
Vue源码解析之数据响应系统的使用
2019/04/24 Javascript
Python lambda和Python def区别分析
2014/11/30 Python
python实现域名系统(DNS)正向查询的方法
2016/04/19 Python
用Python将IP地址在整型和字符串之间轻松转换
2017/03/22 Python
深入理解Python爬虫代理池服务
2018/02/28 Python
python如何实现反向迭代
2018/03/20 Python
将python代码和注释分离的方法
2018/04/21 Python
pandas.loc 选取指定列进行操作的实例
2018/05/18 Python
python 并发编程 非阻塞IO模型原理解析
2019/08/20 Python
python解析xml文件方式(解析、更新、写入)
2020/03/05 Python
Python实现给PDF添加水印的方法
2021/01/25 Python
css3背景图片透明叠加属性cross-fade简介及用法实例
2013/01/08 HTML / CSS
有模特经验的简历自我评价
2013/09/19 职场文书
新领导上任欢迎词
2014/01/13 职场文书
区优秀教师事迹材料
2014/02/10 职场文书
预备党员入党自我评价范文
2014/03/10 职场文书
旺仔牛奶广告词
2014/03/20 职场文书
减负增效提质方案
2014/05/23 职场文书
我们的节日元宵活动方案
2014/08/23 职场文书
2015年保险公司内勤工作总结
2015/05/23 职场文书
MySQL千万级数据表的优化实战记录
2021/08/04 MySQL
Python 中的 copy()和deepcopy()
2021/11/07 Python
Vue Mint UI mt-swipe的使用方式
2022/06/05 Vue.js