加速python,保护你的源代码

来源:互联网 发布:编程培训中心哪里好 编辑:程序博客网 时间:2024/05/29 04:45
1. 背景最近一个项目需要DTMF解码,使用到傅立叶变换的一种变种:Goertzel算法,这是计算密集型算法,尽管Goertzel算法已经将基本傅立叶变换大大简化,但是其计算量还是比较大,使用python实现后效率较低,要几秒才能计算出一个完整的电话号码串,所以不能实际应用。碰到此情况一般第一考虑是将Goertzel算法使用C/C++实现,然后python调用,但是此项目我希望更pythonic一点,也方便以后维护,结果多方抉择选中了cython方案。2. cython简介cython网上有很多介绍和教程之类的,我就不赘述了,简单“科普”一下。cython是python的一个扩展模块,主要功用是将python代码编译成C/C++,然后再编译成python扩展模块(windows上是*.pyd)。cython的最简单用法就是将”任何“合法的python代码直接编译为pyd,则其中的代码不经python解析器而直接调用python的api,这样你就可以隐藏你的源代码了,并且也获得了一点点的效率提升(我实际测试大约能提高10%)。次简单用法就是将你的python代码中没有使用到动态特性的变量使用cython语法声明为静态类型,这样你就可以用对源代码进行最小的改动,获得很大的效率提升,而且代码可读性没有任何影响,还是“python代码”。真实的案例是我将python写成的DTMF解码模块仅是将变量切换为静态类型后,效率提升*30倍*,最终的pyd模块的效率已经和C++的实现差别不大,~200ms能完整解出一个完整的号码串,不管是离线应用或在线应用都满足需求,所以我就没有再进一步的优化了,在花大力气之后即使能优化成100ms,0.1s和0.2s的区别在我的应用中不是人能感知的。3. cython语法(通过一些代码片段管中窥豹)cython文件一般后缀为pyx(还有pxi/pxd)cython语法代码前要增加cython标识,比如:cdef, cpdef等。    1. 变量声明       cdef int variable    cdef float* variable    cdef object variable  #python对象    2. 类实例变量声明    使用cdef将所有实例变量在正常的类变量位置先声明,然后在__init__()中初始化    有code有真相:    ------------       cdef class dtmfDetector:    cdef int GOERTZEL_N, SAMPLING_RATE, debug    cdef int* freqs    def __init__(self, int pfreq=8000):         self.GOERTZEL_N = 92         self.SAMPLING_RATE = 8000         self.freqs = [697,770,852,941,1209,1336,1477,1633]    3. 让cython函数可以被其他模块的python代码调用    将函数声明为cpdef即可,如果声明为cdef则为纯粹的c函数,其文档介绍即使将函数声明为cpdef,如果是其他c函数调用(包括cdef定义的函数),则cython会优化为cdef,效率也很高    -----------       cpdef reset(self):        self.sample_index = 0    4. 使用数组       cdef float* variable = [0.1,1.2,2.3,3.4,5]    variable[2] = 10.0    5. 循环优化    可以放心的使用range循环,cython会将其优化为c类型的for语句形如: for (i = 0; i < 100; i++)    或者你也可以使用cython特有的语法:       for i from 0 < i < highvalue by 2: #by是步进,可选    6. 使用c/c++标准库函数       from libc.string cimport memset    import cython    cdef int variable[5]    memset(variable, 0, cython.sizeof(variable))    7. 直接调用c/c++源文件中的函数或类(python/cython/c混合编程)       cdef extern from "otherfile.h":        int myCFunc() #myCFunc()在otherfile.h声明,可能在otherfile.c定义        cppclass MyCPlusClass: #在otherfile.h声明,可能在othercplusfile.cpp定义            MyCPlusClass()            void openDoor()    def pythonFunc():            cdef MyCPlusClass* newclass = new MyCPlusClass()            cdef MyCPlusClass newclassInStack            print(myCFunc())            newclass.openDoor()            newclassInStack.openDoor()            del newclass #创建在堆上面的对象需要手工销毁4. 编译pyx    1. 安装cython和编译cython文件都需要c/c++编译环境,    如果你安装了VS,则一般不用更多设置,    如果没有,在windows环境下建议安装Mingw,安装完成后将c:\MinGW添加到path系统变量,然后在C:\PythonXX\Lib\distutils\ 中添加一个    distutils.cfg文件,内容为:      [build]    compiler = mingw32    [build_ext]    compiler = mingw32    2. 安装完cython后,如果C:\PythonXX\Scripts没有在path系统变量,也建议添加    3. 直接执行 cython yourfile.pyx 则生成 yourfile.c 文件,然后使用编译器编译连接即可,稍复杂,网上很多教程,我就不再重复了,我这里说的是简单的方法    4. 新建一个setup.py (名字可以随便起) :       from distutils.core import setup    from Cython.Build import cythonize    setup(        name = 'Your module',        ext_modules = cythonize("yourfile.pyx")    )    5. 执行命令:       python.exe setup.py build_ext --inplace     则在yourfile.pyx 同一目录下生成yourfile.pyd    6. 使用yourfile.pyd和yourfile.py一样,没有任何区别,       import yourfile ...    7. cython -a youfile.pyx 则生成yourfile.c的同时还会生成yourfile.html文件,列出cython代码行和c代码行的一一对应关系,点击代码行则显示对应的c代码,特别方便进行代码优化。5. profiling    使用cython的目的是效率,你肯定关心cython究竟给我提高了多少效率,所以“测速”的功能肯定是不能缺的。    1. 在pyx文件的头两行内添加一行:       # cython: profile=True    2. 测试代码:       import yourmodule, pstats, cProfile    cProfile.runctx("yourmodule.func(args)", globals(), locals(), "Profile.prof")    s = pstats.Stats("Profile.prof")    s.strip_dirs().sort_stats("time").print_stats()    则打印出每个函数的调用次数和执行时间等内容    注意在发行你的软件时切换profile=False或删除此行6. 结束语    cython是不是很帅?python的开发效率,c的执行效率!    使用cython消除程序热点(效率瓶颈)和隐藏核心代码,通过 py2exe/cx_freeze 之类的打包软件将你的程序打包,然后就可以放心的发布你的软件了。   7. 再结束:和nuitka对比    除cython外,nuitka可以将全部代码都转换成C++代码然后编译,不过nuitka是直接调用python的api,不像cython,生成的是真正独立的c代码,可以不调用python的api高效率的实现一些计算密集任务,所以我还是推荐cython,而不是nuitka。

0 0
原创粉丝点击