A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 不二晨 金牌黑马   /  2018-11-12 09:04  /  876 人查看  /  3 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

多说也挺惨的,不能平静的死去,不知道哪里冒出一堆垃圾用户 ..

Python 慢是因为这货吃 CPU,而且多线程还不能有效利用多核,然而这货不仅吃 CPU 还吃内存,非常贪婪。
我的前一篇文章里面提到过一个项目,一直都没留心,这货大概加载了 70M 的磁盘文件到内存中以 dict 的形式保存,内存使用立马就飙升了大几百兆 .. (⊙v⊙)
看看 Python 的对象到底有多吃内存(下面的代码都是基于 Python3.6 的,Python2.x 只会多不会少):
>>> import sys>>> sys.getsizeof(1) 28  # bytes>>> sys.getsizeof(1<<64)  # long in Py2 36>>> sys.getsizeof(1.1) 24>>> sys.getsizeof('s') 50>>> sys.getsizeof('ss') 51>>> sys.getsizeof(b'b') 34>>> sys.getsizeof(b'bb') 35>>> from decimal import Decimal>>> sys.getsizeof(Decimal(3.4)) 104
对于容器类型的对象,我们得使用这段 代码 递归的计算内存大小,这里就直接复制过来了:
from __future__ import print_functionfrom sys import getsizeof, stderrfrom itertools import chainfrom collections import dequetry:    from reprlib import reprexcept ImportError:    passdef total_size(o, handlers={}, verbose=False):    """ Returns the approximate memory footprint an object and all of its contents.    Automatically finds the contents of the following builtin containers and    their subclasses:  tuple, list, deque, dict, set and frozenset.    To search other containers, add handlers to iterate over their contents:        handlers = {SomeContainerClass: iter,                    OtherContainerClass: OtherContainerClass.get_elements}    """    dict_handler = lambda d: chain.from_iterable(d.items())    all_handlers = {        tuple: iter,        list: iter,        deque: iter,        dict: dict_handler,        set: iter,        frozenset: iter,    }    all_handlers.update(handlers)     # user handlers take precedence    seen = set()                      # track which object id's have already been seen    default_size = getsizeof(0)       # estimate sizeof object without __sizeof__    def sizeof(o):        if id(o) in seen:       # do not double count the same object            return 0        seen.add(id(o))        s = getsizeof(o, default_size)        if verbose:            print(s, type(o), repr(o), file=stderr)        for typ, handler in all_handlers.items():            if isinstance(o, typ):                s += sum(map(sizeof, handler(o)))                break        return s    return sizeof(o)##### Example call #####if __name__ == '__main__':    d = dict(        a=1, b=2.5, c=1<<64,        d=(1, 2, 3), e=[4, 5, 6], f={7, 8, 9},        g=b'bytes', h='unicode'    )    print(total_size(d, verbose=True))
输出是这样的:
368 <class 'dict'> {'a': 1, 'b': 2.5, 'c': 18446744073709551616, 'd': (1, 2, 3), ...}50 <class 'str'> 'a'28 <class 'int'> 150 <class 'str'> 'b'24 <class 'float'> 2.550 <class 'str'> 'c'36 <class 'int'> 1844674407370955161650 <class 'str'> 'd'72 <class 'tuple'> (1, 2, 3)28 <class 'int'> 228 <class 'int'> 350 <class 'str'> 'e'88 <class 'list'> [4, 5, 6]28 <class 'int'> 428 <class 'int'> 528 <class 'int'> 650 <class 'str'> 'f'224 <class 'set'> {7, 8, 9}28 <class 'int'> 828 <class 'int'> 928 <class 'int'> 750 <class 'str'> 'g'38 <class 'bytes'> b'bytes'50 <class 'str'> 'h'56 <class 'str'> 'unicode'1558
没看错,这个小小的 dict 就干掉了近 1.5K 的内存... 可以的,实力在这里大家都看得到。
当然 Python 对于小型对象会使用对象池的方式来优化内存的使用率,但是这只能应用于大量相同的小对象,而且这篇文章里面提到了:
CPython manages small objects (less than 256 bytes) in special pools on 8-byte boundaries. There are pools for 1-8 bytes, 9-16 bytes, and all the way to 249-256 bytes. When an object of size 10 is allocated, it is allocated from the 16-byte pool for objects 9-16 bytes in size. So, even though it contains only 10 bytes of data, it will cost 16 bytes of memory. If you allocate 1,000,000 objects of size 10, you actually use 16,000,000 bytes and not 10,000,000 bytes as you may assume. This 60% overhead is obviously not trivial.
所以,谨慎使用 Python 对象缓存过大的数据集,万能的 Google 告诉我们可以使用标准库shelve / sqlite3.connect(':memory:') ,第三方工具 numpy / redis 以及优化过的数据结构trie 等等来代替 dict-like 的数据集。
BTW
这篇文章提供了一个案例教我们怎样去优化内存的使用率,主要使用了 Heapy 这个工具来定位吃内存较多的对象,通过干掉临时对象(del large_data),使用__slots__ 魔法,干掉 tuple,使用 Cython,将对象的方法变成函数等方式来优化内存使用率。
另外,objgraph 可以用来追查内存泄露相关的问题。

3 个回复

倒序浏览
~(。≧3≦)ノ⌒☆
回复 使用道具 举报
回复 使用道具 举报
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马