月度归档:2016年10月

Python 性能优化指导(加速器和一些小技巧)

  1. 使用测量工具,量化性能才能改进性能,常用的timeit和memory_profiler,此外还有profile、cProfile、hotshot等,memory_profiler用了psutil,所以不能跟踪cpython的扩展;
  2. 用C来解决费时的处理,c是效率的代名词,也是python用来解决效率问题的主要途径,甚至有时候我都觉得python是c的完美搭档。常用的是Cython,直接把py代码c化然后又能像使用py包一样使用,其次是ctypes,效率最最高的存在,最后还有CPython和cffi都是屌屌的存在;
  3. 优化算法,所有语言通病,算法的提升我觉得是在所有提升之上的,但也是最难的,好在现在大部分常用的算法都已经封包,除非自己给自己挖坑,所以弄懂标准库里的数据结构和常用api是如何实现的很重要;
  4. 2里的实现有人做了更高效的包用以替换python中常见的一些实现,如果瓶颈在stringio、pickle、profile这类上的可以考虑替换为c的版本;
  5. 数据结构尽量使用元组tuple,特别是数据量大的时候,实在不行list也可以,尽量不要用class,如果一定要用可以加slot,效率再不够就只能结合2来加速了;
  6. 延迟加载,import不是一定要写在一页的开始,哪里都可以,越碎片越能把包的加载延迟甚至不被加载;
  7. 用multiprocessing来实现多线程,可以跳出GIL的限制;
  8. python处理循环很烂,解释性语言就这样,跟其它编译型语言比就是蜗牛,所以减少循环次数和嵌套次数能显著提升性能,当然了使用pypy就没有这个问题了;
  9. 使用加速器,很喜欢psyco的使用方式,如果用2.7-的版本那么不失为一个懒人的选择,现在已经不再维护,创始人去了pypy,pypy是用Python实现的python,底层转为平台依赖的c、.net、java的中间语言,方式非常聪明,大爱,但是缺点是库的支持还不完善,我的项目基本都能支持,解决几个小问题即可,如果性能瓶颈在循环和内存上可以试试,最大的好处是不需要更改一句代码和做另外的设置,没有任何侵入。

Update-20161101:
1.更新第一条,objgraph更好用,兼容py3,重点是速度快,除了查看top对象列表外还可以生成对象图谱来分析oom,用了cython也可以分析,配合memory_profiler很完美;
2.吐槽一下pympler ,输出非常漂亮,可惜就是慢,如果数据量不大分析倒是挺好用的。
3.使用”del a”语法,如果这个代码是被使用在Cython中,因为它可以立即释放这部分内存;

参考资料:
Python 代码性能优化技巧:https://www.ibm.com/developerworks/cn/linux/l-cn-python-optim/
Python性能优化技巧:http://kuanghy.github.io/2016/09/26/python-optimize

Python中使用自定义类class作为海量数据结构时内存占用巨大的问题

最近碰到处理一个二十多兆的文件时内存蹭蹭的吃掉四百多兆,吓死宝宝了。

无奈毕竟接触python时间有限,还没有完整的看过python的一些基础知识,我想一个合格的pythoner应该不会碰到这个问题。当然像我这样的操作党碰到的问题映像也更深一点。最郁闷的是网上讨论这个的很有限,google的结果也是三天才找到相应的说明,我想这里很重要的一点是海量数据在存储和很低层的操作时并不会使用python,嵌入一个c片段即可。好吧这招我还不会,而且在用的库要改也是件痛苦的事,为了让项目尽早上线有个折中的方案顶上即可。在无尽的排查后发现,原来python这个东东为了让大家写的爽,在性能上事丢的不要不要的,一般的系统还看不出问题,一但不是它原声的一些对象问题就来了,原来python在实现很多功能时并不像c那样极致,而是丢失性能换来魔法般的灵活性,而这个恶魔就是字典,自定义类的属性它居然使用字典来实现的。当然python设计时假设class的使用场景并没有特定于像c的struct那么高效,好在留了一手,可以像如下添加__slots__来缩减class,使得其占用内存更紧凑接近于list,对了list比tuple要差一点,毕竟它功能更多一点,不过我觉得与其省下list这点还不如换c更实在。添加__slots__后的代码如下:

class Date:
    __slots__ = ['year', 'month', 'day']
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

Celery在Flask的初始化中的问题

Flask的初始化有好多种,像工厂、单例都是不错的,我使用的是声明+init的方式,比如celery会是这样:

base.py中:celery = Celery()

__init__.py中在create__app()方法中会将base中的组件逐个init初始化。

大部分flask的组件都支持这种初始化的方式,因为python是解释型语言所以在使用不支持这种的组件如celery会有问题,比如像上面那样初始化后celery无法正确设置broker哪怕重新用celery.update_config(xxx)也没有用,其他参数似乎可以生效的。后来找到了celery-flask-helper,发现可以解决这个问题,不过在我的py35中无法使用它pypi的包,最后是copy代码直接引用到我的app中才可以。