我正在读这个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 :由于大多数答案都是“不优化,如果速度慢,请稍后再做”,我从Triptych
和Ali A
获得了一些提示和链接对于做的。
我会稍微改变一下这个问题,并要求不要。
我们是否可以从遇到“缓慢”的人那里获得一些经验,问题是什么以及如何纠正?
Edit2 :对于那些不在这里的人是interesting read
编辑3:有问题timeit
的使用不正确请参阅 dF的答案,了解正确用法,以及三个代码的时间安排。
答案 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中通常的经验法则是
在编码时,优于直接且可读的代码而不是优化。
描述您的代码(profile / cProfile
and pstats
是您的朋友),以确定您需要优化的内容(通常是紧密循环)。
需要注意的一点是:与许多其他语言相比,函数调用在Python中相对昂贵,这就是为什么示例中的优化会产生差异,即使len
对于列表是O(1)。 / p>
答案 1 :(得分:4)
阅读本文:Python Speed / Performance Tips
此外,在您的示例中,总时间非常短,误差幅度将超过任何实际的速度差异。
答案 2 :(得分:2)
这适用于所有编程,而不仅仅是Python:
除非你有一个导致你痛苦的缓慢问题,否则我甚至不会费心去做任何其他事情。
也许最重要的是单元测试会在实际过程中帮助你。
答案 3 :(得分:1)
在切线相关的注释中,将生成器链接在一起比将列表推导链接在一起要有效得多,而且通常更直观。
至于开发人员在Python中进行优化的经验法则,它们与所有语言中的相同。
答案 4 :(得分: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倍!
所以分析
答案 7 :(得分:0)
我们可以从中获得一些经验吗? 什么,面对'慢'的人 是问题,它是怎么回事 校正?
问题是GUI应用程序中的数据检索速度慢。我通过在桌子上添加一个索引获得了50倍的加速,并被广泛称为英雄和救世主。