我正在使用memory_profiler
包(从pip下载),更具体地说,通过首先创建临时列表而不是循环遍历“迭代器列表”来查看循环列表的内存效率。
这是我一段时间遇到的问题,我想对我的解决方案进行基准测试。问题是我需要将列表中的每个元素与同一列表中的下一个元素进行比较,直到所有元素都被“处理”。所以我想这将是一个O(n ^ 2)解决方案(如果选择最天真的解决方案,对于列表中的每个元素,循环遍历列表)。
无论如何,下面的三个函数都在做同样的事情(或多或少);循环遍历自己压缩的列表 - 逐个偏移。
import cProfile
@profile
def zips():
li = range(1,20000000)
for tup in zip(li,li[1:]):
pass
del li
@profile
def izips():
from itertools import izip
li = range(1,20000000)
for tup in izip(li,li[1:]):
pass
del li
@profile
def izips2():
from itertools import izip
li = range(1,20000000)
for tup in izip(li,li[1:]):
del tup
del li
if __name__ == '__main__':
zips()
# izips()
# izips2()
令人惊讶的部分(对我来说)是在内存使用中,首先我运行zips()函数,虽然我认为我已经清理了,但我的内存仍然是〜1.5 GB:
ipython -m memory_profiler python_profiling.py
Filename: python_profiling.py
Line # Mem usage Increment Line Contents
================================================
10 @profile
11 27.730 MB 0.000 MB def zips():
12 649.301 MB 621.570 MB li = range(1,20000000)
13 3257.605 MB 2608.305 MB for tup in zip(li,li[1:]):
14 1702.504 MB -1555.102 MB pass
15 1549.914 MB -152.590 MB del li
然后我关闭解释器实例并重新打开它以运行下一个测试,即izips()函数:
ipython -m memory_profiler python_profiling.py
Filename: python_profiling.py
Line # Mem usage Increment Line Contents
================================================
17 @profile
18 27.449 MB 0.000 MB def izips():
19 27.449 MB 0.000 MB from itertools import izip
20 649.051 MB 621.602 MB li = range(1,20000000)
21 1899.512 MB 1250.461 MB for tup in izip(li,li[1:]):
22 1746.922 MB -152.590 MB pass
23 1594.332 MB -152.590 MB del li
然后最后我运行了一个测试(在重新启动之间的解释器之后),我尝试显式删除for循环中的元组,以确保释放它的内存(也许我没想到)那是正确的?)。事实证明这没有什么区别,所以我猜测我要么不提示GC,要么就是我内存开销的来源。
ipython -m memory_profiler python_profiling.py
Filename: python_profiling.py
Line # Mem usage Increment Line Contents
================================================
25 @profile
26 20.109 MB 0.000 MB def izips2():
27 20.109 MB 0.000 MB from itertools import izip
28 641.676 MB 621.566 MB li = range(1,20000000)
29 1816.953 MB 1175.277 MB for tup in izip(li,li[1:]):
30 1664.387 MB -152.566 MB del tup
31 1511.797 MB -152.590 MB del li
底线: 我认为for循环本身的开销是最小的,因此,我期待的只是大约620.000 MB(存储列表所需的内存),但它看起来像我有~2个大小的列表内存中的20.000.000甚至更多的开销。任何人都可以帮我解释所有这些内存被用于什么? (以及每次运行结束时约1.5 GB的内容是什么?)
答案 0 :(得分:3)
请注意,操作系统会以块的形式分配内存,并不一定要一次性回收所有内存。我发现内存分析包非常不准确,因为它似乎没有考虑到这一点。
您的li[1:]
切片会创建一个 new 列表,其中包含(2 * 10 ** 7) - 1个元素,几乎是一个全新的副本,可以轻松地将列表所需的内存空间加倍。 zip()
调用还返回一个全新的列表对象,即压缩操作的输出,再次需要中间结果的内存,再加上2000万个2元素元组。
您可以使用新的迭代器而不是切片:
def zips():
from itertools import izip
li = range(1,20000000)
next_li = iter(li)
next(next_li) # advance one step
for tup in izip(li, next_li):
pass
del li
从iter()
调用返回的列表迭代器更轻量级;它只保留对原始列表和指针的引用。将其与izip()
相结合也可以避免创建输出列表。