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实现的百度贴吧网络爬虫实例
Apr 17 Python
Python中的pass语句使用方法讲解
May 14 Python
Python PIL读取的图像发生自动旋转的实现方法
Jul 05 Python
利用pandas合并多个excel的方法示例
Oct 10 Python
windows中安装Python3.8.0的实现方法
Nov 19 Python
Python3 中作为一等对象的函数解析
Dec 11 Python
最小二乘法及其python实现详解
Feb 24 Python
Python基于百度AI实现OCR文字识别
Apr 02 Python
Python应用实现双指数函数及拟合代码实例
Jun 19 Python
Python基于callable函数检测对象是否可被调用
Oct 16 Python
Python创建文件夹与文件的快捷方法
Dec 08 Python
教你怎么用Python监控愉客行车程
Apr 29 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面试题之文件目录操作
2015/10/15 PHP
PHP simplexml_import_dom()函数讲解
2019/02/03 PHP
分享8款优秀的 jQuery 加载动画和进度条插件
2012/10/24 Javascript
Javascript 多物体运动的实现
2014/12/24 Javascript
简述AngularJS相关的一些编程思想
2015/06/23 Javascript
举例详解Python中smtplib模块处理电子邮件的使用
2015/06/24 Javascript
JS如何判断是否为ie浏览器的方法(包括IE10、IE11在内)
2015/12/13 Javascript
JS实现简单的二维矩阵乘积运算
2016/01/26 Javascript
js匿名函数作为函数参数详解
2016/06/01 Javascript
巧方法 JavaScript获取超链接的绝对URL地址
2016/06/14 Javascript
JavaScript toUpperCase()方法使用详解
2016/08/26 Javascript
基于vue2框架的机器人自动回复mini-project实例代码
2017/06/13 Javascript
关于使用js算总价的问题
2017/06/23 Javascript
nodejs实现连接mongodb数据库的方法示例
2018/03/15 NodeJs
JS常见构造模式实例对比分析
2018/08/27 Javascript
详解react native页面间传递数据的几种方式
2018/11/07 Javascript
JavaScript函数定义方法实例详解
2019/03/05 Javascript
详解使用WebPack搭建React开发环境
2019/08/06 Javascript
javascript设计模式 ? 简单工厂模式原理与应用实例分析
2020/04/09 Javascript
Js和VUE实现跑马灯效果
2020/05/25 Javascript
javascript实现雪花飘落效果
2020/08/19 Javascript
vant时间控件使用方法详解
2020/12/24 Javascript
python创建和使用字典实例详解
2013/11/01 Python
python实现360皮肤按钮控件示例
2014/02/21 Python
python使用socket连接远程服务器的方法
2015/04/29 Python
在Django的通用视图中处理Context的方法
2015/07/21 Python
python提取xml里面的链接源码详解
2019/10/15 Python
Python OpenCV实现测量图片物体宽度
2020/05/27 Python
MoviePy简介及Python视频剪辑自动化
2020/12/18 Python
西班牙用户之间买卖视频游戏的平台:Wakkap
2020/03/21 全球购物
公司门卫管理制度
2014/02/01 职场文书
2014年四风个人对照检查及整改措施
2014/10/28 职场文书
2015年母亲节寄语
2015/03/23 职场文书
申论不会写怎么办?教您掌握这6点思维和原则
2019/07/17 职场文书
python实现简单的井字棋
2021/05/26 Python
Flask搭建一个API服务器的步骤
2021/05/28 Python