首页 文章

如何分析Python脚本?

提问于
浏览
1031

项目欧拉和其他编码竞赛通常有最长的运行时间或人们吹嘘他们的特定解决方案运行的速度 . 使用python,有时候这些方法有些笨拙 - 即将时序代码添加到 __main__ .

分析python程序运行多长时间的好方法是什么?

23 回答

  • 19

    在Virtaal的source中,有一个非常有用的类和装饰器可以很容易地进行分析(甚至对于特定的方法/函数) . 然后可以在KCacheGrind中非常舒适地查看输出 .

  • 117

    我认为cProfile非常适合分析,而kcachegrind非常适合可视化结果 . 中间的pyprof2calltree处理文件转换 .

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

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

    apt-get install kcachegrind
    pip install pyprof2calltree
    

    结果:

    Screenshot of the result

  • 3

    这取决于你想要从分析中看到什么 . 简单的时间指标可以由(bash)给出 .

    time python python_prog.py
    

    甚至'/ usr / bin / time'也可以使用'--verbose'标志输出详细的指标 .

    要检查每个函数给出的时间度量并更好地了解函数花费的时间,可以在python中使用内置的cProfile .

    进入更详细的指标,如性能,时间不是唯一的指标 . 你可以担心内存,线程等
    分析选项:

    1. line_profiler 是另一个通常用于逐行查找时序度量的分析器 .
    2. memory_profiler 是一个分析内存使用情况的工具 .
    3. heapy (from project Guppy) 描述如何使用堆中的对象 .

    这些是我倾向于使用的一些常见的 . 但是如果你想了解更多信息,请尝试阅读本文book这是一本非常好的书,首先考虑到性能 . 您可以转到使用Cython和JIT(即时)编译的python的高级主题 .

  • 1

    当我不是服务器上的root用户时,我使用lsprofcalltree.py并运行我的程序,如下所示:

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

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

  • 3

    另外值得一提的是GUI cProfile dump viewer RunSnakeRun . 它允许您进行排序和选择,从而放大程序的相关部分 . 图中矩形的大小与所花费的时间成比例 . 如果将鼠标悬停在矩形上,则会突出显示表格中的调用以及 Map 上的任何位置 . 双击矩形时,它会放大该部分 . 它将显示谁调用该部分以及该部分调用的内容 .

    描述性信息非常有用 . 它显示了该位的代码,在处理内置库调用时可能会有所帮助 . 它告诉您查找代码的文件和行 .

    也想指出OP说'剖析',但看起来他的意思是“时机” . 请记住,在分析时程序运行速度会变慢 .

    enter image description here

  • 28

    @ Maxy对this answer的评论帮助了我,我认为它应该得到自己的答案:我已经有了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丢失的信息较少:

    gprof2dot example output

  • 3

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

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

    最好的是你可以与图表互动 . 例如,要放大一个可以单击一个弧,并且弧及其后代将被放大为新的旭日以显示更多细节 .

    enter image description here

  • 11

    pprofile

    line_profiler (已在此处展示)也启发了pprofile,其描述如下:

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

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

    vprof

    还有vprof,一个Python包描述为:

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

    heatmap

  • 175

    在Joe Shaw关于多线程代码没有按预期工作的回答之后,我认为cProfile中的 runcall 方法只是围绕profiled函数调用进行 self.enable()self.disable() 调用,所以你可以自己做这个并且拥有你想要的任何代码 . - 对现有代码的干扰最小 .

  • 0

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

    它有一些独特的功能,如

    • 将探查器附加到正在运行的(CPython)程序

    • 使用Yappi集成进行按需分析

    • 在另一台机器上的配置文件

    • 多个进程支持(多处理,django ......)

    • 实时采样/ CPU视图(带时间范围选择)

    • 通过cProfile / profile集成进行确定性分析

    • 分析现有的PStats结果

    • 打开DOT文件

    • Programatic API访问

    • 按方法或行分组样本

    • PyDev集成

    • PyCharm集成

    注意:它是商业的,但是免费的开源 .

  • 6

    Simplestquickest 找到所有时间的方式 .

    1. pip install snakeviz
    
    2. python -m cProfile -o temp.dat <PROGRAM>.py
    
    3. snakeviz temp.dat
    

    在浏览器中绘制饼图 . 最大的一块是问题功能 . 非常简单 .

  • 10

    还有一个名为statprof的统计分析器 . 它更适合像游戏这样的软实时应用程序,但可能没有cProfile那么精确 .

    version in pypi有点旧,所以可以通过指定the git repositorypip 一起安装:

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

    你可以像这样运行它:

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

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

  • 46

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

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

    Python包含一个名为cProfile的探查器 . 它不仅给出了总运行时间,还给出了每个函数的单独时间,并告诉您每个函数被调用了多少次,这样可以很容易地确定应该在哪里进行优化 .

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

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

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

    python -m cProfile myscript.py
    

    为了使它更容易,我制作了一个名为'profile.bat'的小批处理文件:

    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中一个 Headers 为Python Profiling的视频资源的链接
    Also via YouTube .

  • 3

    一个很好的分析模块是line_profiler(使用脚本kernprof.py调用) . 它可以下载here .

    我的理解是cProfile只提供有关每个函数花费的总时间的信息 . 所以各行代码都没有定时 . 这是科学计算中的一个问题,因为通常一条线路可能需要花费很多时间 . 另外,正如我记得的那样,cProfile并没有 grab 我在numpy.dot上花费的时间 .

  • 127

    要添加到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关于如何理解收集的统计信息 .

  • 9

    我最近创建了tuna,用于可视化Python运行时和导入配置文件;这在这里可能会有所帮助 .

    enter image description here

    安装时

    pip3 install tuna
    

    创建运行时配置文件

    python -mcProfile -o program.prof yourfile.py
    

    或导入配置文件(需要Python 3.7)

    python -X importprofile yourfile.py 2> import.log
    

    然后只需在文件上运行金枪鱼

    tuna program.prof
    
  • 33

    曾经想知道python脚本到底在做什么?输入Inspect Shell . Inspect Shell允许您打印/更改全局变量并运行函数,而不会中断正在运行的脚本 . 现在有自动完成和命令历史记录(仅限Linux) . Inspect Shell不是pdb样式的调试器 .

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

    你可以使用它(和你的 Watch ) .

  • 6

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

    我真的错过了我可以在我的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)
    

    有关详细信息,请参阅docs或其他答案 .

  • 10

    前段时间我制作了pycallgraph,用Python代码生成可视化 . Edit: 我已更新该示例以使用最新版本 .

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

    pycallgraph graphviz -- ./mypythonscript.py
    

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

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

    这些中的任何一个都将生成类似于下图的 pycallgraph.png 文件:

    enter image description here

  • 1123

    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:一种创建调用图图像的工具
    安装:

    sudo pip install pycallgraph
    

    跑:

    pycallgraph mine.py args
    

    视图:

    gimp pycallgraph.png
    

    您可以使用任何您喜欢的方式来查看png文件,我使用了gimp
    不幸的是我常常得到

    dot:图形对于cairo-renderer位图来说太大了 . 按比例缩小0.257079以适应

    这让我的图像变得非常小 . 所以我通常创建svg文件:

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

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

    sudo pip install graphviz
    

    通过@maxy / @quodlibetor使用gprof2dot替代绘图:

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

    它's worth pointing out that using the profiler only works (by default) on the main thread, and you won' t如果您使用它们,则从其他线程获取任何信息 . 这可能是一个问题,因为在profiler documentation中完全没有提及 .

    如果您还想要分析线程,您需要查看文档中的threading.setprofile() function .

    您还可以创建自己的 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 类而不是标准类 . 它可能会给你更多的灵活性,但是我很有 Value ,特别是如果你使用的第三方代码不会使用你的类 .

  • 28

    我的方法是使用yappi(https://code.google.com/p/yappi/) . 它与RPC服务器结合起来特别有用,其中(甚至仅用于调试)您可以注册方法来启动,停止和打印分析信息,例如:通过这种方式:

    @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
    

    它对于短脚本可能不是很有用,但有助于优化服务器类型的进程,特别是考虑到 printProfiler 方法可以多次调用以分析和比较,例如,不同的程序使用场景 .

相关问题