Python 在生成器和列表上迭代的速度差
在下面的简单示例中,有两个函数对随机数列表进行排序。第一个方法传递生成器表达式,第二个方法首先创建列表:Python 在生成器和列表上迭代的速度差,python,performance,generator,Python,Performance,Generator,在下面的简单示例中,有两个函数对随机数列表进行排序。第一个方法传递生成器表达式,第二个方法首先创建列表: import random l = [int(1000*random.random()) for i in xrange(10*6)] def sort_with_generator(): return sorted(a for a in l) def sort_with_list(): return sorted([a for a in l]) 与进行基准测试表明,第
import random
l = [int(1000*random.random()) for i in xrange(10*6)]
def sort_with_generator():
return sorted(a for a in l)
def sort_with_list():
return sorted([a for a in l])
与进行基准测试表明,第二个选项(sort_with_list
)的速度大约是生成器表达式的两倍
有人能解释一下发生了什么,以及为什么第一种方法比第二种方法慢得多吗?您的第一个示例是一个遍历列表的生成器表达式。第二个示例是在列表上迭代的列表表达式。事实上,第二个例子稍微快一点
>>> import timeit
>>> timeit("sorted(a for a in l)", setup="import random;l = [int(1000*random.random()) for i in xrange(10*6)]")
5.963912010192871
>>> timeit("sorted([a for a in l])", setup="import random;l = [int(1000*random.random()) for i in xrange(10*6)]")
5.021576881408691
这样做的原因无疑是创建列表是一次完成的,而遍历生成器需要函数调用。
生成器不会加速这样的小列表(列表中有60个元素,非常小)。这主要是为了在创建长列表时节省内存。列表表达式首先将数据加载到内存中。然后使用结果列表执行任何操作。让分配时间为
T2
(对于第二种情况)。
生成器表达式不会一次分配时间,但会更改时间的迭代器值t1[i]
。所有t1[i]
的总和将为t1
<代码>T1≈ <代码>T2
但是当您调用sorted()
时,在第一种情况下,时间T1
添加了与排序相比的每对内存分配时间(tx1[i]
)。结果,T1
与所有tx1[i]
的总和相加
因此,T2
T1+sum(tx1[i])
如果您查看排序的,,您传入的任何序列都会首先复制到新列表中
newlist = PySequence_List(seq);
生成器
-->列表
似乎比列表
-->列表
慢
>>> timeit.timeit('x = list(l)', setup = 'l = xrange(1000)')
16.656711101531982
>>> timeit.timeit('x = list(l)', setup = 'l = range(1000)')
4.525658845901489
至于为什么必须复制,想想排序是如何工作的。排序不是线性算法。我们多次遍历数据,有时在两个方向上遍历数据。生成器用于生成一个序列,通过该序列,我们从开始到之后的某个地方迭代一次,而且只迭代一次。列表允许随机访问
另一方面,从生成器创建列表意味着内存中只有一个列表,而复制一个列表意味着内存中有两个列表。好的时空权衡
Python使用的是合并排序和插入排序的混合体。列表示例中的每个元素是否都添加了1?我不知所措。你能把这两者分开并分别进行基准测试吗?可能解释器正在对列表进行智能缓存或类似的奇怪操作。列表理解会立即在内存中创建整个列表,而生成器表达式通过传递给排序函数的元组为结果序列的每个元素提供数据。因此,列表理解速度更快,但会消耗更多内存。生成器表达式速度较慢,但在任何给定时间,仅为列表中的一个元素保留内存。有关更多信息,请查看此问题:@elyase道歉,在粘贴过程中悄悄出现-否,除了表达式之外,它们应该是相同的。问题可以简化为列表(a代表l中的a)
与[a代表l中的a]
。这就是区别的来源。后者的速度与使用“排序”时的速度相同。将两者翻转过来,告诉我生成器是否更快。我还假设它在两个独立的情况下执行a+1
,因为每个都有单独的l
初始化。我怀疑我们是否会观察到同样的现象。实际上,我的代码应该已经初始化了10**6
items=D。它们似乎在10**5
和10**5
之间达到了收支平衡。我还是不知道我为什么会理解。@布赖恩:嗯?如果你们两个想说,如果我把随机数的生成直接移动到sorted()
调用中,就会改变结果:我试过了,只是为了确保,在我发布答案之前。正如我所怀疑的那样,没有。没有。我们建议在一个列表上进行两次连续迭代可能会由于某种缓存之类的原因导致性能差异。我想得越多,就越不可能发现排序的没有分配“每对比较的内存”,所以这没有什么意义。对于需要大量内存的巨大列表。生成器的排序效率可能较低,但原因并非如此。那么,您如何解释生成器表达式没有存储在所有以前值的内存中什么时候迭代?那么他们如何排序呢?它显然存储了它排序的值,是的。成对,否。由于在本例中没有键或cmp函数,因此它存储的是排序的列表。否,生成器-->列表不比列表-->列表慢。但是,这可能比首先生成列表然后将其复制到列表中要慢。所以还是+1。