【问题标题】:Why is this genexp performing worse than a list comprehension?为什么这个 genexp 的性能比列表理解差?
【发布时间】:2011-01-11 13:03:37
【问题描述】:

我试图找到一种最快的方法来计算列表中与特定过滤器匹配的项目数。 在这种情况下,查找列表中有多少个奇数。

在执行此操作时,我对列表推导式与等效生成器表达式的比较结果感到惊讶:

python -m timeit -s "L = xrange(1000000)" "sum([1 for i in L if i & 1])"
10 loops, best of 3: 109 msec per loop

python -m timeit -s "L = xrange(1000000)" "sum(1 for i in L if i & 1)"
10 loops, best of 3: 125 msec per loop

我也尝试过将 L 作为常规列表,并且大小不同,但在所有情况下,列表理解都会获胜。

与创建包含 100 万个项目的新列表的 listcomp 相比,genexp 做了什么导致它变慢...?

(顺便说一句,我发现最快的方法是:x = 1; len(filter(x.__and__, L))。是的,我知道编写这样的代码会杀死小猫,我这样做是为了好玩)

【问题讨论】:

    标签: python list-comprehension generator-expression


    【解决方案1】:

    当基本上无限的内存可用时(在小型基准测试中总是会出现这种情况,尽管在实际问题中通常不是这样!-),列表往往会优于生成器,因为它们可以在一次“大束”(没有内存碎片等),而生成器需要(内部)额外的努力来避免这种“大束”方法,方法是保留堆栈帧状态以允许恢复执行。

    列表方法或生成器方法在实际程序中是否会更快取决于确切的内存情况,包括碎片,这在“微基准”中几乎不可能准确再现. IOW,最后,如果你真的关心性能,你必须仔细地对你的实际程序进行基准测试(并单独分析),而不仅仅是一般情况下的“玩具”微基准测试。

    【讨论】:

    • 1+。还可以注意到,在许多情况下,生成器可能会使用较少的内存,因为它们的性质类似于流。考虑将文件中的每一行读取到一个列表中,并将其与读取每一行进行比较,使用它并丢弃它。
    【解决方案2】:

    据我记得,必须为每个结果激活一个生成器框架,而列表推导使用一个激活框架。列表压缩中的增量成本是内存的增加成本——在您的情况下是对 int 的引用。如果每个项目都是一个新实例并且使用更多内存,那么这种关系可能会颠倒过来。

    更新:经过测试,它确实反转了

    ~% python -m timeit -s "L = xrange(1000000);oint=type('intEx', (int,),{})" "sum([oint(1) for i in L if i & 1])" 
    10 loops, best of 3: 414 msec per loop
    
    ~% python -m timeit -s "L = xrange(1000000);oint=type('intEx', (int,),{})" "sum(oint(1) for i in L if i & 1)" 
    10 loops, best of 3: 392 msec per loop
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-01
      • 2017-10-12
      相关资源
      最近更新 更多