转换为发电机3.4倍减速

Ton*_*nen 11 python optimization performance generator while-loop

怎么了?有人可以解释一下这里发生了什么,我在紧密循环中改变了:

##            j=i
##            while j < ls - 1 and len(wordlist[j]) > lc: j+=1
            j = next(j for j in range(i,ls) if len(wordlist[j]) <=  lc)
Run Code Online (Sandbox Code Playgroud)

评论的版本运行整个程序:625毫秒,下一个生成器版本2.125秒的时间内运行整个程序.

有什么理由可以说这个更加pythonic的版本会导致性能上的这种灾难?

编辑:也许它是由使用psyco模块引起的?当然至少没有psyco的Python 2.7的运行时间对于下一个版本来说是2.141,意味着与使用psyco的Python 2.6几乎相同.

删除*.pyc文件后,我得到的代码没有减速.然后,当我从库模块中删除了psyco的导入时,我还得到了2.6时序,没有psyco使用,非psyco版本和psyco版本的结果(现在库例程也慢了,它的时间也相关:)

不是psyco:

  1. while:库中准备:532毫秒,总运行时间2.625秒
  2. 下一篇:库中的准备:532毫秒,总运行时间(time.clock()):2.844秒(版本与xrange相同的壁时间)

Psyco是:

  1. while:库中准备:297毫秒,总运行时间:609 ... 675毫秒
  2. 下一篇:库中的准备:297毫秒,总运行时间:1.922秒(程序中无处不在的范围而不是xrange的版本:1.985秒)

在带有2GB RAM的WindowsXP AMD Sempron 3100+系统中运行.使用两个全局变量计算循环和调用:

    j=i
    callcount += 1
    while j < ls - 1 and len(wordlist[j]) > lc:
        j+=1
        loopcount += 1
Run Code Online (Sandbox Code Playgroud)

使用psyco进行测试输入的结果:

Finished in 625 ms
Loopcount: 78317
Callcount: 47970
Ration: 1.633
Run Code Online (Sandbox Code Playgroud)

因此循环在紧密循环内,但平均只执行几次(注意两个增量的全局计数器没有减慢psyco中的代码)

结论: 尽管算法相对于词汇长度具有高度敏感性,这导致我通过此循环考虑传递一些不可能的词,后来通过字典查找检查递归的基本情况,即O(n),因此高度有益的早期优化变得不是很有用,即使输入较长并且在函数开头移动callcount计数器,也表明调用计数不受词汇长度的影响,但是外部循环计数被切片减少(最初发布的代码是elif部分if语句).

使用while循环运行时间更长(29 372个解决方案)并删除整个循环(使用i代替j)(库准备312 ms):

  1. 没有循环:elif分支计数:485488,outerloopcount:10129147,比率:0,048,运行时间6,000秒(无计数器:4,594秒)
  2. 循环:loopcount:19355114,outercount:8194033,比率:0,236,运行时5,704秒(无计数器:4,688秒)

(没有循环,计数器和psyco的运行时间:32,792秒,库608毫秒)

因此,如果没有额外的计数器,使用psyco的循环的好处是更难的情况:(4688-4594)*100/4688.0%= 2%

这激发了我扭转另一个早期的优化,我在DaniWeb中想到了这个优化.早期版本的代码运行得更快,当最小的字大小是全局的,而不是参数.根据文档记录,局部变量调用更快,但显然使递归的成本更重.现在在更难的情况,在没有优化字长的情况下,优化的另一个逆转带来了更多预期的性能行为:使用psyco的运行时间为312毫秒准备,总计4,469 ... 4,484秒的运行时间.因此,这使得代码更清晰,并且在这种情况下带来了更多的好处,因为已删除的循 并且将参数放入带有while循环的版本,并没有太多改变运行时间(库编写代码的变化变大)

**What I learned from this: If you do n optimizations for speed 
you must check the first n-1 optimizations after doing nth one**
Run Code Online (Sandbox Code Playgroud)

Dav*_*rby 0

两者并不等同。

j=i
while j < ls - 1 and len(wordlist[j]) > lc: 
    j+=1
Run Code Online (Sandbox Code Playgroud)

一旦 wordlist[j] <= lc 就会停止 while 循环。如果列表中的第一个单词短于或等于 lc,则可以想象它会执行循环零次。

j = next(j for j in range(i,ls) if len(wordlist[j]) <=  lc)
Run Code Online (Sandbox Code Playgroud)

将继续迭代 i 到 ls 的整个范围,无论列表中单词的长度如何。

编辑:忽略上面的内容 - 正如 Amber 指出的那样,对 next() 的调用意味着生成器表达式仅在返回第一个结果之前进行求值。在这种情况下,我怀疑时间差异来自于使用 range() 而不是 xrange() (除非这是 Python 3.x)。在 Python 2.x 中,range() 将在内存中创建完整列表,即使生成器表达式仅返回第一个值。

  • 实际上并非如此。生成器是惰性计算的,因此调用“next()”只会获取结果中的第一个元素,这意味着生成器不会计算“if”条件为真之外的任何内容。 (3认同)
  • @Amber:该死,你是对的。我完全忽略了 next() 的调用。 (2认同)