``生成器的收益与`列表性能的收益

问题描述 投票:6回答:1
Python 3.6.8 (default, Oct  7 2019, 12:59:55) 
Type 'copyright', 'credits' or 'license' for more information
IPython 7.9.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: def yield_from_generator(): 
   ...:     yield from (i for i in range(10000)) 
   ...:                                                                                                                                    

In [2]: def yield_from_list(): 
   ...:     yield from [i for i in range(10000)] 
   ...:                                                                                                                                    

In [3]: import timeit                                                                                                                      

In [4]: timeit.timeit(lambda: list(yield_from_generator()), number=10000)                                                                  
Out[4]: 5.3820097140014695

In [5]: timeit.timeit(lambda: list(yield_from_list()), number=10000)                                                                       
Out[5]: 4.333915593000711

我运行yield from生成器和yield from多次列出。列表版本始终提供更好的性能,而我的直觉告诉我相反的结论-创建列表需要在启动时分配内存。为什么我们会注意到这种性能差异?

python list generator yield coroutine
1个回答
0
投票

简短的答案是,表面语法使它们看起来比实际看起来更相似

我将更详细地分解一系列功能(dis模块对此很有帮助),我将把这些东西分为设置成本和每产值成本。我们开始于:

def yield_from_generator():
    yield from (i for i in range(10000))

费用是:

  • 设置:创建范围对象并调用嵌入式生成器表达式
  • 每笔收益:genexpr的收益,它还会在next迭代器上调用range。请注意,这里有两个上下文开关

接下来我们看:

def yield_from_list():
    yield from [i for i in range(10000)]

费用为:

  • 设置:创建一个新列表并使用列表理解来填充它。这使用特殊的list操作码,因此会很快
  • 单收益:只需恢复list的迭代器就可以了,所以很快

接下来我们看一个类似的功能:

def yield_from_list2():
    yield from list(i for i in range(10000))

这不使用特殊的列表操作码,并且具有生成器的双重嵌套,因此再次变慢。费用是:

  • 设置:创建一个新的生成器表达式并将其传递给列表构造函数,这将迭代在范围对象上迭代的生成器表达式
  • 单产量:使用list的迭代器,所以又快了

最后是一个只强调yield from的快速版本:

def yield_from_generator2():
    yield from range(10000)

费用为:

  • 设置:创建range对象
  • 单收益:直接恢复range迭代器

我的笔记本电脑上所有这些的时间是:

yield_from_generator  639 µs
yield_from_list       536 µs
yield_from_list2      689 µs
yield_from_generator2 354 µs

希望现在更加清晰了。另一个版本是:

def yield_from_list3():
    yield from list(range(10000))

以401 µs的速度运行,但希望它在性能上居于中间,这是显而易见的

© www.soinside.com 2019 - 2024. All rights reserved.