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

Cython的简单使用的更多相关文章

  1. 这是啥-Cython语言简单介绍

    Cython是一种既可以编写c又可以编写python的编程语言,他的目标是成为一个python语言的超集,为python提供高层次的.面向对象的.函数化.动态编程功能.不同于纯粹的python,它提供 ...

  2. 【原创】cython and python for kenlm

    未经允许不可转载 Kenlm相关知识 Kenlm下载地址 kenlm中文版本训练语言模型 如何使用kenlm训练出来的模型C++版本 关于Kenlm模块的使用及C++源码说明 加载Kenlm模块命令 ...

  3. 如何实现 C/C++ 与 Python 的通信?

    属于混合编程的问题.较全面的介绍一下,不仅限于题主提出的问题.以下讨论中,Python指它的标准实现,即CPython(虽然不是很严格) 本文分4个部分 1. C/C++ 调用 Python (基础篇 ...

  4. 非极大值抑制(NMS)的几种实现

    因为之前对比了RoI pooling的几种实现,发现python.pytorch的自带工具函数速度确实很慢,所以这里再对Faster-RCNN中另一个速度瓶颈NMS做一个简单对比试验. 这里做了四组对 ...

  5. Python性能分析

    Python性能分析 https://www.cnblogs.com/lrysjtu/p/5651816.html https://www.cnblogs.com/cbscan/articles/33 ...

  6. C/C++ 与 Python 的通信

    作者:Jerry Jho链接:https://www.zhihu.com/question/23003213/answer/56121859来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商 ...

  7. 【Python开发】C和Python之间的接口实现

    作者:Jerry Jho 链接:https://www.zhihu.com/question/23003213/answer/56121859 ## 更新:关于ctypes,见拙作 聊聊Python ...

  8. python 集成cython 简单测试

      实际开发中我们可能需要集成c/c++ 编写的模块,我们可以通过cython 解决类似的问题 以下测试一个简单的c add 方法, 使用venv 同时构建为一个pip 包 环境准备 venv 初始化 ...

  9. 用Cython加速Python程序以及包装C程序简单测试

    用Cython加速Python程序 我没有拼错,就是Cython,C+Python=Cython! 我们来看看Cython的威力,先运行下边的程序: import time def fib(n): i ...

随机推荐

  1. 【总结】 Lucas定理

    \(Lucas\)定理: \(C^x_y≡C^{x/p}_{y/p}*C^{x\%p}_{y\%p} ~~(mod~p)\) 证明不会2333 void pre(){ A[0]=A[1]=B[0]=B ...

  2. Mysql读写分离php脚本

    <?php/*php如何连接mysql*/ /*$link = mysql_connect(‘localhost‘, ‘root‘, ‘‘);if (!$link) {die(‘Could no ...

  3. 自学Python6.2-类、模块、包

    自学Python之路-Python基础+模块+面向对象自学Python之路-Python网络编程自学Python之路-Python并发编程+数据库+前端自学Python之路-django 自学Pyth ...

  4. suoi37 清点更多船只 (卡空间线段树)

    sbw巨佬的卡空间方法,把线段树的叶节点只记到长度为16的区间,然后在叶节点上暴力修改查询,这样点数是$\frac{N}{8}$的,可以过... orz #include<bits/stdc++ ...

  5. JDK源码分析(2)LinkedList

    JDK版本 LinkedList简介 LinkedList 是一个继承于AbstractSequentialList的双向链表.它也可以被当作堆栈.队列或双端队列进行操作. LinkedList 实现 ...

  6. Centos6.5 防火墙开放端口

    0. 说明 centos6.5处于对安全的考虑,严格控制网络进去.所以在安装mysql或者使用tomcat,需要开放端口3306或8080. 通常的解决办法有两个.一个是直接关闭防火墙(非常不推荐): ...

  7. 解决invalid record found in VCF4 file (at least 8 tab-delimited fields expected)问题,批量修改空格改为制表格格式

    出现这种问题说明一般存在两个问题: 第一,vcf文件不足8个分割制表符,比如像如下文件: 为了解决这个问题,说明在做snp filter时候,需要提取至少8个制表符的字符串,比如,像如下文件所示: 第 ...

  8. 数据库入门理论知识介绍以及编译安装MySql

    数据库入门理论知识介绍以及编译安装MySql 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 前言: 1.目前90%以上的公司面临的运维的瓶颈都在后端 最常见的2大瓶颈就是: 1&g ...

  9. nginx安装ngx_lua_waf防护

    ngx_lua_waf基于ngx_lua的web应用防火墙,使用起来简单,高性能和轻量级. ♦防止sql注入,本地包含,部分溢出,fuzzing测试,xss,SSRF等web攻击♦防止svn/备份之类 ...

  10. maven的pom.xml文件标签含义

    pom作为项目对象模型.通过xml表示maven项目,使用pom.xml来实现.主要描述了项目:包括配置文件:开发者需要遵循的规则,缺陷管理系统,组织和licenses,项目的url,项目的依赖性,以 ...