Python中的优化 - 做,不应该和经验法则

时间:2008-12-31 18:58:00

标签: python optimization

我正在读这个post,然后我遇到了一个代码:

jokes=range(1000000)
domain=[(0,(len(jokes)*2)-i-1) for i in range(0,len(jokes)*2)]

我认为在列表理解之外计算len(笑话)的值不是更好吗?

好吧,我试过了,并计时了三个代码

jv@Pioneer:~$ python -m timeit -s 'jokes=range(1000000);domain=[(0,(len(jokes)*2)-i-1) for i in range(0,len(jokes)*2)]'
10000000 loops, best of 3: 0.0352 usec per loop
jv@Pioneer:~$ python -m timeit -s 'jokes=range(1000000);l=len(jokes);domain=[(0,(l*2)-i-1) for i in range(0,l*2)]'
10000000 loops, best of 3: 0.0343 usec per loop
jv@Pioneer:~$ python -m timeit -s 'jokes=range(1000000);l=len(jokes)*2;domain=[(0,l-i-1) for i in range(0,l)]'
10000000 loops, best of 3: 0.0333 usec per loop

观察第一个和第二个之间的边际差异2.55%让我思考 - 是第一个列表理解

domain=[(0,(len(jokes)*2)-i-1) for i in range(0,len(jokes)*2)]

通过python内部优化?或者2.55%是一个足够大的优化(假设len(笑话)= 1000000)?

如果是这样 - Python中的其他隐式/内部优化是什么?

developer's rules of thumb for optimization in Python是什么?

Edit1 :由于大多数答案都是“不优化,如果速度慢,请稍后再做”,我从TriptychAli A获得了一些提示和链接对于做的。 我会稍微改变一下这个问题,并要求不要

我们是否可以从遇到“缓慢”的人那里获得一些经验,问题是什么以及如何纠正?

Edit2 :对于那些不在这里的人是interesting read

编辑3:有问题timeit的使用不正确请参阅 dF的答案,了解正确用法,以及三个代码的时间安排。

8 个答案:

答案 0 :(得分:12)

你没有正确使用timeit-s(setup)的参数是一个最初要执行的语句,所以你真的只是测试一个空语句。你想做什么

$ python -m timeit -s "jokes=range(1000000)" "domain=[(0,(len(jokes)*2)-i-1) for i in range(0, len(jokes)*2)]"
10 loops, best of 3: 1.08 sec per loop
$ python -m timeit -s "jokes=range(1000000)" "l=len(jokes);domain=[(0,(l*2)-i-1) for i in range(0, l*2)]"
10 loops, best of 3: 908 msec per loop
$ python -m timeit -s "jokes=range(1000000)" "l=len(jokes*2);domain=[(0,l-i-1) for i in range(0, l)]"
10 loops, best of 3: 813 msec per loop

虽然加速仍不显着,但更为显着(分别为16%和25%)。因为它不会使代码变得更复杂,所以这种简单的优化可能是值得的。

要解决实际问题...... Python中通常的经验法则是

  1. 在编码时,优于直接且可读的代码而不是优化。

  2. 描述您的代码(profile / cProfile and pstats是您的朋友),以确定您需要优化的内容(通常是紧密循环)。

  3. 作为最后的手段,请将这些功能重新实现为C扩展程序,使用pyrexcython等工具可以更轻松地实现这些功能。

  4. 需要注意的一点是:与许多其他语言相比,函数调用在Python中相对昂贵,这就是为什么示例中的优化会产生差异,即使len对于列表是O(1)。 / p>

答案 1 :(得分:4)

阅读本文:Python Speed / Performance Tips

此外,在您的示例中,总时间非常短,误差幅度将超过任何实际的速度差异。

答案 2 :(得分:2)

这适用于所有编程,而不仅仅是Python:

  1. 资料
  2. 识别瓶颈
  3. 优化
  4. 除非你有一个导致你痛苦的缓慢问题,否则我甚至不会费心去做任何其他事情。

    也许最重要的是单元测试会在实际过程中帮助你。

答案 3 :(得分:1)

在切线相关的注释中,将生成器链接在一起比将列表推导链接在一起要有效得多,而且通常更直观。

至于开发人员在Python中进行优化的经验法则,它们与所有语言中的相同。

  1. 不要优化。
  2. (高级)稍后优化。

答案 4 :(得分:1)

列表中的len是O(1)。它不需要扫描整个列表来查找长度,因为列表的大小是为了查找而存储的。但显然,将它提取到局部变量中仍然稍微快一些。

要回答你的问题,我绝不会关心大约5%的性能变化,除非我在某些模拟中做了一些疯狂的内部循环优化。在这种情况下,你可以通过不使用范围来加快速度。

答案 5 :(得分:1)

最重要的事情是编写惯用的,清晰的,漂亮的Python代码。 stdlib中已经发现了许多常见任务,因此您不必重写较慢的版本。 (我在这里特别考虑字符串方法和itertools。)也可以自由地使用Python的内置容器。例如,dict已经对其进行了“snot优化”,并且说使用dicts的Python代码将比普通的C更快!

如果这还不够快,你可以使用一些黑客,但它也表明你可能应该将一些工作卸载到C扩展模块。

关于列表推导:CPython能够对常规累加器循环进行一些优化。即LIST_APPEND操作码使得附加到列表本机操作。

答案 6 :(得分:1)

我有一个解析日志文件并生成数据仓库的程序。典型的运行涉及大约200M日志文件行,并且运行一天中的大部分时间。非常值得优化!

由于它是一个解析器,并且解析了一些相当可变且特殊且不可靠的文本,因此大约有100个正则表达式,事先尽职尽责地重新编译(),并应用于每个200M日志文件行。我很确定他们是我的瓶颈,一直在思考如何改善这种状况。有一些想法:一方面,制造更少,更漂亮的RE;另一方面,更简单;像这样的东西。

我用CProfile进行了概述,并在“runsnake”中查看了结果。

RE处理仅占代码执行时间的10%左右。那不是它!

事实上,在runnake显示屏中的一个大方形斑点立刻告诉我,我花了大约60%的时间用于其中一个臭名昭着的“一线更改”我已经添加了一天,消除了非打印字符(其中偶尔出现,但总是代表一些虚假的东西,我真的不关心它)。这些让我的解析和抛出异常变得混乱,因为它停止了我日志文件分析的日子。

  

line =''。join([c for c in line curses.ascii.isprint(c)])

你去:那条线触及每条200M线的每个字节(这些线的平均长度为几百字节)。难怪这是我执行时间的60%!

现在我知道有更好的方法来处理这个问题,例如str.translate()。但是这样的线路很少见,而且无论如何我都不关心它们,最后它们会抛出异常:现在我只是在正确的位置捕获异常并跳过线路。瞧!该程序的速度快了约3倍!

所以分析

  1. 突出显示,大约一秒钟,问题实际上是
  2. 使我的注意力从错误的假设出发,即问题在哪里(这可能是更大的回报)

答案 7 :(得分:0)

  

我们可以从中获得一些经验吗?   什么,面对'慢'的人   是问题,它是怎么回事   校正?

问题是GUI应用程序中的数据检索速度慢。我通过在桌子上添加一个索引获得了50倍的加速,并被广泛称为英雄和救世主。