性能分析
本节作者:Martin Albrecht (malb@informatik.uni-bremen.de)
“Premature optimization is the root of all evil.”
“盲目的优化是万恶之源。”
- Donald Knuth
有时检查代码的瓶颈对于了解哪一部分占用了最多的计算时间是很有用的。这可以帮助确定最需要优化哪一部分。Python和Sage提供几种性能分析选项。
最简单的是在交互命令行中使用prun
命令。它返回一个描述每个函数占用多少计算时间的摘要。例如,要分析有限域上的矩阵乘法,可以这样做:
sage: k,a = GF(2**8, 'a').objgen()
sage: A = Matrix(k,10,10,[k.random_element() for _ in range(10*10)])
sage: %prun B = A*A
32893 function calls in 1.100 CPU seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
12127 0.160 0.000 0.160 0.000 :0(isinstance)
2000 0.150 0.000 0.280 0.000 matrix.py:2235(__getitem__)
1000 0.120 0.000 0.370 0.000 finite_field_element.py:392(__mul__)
1903 0.120 0.000 0.200 0.000 finite_field_element.py:47(__init__)
1900 0.090 0.000 0.220 0.000 finite_field_element.py:376(__compat)
900 0.080 0.000 0.260 0.000 finite_field_element.py:380(__add__)
1 0.070 0.070 1.100 1.100 matrix.py:864(__mul__)
2105 0.070 0.000 0.070 0.000 matrix.py:282(ncols)
...
这里ncals
是调用的次数,tottime
是给定函数所用的总时间(不包括调用子函数的时间),percal
是tottime
除以ncals
的商。cumtime
是函数用的时间和所有子函数用的时间 (即从调用开始到退出的时间),percall
是cumtime
除以基本调用次数的商,filename:lineno(function)
提供了每个函数的相关信息。经验规律是:列表中函数排的越靠前,所花费的时间越多,也就越需要优化。
通常,prun?
会提供如何使用性能分析器的详细信息,并解析输出结果的含义。
分析的数据可以写入一个对象,这样可以就近检查:
sage: %prun -r A*A
sage: stats = _
sage: stats?
注意:输入stats = prun -r A\*A
会显示语法错误, 因为prun是IPython shell的命令,而不是一个正常的函数。
要想得到漂亮的图形化分析结果,可以使用hotshot分析器。 它是调用hotshot2cachetree
和程序kachegrind
(仅在Unix下有效)的一个小脚本。使用hotshot分析器分析同一个例子:
sage: k,a = GF(2**8, 'a').objgen()
sage: A = Matrix(k,10,10,[k.random_element() for _ in range(10*10)])
sage: import hotshot
sage: filename = "pythongrind.prof"
sage: prof = hotshot.Profile(filename, lineevents=1)
sage: prof.run("A*A")
<hotshot.Profile instance at 0x414c11ec>
sage: prof.close()
结果保存在当前工作目录的pythongrind.prof
文件中。它可以被转换为可以可视化的cachegrind格式。
在系统shell中输入
$hotshot2calltree -o cachegrind.out.42 pythongrind.prof
输出文件cachegrind.out.42
可以由kcachegrind
查看。请注意遵守命名习惯cachegrind.out.XX
.