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实现巡检系统(solaris)示例
Apr 02 Python
python统计一个文本中重复行数的方法
Nov 19 Python
python实现红包裂变算法
Feb 16 Python
Python 类与元类的深度挖掘 II【经验】
May 06 Python
Python基于scapy实现修改IP发送请求的方法示例
Jul 08 Python
Python UnboundLocalError和NameError错误根源案例解析
Oct 31 Python
python中退出多层循环的方法
Nov 27 Python
十分钟搞定pandas(入门教程)
Jun 21 Python
python进行参数传递的方法
May 12 Python
Python如何避免文件同名产生覆盖
Jun 09 Python
Python通过zookeeper实现分布式服务代码解析
Jul 22 Python
Python何绘制带有背景色块的折线图
Apr 23 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
博士208HAF收音机实习报告
2021/03/02 无线电
PHP与MySQL开发的8个技巧小结
2010/12/17 PHP
解析php做推送服务端实现ios消息推送
2013/07/01 PHP
详解Yii2 之 生成 URL 的方法
2017/06/16 PHP
JavaScript小技巧 2.5 则
2010/09/12 Javascript
jQuery操作CheckBox的方法介绍(选中,取消,取值)
2014/02/04 Javascript
红米手机抢购的js代码
2014/03/10 Javascript
用jquery实现动画跳到顶部和底部(这个比较简单)
2014/09/01 Javascript
JS实现日期时间动态显示的方法
2015/12/07 Javascript
JavaScript获取客户端IP的方法(新方法)
2016/03/11 Javascript
基于原生JS实现图片裁剪
2016/08/01 Javascript
利用Angularjs和原生JS分别实现动态效果的输入框
2016/09/01 Javascript
详解Angular的内置过滤器和自定义过滤器【推荐】
2016/12/26 Javascript
[原创]SyntaxHighlighter自动识别并加载脚本语言
2017/02/07 Javascript
JavaScript中使用Async实现异步控制
2017/08/15 Javascript
Vue.js在数组中插入重复数据的实现代码
2017/11/17 Javascript
深入浅析js原型链和vue构造函数
2018/10/25 Javascript
layui关闭层级、简单监听的实例
2019/09/06 Javascript
Vue中正确使用Element-UI组件的方法实例
2020/10/13 Javascript
通过实例解析js可枚举属性与不可枚举属性
2020/12/02 Javascript
在Python中使用第三方模块的教程
2015/04/27 Python
python版学生管理系统
2018/01/10 Python
Django 视图层(view)的使用
2018/11/09 Python
Python面向对象之继承和多态用法分析
2019/06/08 Python
安装docker-compose的两种最简方法
2019/07/30 Python
python函数局部变量、全局变量、递归知识点总结
2019/11/15 Python
利用纯css3实现的文字亮光特效的代码演示
2014/11/27 HTML / CSS
外包公司软件测试工程师
2014/11/01 面试题
大学生演讲稿
2014/04/25 职场文书
新书发布会策划方案
2014/06/09 职场文书
自我推荐信格式模板
2015/03/24 职场文书
四年级数学教学反思
2016/02/16 职场文书
教你利用python实现企业微信发送消息
2021/05/23 Python
React如何创建组件
2021/06/27 Javascript
浅谈Python中对象是如何被调用的
2022/04/06 Python
Mongodb 迁移数据块的流程介绍分析
2022/04/18 MongoDB