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 14 Python
python网络编程实例简析
Sep 26 Python
Python批量修改文本文件内容的方法
Apr 29 Python
快速查询Python文档方法分享
Dec 27 Python
Python3.遍历某文件夹提取特定文件名的实例
Apr 26 Python
Python操作rabbitMQ的示例代码
Mar 19 Python
手把手教你使用Python创建微信机器人
Apr 29 Python
Python爬取豆瓣视频信息代码实例
Nov 16 Python
Python多线程通信queue队列用法实例分析
Mar 24 Python
零基础学python应该从哪里入手
Aug 11 Python
python音频处理的示例详解
Dec 23 Python
pytorch常用数据类型所占字节数对照表一览
May 17 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
Zend studio for eclipse中使php可以调用mysql相关函数的设置方法
2008/10/13 PHP
PHP 文本文章分页代码 按标记或长度(不涉及数据库)
2012/06/07 PHP
PHP入门教程之使用Mysqli操作数据库的方法(连接,查询,事务回滚等)
2016/09/11 PHP
详解PHP中foreach的用法和实例
2016/10/25 PHP
PHP isset()与empty()的使用区别详解
2017/02/10 PHP
PHP正则匹配操作简单示例【preg_match_all应用】
2017/07/10 PHP
laravel中的一些简单实用功能
2018/11/03 PHP
thinkphp框架类库扩展操作示例
2019/11/26 PHP
js计算任意值之间随机数的方法
2015/01/16 Javascript
浅谈JSON.parse()和JSON.stringify()
2015/07/14 Javascript
js实现弹窗暗层效果
2017/01/16 Javascript
js简单实现网页换肤功能
2017/04/07 Javascript
详谈js使用in和hasOwnProperty获取对象属性的区别
2017/04/25 Javascript
原生js中ajax访问的实例详解
2017/09/19 Javascript
微信小程序icon组件使用详解
2018/01/31 Javascript
微信小程序实现左侧滑栏过程解析
2019/08/26 Javascript
layui下拉框获取下拉值(select)的例子
2019/09/10 Javascript
微信用户访问小程序的登录过程详解
2019/09/20 Javascript
Python实现字符串逆序输出功能示例
2017/06/24 Python
深入理解Python中的*重复运算符
2017/10/28 Python
Ubuntu18.04中Python2.7与Python3.6环境切换
2019/06/14 Python
django创建超级用户时指定添加其它字段方式
2020/05/14 Python
Django执行源生mysql语句实现过程解析
2020/11/12 Python
一款利用纯css3实现的360度翻转按钮的实例教程
2014/11/05 HTML / CSS
Origins加拿大官网:雅诗兰黛集团高端植物护肤品牌
2017/11/19 全球购物
沙龙级头发造型工具:FOXYBAE
2018/07/01 全球购物
美国花园雕像和家居装饰网上商店:Design Toscano
2019/03/09 全球购物
美国在线购买和出售礼品卡网站:EJ Gift Cards
2019/06/09 全球购物
《鹬蚌相争》教学反思
2014/04/22 职场文书
中职毕业生自我鉴定
2014/09/13 职场文书
四风问题个人自查剖析材料思想汇报
2014/09/21 职场文书
2015年度员工自我评价范文
2015/03/11 职场文书
2015迎新晚会开场白
2015/05/29 职场文书
2016春季幼儿园开学寄语
2015/12/03 职场文书
Python 如何解决稀疏矩阵运算
2021/05/26 Python
mysql查找连续出现n次以上的数字
2022/05/11 MySQL