迭代生成器和列表之间的速度差异

时间:2013-09-06 01:57:18

标签: python performance generator

在以下简单的示例中,有两个函数可以对随机数列表进行排序。第一种方法传递sorted生成器表达式,第二种方法首先创建一个列表:

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])

使用line profiler进行基准测试表明第二个选项(sort_with_list)大约是生成器表达式的两倍。

任何人都可以解释发生了什么,为什么第一种方法比第二种方法慢得多?

3 个答案:

答案 0 :(得分:6)

您的第一个示例是迭代列表的生成器表达式。第二个示例是一个迭代列表的列表表达式。实际上,第二个例子稍快一些。

>>> 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个元素,非常小)。主要是在创建长列表时节省内存。

答案 1 :(得分:2)

如果您查看sorted newlist = PySequence_List(seq); ,则您传入的任何序列都会先被复制到新列表中。

generator

list - > list似乎慢于list - > >>> timeit.timeit('x = list(l)', setup = 'l = xrange(1000)') 16.656711101531982 >>> timeit.timeit('x = list(l)', setup = 'l = range(1000)') 4.525658845901489

{{1}}

至于为什么必须制作副本,请考虑排序的工作原理。排序不是线性算法。我们多次遍历数据,有时会在两个方向上遍历数据。生成器用于生成一个序列,通过该序列,我们迭代一次且仅一次,从开始到它之后的某个地方。列表允许随机访问。

另一方面,从生成器创建列表将仅意味着内存中的一个列表,而制作列表的副本将意味着内存中的两个列表。良好的时空权衡。

Python使用the source,它是合并排序和插入排序的混合体。

答案 2 :(得分:0)

列表表达式首先将数据加载到内存中。然后用结果列表进行任何操作。让分配时间为T2(第二种情况)。 生成器表达式不会立即分配时间,但会更改时间t1[i]的迭代器值。所有t1[i]的总和将为T1T1T2

但是当你打电话给sorted()时,在第一种情况下,时间T1增加了每对的分配记忆时间与排序(tx1[i])的比较。结果,T1添加了所有tx1[i]的总和。

因此,T2< T1 + sum(tx1[i])