如何提高Python代码速度

时间:2014-09-04 18:31:46

标签: python performance

我正在解决这个python挑战http://coj.uci.cu/24h/problem.xhtml?abb=2634,这是我的答案

c = int(input())
l = []
for j in range(c) :
    i = raw_input().split()[1].split('/')
    l.append(int(i[1]))
for e in range(1,13) :
    print e , l.count(e)

但它不是最快的python解决方案,所以我试图找到如何提高速度,我发现xrange比范围更快。但是,当我尝试以下代码时,它实际上更慢

c = int(input())
l = []
for j in xrange(c):
    i = raw_input().split()[1].split('/')[1]
    l.append(i)
for e in xrange(1,13) :
    print e , l.count(`e`)

所以我有两个问题:

  1. 如何提高脚本的速度
  2. 我在哪里可以找到有关如何提高python速度的信息
  3. 当我在寻找此信息时,我发现了这样的网站https://wiki.python.org/moin/PythonSpeed/PerformanceTips 但是它没有指定,例如,如果在单行或多行中多次拆分字符串更快/更慢,例如使用上面提到的部分脚本:

    i = raw_input().split()[1].split('/')[1]
    

    VS

    i = raw_input().split()
    i = i[1].split('/')
    i = i[1]
    

    编辑:我已经尝试了所有建议,但我的第一个答案仍然是最快的,我不知道为什么。我的冷杉答案是151毫秒,@ Bakuriu的回答是197毫秒,我的答案是使用collections.Counter是188毫秒。

    编辑2:请忽略我上次编辑,我发现上面提到的检查代码性能的方法不起作用,如果你上传相同代码的次数超过每次性能不同有时候它更慢,有时更快

1 个答案:

答案 0 :(得分:9)

假设您正在使用CPython,黄金法则是将尽可能多的工作推入内置函数,因为它们是用C语言编写的,因此避免了解释器开销

这意味着你应该:

  • 当某个功能/方法已经执行了您想要的操作时,避免使用显式循环
  • 避免在内部循环中进行昂贵的查找。在极少数情况下,您可以使用局部变量来存储内置函数。
  • 使用正确的数据结构。不要简单地使用listdict。标准库包含其他数据类型,并且有许多库。考虑哪个应该是解决问题的有效操作并选择正确的数据结构
  • 避免元编程。如果您需要速度,则不希望简单的属性查找在后台触发具有复杂逻辑的10个方法调用。 (但是你真的不需要速度元编程真的很酷!)
  • 分析您的代码以找出瓶颈并优化瓶颈。通常我们对某些具体代码的性能的考虑是完全错误的。
  • 使用dis模块反汇编字节码。这为您提供了一种简单的方法来查看解释器的真正功能。如果你真的想知道解释器是如何工作的,你应该尝试阅读包含解释器主循环的PyEval_EvalFrameEx的源代码(注意: hic sunt leones !)。

关于CPython,你应该阅读Guido Van Rossum的An optimization anecdote。它提供了许多有关各种解决方案如何改变性能的见解。另一个例子可能是this回答(免责声明:这是我的),对于那些不习惯CPython工作的人来说,最快的解决方案可能非常直观。

另一个好处是研究所有最常用的内置和stdlib数据类型,因为每个数据类型都有正负比例。在这种特定情况下,调用list.count()是一项繁重的操作,因为它必须在每次执行时扫描整个列表。这可能是您的解决方案消耗了大量时间。

最小化解释器开销的一种方法是使用collections.Counter,这也避免了多次扫描数据:

from collections import Counter

counts = Counter(raw_input().split('/')[-2] for _ in range(int(raw_input())))

for i in range(1, 13):
    print(i, counts[str(i)])

请注意,不需要将月份转换为整数,因此您可以避免这些函数调用(假设月份总是以相同的方式编写。没有077)。

此外,我不明白为什么要分割空格,然后在/上,只需按/拆分并从列表中取一个到最后一个元素。< / p>

另一个(重要的)优化可能是读取所有stdin以避免多个IO调用,但是这可能不适用于这种情况,因为他们告诉您有多少员工可能意味着他们是不发送EOF。


请注意,不同版本的python具有完全不同的优化代码的方式。例如,当您在JIT能够分析和优化的循环中执行简单操作时,PyPy的JIT效果最佳。所以它与你在CPython中所做的相反。