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命令行参数解析模块getopt使用实例
Apr 13 Python
Python随手笔记之标准类型内建函数
Dec 02 Python
python中lambda()的用法
Nov 16 Python
ubuntu安装mysql pycharm sublime
Feb 20 Python
python实现超简单的视频对象提取功能
Jun 04 Python
Pycharm更换python解释器的方法
Oct 29 Python
Python列表元素常见操作简单示例
Oct 25 Python
python写一个随机点名软件的实例
Nov 28 Python
Python基础之变量基本用法与进阶详解
Jan 03 Python
Python通过队列来实现进程间通信的示例
Oct 14 Python
Python OpenCV 彩色与灰度图像的转换实现
Jun 05 Python
Python-OpenCV教程之图像的位运算详解
Jun 21 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代码飞起来的40条小技巧(提升php效率)
2010/04/12 PHP
vs中通过剪切板循环来循环粘贴不同内容
2011/04/30 PHP
PHP实现的多文件上传类及用法示例
2016/05/06 PHP
showModelessDialog()使用详解
2006/09/07 Javascript
JavaScript 加号(+)运算符号
2009/12/06 Javascript
jQuery EasyUI API 中文文档 搜索框
2011/09/29 Javascript
浅析jquery的js图表组件highcharts
2014/03/06 Javascript
Angularjs整合微信UI(weui)
2016/03/15 Javascript
js实现的下拉框二级联动效果
2016/04/30 Javascript
Node.js刷新session过期时间的实现方法推荐
2016/05/18 Javascript
JavaScript 实现的checkbox经典实例分享
2016/10/16 Javascript
vue.js 表格分页ajax 异步加载数据
2016/10/18 Javascript
bootstrap table之通用方法( 时间控件,导出,动态下拉框, 表单验证 ,选中与获取信息)代码分享
2017/01/24 Javascript
Angular.js与node.js项目里用cookie校验账户登录详解
2017/02/22 Javascript
[01:05]主宰至宝剑心之遗
2017/03/16 DOTA
[04:19]DOTA2完美大师赛第四天精彩集锦
2017/11/26 DOTA
简明 Python 基础学习教程
2007/02/08 Python
python2.7使用plotly绘制本地散点图和折线图
2019/04/02 Python
python读csv文件时指定行为表头或无表头的方法
2019/06/26 Python
django 2.2和mysql使用的常见问题
2019/07/18 Python
使用python实现kNN分类算法
2019/10/16 Python
Python StringIO如何在内存中读写str
2020/01/07 Python
Python3的socket使用方法详解
2020/02/18 Python
HTML5 CSS3新的WEB标准和浏览器支持
2009/07/16 HTML / CSS
美国最大的户外装备和服装购物网站:Backcountry
2019/10/15 全球购物
nohup的用法
2014/08/10 面试题
UNIX文件系统常用命令
2012/05/25 面试题
小学教研工作制度
2014/01/15 职场文书
民族团结先进个人材料
2014/02/05 职场文书
设备管理实施方案
2014/05/31 职场文书
党干部专题民主生活会对照检查材料思想汇报
2014/10/06 职场文书
群众路线个人自我剖析材料
2014/10/07 职场文书
护士自荐信怎么写
2015/03/06 职场文书
如何解决.cuda()加载用时很长的问题
2021/05/24 Python
python 爬取京东指定商品评论并进行情感分析
2021/05/27 Python
关于EntityWrapper的in用法
2022/03/22 Java/Android