【问题标题】:Why is this generator expression function slower than the loop version?为什么这个生成器表达式函数比循环版本慢?
【发布时间】:2015-11-29 16:16:21
【问题描述】:

我一直根据生成器表达式往往比普通循环更有效的理论进行操作。但后来我遇到了以下示例:编写一个函数,它给定一个数字 N 和一些因子 ps,返回 N 下所有数字的总和,这些数字是至少一个因子的倍数。

这是一个循环版本和一个较短的生成器表达式版本:

def loops(N, ps):
    total_sum = 0 
    for i in xrange(N):
        for p in ps: 
            if i%p == 0:
                total_sum += i
                break
    return total_sum

def genexp(N, ps):
    return sum(i for i in xrange(N)
               if any(i%p == 0 for p in ps))

我希望两者的表现大致相同,也许理解版本会快一点,但我没想到的是:

for func in ('loops', 'genexp'):
    print func, timeit.timeit('%s(100000, [3,5,7])' % func, 
                              number=100, 
                              setup='from __main__ import %s' % func)


loops 2.82878184319
genexp 10.1663100719

慢 4 倍还差得远!为什么?我误会了什么?

【问题讨论】:

  • 你有生成器表达式,而不是列表推导式。
  • @MartijnPieters 谢谢!显然我不是蟒蛇人:)

标签: python performance python-2.7 generator generator-expression


【解决方案1】:

首先:生成器表达式是内存高效的,不一定是速度高效的。

您的紧凑型 genexp() 版本速度较慢,原因有两个:

  • 生成器表达式是使用新作用域实现的(如新函数)。您正在生产 N 个新范围,每个 any() 测试一个。创建一个新作用域并再次将其拆除是相对昂贵的,尤其是在循环中完成然后与不这样做的代码进行比较时。

  • sum()any() 名称是要查找的附加全局变量。在 any() 的情况下,每个测试需要额外的 N 个全局查找。全局变量必须在字典中查找,而局部变量则通过 C 数组中的索引查找(非常快)。

后者只是一个很小的组成部分,大部分成本在于创建和销毁框架(范围);如果您创建一个版本,其中 _any_sum 是您获得的函数的本地变量,但性能略有提升:

>>> def genexp_locals(N, ps, _any=any, _sum=sum):
...     return _sum(i for i in xrange(N)
...                if _any(i%p == 0 for p in ps))
... 
>>> for func in ('loops', 'genexp', 'genexp_locals'):
...     print func, timeit.timeit('%s(100000, [3,5,7])' % func, 
...                               number=100, 
...                               setup='from __main__ import %s' % func)
... 
loops 2.00835800171
genexp 6.45241594315
genexp_locals 6.23843789101

我没有为xrange 创建一个本地来保持这方面的相同。从技术上讲,_any 名称是由生成器表达式代码对象作为闭包而不是本地查找的,它不像全局查找那么慢,但也没有本地查找那么快。

【讨论】:

  • 我认为“每个 any() 测试的 N 个新范围”应该是“N 个新范围,每个 any() 测试一个”。而且我认为还有一个你没有提到的成本,即生成器对象和它们的消费者之间来回的成本。
  • @Manuel:与所有其他开销相比,开销可以忽略不计。感谢您对措辞的建议,我已将其折叠起来。
  • 你的意思是一般情况下可以忽略不计,还是只是在这种特殊情况下(因为这里的这些生​​成器几乎不产生任何东西)?
  • @Manuel:在这种特殊情况下。
猜你喜欢
  • 2013-08-05
  • 2017-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-24
  • 2017-06-18
  • 2014-08-19
相关资源
最近更新 更多