怎么了?有人可以解释一下我在这里发生了什么,但我却陷入了紧张的循环中:

##            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:
  • 时间:在库中准备:532
    ms,总运行时间2.625 s
  • 下一个:在库中进行准备:532毫秒,
    总运行时间(time.clock()):
    2.844 s(与xrange相同的壁挂时间的版本)

  • 精神科:
  • 时间:库中的准备工作:297
    ms,总运行时间:609..675 ms
  • 下一个:在库中进行准备:297毫秒,
    总运行时间:
    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):
  • 不带循环:elif分支数:485488,外循环计数:10129147,比率:0,048,运行时间6,000 s(不带计数器:4,594 s )
  • 使用循环:循环计数:19355114,外部计数:8194033,比率:0,236,运行时间5,704 s(不含计数器的:4,688 s )

  • (无循环,计数器和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
    

    10-08 05:10