怎么了?有人可以解释一下我在这里发生了什么,但我却陷入了紧张的循环中:
## 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)
版本运行整个程序时,已注释的运行了整个程序: 625 ms ,下一个生成器版本在 2.125 s 的时间运行了整个程序。
这个更具Python风格的版本在性能上造成如此巨大灾难的原因可能是什么?
编辑:也许是由于使用 psyco模块引起的?当然,至少没有psyco的Python 2.7的运行时间为下一个版本为2.141,意味着几乎与具有psyco的Python 2.6相同。
删除* .pyc文件后,我没有看到要放慢速度的代码。然后,当我也从库模块中删除了psyco的导入时,我也获得了2.6计时,可以在不使用psyco的情况下使用,对于非psyco版本和psyco版本的结果(因为现在库例程也变慢了,并且计时也很重要:)
不是psyco:
ms,总运行时间2.625 s
总运行时间(time.clock()):
2.844 s(与xrange相同的壁挂时间的版本)
精神科:
ms,总运行时间:609..675 ms
总运行时间:
1.922 s(程序中到处都带有范围而不是xrange的版本:1.985 s)
在具有2GB RAM的WindowsXP AMD Sempron 3100+系统上运行。用两个全局变量计数循环和调用:
j=i
callcount += 1
while j < ls - 1 and len(wordlist[j]) > lc:
j+=1
loopcount += 1
用psyco进行测试输入的结果:
Finished in 625 ms
Loopcount: 78317
Callcount: 47970
Ration: 1.633
因此,该循环处于紧密循环内,但平均仅执行两次(请注意,全局计数器的两个增量不会降低psyco中的代码的速度)
结论:
尽管算法相对于词汇长度具有高度敏感的性质,这使我通过此循环从考虑中传递了一些不可能的单词,但后来通过字典查找(O(n))检查了递归的基本情况,因此非常有用早期的优化对并不是很有益,即使输入更长的时间并在函数开始处移动callcount计数器,这也表明调用次数不受词汇量长度的影响,但外循环计数却被大大减少了(最初发布的代码位于if语句的elif部分)。
更长的运行时间(29 372解决方案),其中while循环和整个循环都被删除(使用i代替j)(库准备312 ms):
(无循环,计数器和psyco的运行时间:32,792 s,库608 ms)
因此,如果没有额外的计数器,则使用psyco的此循环的 yield 就更难了:(4688-4594)* 100/4688.0%= 2%
这激发了我逆转另一个早先的优化,我在DaniWeb中曾想过。较早版本的代码运行的速度更快,而最小字长是全局,而不是全局参数。根据文档,局部变量调用速度更快,但显然使递归更重的成本超过了后者。现在,在更困难的情况下,这种优化的另一种逆转在没有优化单词长度的的情况下带来了更多预期的性能行为:psyco的运行时间为312 ms准备工作, 4,469..4,484 s的总运行时间。因此,这使代码更整洁,并且在这种情况下,由于删除了循环,因此带来了更多好处。并使用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**
最佳答案
我发现使用生成器通常比生成整个列表要慢,这有点违反直觉。我已经设法通过添加[]
对来解决性能瓶颈。
例如比较这些:
$ python -m timeit -n 1000 "' '.join(c for c in 'hello world')"
1000 loops, best of 3: 6.11 usec per loop
$ python -m timeit -n 1000 "' '.join([c for c in 'hello world'])"
1000 loops, best of 3: 3.79 usec per loop
首先生成整个列表几乎比使用生成器快两倍,即使在这种简单情况下也是如此!
编辑:正如Thomas Wouters在评论中指出的,此处生成器较慢的原因是因为它是如此简单。为了保持平衡,以下是他的测试,其中发电机是明显的赢家:
$ python -m timeit -s "s = 'hello world' * 10000" -s "class C: pass" "for i in (C() for c in s): pass"
10 loops, best of 3: 33.6 msec per loop
$ python -m timeit -s "s = 'hello world' * 10000" -s "class C: pass" "for i in [C() for c in s]: pass"
10 loops, best of 3: 172 msec per loop