利用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单链表实现代码实例
Nov 21 Python
Python中的类与对象之描述符详解
Mar 27 Python
用python找出那些被“标记”的照片
Apr 20 Python
利用matplotlib+numpy绘制多种绘图的方法实例
May 03 Python
对Pandas MultiIndex(多重索引)详解
Nov 16 Python
Python判断一个文件夹内哪些文件是图片的实例
Dec 07 Python
python脚本实现音频m4a格式转成MP3格式的实例代码
Oct 09 Python
numpy 矩阵形状调整:拉伸、变成一位数组的实例
Jun 18 Python
Django怎么在admin后台注册数据库表
Nov 14 Python
python3中calendar返回某一时间点实例讲解
Nov 18 Python
python实现excel公式格式化的示例代码
Dec 23 Python
用python自动生成日历
Apr 24 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中记录用户访问过的产品,在cookie记录产品id,id取得产品信息
2011/05/04 PHP
php自定义函数call_user_func和call_user_func_array详解
2011/07/14 PHP
pdo中使用参数化查询sql
2011/08/11 PHP
一个简单且很好用的php分页类
2013/10/26 PHP
php获取数组长度的方法(有实例)
2013/10/27 PHP
常用PHP框架功能对照表
2014/10/23 PHP
smarty模板引擎从配置文件中获取数据的方法
2015/01/22 PHP
Yii2使用$this-&gt;context获取当前的Module、Controller(控制器)、Action等
2017/03/29 PHP
深入理解PHP的远程多会话调试
2017/09/21 PHP
php面向对象程序设计中self与static的区别分析
2019/05/21 PHP
使用JavaScript获取地址栏参数的方法
2014/12/19 Javascript
JavaScript正则表达式中的ignoreCase属性使用详解
2015/06/16 Javascript
javascript实现uploadify上传格式以及个数限制
2015/11/23 Javascript
jquery实现(textarea)placeholder自动换行
2016/12/22 Javascript
JavaScript函数节流和函数去抖知识点学习
2018/07/31 Javascript
使用Node搭建reactSSR服务端渲染架构
2018/08/30 Javascript
用图片替换checkbox原始样式并实现同样的功能
2018/11/15 Javascript
微信小程序 调用微信授权窗口相关问题解决
2019/07/25 Javascript
[01:02:02]DOTA2上海特级锦标赛A组败者赛 EHOME VS CDEC第二局
2016/02/25 DOTA
python计算程序开始到程序结束的运行时间和程序运行的CPU时间
2013/11/28 Python
跟老齐学Python之一个免费的实验室
2014/09/14 Python
Python分治法定义与应用实例详解
2017/07/28 Python
利用pandas进行大文件计数处理的方法
2018/07/25 Python
django基于存储在前端的token用户认证解析
2019/08/06 Python
Python中常用的高阶函数实例详解
2020/02/21 Python
使用Pyhton 分析酒店针孔摄像头
2020/03/04 Python
Django 解决开发自定义抛出异常的问题
2020/05/21 Python
三星美国官网:Samsung美国
2017/02/06 全球购物
草莓网化妆品日本站:Strawberrynet日本
2017/10/20 全球购物
Michael Kors英国官网:美国奢侈品品牌
2019/11/13 全球购物
测试工程师岗位职责
2013/11/28 职场文书
我与祖国共奋进演讲稿
2014/09/13 职场文书
酒店采购员岗位职责
2015/04/03 职场文书
业务内勤岗位职责
2015/04/13 职场文书
2019大学竞选班长发言稿
2019/06/27 职场文书
Oracle 区块链表创建过程详解
2021/05/15 Oracle