文档章节

Cython的简单使用

mickelfeng
 mickelfeng
发布于 2017/06/04 09:46
字数 2274
阅读 35
收藏 1
点赞 0
评论 0

Cython是一个快速生成Python扩展模块的工具,从语法层面上来讲是Python语法和C语言语法的混血,当Python性能遇到瓶颈时,Cython直接将C的原生速度植入Python程序,这样使Python程序无需使用C重写,能快速整合原有的Python程序,这样使得开发效率和执行效率都有很大的提高,而这些中间的部分,都是Cython帮我们做了,接下来简单说一下Cython的安装和使用方法

  一、首先Cython官网地址是:http://cython.org/ 这里有cython的安装和开发文档,关于Cython的下载可以在pypi上直接下载安装包:https://pypi.python.org/pypi/Cython/ 由于是在Linux下安装使用,这里下载的是Cython-0.25.2.tar.gz,上传至linux执行如下步骤安装:

tar -xvzf Cython-0.25.2.tar.gz
cd Cython-0.25.2
python setup.py install

  这样Cython模块就安装成功了

  然后我们首先看一下cython的基本使用,首先 mkdir hello_pack && cd hello_pack 建立一个目录并且进入,然后编写一个hello.pyx的脚本,代码如下:

# coding=utf-8
def print_hello(name):
    print "Hello %s!" % name

  代码很简单,就是一个函数,然后编写一个setup.py,代码如下:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
ext_modules = [Extension("hello",["hello.pyx"])]
setup(
    name = "Hello pyx",
    cmdclass = {'build_ext': build_ext},
    ext_modules = ext_modules
)

  这里导入了Cython的模块,其中setup name指定模块的名称,然后执行编译命令:

python setup.py build_ext --inplace

  编译完之后,看到当前目录下会生成两个文件,一个是hello.c一个是hello.so,hello.c就是转换而成的c代码,而hello.so就是我们需要的python经过Cython编译之后的模块,我们为了当前目录可被调用,建立__init__.py内容如下:

# coding=utf-8
from hello import *

  然后执行 cd .. 回到上层目录,建立一个hello.py,代码如下:

#!/usr/bin/python
# coding=utf-8
from hello_pack import hello
hello.print_hello("cython")

  很简单,就是调用我们编译好的hello模块,然后执行里面的方法,现在直接执行hello.py,看到输出结果正常

  

 

  那么现在,我们就完成了Cython的基本使用,其实setup.py编译脚本也可以写成如下这样:

from distutils.core import setup
from Cython.Build import cythonize
setup(
    name='Hello pyx',
    ext_modules=cythonize('hello.pyx')
)

  这种写法更通用,编译的时候直接使用 python setup.py build 就可以了,注意不是install,执行install后会把模块复制到python系统目录下了;

  执行完之后,会看到当前目录有一个build目录,然后进去会发现有如下两个目录:

  

  第二个temp是编译过程中汇编生成的.o文件,第一个lib开头的目录中存放的就是hello.so文件,直接把这个文件拷贝出来就可以使用了,另外为了方便,运行脚本直接和so文件放在一个目录下就可以,直接使用import hello即可导入,也不用建立__init__.py了

  二、以上就是基本的Cython使用,下面借助一个案例来说明Cython和Python程序有哪些性能方面的提升

  首先我们编写一个compute.py,封装了两个计算的方法,代码如下:

# coding=utf-8
import math
def spherical_distance(lon1, lat1, lon2, lat2):
    radius = 3956
    x = math.pi/180.0
    a = (90.0 - lat1)*x
    b = (90.0 - lat2)*x
    theta = (lon2 - lon1)*x
    distance = math.acos(math.cos(a)*math.cos(b)) + (math.sin(a) * math.sin(b) * math.cos(theta))
    return radius * distance

def f_compute(a, x, N):
    s = 0
    dx = (x - a)/N
    for i in range(N):
        s += ((a + i * dx) ** 2 - (a + i * dx))
    return s * dx

  第一个方法可以计算地球表面任意两个经纬度之间的距离,这个方法使用了很多三角函数,这些三角函数由python的math模块提供;第二个方法就是纯数字的计算

  那么现在编写一个执行脚本来调用函数,test.py代码如下:

#!/usr/bin/env python
# coding=utf-8
import compute
import time

lon1, lat1, lon2, lat2 = -72.345, 34.323, -61.823, 54.826

start_time = time.clock()
compute.f_compute(3.2, 6.9, 1000000)
end_time = time.clock()
print "runing1 time: %f s" % (end_time - start_time)
start_time = time.clock()
for i in range(1000000):
    compute.spherical_distance(lon1, lat1, lon2, lat2)
end_time = time.clock()
print "runing2 time: %f s" % (end_time - start_time)

  代码就是分别执行100000次数字计算和距离计算,并打印之间,执行结果如下:

  

  可以看到数字计算耗时0.33s,距离计算耗时1.34s,然后我们尝试直接用Cython模块进行编译

  首先复制出来一份 cp compute.py compute1.pyx 然后编写setup1.py

from distutils.core import setup
from Cython.Build import cythonize
setup(
    name='compute_module',
    ext_modules=cythonize('compute1.pyx'),
)

  写完之后直接执行 python setup1.py build 编译,编译完之后导入compute1.so这个模块,现在只需要修改头部import compute为import compute1 as compute这样能保证下面调用代码不变,然后执行,结果如下:

  

  现在会发现时间比刚才快了一点,说明性能有所提升,然后继续进行优化,这次全部变量都使用静态类型,即变量类型提前定义,compute2.pyx代码如下:

# coding=utf-8
import math
cpdef float spherical_distance(float lon1, float lat1, float lon2, float lat2):
    cdef float radius = 3956
    cdef float pi = 3.14159265
    cdef float x = pi/180.0
    cdef float a,b,theta,distance
    a = (90.0 - lat1)*x
    b = (90.0 - lat2)*x
    theta = (lon2 - lon1)*x
    distance = math.acos(math.cos(a)*math.cos(b)) + (math.sin(a) * math.sin(b) * math.cos(theta))
    return radius * distance

def f_compute(double a, double x, int N):
    cdef int i
    cdef double s = 0
    cdef double dx = (x - a)/N
    for i in range(N):
        s += ((a + i * dx) ** 2 - (a + i * dx))
    return s * dx

  现在可以看到类型全部做了定义,在cython里面,类型定义使用cdef float这种方式进行;对于方法来说,如果模块内部相互调用那么同样使用cdef double这种的方式来定义,如果这个方法要在我们外部执行脚本中调用,那么要么是python原生方法不做任何修改,要么写成cpdef float这种类型的形式,方法定义类型可以提高效率,但是提高不大,但是变量静态类型可以极大的提高效率,原因是参与计算的主要是变量;假如一个函数被频繁调用,那么有必要使用cdef或者cpdef来定义;现在同样方法编译compute2模块,然后在test.py中调用,执行结果如下:

  

  现在发现纯数字计算的时间几乎变成了0秒!而第二个用了0.81s,比刚才快了,但是似乎并不是很快,原因仔细想想会发现,这里面调用了很多三角函数,使用的还是python内置的math模块,这里可以用C语言的math.h来代替,所以再写一个compute3.pyx如下:

# coding=utf-8
#import math
cdef extern from "math.h":
    float cosf(float theta)
    float sinf(float theta)
    float acosf(float theta)

def spherical_distance(float lon1, float lat1, float lon2, float lat2):
    cdef float radius = 3956
    cdef float pi = 3.14159265
    cdef float x = pi/180.0
    cdef float a,b,theta,distance
    a = (90.0 - lat1)*x
    b = (90.0 - lat2)*x
    theta = (lon2 - lon1)*x
    distance = acosf(cosf(a)*cosf(b)) + (sinf(a) * sinf(b) * cosf(theta))
    return radius * distance

def f_compute(double a, double x, int N):
    cdef int i
    cdef double s = 0
    cdef double dx = (x - a)/N
    for i in range(N):
        s += ((a + i * dx) ** 2 - (a + i * dx))
    return s * dx

  这次我们屏蔽了自身的math模块,然后调用了C的头文件math.h,并引入了cosf,sinf,acosf,实际上这三个函数返回的都是float类型,和我们定义的一致,如果我们想使用double类型,那么应该直接使用cos,sin,acos函数,并且我们可以对这些函数再包装,这样cpdef就派上用场了,比如下面:

cdef extern from "math.h":
    double cos(double)
    double sin(double)

cpdef double tangent(double x):
    return sin(x)/cos(x)

  比如这里定义了一个测试的tangent正切函数,就是上面这样写;同样我们现在编译compute3.pyx并测试,结果如下:

  

  现在第一个仍然是接近于0s完成,而第二个三角函数换成C的之后,性能有的非常大的提升,可以说几乎接近于原生C语言的效率了,这样看来从Python到Cython性能得到了很大的优化,但是代码量确差别没有用C重写那么大

  根据上面的案例,我们知道在数字,浮点数等计算中Cython可以极大的提高性能,而这方面多线程几乎不能提高任何性能,有时候反而会降低;但是对于io密集型的场合,用Cython基本上也没有性能上太大的提升,而多线程的将拥有更加出色的性能,所以Cython应该专注与计算方面的优化;总结一下也就是对于IO密集型应用,优化可以考虑使用多线程或者多进程方式,对于计算密集型的场合,遇到瓶颈时可以考虑使用Cython或者封装C相关的模块

  最后,虽然Cython非常好用,但也不能疯狂的使用,推荐一句名言:我们应该忘记小的效率,过早的优化是一切罪恶的根源,有 97% 的案例如此。简单来说就是选择恰当的时机进行优化

 

© 著作权归作者所有

共有 人打赏支持
mickelfeng

mickelfeng

粉丝 226
博文 964
码字总数 548356
作品 0
成都
高级程序员
cython的使用

0. 环境配置 要使用cython首先得有的她的环境 [ 废话,^_^ ],系统上有pip包管理环境的话直接:pip install cython 即可安装cython或者也可以源码安装: https://github.com/cython/cython/wi...

gfsfg8545 ⋅ 2017/08/27 ⋅ 0

Python 与 C/C++ 交互的几种方式

python作为一门脚本语言,其好处是语法简单,很多东西都已经封装好了,直接拿过来用就行,所以实现同样一个功能,用Python写要比用C/C++代码量会少得多。但是优点也必然也伴随着缺点(这是肯...

吴金瑞 ⋅ 2017/05/09 ⋅ 0

Cython 简化工具--Runcython

Runcython 旨在简化 Cython 的使用过程,而不会牺牲掉可扩展性。在任何 Python 程序中使用 cython,main.py,只需要做 $ mv main.py main.pyx && runcython main.pyx 在标准的 cython 的构建...

孔小菜 ⋅ 2015/09/22 ⋅ 0

pyjnius的安装

有时某些功能有java的开源项目,因此我需要在python中直接调用java。网上有几种方式,后来发现有一个 kivy 的项目可以用它来作android手机的应用开发,它下面有一个 pyjnius 的项目,就是用来...

limodou ⋅ 2013/08/29 ⋅ 0

Speed-up with Cython and Numpy in Python

Cython代码和Python代码区别 代码运行在IPython-Notebook中,在IPython-Notebook中导入cython环境。 1 %load_ext cython Cython可以在Python中掺杂C和C++的静态类型,cython编译器可以把Cyt...

Arthurcsh ⋅ 2017/10/13 ⋅ 0

Python, C-Python, Cython代码与GIL的交互

这篇笔记相对Python来说,有点底层,先来解释几个名词: C-Python: 或者CPython,指C实现的Python虚拟机的基础API。最通用的Python就是是基于C实现的,它的底层API称为C-Python API,所有Pyt...

鉴客 ⋅ 2012/02/23 ⋅ 0

Python 性能优化

原文出处:xybaby 本文除非特殊指明,”python“都是代表CPython,即C语言实现的标准python,且本文所讨论的是版本为2.7的CPython。另外,本文会不定期更新,如果大家有一些好的想法,请在评...

xybaby ⋅ 2017/11/27 ⋅ 0

Cython 三分钟入门

作者:perrygeo 译者:赖勇浩(http://laiyonghao.com) 原文:http://www.perrygeo.net/wordpress/?p=116 我最喜欢的是Python,它的代码优雅而实用,可惜纯粹从速度上来看它比大多数语言都要...

鉴客 ⋅ 2012/02/23 ⋅ 4

另一个 Python Protobuf 库--Pyrobuf

Pyrobuf 是谷歌Python版 Protobuf 库的替代品,使用 Cython 编写,提供更好的性能(大概快 1.5-2 倍),支持 Python 3;提供简单的 JSON 序列化/反序列化和原生 Python 词典。Pyrobuf 仅要求...

叶秀兰 ⋅ 2015/12/23 ⋅ 2

C/C++ 和 Python混合编程

链接:https://www.zhihu.com/question/23003213/answer/56121859 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 #include int main(int argc, char *...

sirius_0 ⋅ 04/21 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

磁盘管理—逻辑卷lvm

4.10-4.12 lvm 操作流程: 磁盘分区-->创建物理卷-->划分为卷组-->划分成逻辑卷-->格式化、挂载-->扩容。 磁盘分区 注: 创建分区时需要更改其文件类型为lvm(代码8e) 分区 3 已设置为 Linu...

弓正 ⋅ 10分钟前 ⋅ 0

Spring源码解析(六)——实例创建(上)

前言 经过前期所有的准备工作,Spring已经获取到需要创建实例的 beanName 和对应创建所需要信息 BeanDefinition,接下来就是实例创建的过程,由于该过程涉及到大量源码,所以将分为多个章节进...

MarvelCode ⋅ 30分钟前 ⋅ 0

a href="#"

<a href="#">是链接到本页,因为你有的时候需要有个链接的样式,但是又不希望他跳转,这样写,你可以把这个页面去试试

颖伙虫 ⋅ 37分钟前 ⋅ 0

js模拟栈和队列

栈和队列 栈:LIFO(先进后出)一种数据结构 队列:LILO(先进先出)一种数据结构 使用的js方法 1.push();可以接收任意数量的参数,把它们逐个推进队尾(数组末尾),并返回修改后的数组长度。 2....

LIAOJIN1 ⋅ 37分钟前 ⋅ 0

180619-Yaml文件语法及读写小结

Yaml文件小结 Yaml文件有自己独立的语法,常用作配置文件使用,相比较于xml和json而言,减少很多不必要的标签或者括号,阅读也更加清晰简单;本篇主要介绍下YAML文件的基本语法,以及如何在J...

小灰灰Blog ⋅ 46分钟前 ⋅ 0

IEC60870-5-104规约传送原因

1:周期循环2:背景扫描3:自发4:初始化5:请求6:激活7:激活确认8:停止激活9:停止激活确认10:激活结束11:远程命令引起的返送信息12:当地命令引起的返送信息13:文件传送20:响应总召...

始终初心 ⋅ 59分钟前 ⋅ 0

【图文经典版】冒泡排序

1、可视化排序过程 对{ 6, 5, 3, 1, 8, 7, 2, 4 }进行冒泡排序的可视化动态过程如下 2、代码实现    public void contextLoads() {// 冒泡排序int[] a = { 6, 5, 3, 1, 8, 7, 2, ...

pocher ⋅ 今天 ⋅ 0

ORA-12537 TNS-12560 TNS-00530 ora-609解决

oracle 11g不能连接,卡住,ORA-12537 TNS-12560 TNS-00530 TNS-12502 tns-12505 ora-609 Windows Error: 54: Unknown error 解决方案。 今天折腾了一下午,为了查这个问题。。找了N多方案,...

lanybass ⋅ 今天 ⋅ 0

IDEA反向映射Mybatis

1.首先在pom文件的plugins中添加maven对mybatis-generator插件的支持 ` <!-- mybatis逆向工程 --><plugin><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-ma......

lichengyou20 ⋅ 今天 ⋅ 0

4.10/4.11/4.12 lvm讲解 4.13 磁盘故障小案例

准备磁盘分区 fdisk /dev/sdb n 创建三个新分区,分别1G t 改变分区类型为8e 准备物理卷 pvcreate /dev/sdb1 pvcreate /dev/sdb2 pvcreate /dev/sdb3 pvdisplay/pvs 列出当前的物理卷 pvremo...

Linux_老吴 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部