利用ctypes提高Python的执行速度


Posted in Python onSeptember 09, 2016

前言

ctypes是Python的外部函数库。它提供了C兼容的数据类型,并且允许调用动态链接库/共享库中的函数。它可以将这些库包装起来给Python使用。这个引入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)

输出

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)

输出

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)

输出

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)

输出

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)

输出

Python version: 0.850172 seconds
C version : 0.27645 seconds

总结

以上就是这篇文章的全部内容,希望对大家学习或使用Python能有一定的帮助,如果有疑问大家可以留言交流。

Python 相关文章推荐
Python模拟脉冲星伪信号频率实例代码
Jan 03 Python
理论讲解python多进程并发编程
Feb 09 Python
用python3教你任意Html主内容提取功能
Nov 05 Python
Python下简易的单例模式详解
Apr 08 Python
python求最大值,不使用内置函数的实现方法
Jul 09 Python
Python 实现一个手机号码获取妹子名字的功能
Sep 25 Python
Window10下python3.7 安装与卸载教程图解
Sep 30 Python
python 字典访问的三种方法小结
Dec 05 Python
Python flask框架端口失效解决方案
Jun 04 Python
Python实现自动整理文件的脚本
Dec 17 Python
一文读懂python Scrapy爬虫框架
Feb 24 Python
python文件与路径操作神器 pathlib
Apr 01 Python
python实现批量监控网站
Sep 09 #Python
利用python批量检查网站的可用性
Sep 09 #Python
Python如何判断数独是否合法
Sep 08 #Python
python框架django基础指南
Sep 08 #Python
python中星号变量的几种特殊用法
Sep 07 #Python
Python 实现 贪吃蛇大作战 代码分享
Sep 07 #Python
python 转换 Javascript %u 字符串为python unicode的代码
Sep 06 #Python
You might like
PHP 模板高级篇总结
2006/12/21 PHP
编译php 5.2.14+fpm+memcached(具体操作详解)
2013/06/18 PHP
php可扩展的验证类实例(可对邮件、手机号、URL等验证)
2015/07/09 PHP
PHP+Mysql无刷新问答评论系统(源码)
2016/12/20 PHP
PHP会话控制实例分析
2016/12/24 PHP
PHP实现的常规正则验证helper公共类完整实例
2017/04/27 PHP
Thinkphp5行为使用方法汇总
2017/12/21 PHP
简洁短小的 JavaScript IE 浏览器判定代码
2010/03/21 Javascript
jqGrid jQuery 表格插件测试代码
2011/08/23 Javascript
jQuery插件制作之参数用法实例分析
2015/06/01 Javascript
Underscore源码分析
2015/12/30 Javascript
Bootstrap Validator 表单验证
2016/07/25 Javascript
基于Bootstrap的Metronic框架实现条码和二维码的生成及打印处理操作
2016/08/29 Javascript
Zepto实现密码的隐藏/显示
2017/04/07 Javascript
jQuery实现通过方向键控制div块上下左右移动的方法【测试可用】
2018/04/26 jQuery
JS中使用react-tooltip插件实现鼠标悬浮显示框
2019/05/15 Javascript
微信小程序动态添加view组件的实例代码
2019/05/23 Javascript
JavaScript Array对象使用方法解析
2019/09/24 Javascript
基于JavaScript或jQuery实现网站夜间/高亮模式
2020/05/30 jQuery
[01:02:02]DOTA2上海特级锦标赛A组败者赛 EHOME VS CDEC第二局
2016/02/25 DOTA
Django框架的中的setting.py文件说明详解
2018/10/15 Python
python中turtle库的简单使用教程
2020/11/11 Python
详解Python 中的 defaultdict 数据类型
2021/02/22 Python
html5 http的轮询和Websocket原理
2018/10/19 HTML / CSS
英国第一豪华护肤品牌:Elemis
2017/10/12 全球购物
实习期自我鉴定
2013/10/11 职场文书
客户表扬信范文
2014/01/10 职场文书
《狮子和兔子》教学反思
2014/03/02 职场文书
cf战队收人广告词
2014/03/14 职场文书
幼儿园安全责任书
2014/04/14 职场文书
巾帼文明岗申报材料
2014/05/01 职场文书
2014年妇女工作总结
2014/12/06 职场文书
史上最牛辞职信
2015/05/13 职场文书
导游词之嵊泗列岛
2019/10/30 职场文书
MySQL Shell的介绍以及安装
2021/04/24 MySQL
http通过StreamingHttpResponse完成连续的数据传输长链接方式
2022/02/12 Python