总和是做什么的?

dog*_*ang 5 python sum generator python-internals

首先,我想测试生成器和列表理解之间的内存使用情况.本书给了我一个小小的代码片段,我在我的PC上运行它(python3.6,Windows),发现一些意想不到的东西.

  1. 在书中,它说,因为列表理解必须创建一个真实的列表并为它分配内存,所以从列表理解中的itering必须比从生成器中的itering慢.
  2. 当然,列表理解比生成器使用更多的内存.

FOllowing是我的代码,它不满足以前的意见(在sum函数中).

import tracemalloc
from time import time


def timeIt(func):
    start = time()
    func()
    print('%s use time' % func.__name__, time() - start)
    return func


tracemalloc.start()

numbers = range(1, 1000000)


@timeIt
def lStyle():
    return sum([i for i in numbers if i % 3 == 0])


@timeIt
def gStyle():
    return sum((i for i in numbers if i % 3 == 0))


lStyle()

gStyle()

shouldSize = [i for i in numbers if i % 3 == 0]

snapshotL = tracemalloc.take_snapshot()
top_stats = snapshotL.statistics('lineno')
print("[ Top 10 ]")
for stat in top_stats[:10]:
    print(stat)
Run Code Online (Sandbox Code Playgroud)

输出:

lStyle use time 0.4460000991821289
gStyle use time 0.6190001964569092
[ Top 10 ]
F:/py3proj/play.py:31: size=11.5 MiB, count=333250, average=36 B
F:/py3proj/play.py:33: size=448 B, count=1, average=448 B
F:/py3proj/play.py:22: size=136 B, count=1, average=136 B
F:/py3proj/play.py:17: size=136 B, count=1, average=136 B
F:/py3proj/play.py:14: size=76 B, count=2, average=38 B
F:/py3proj/play.py:8: size=34 B, count=1, average=34 B
Run Code Online (Sandbox Code Playgroud)

两点:

  • 生成器使用更多时间和相同的内存空间.
  • sum函数中的list-comprehension似乎没有创建总列表

我想也许sum函数做了我不知道的事情.谁能解释一下?

这本书是High Perfoamance Python.chapter 5.但我确实做了一些与本书不同的检查其他背景下的有效性.而他的代码在这里是book_code,他没有把列表理解放在总和函数中.

Ser*_*sta 2

当谈到时间性能测试时,我确实依赖该timeit模块,因为它会自动执行多次运行代码。

在我的系统上,timeit 给出以下结果(由于多次运行,我大大减小了大小):

>>> timeit.timeit("sum([i for i in numbers if i % 3 == 0])", "numbers = range(1, 1000)")
59.54427594248068
>>> timeit.timeit("sum((i for i in numbers if i % 3 == 0))", "numbers = range(1, 1000)")
64.36398425334801
Run Code Online (Sandbox Code Playgroud)

因此生成器速度慢了大约 8% (*)。这并不令人意外,因为生成器必须动态执行一些代码才能获取下一个值,而预先计算的列表仅增加其当前指针。

恕我直言,内存评估更复杂,所以我使用了来自 activestate 的对象及其内容的计算内存占用(Python 配方)

>>> numbers = range(1, 100)
>>> numbers = range(1, 100000)
>>> l = [i for i in numbers if i % 3 == 0]
>>> g = (i for i in numbers if i % 3 == 0)
>>> total_size(l)
1218708
>>> total_size(g)
88
>>> total_size(numbers)
48
Run Code Online (Sandbox Code Playgroud)

我的解释是,列表将内存用于其所有项目(这并不奇怪),而生成器只需要很少的值和一些代码,因此生成器的内存占用要少得多。

我强烈认为您已将其用于tracemalloc不该用于的用途。它的目的是搜索可能的内存泄漏(大块内存从未被释放),而不是控制单个对象使用的内存。


注意:我只能测试小尺寸。但对于非常大的大小,列表可能会耗尽可用内存,并且机器将使用交换中的虚拟内存。在这种情况下,列表版本会变得慢很多。那里有更多详细信息