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 相关文章推荐
浅谈Django学习migrate和makemigrations的差别
Jan 18 Python
Python cookbook(数据结构与算法)对切片命名清除索引的方法
Mar 13 Python
Python实现正整数分解质因数操作示例
Aug 01 Python
PyQt5 QTableView设置某一列不可编辑的方法
Jun 25 Python
python如何给字典的键对应的值为字典项的字典赋值
Jul 05 Python
详解Python3定时器任务代码
Sep 23 Python
python读取csv文件指定行的2种方法详解
Feb 13 Python
python多项式拟合之np.polyfit 和 np.polyld详解
Feb 18 Python
Django之全局使用request.user.username的实例详解
May 14 Python
Python爬虫如何应对Cloudflare邮箱加密
Jun 24 Python
浅谈Python 命令行参数argparse写入图片路径操作
Jul 12 Python
什么是Python包的循环导入
Sep 08 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之第二天
2006/10/09 PHP
全面解读PHP的人气开发框架Laravel
2015/10/15 PHP
javascript函数中参数传递问题示例探讨
2014/07/31 Javascript
使用jquery提交form表单并自定义action的实现代码
2016/05/25 Javascript
购物车前端开发(jQuery和bootstrap3)
2016/08/27 Javascript
jquery 属性选择器(匹配具有指定属性的元素)
2016/09/06 Javascript
关于js函数解释(包括内嵌,对象等)
2016/11/20 Javascript
有关suggest快速删除后仍然出现下拉列表的bug问题
2016/12/02 Javascript
使用jQuery操作DOM的方法小结
2017/02/27 Javascript
jQuery插件FusionWidgets实现的AngularGauge图效果示例【附demo源码】
2017/03/23 jQuery
ES6中字符串string常用的新增方法小结
2017/11/07 Javascript
JavaScript实现全选取消效果
2017/12/14 Javascript
详解vue-router 命名路由和命名视图
2018/06/01 Javascript
详解vue 数组和对象渲染问题
2018/09/21 Javascript
详解Vue中watch对象内属性的方法
2019/02/01 Javascript
Node.js动手撸一个静态资源服务器的方法
2019/03/09 Javascript
[49:21]TNC vs VG 2019DOTA2国际邀请赛淘汰赛 胜者组赛BO3 第三场 8.20.mp4
2019/08/22 DOTA
Python实现的数据结构与算法之基本搜索详解
2015/04/22 Python
在Python中使用SQLite的简单教程
2015/04/29 Python
python利用Guetzli批量压缩图片
2017/03/23 Python
Python通过future处理并发问题
2017/10/17 Python
使用Python画股票的K线图的方法步骤
2019/06/28 Python
Python中用pyinstaller打包时的图标问题及解决方法
2020/02/17 Python
python 对任意数据和曲线进行拟合并求出函数表达式的三种解决方案
2020/02/18 Python
pycharm如何实现跨目录调用文件
2020/02/28 Python
keras实现多GPU或指定GPU的使用介绍
2020/06/17 Python
python 基于卡方值分箱算法的实现示例
2020/07/17 Python
CSS3中动画属性transform、transition和animation属性的区别
2016/09/25 HTML / CSS
CSS3的first-child选择器实战攻略
2016/04/28 HTML / CSS
YOOX美国官方网站:全球著名的多品牌时尚网络概念店
2016/09/11 全球购物
Eagle Eyes Optics鹰眼光学:高性能太阳镜
2018/12/07 全球购物
母亲七十大寿答谢词
2014/01/18 职场文书
小学信息技术教学反思
2014/02/10 职场文书
村干部承诺书
2014/03/28 职场文书
Vue中foreach数组与js中遍历数组的写法说明
2021/06/05 Vue.js
Golang连接并操作MySQL
2022/04/14 MySQL