加速python,保护你的源代码

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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值