免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 12261 | 回复: 3
打印 上一主题 下一主题

加速python,保护你的源代码 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2014-08-07 08:15 |只看该作者 |倒序浏览
本帖最后由 cdhigh 于 2014-08-07 18:46 编辑

加速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. 变量声明
   
  1. cdef int variable
  2. cdef float* variable
  3. cdef object variable  #python对象
复制代码


    2. 类实例变量声明
    使用cdef将所有实例变量在正常的类变量位置先声明,然后在__init__()中初始化
    有code有真相:
    ------------
   
  1. cdef class dtmfDetector:
  2. cdef int GOERTZEL_N, SAMPLING_RATE, debug
  3. cdef int* freqs
  4. def __init__(self, int pfreq=8000):
  5.      self.GOERTZEL_N = 92
  6.      self.SAMPLING_RATE = 8000
  7.      self.freqs = [697,770,852,941,1209,1336,1477,1633]
复制代码


    3. 让cython函数可以被其他模块的python代码调用
    将函数声明为cpdef即可,如果声明为cdef则为纯粹的c函数,其文档介绍即使将函数声明为cpdef,如果是其他c函数调用(包括cdef定义的函数),则cython会优化为cdef,效率也很高
    -----------
   
  1. cpdef reset(self):
  2.     self.sample_index = 0
复制代码


    4. 使用数组
   
  1. cdef float* variable = [0.1,1.2,2.3,3.4,5]
  2. variable[2] = 10.0
复制代码


    5. 循环优化
    可以放心的使用range循环,cython会将其优化为c类型的for语句形如: for (i = 0; i < 100; i++)
    或者你也可以使用cython特有的语法:
   
  1. for i from 0 < i < highvalue by 2: #by是步进,可选
复制代码


    6. 使用c/c++标准库函数
   
  1. from libc.string cimport memset
  2. import cython
  3. cdef int variable[5]
  4. memset(variable, 0, cython.sizeof(variable))
复制代码


    7. 直接调用c/c++源文件中的函数或类(python/cython/c混合编程)
   
  1. cdef extern from "otherfile.h":
  2.     int myCFunc() #myCFunc()在otherfile.h声明,可能在otherfile.c定义
  3.     cppclass MyCPlusClass: #在otherfile.h声明,可能在othercplusfile.cpp定义
  4.         MyCPlusClass()
  5.         void openDoor()

  6. def pythonFunc():
  7.         cdef MyCPlusClass* newclass = new MyCPlusClass()
  8.         cdef MyCPlusClass newclassInStack
  9.         print(myCFunc())
  10.         newclass.openDoor()
  11.         newclassInStack.openDoor()
  12.         del newclass #创建在堆上面的对象需要手工销毁
复制代码


4. 编译pyx

    1. 安装cython和编译cython文件都需要c/c++编译环境,    如果你安装了VS,则一般不用更多设置,
    如果没有,在windows环境下建议安装Mingw,安装完成后将c:\MinGW添加到path系统变量,然后在C:\PythonXX\Lib\distutils\ 中添加一个
    distutils.cfg文件,内容为:
  
  1. [build]
  2. compiler = mingw32
  3. [build_ext]
  4. compiler = mingw32
复制代码


    2. 安装完cython后,如果C:\PythonXX\Scripts没有在path系统变量,也建议添加

    3. 直接执行 cython yourfile.pyx 则生成 yourfile.c 文件,然后使用编译器编译连接即可,稍复杂,网上很多教程,我就不再重复了,我这里说的是简单的方法

    4. 新建一个setup.py (名字可以随便起) :
   
  1. from distutils.core import setup
  2. from Cython.Build import cythonize
  3. setup(
  4.     name = 'Your module',
  5.     ext_modules = cythonize("yourfile.pyx")
  6. )
复制代码


    5. 执行命令:
   
  1. python.exe setup.py build_ext --inplace
复制代码

    则在yourfile.pyx 同一目录下生成yourfile.pyd

    6. 使用yourfile.pyd和yourfile.py一样,没有任何区别,
   
  1. import yourfile ...
复制代码


    7. cython -a youfile.pyx 则生成yourfile.c的同时还会生成yourfile.html文件,列出cython代码行和c代码行的一一对应关系,点击代码行则显示对应的c代码,特别方便进行代码优化。

5. profiling
    使用cython的目的是效率,你肯定关心cython究竟给我提高了多少效率,所以“测速”的功能肯定是不能缺的。

    1. 在pyx文件的头两行内添加一行:
   
  1. # cython: profile=True
复制代码


    2. 测试代码:
   
  1. import yourmodule, pstats, cProfile
  2. cProfile.runctx("yourmodule.func(args)", globals(), locals(), "Profile.prof")
  3. s = pstats.Stats("Profile.prof")
  4. 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。

评分

参与人数 1信誉积分 +10 收起 理由
substr函数 + 10

查看全部评分

论坛徽章:
4
金牛座
日期:2013-10-11 16:12:50卯兔
日期:2014-07-31 09:17:19辰龙
日期:2014-08-08 09:28:02狮子座
日期:2014-09-14 20:32:05
2 [报告]
发表于 2014-08-07 08:41 |只看该作者
大神,你一出招就是大招呀。赞

求职 : 系统工程师等
论坛徽章:
5
技术图书徽章
日期:2014-04-19 22:01:05天蝎座
日期:2014-05-09 13:49:28双子座
日期:2014-06-16 15:08:042015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:55:28
3 [报告]
发表于 2014-08-07 08:57 |只看该作者
学习:wink:

论坛徽章:
26
2015亚冠之胡齐斯坦钢铁
日期:2015-06-25 21:40:202015亚冠之柏斯波利斯
日期:2015-08-31 17:03:192015亚冠之柏斯波利斯
日期:2015-11-07 13:10:00程序设计版块每日发帖之星
日期:2015-11-10 06:20:00每日论坛发贴之星
日期:2015-11-10 06:20:00程序设计版块每日发帖之星
日期:2015-11-26 06:20:00程序设计版块每日发帖之星
日期:2015-12-02 06:20:00黄金圣斗士
日期:2015-12-07 17:57:4615-16赛季CBA联赛之天津
日期:2015-12-23 18:34:14程序设计版块每日发帖之星
日期:2016-01-02 06:20:00程序设计版块每日发帖之星
日期:2016-01-06 06:20:00每日论坛发贴之星
日期:2016-01-06 06:20:00
4 [报告]
发表于 2015-11-21 10:40 |只看该作者
赶紧收藏起来![]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP