代码分析
首先,你应该能够找到脚本的瓶颈,并注意到没有优化可以弥补数据结构中糟糕的选择或算法设计中的缺陷。其次,不要试图在编码过程中过早优化,而牺牲可读性/设计/质量。Donald Knuth 就优化发表了以下声明:
“我们应该忘记效率低,大约 97%的时间说:过早的优化是所有邪恶的根源。但我们不应该放弃我们在那个关键的 3%的机会”
要分析你的代码,你可以使用以下工具:cProfile
(或较慢的 profile
)来自标准库 line_profiler
和 timeit
。他们每个人都有不同的目的。
cProfile
是一个确定性分析器:监视函数调用,函数返回和异常事件,并为这些事件之间的间隔(最多 0.001 秒)进行精确计时。库文档([ https://docs.python.org/2/library/profile.html] [1 ]) 为我们提供了一个简单的用例
import cProfile
def f(x):
return "42!"
cProfile.run('f(12)')
或者,如果你希望包装现有代码的一部分:
import cProfile, pstats, StringIO
pr = cProfile.Profile()
pr.enable()
# ... do something ...
# ... long ...
pr.disable()
sortby = 'cumulative'
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats()
print s.getvalue()
这将创建如下表所示的输出,你可以在其中快速查看程序花费大部分时间的位置并确定要优化的功能。
3 function calls in 0.000 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 <stdin>:1(f)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
模块 line_profiler
([ https://github.com/rkern/line_profiler] [1 ]) 对于对代码进行逐行分析非常有用。对于长脚本来说,这显然是不可控制的,但是针对的是片段。有关详细信息,请参阅文档。最简单的入门方法是使用 kernprof 脚本,如包页面中所述,请注意你需要手动指定要分析的函数。
$ kernprof -l script_to_profile.py
kernprof 将创建一个 LineProfiler 实例,并将其插入到名称为 profile 的 __builtins__
名称空间中。它已被编写为用作装饰器,因此在你的脚本中,你可以使用 @profile
来装饰要分析的功能。
@profile
def slow_function(a, b, c):
...
kernprof 的默认行为是将结果放入二进制文件 script_to_profile.py.lprof
中。你可以告诉 kernprof 使用[-v / - view]选项立即在终端上查看格式化结果。否则,你可以稍后查看结果:
$ python -m line_profiler script_to_profile.py.lprof
最后,timeit
提供了一种从命令行和 python shell 测试一个衬里或小表达式的简单方法。该模块将回答诸如以下问题:在将列集转换为列表时,更快地执行列表解析还是使用内置的 list()
。查找 setup
关键字或 -s
选项以添加设置代码。
>>> import timeit
>>> timeit.timeit('"-".join(str(n) for n in range(100))', number=10000)
0.8187260627746582
从一个终端
$ python -m timeit '"-".join(str(n) for n in range(100))'
10000 loops, best of 3: 40.3 usec per loop