你怎么能描述一个脚本?

欧拉项目和其他编码竞赛往往有最长的时间来运行,或者人们吹嘘自己的解决scheme运行得有多快。 使用python,有时这些方法有点奇怪 – 即将计时代码添加到__main__

什么是一个好方法来分析一个python程序需要运行多久?

Python包含一个名为cProfile的分析器。 它不仅给出总运行时间,而且还分别计算每个函数的时间,并告诉你每个函数被调用了多less次,这样就很容易确定你应该在哪里进行优化。

你可以从你的代码或解释器中调用它,如下所示:

 import cProfile cProfile.run('foo()') 

更有用的是,您可以在运行脚本时调用cProfile:

 python -m cProfile myscript.py 

为了使它更容易,我做了一个叫'profile.bat'的小batch file:

 python -m cProfile %1 

所以我所要做的就是运行:

 profile euler048.py 

我得到这个:

 1007 function calls in 0.061 CPU seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.061 0.061 <string>:1(<module>) 1000 0.051 0.000 0.051 0.000 euler048.py:2(<lambda>) 1 0.005 0.005 0.061 0.061 euler048.py:2(<module>) 1 0.000 0.000 0.061 0.061 {execfile} 1 0.002 0.002 0.053 0.053 {map} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler objects} 1 0.000 0.000 0.000 0.000 {range} 1 0.003 0.003 0.003 0.003 {sum} 

编辑:从PyCon 2013更新了一个很好的video资源的链接,名为Python Profiling

pycallgraph时间我做了pycallgraph ,它可以从你的Python代码生成一个可视化文件。 编辑:我已经更新了示例以使用最新版本。

pip install pycallgraph并安装GraphViz之后,您可以从命令行运行它:

 pycallgraph graphviz -- ./mypythonscript.py 

或者,您可以configuration您的代码的特定部分:

 from pycallgraph import PyCallGraph from pycallgraph.output import GraphvizOutput with PyCallGraph(output=GraphvizOutput()): code_to_profile() 

pycallgraph.png任何一个都会生成一个类似于下图所示的pycallgraph.png文件:

在这里输入图像描述

值得指出的是,使用profiler只能在主线程上工作(默认情况下),如果你使用它们,你不会从其他线程得到任何信息。 这可能是一个小问题,因为它在profiler文档中完全没有提到。

如果您还想要configuration线程,则需要查看文档中的threading.setprofile()函数 。

你也可以创build自己的threading.Thread子类来做到这一点:

 class ProfiledThread(threading.Thread): # Overrides threading.Thread.run() def run(self): profiler = cProfile.Profile() try: return profiler.runcall(threading.Thread.run, self) finally: profiler.dump_stats('myprofile-%d.profile' % (self.ident,)) 

并使用该ProfiledThread类而不是标准的。 这可能会给你更多的灵活性,但我不确定这是否值得,尤其是如果你使用的第三方代码不会使用你的类。

python wiki是分析资源的绝佳网页: http : //wiki.python.org/moin/PythonSpeed/PerformanceTips#Profiling_Code

正如Python文档: http : //docs.python.org/library/profile.html

如Chris Lawlor所示cProfile是一个很好的工具,可以很容易地用于打印到屏幕上:

 python -m cProfile -s time mine.py <args> 

或者提交:

 python -m cProfile -o output.file mine.py <args> 

PS>如果您使用的是Ubuntu,请确保安装python-profile

 sudo apt-get install python-profiler 

如果你输出到文件,你可以使用以下工具获得很好的可视化

PyCallGraph:创build调用graphics图像的工具
安装:

  sudo pip install pycallgraph 

跑:

  pycallgraph mine.py args 

视图:

  gimp pycallgraph.png 

你可以用你喜欢的任何东西来查看png文件,我用gimp
不幸的是我经常得到

点:graphics对于cairo-renderer位图来说太大了。 按0.257079进行缩放以适合

这使我的图像不可用的小。 所以我通常创buildsvg文件:

 pycallgraph -f svg -o pycallgraph.svg mine.py <args> 

PS>确保安装graphviz(提供点程序):

 sudo pip install graphviz 

使用gprof2dot通过@maxy / @quodlibetor进行替代graphics化:

 sudo pip install gprof2dot python -m cProfile -o profile.pstats mine.py gprof2dot -f pstats profile.pstats | dot -Tsvg -o mine.svg 

@ Maxy的这个答案的评论帮了我很多,我认为它值得自己的答案:我已经有cProfile生成的.pstats文件,我不想用pycallgraph重新运行的东西,所以我用gprof2dot ,并得到了漂亮svgs:

 $ sudo apt-get install graphviz $ git clone https://github.com/jrfonseca/gprof2dot $ ln -s "$PWD"/gprof2dot/gprof2dot.py ~/bin $ cd $PROJECT_DIR $ gprof2dot.py -f pstats profile.pstats | dot -Tsvg -o callgraph.svg 

和BLAM!

它使用点(pycallgraph使用相同的东西),所以输出看起来相似。 我得到gprof2dot丢失较less信息的印象:

gprof2dot示例输出

另外值得一提的是GUI cProfile转储查看器RunSnakeRun 。 它允许您sorting和select,从而放大程序的相关部分。 图片中矩形的大小与所花费的时间成正比。 如果将鼠标hover在矩形上,则会突出显示表格中以及地图上各处的呼叫。 当你双击一个矩形时,它会放大该部分。 它会告诉你谁调用这个部分以及那个部分叫什么。

描述性信息非常有帮助。 它向您显示了该位的代码,当您处理内置的库调用时这可能会有所帮助。 它告诉你什么文件和什么行可以find代码。

也想指出,OP说'分析',但他似乎是'时间'。 请记住,分析时程序运行速度会变慢。

在这里输入图像描述

研究这个话题时,我遇到了一个叫SnakeViz的方便工具。 SnakeViz是一个基于Web的剖析可视化工具。 这是非常容易安装和使用。 我通常使用的方法是用%prun生成一个stat文件,然后在SnakeViz中进行分析。

所使用的主要技术是Sunburst图 ,如下图所示,其中函数调用的层次结构被安排为以angular度宽度编码的弧和层次的时间信息。

最好的事情是你可以与图表互动。 例如,放大一个可以点击一个弧,弧和它的后代将被放大为一个新的阳光来显示更多的细节。

在这里输入图像描述

一个不错的分析模块是line_profiler(使用脚本kernprof.py调用)。 它可以在这里下载。

我的理解是,cProfile只给出了在每个函数中花费的总时间的信息。 所以个别的代码行不是定时的。 这是科学计算中的一个问题,因为往往一条线可能需要很长时间。 另外,正如我记得,cProfile没有赶上我在numpy.dot花费的时间。

pprofile

line_profiler (这里已经介绍过)也启发了pprofile ,它被描述为:

线粒度,线程感知的确定性和统计纯python分析器

它提供line_profiler行粒度,是纯Python,可以用作独立的命令或模块,甚至可以生成callgrind格式的文件,这些文件可以用[k|q]cachegrind轻松分析。

vprof

还有一个vprof ,一个Python包,描述如下:

为各种Python程序特性(如运行时间和内存使用情况)提供丰富且交互式的可视化。

热图

我认为cProfile非常适合分析,而kcachegrind则可以很好地显示结果。 pyprof2calltree处理文件转换。

 python -m cProfile -o script.profile script.py pyprof2calltree -i script.profile -o script.calltree kcachegrind script.calltree 

要安装所需的工具(至less在Ubuntu上):

 apt-get install kcachegrind pip install pyprof2calltree 

根据Joe Shaw关于multithreading代码不能按预期工作的回答,我认为runcall中的runcall方法仅仅是在调用函数的时候进行self.enable()self.disable()调用,所以你可以简单的做你自己和任何你想要的代码之间的现有代码的干扰最小。

在Virtaal的源代码中,有一个非常有用的类和装饰器,可以很容易地进行性能分析(即使对于特定的方法/函数)。 然后可以在KCacheGrind中非常舒适地查看输出。

cProfile非常适合快速分析,但大部分时间都是以错误结束。 函数runctx通过正确地初始化环境和variables来解决这个问题,希望对某个人有用:

 import cProfile cProfile.runctx('foo()', None, locals()) 

有很多很好的答案,但他们要么使用命令行或一些外部程序来分析和/或sorting结果。

我真的错过了一些我可以在我的IDE(eclipse-PyDev)中使用的方式,而无需触摸命令行或安装任何东西。 所以在这里。

分析没有命令行

 def count(): from math import sqrt for x in range(10**5): sqrt(x) if __name__ == '__main__': import cProfile, pstats cProfile.run("count()", "{}.profile".format(__file__)) s = pstats.Stats("{}.profile".format(__file__)) s.strip_dirs() s.sort_stats("time").print_stats(10) 

有关更多信息,请参阅文档或其他答案。

我的方法是使用yappi( https://code.google.com/p/yappi/ )。 与RPC服务器(甚至只是为了debugging)结合使用,您可以注册方法来启动,停止和打印性能分析信息,例如:

 @staticmethod def startProfiler(): yappi.start() @staticmethod def stopProfiler(): yappi.stop() @staticmethod def printProfiler(): stats = yappi.get_stats(yappi.SORTTYPE_TTOT, yappi.SORTORDER_DESC, 20) statPrint = '\n' namesArr = [len(str(stat[0])) for stat in stats.func_stats] log.debug("namesArr %s", str(namesArr)) maxNameLen = max(namesArr) log.debug("maxNameLen: %s", maxNameLen) for stat in stats.func_stats: nameAppendSpaces = [' ' for i in range(maxNameLen - len(stat[0]))] log.debug('nameAppendSpaces: %s', nameAppendSpaces) blankSpace = '' for space in nameAppendSpaces: blankSpace += space log.debug("adding spaces: %s", len(nameAppendSpaces)) statPrint = statPrint + str(stat[0]) + blankSpace + " " + str(stat[1]).ljust(8) + "\t" + str( round(stat[2], 2)).ljust(8 - len(str(stat[2]))) + "\t" + str(round(stat[3], 2)) + "\n" log.log(1000, "\nname" + ''.ljust(maxNameLen - 4) + " ncall \tttot \ttsub") log.log(1000, statPrint) 

然后,当你的程序工作时,你可以通过调用startProfiler RPC方法随时启动分析器,并通过调用printProfiler (或者修改rpc方法将其返回给调用者)来获取这样的输出:

 2014-02-19 16:32:24,128-|SVR-MAIN |-(Thread-3 )-Level 1000: name ncall ttot tsub 2014-02-19 16:32:24,128-|SVR-MAIN |-(Thread-3 )-Level 1000: C:\Python27\lib\sched.py.run:80 22 0.11 0.05 M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\pyAheadRpcSrv\xmlRpc.py.iterFnc:293 22 0.11 0.0 M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\serverMain.py.makeIteration:515 22 0.11 0.0 M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\pyAheadRpcSrv\PicklingXMLRPC.py._dispatch:66 1 0.0 0.0 C:\Python27\lib\BaseHTTPServer.py.date_time_string:464 1 0.0 0.0 c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\_psmswindows.py._get_raw_meminfo:243 4 0.0 0.0 C:\Python27\lib\SimpleXMLRPCServer.py.decode_request_content:537 1 0.0 0.0 c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\_psmswindows.py.get_system_cpu_times:148 4 0.0 0.0 <string>.__new__:8 220 0.0 0.0 C:\Python27\lib\socket.py.close:276 4 0.0 0.0 C:\Python27\lib\threading.py.__init__:558 1 0.0 0.0 <string>.__new__:8 4 0.0 0.0 C:\Python27\lib\threading.py.notify:372 1 0.0 0.0 C:\Python27\lib\rfc822.py.getheader:285 4 0.0 0.0 C:\Python27\lib\BaseHTTPServer.py.handle_one_request:301 1 0.0 0.0 C:\Python27\lib\xmlrpclib.py.end:816 3 0.0 0.0 C:\Python27\lib\SimpleXMLRPCServer.py.do_POST:467 1 0.0 0.0 C:\Python27\lib\SimpleXMLRPCServer.py.is_rpc_path_valid:460 1 0.0 0.0 C:\Python27\lib\SocketServer.py.close_request:475 1 0.0 0.0 c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\__init__.py.cpu_times:1066 4 0.0 0.0 

对于短脚本来说可能并不是很有用,但有助于优化服务器types的进程,特别是考虑到printProfiler方法可以随时调用多次,以便分析和比较不同的程序使用情况。

想知道python脚本正在做什么? 进入Inspect Shell。 使用Inspect Shell可以在不中断运行脚本的情况下打印/更改全局variables并运行函数。 现在有自动完成和命令历史logging(仅在Linux上)。

Inspect Shell不是一个pdb样式的debugging器。

https://github.com/amoffat/Inspect-Shell

你可以使用那个(和你的手表)。

要添加到https://stackoverflow.com/a/582337/1070617

我写了这个模块,可以让你使用cProfile并轻松查看它的输出。 更多在这里: https : //github.com/ymichael/cprofilev

 $ python -m cprofilev /your/python/program # Go to http://localhost:4000 to view collected statistics. 

另请参阅:http: //ymichael.com/2014/03/08/profiling-python-with-cprofile.html了解如何理解收集的统计信息。

一个处理Python分析的新工具是PyVmMonitor: http ://www.pyvmmonitor.com/

它有一些独特的function,如

  • 将分析器附加到正在运行的(CPython)程序
  • 随着Yappi集成需求分析
  • 在另一台机器上的configuration文件
  • 多进程支持(多处理,Django的…)
  • 实时采样/ CPU视图(带时间范围select)
  • 通过cProfile / profile整合进行确定性分析
  • 分析现有的PStats结果
  • 打开DOT文件
  • 程序化API访问
  • 按方法或行分组样本
  • PyDev集成
  • PyCharm集成

注意:这是商业的,但免费的开源。

还有一个叫做statprof的统计分析器。 这是一个采样分析器,所以它为代码添加了最小的开销,并且提供了基于行的(而不仅仅是基于函数的)时序。 它更适合于像游戏这样的软实时应用程序,但可能比cProfile的精度要低。

pypi中的版本有点旧 ,所以可以通过指定git仓库来安装它:

 pip install git+git://github.com/bos/statprof.py@1a33eba91899afe17a8b752c6dfdec6f05dd0c01 

你可以像这样运行它:

 import statprof with statprof.profile(): my_questionable_function() 

另见https://stackoverflow.com/a/10333592/320036

当我不在服务器上的根目录时,我使用lsprofcalltree.py并像这样运行我的程序:

 python lsprofcalltree.py -o callgrind.1 test.py 

然后我可以用任何与callgrind兼容的软件打开报告,如qcachegrind

这取决于你想从分析中看到什么。 简单的时间度量可以由(bash)给出。

 time python python_prog.py 

即使'/ usr / bin / time'也可以使用'–verbose'标志来输出详细的指标。

要检查每个函数给出的时间度量标准,为了更好地理解在函数上花费了多less时间,可以在Python中使用内置的cProfile。

进入更详细的指标,如性能,时间不是唯一的指标。 你可以担心内存,线程等
性能分析选项:
1. line_profiler是另一个通常用于查找时间指标的分析器。
2. memory_profiler是一个工具来分析内存使用情况。
3. heapy(来自Guppy项目)剖析如何使用堆中的对象。

这些是我倾向于使用的一些常见的东西。 但是,如果你想了解更多,请阅读这本书这是一本相当不错的书,以performance为出发点。 您可以转到有关使用Cython和JIT(Just-in-time)编译的Python的高级主题。