Cython的简单使用-程序员宅基地

技术标签: python  操作系统  c/c++  

from:http://www.cnblogs.com/freeweb/p/6548208.html

补充:在我mac上的加速效果:

最初:
runing1 time: 0.233466 s
runing2 time: 0.930724 s
使用Cython编译:
runing1 time: 0.178522 s
runing2 time: 0.646462 s
使用cdef等静态变量:
runing1 time: 0.001126 s
runing2 time: 0.595804 s
使用c库:
runing1 time: 0.001176 s
runing2 time: 0.211764 s

  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% 的案例如此。简单来说就是选择恰当的时机进行优化

转载于:https://www.cnblogs.com/bonelee/p/9936196.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/djph26741/article/details/101520423

智能推荐

w10计算机用户名密码忘了,电脑w10系统开机密码忘了-程序员宅基地

文章浏览阅读1.4w次。如果是普通账户密码忘了请用第一种方法。方法(一)重新启动电脑,启动到系统登录界面时,同时按住Ctrl+Alt键,然后连击Del键两次,会出现新的登录界面,用户名处输入“Administrator”密码为空,回车即可登录,登录后,打开控制面板选/用户账户/更改账户/点击原来的“账户名”/更改我的密码/输入新密码,再次输入新密码,然后点击“更改密码”按钮即可。如果是计算机管理员密码忘了,请用第二种方法..._w10系统不知道电脑的账户密码是多少

MQ的概念和RabbitMQ知识点(无代码)-程序员宅基地

文章浏览阅读1.2w次,点赞7次,收藏76次。MQ全称是MessageQueue(消息队列),是保存消息在传输过程中的一种容器,既是存储消息的一种中间件。多是应用在分布式系统中进行通信的第三方中间件,如下图所示,发送方成为生产者,接收方称为消费者。............_mq

如何做好Bug分析-程序员宅基地

文章浏览阅读1.5k次,点赞47次,收藏18次。Bug分析是QA的一项主要技能,需要针对项目中遇到的经典问题进行分类分析, 直达问题本质。 并且能够给团队其他项目或者成员起到典型的借鉴作用。 当然也有一些非常经典的问题可以进行技术深挖, 以供参考。 个人认为比较典型的「Bug分析」是stackoverflow, 当然, 一个完善的bug分析库, 可以进行问题分类总结。 对于测试新人是有很大的帮助的。本质上, 在测试领域很多问题是可重现可整理可规避的。另外, bug分析本身是为了拓宽每个人的认知边界, 缩小团队间的乔哈里窗以达到最佳的合作状态。一个「好的B

H5020NL PULSE 50PIN千兆四口网络变压器 HQST H85001S建议IC配置型号_4口网络变压器-程序员宅基地

文章浏览阅读800次。HQST导读:PULSE普思是网络通讯行业中龙头企业之一,其中网络变压器产品大都由国内代工厂代为生产,H5020NLHX5020NL千兆四口网络变压器是普思公司经典老牌产品,相对整个市场用量不是很大,集中生产约一月20万颗左右……PULSE普思是网络通讯行业中龙头企业之一,其中网络变压器产品大都由国内代工厂代为生产,H5020NLHX5020NL千兆四口网络变压器是普思公司经典老牌产品,相对整个市场用量不是很大,集中生产约一月20万颗左右,……PULSE H5020NL千兆网络变压器对应HQS._4口网络变压器

D20 EME 支持2k MAC地址表-程序员宅基地

文章浏览阅读242次,点赞3次,收藏9次。交换机,壳体采用镀锌钢板,结构紧凑,支持八个百兆端口,可配置一至四个百兆光纤端口。两路冗余电源设计,支持4pin可插拔端子,交直流通用,同时提供电源防接保护及过压、欠压保护,极大提升产品工作的稳定性。2.支持两路冗余电源设计,4pin可插拔端子,支持12~36V宽电压输入,交直流通用,同时提供电源防反接保护及过压、欠压保护,极大提升产品工作的稳定性。4.-40℃~75℃工作温度,-40~85℃存储温度,在极端气象条件下也能安全运行。8.支持IEEE802.3,IEEE802.3u,IEEE802.3x。

阿昌教你如何使用通义灵码-程序员宅基地

文章浏览阅读946次。Hi,我是阿昌,今天教你如何使用通义灵码。_通义灵码

随便推点

curviloft插件怎么用_【su】插件及其功能图示-程序员宅基地

文章浏览阅读3.3k次。01、TGI3D首先出场的是TGI3D,这个插件是我发目前认为最强大的插件。因为它可以处理线、面、体、贴图,还可以照片匹配建模,这个照片匹配建模不是SketchUp自带的那种简单匹配,而是可以匹配超复杂的模型。简直爱不释手,但遗憾的是这个插件是一个商业版的插件。02、SoapskinbubbleSoapskinbubble,这是个极富梦幻色彩的插件。其原理是以最适合的曲面来封闭几条规定的空间曲线,..._su curvioft

JAVA使用Gson排除特定字段_java po排除某个字段-程序员宅基地

文章浏览阅读2.4k次。1. 忽略值为NULLGson gson = new GsonBuilder().serializeNulls().create(); 2. 使用Java关键字transientclass Item { String name; public transient int age; } 3. 使用@Expose注解class Item { String name; @Ex_java po排除某个字段

网关、安全网关?与防火墙的区别(2),网络安全多线程断点续传-程序员宅基地

文章浏览阅读640次,点赞6次,收藏18次。网关是一个大的概念,没有特指是什么设备,很多设备都可以做网关,普通的PC机也能做,常用的网关设备是路由器。网关的作用主要是用来连接两个不同的网络,比如可以连接两个IP地址不相同的网络,或连接两个操作系统不同的网络,如WINDOWS与LINUX互连,或连接两个网络协议不同的网络,如TCP/IP与IPX.或拓扑结构不同的网络,如以太网和令牌环网。总之网关是一种中间媒介。而防火墙也可以做网关,但它的主要做用只是用来防病毒或防黑客,网关只算是防火墙的一个功能。网关与防火墙的区别。

解决:ModuleNotFoundError: No module named ‘pymysql’_modulenotfounderror: no module named 'pymysql-程序员宅基地

文章浏览阅读4.1k次,点赞42次,收藏34次。背景在使用之前的代码时,报错: Traceback (most recent call last): File "xxx", line xx, in import pymysql ModuleNotFoundError: No module named 'pymysql'翻译:```追溯(最近一次通话):文件“xxx”,第xx行,在导入pymysqlModuleNotFoundError:没有名为“pymysql”的模块```原因 ......_modulenotfounderror: no module named 'pymysql

android读取生成excel,Android创建与读取Excel-程序员宅基地

文章浏览阅读275次。1 import java.io.File;23 import java.io.IOException;45 import java.util.Locale;6789 import jxl.CellView;1011 import jxl.Workbook;1213 import jxl.WorkbookSettings;1415 import jxl.format.UnderlineStyle;..._android excel生成读取类

VS2015离线安装 安装包损坏或丢失_vs2015离线版csdn-程序员宅基地

文章浏览阅读4.3w次,点赞16次,收藏126次。1、去微软官网下载完成ISO镜像,最好不要在线安装,打开官方链接 https://www.visualstudio.com/zh-cn/downloads/download-visual-studio-vs.aspx按下图操作:2、用虚拟光驱加载,或者直接右键解压。在安装前,先安装两个证书。亲测,安装后,减少了很多“安装包损坏或丢失”的现象。两证书下载地址链接: https:/..._vs2015离线版csdn