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 相关文章推荐
Python3中多线程编程的队列运作示例
Apr 16 Python
Python多线程结合队列下载百度音乐的方法
Jul 27 Python
centos 安装python3.6环境并配置虚拟环境的详细教程
Feb 22 Python
python实现人脸识别经典算法(一) 特征脸法
Mar 13 Python
TensorFlow的权值更新方法
Jun 14 Python
Python3.5常见内置方法参数用法实例详解
Apr 29 Python
详解python和matlab的优势与区别
Jun 28 Python
Python实现简单的列表冒泡排序和反转列表操作示例
Jul 10 Python
python tqdm 实现滚动条不上下滚动代码(保持一行内滚动)
Feb 19 Python
windows系统Tensorflow2.x简单安装记录(图文)
Jan 18 Python
python之json文件转xml文件案例讲解
Aug 07 Python
python库Tsmoothie模块数据平滑化异常点抓取
Jun 10 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
php5数字型字符串加解密代码
2008/04/24 PHP
php中动态调用函数的方法
2015/03/16 PHP
基于JQuery+PHP编写砸金蛋中奖程序
2015/09/08 PHP
php微信分享到朋友圈、QQ、朋友、微博
2019/02/18 PHP
laravel 模型查询按照whereIn排序的示例
2019/10/16 PHP
js 蒙版进度条(结合图片)
2010/03/10 Javascript
javascript innerHTML使用分析
2010/12/03 Javascript
js关闭子窗体刷新父窗体实现方法
2012/12/04 Javascript
JavaScript实现统计文本框Textarea字数增强用户体验
2012/12/21 Javascript
JavaScript获取当前网页标题(title)的方法
2015/04/03 Javascript
vuex实现简易计数器
2016/10/27 Javascript
jQuery层级选择器实例代码
2017/02/06 Javascript
微信小程序实战之自定义抽屉菜单(7)
2017/04/18 Javascript
ajax请求data遇到的问题分析
2018/01/18 Javascript
[05:22]DOTA2 2015国际邀请赛中国区预选赛首日TOP10
2015/05/26 DOTA
下载安装setuptool和pip linux安装pip    
2014/01/24 Python
线程和进程的区别及Python代码实例
2015/02/04 Python
Python实现的简单文件传输服务器和客户端
2015/04/08 Python
python表格存取的方法
2018/03/07 Python
Python基于lxml模块解析html获取页面内所有叶子节点xpath路径功能示例
2018/05/16 Python
python 正确保留多位小数的实例
2018/07/16 Python
python 堆和优先队列的使用详解
2019/03/05 Python
详解用python生成随机数的几种方法
2019/08/04 Python
Python scipy的二维图像卷积运算与图像模糊处理操作示例
2019/09/06 Python
python 动态渲染 mysql 配置文件的示例
2020/11/20 Python
利用Python如何画一颗心、小人发射爱心
2021/02/21 Python
解决TensorFlow训练模型及保存数量限制的问题
2021/03/03 Python
大韩航空官方网站:Korean Air
2017/10/25 全球购物
党支部书记先进事迹
2014/01/17 职场文书
2014年三万活动总结
2014/04/26 职场文书
大学活动总结模板
2014/07/10 职场文书
优化经济发展环境工作总结
2015/08/11 职场文书
女方家长婚礼答谢词
2015/09/29 职场文书
2015年新教师个人工作总结
2015/10/14 职场文书
《用字母表示数》教学反思
2016/02/17 职场文书
Win11怎么添加用户?Win11添加用户账户的方法
2022/07/15 数码科技