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实现代码统计工具(终极篇)
Jul 04 Python
python中Apriori算法实现讲解
Dec 10 Python
python版微信跳一跳游戏辅助
Jan 11 Python
python合并同类型excel表格的方法
Apr 01 Python
Python中数组,列表:冒号的灵活用法介绍(np数组,列表倒序)
Apr 18 Python
Python使用jsonpath-rw模块处理Json对象操作示例
Jul 31 Python
python实现各种插值法(数值分析)
Jul 30 Python
Pytorch卷积层手动初始化权值的实例
Aug 17 Python
Python数学形态学实例分析
Sep 06 Python
python如何随机生成高强度密码
Aug 19 Python
python实现不同数据库间数据同步功能
Feb 25 Python
Python机器学习之逻辑回归
May 11 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注销)
2012/05/31 PHP
简单实用的PHP防注入类实例
2014/12/05 PHP
Zend Framework实现具有基本功能的留言本(附demo源码下载)
2016/03/22 PHP
Smarty高级应用之缓存操作技巧分析
2016/05/14 PHP
基于php(Thinkphp)+jquery 实现ajax多选反选不选删除数据功能
2017/02/24 PHP
url 特殊字符 传递参数解决方法
2010/01/01 Javascript
js Array对象的扩展函数代码
2013/04/24 Javascript
jQuery的:parent选择器定义和用法
2014/07/01 Javascript
Windows8下搭建Node.js开发环境教程
2014/09/03 Javascript
jQuery实现数字加减效果汇总
2014/12/16 Javascript
深入理解JavaScript系列(31):设计模式之代理模式详解
2015/03/03 Javascript
js实现字符串和数组之间相互转换操作
2016/01/12 Javascript
js点击文本框弹出可选择的checkbox复选框
2016/02/03 Javascript
提高Web性能的前端优化技巧总结
2017/02/27 Javascript
ES6数组的扩展详解
2017/04/25 Javascript
基于js中的存储键值对以及注意事项介绍
2018/03/30 Javascript
Vue 路由 过渡动效 数据获取方法
2018/07/31 Javascript
js限制input只能输入有效的数字(第一个不能是小数点)
2018/09/28 Javascript
[48:56]2018DOTA2亚洲邀请赛 3.31 小组赛 A组 VG vs KG
2018/03/31 DOTA
[41:17]完美世界DOTA2联赛PWL S3 access vs CPG 第二场 12.13
2020/12/17 DOTA
使用Python生成url短链接的方法
2015/05/04 Python
详解Python设计模式编程中观察者模式与策略模式的运用
2016/03/02 Python
Python实现定期检查源目录与备份目录的差异并进行备份功能示例
2019/02/27 Python
Python HTML解析模块HTMLParser用法分析【爬虫工具】
2019/04/05 Python
Python两台电脑实现TCP通信的方法示例
2019/05/06 Python
使用python来调用CAN通讯的DLL实现方法
2019/07/03 Python
如何使用python-opencv批量生成带噪点噪线的数字验证码
2020/12/21 Python
Cynthia Rowley官网:全球领先的生活方式品牌
2020/10/27 全球购物
群胜软件Java笔试题
2012/09/29 面试题
个人实用简单的自我评价
2013/10/19 职场文书
见习报告的格式
2014/11/04 职场文书
2015年五一劳动节活动总结
2015/02/09 职场文书
公司团队口号霸气押韵
2015/12/24 职场文书
七年级写作指导之游记作文
2019/10/07 职场文书
详解JSON.parse和JSON.stringify用法
2022/02/18 Javascript
vue使用localStorage持久性存储实现评论列表
2022/04/14 Vue.js