python - 将 while 转换为生成器的速度减慢了 3.4 倍

标签 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)

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

这个更pythonic的版本导致性能如此灾难的原因是什么?

编辑:可能是使用 引起的psyco 模块 ?当然,至少没有 psyco 的 Python 2.7 的运行时间是下一个版本的 2.141,这意味着几乎与带有 psyco 的 Python 2.6 相同。

删除 *.pyc 文件后,我没有让代码变慢。然后,当我也从库模块中删除了 psyco 的导入时,我也得到了 2.6 计时也可以在没有 psyco 的情况下使用,非 psyco 版本和 psyco 版本的结果(因为现在库例程也变慢了,它的计时也很重要:)

不是精神病:
  • 而:图书馆准备:532
    ms,总运行时间 2.625 秒
  • 下一个:图书馆准备:532毫秒,
    总运行时间(time.clock()):
    2.844 秒(xrange 与墙时间相同的版本)

  • 心理:
  • 而:图书馆准备:297
    毫秒,总运行时间:609..675 毫秒
  • 下一个:图书馆准备:297 ms,
    总运行时间:
    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
    

    使用 psyco 测试输入的结果:
    Finished in 625 ms
    Loopcount: 78317
    Callcount: 47970
    Ration: 1.633
    

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

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

    更长的运行时间(29 372 个解决方案)使用 while 循环并删除整个循环(使用 i 而不是 j)(库准备 312 毫秒):
  • 没有循环 : elif 分支计数:485488,外循环计数:10129147,比率:0,048,运行时间 6,000 秒( 无计数器:4,594 秒 )
  • 带环 :循环计数:19355114,外计数:8194033,比率:0,236,运行时间 5,704 秒( 无计数器:4,688 秒 )

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

    所以没有额外的计数器 福利使用 psyco 的这个循环在更难的情况下:(4688-4594)*100/4688.0 % = 2%

    这启发了我 反向另一个较早的优化 ,这是我在 DaniWeb 中想过的。早期版本的代码 跑得更快 ,当最小字长是全局的 ,不是参数。根据文档,局部变量调用更快,但显然使递归更重的成本超过了它。现在在更困难的情况下,另一个优化逆转带来了更多 案例中的预期性能行为 无字长优化:psyco 的运行时间为 312 ms 准备, 4,469..4,484 s 总运行时间 .因此,这使代码更清晰,并在这种情况下带来了更多好处,就像已删除的循环所具有的那样。并且用while循环把参数放到version,并没有太大的改变运行时间(库准备代码的变化变大了)
    **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
    

    关于python - 将 while 转换为生成器的速度减慢了 3.4 倍,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3902522/

    相关文章:

    optimization - 通过 Fortran 中的静态变量实现性能

    c# - HTTPWebResponse + StreamReader 非常慢

    python - 带有PACKET_MMAP的Python套接字

    python - 使用 Beautiful Soup Python 模块将标签替换为纯文本

    python - Pandas Dataframe 过滤器包含 ('.' ),Python 3.6

    java - 绩效改进 - 如何开始

    mysql - 推荐用于 Windows 的 MySQL 调优实用程序

    python - .NET Core 2.0 和 Angular 初始应用程序构建失败 - 找不到 python 后跟 JavaScript 运行时错误

    mysql - 使用复合索引优化查询,即使用where

    .net - 如何突破ASP.NET webservice的限制?