为什么生产的发电机比xrange生产的发电机更快?

Yan*_* Yi 10 python yield python-2.7 python-3.x

我正在调查Python生成器,并决定进行一些实验.

TOTAL = 100000000
def my_sequence():
    i = 0
    while i < TOTAL:
        yield i
        i += 1

def my_list():
    return range(TOTAL)

def my_xrange():
    return xrange(TOTAL)    
Run Code Online (Sandbox Code Playgroud)

在运行每个方法几次并取平均值后,内存使用情况(使用psutil获取进程RSS内存)和所用时间(使用time.time())如下所示:

sequence_of_values = my_sequence() # Memory usage: 6782976B  Time taken: 9.53674e-07 s

sequence_of_values2 = my_xrange() # Memory usage: 6774784B  Time taken: 2.14576e-06 s

list_of_values = my_list() # Memory usage: 3266207744B  Time taken: 1.80253s
Run Code Online (Sandbox Code Playgroud)

我注意到使用xrange生成一个生成器始终(略微)慢于使用yield.为什么会这样?

mgi*_*son 8

我将在这个答案的前言中说,这种规模的时间可能难以准确测量(可能最好使用timeit),并且这些优化几乎不会对实际程序的运行时间产生任何影响......

好的,现在免责声明已经完成......

您需要注意的第一件事是您只是计算生成器/ xrange对象的构造 - 您没有计算实际迭代值1所花费的时间.有些原因导致创建生成器在某些情况下可能比创建xrange对象更快...

  1. 对于生成器案例,您只需要创建一个生成器 - 生成器中的代码实际上没有运行.这相当于大约1个函数调用.
  2. 对于这种xrange情况,你正在调用函数,然后你必须查找全局名称xrange,全局TOTAL,然后你需要调用内置 - 所以在这种情况下更多的事情被执行.

至于内存 - 在两种惰性方法中,使用的内存将由python运行时控制 - 而不是生成器对象的大小.内存使用受到脚本影响的唯一情况是您构建一个包含1亿个项目的列表.

另外请注意,我不能真正确认您的成绩一直在我的系统...使用timeit,其实我得到那个my_xrange有时2快(由〜30%)来构造.

将以下内容添加到脚本的底部:

from timeit import timeit
print timeit('my_xrange()', setup='from __main__ import my_xrange')
print timeit('my_sequence()', setup='from __main__ import my_sequence')
Run Code Online (Sandbox Code Playgroud)

我的结果是(适用CPython于OS-X El-Capitan):

0.227491140366
0.356791973114
Run Code Online (Sandbox Code Playgroud)

然而,pypy似乎有利于发电机构造(我尝试了它的my_xrange第一个和my_sequence第一个并得到相当一致的结果虽然第一个运行似乎有点不利 - 可能是由于JIT热身时间或某事):

0.00285911560059
0.00137305259705
Run Code Online (Sandbox Code Playgroud)

1在这里,我希望 xrange有优势 - 但是,直到你再没有什么是真的timeit,只有当时间差异很大并且只在你做时间的计算机上才是真的.
2见开放免责声明:-P