性能分析
本节作者: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*A32893 function calls in 1.100 CPU secondsOrdered by: internal timencalls 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*Asage: 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 hotshotsage: 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.
