Project Euler和其他编码竞赛通常有最长的运行时间,或者人们吹嘘他们的特定解决方案运行速度有多快。对于Python,有时方法有些笨拙——即向__main__添加计时代码。

描述Python程序运行时间的好方法是什么?


当前回答

一个很好的评测模块是line_profiler(使用kernprof.py脚本调用)。它可以在这里下载。

我的理解是,cProfile只提供每个函数花费的总时间的信息。因此,单独的代码行是不定时的。这是科学计算中的一个问题,因为通常一条线会花费很多时间。而且,我记得,cProfile没有抓住我在say numpy.dot上花费的时间。

其他回答

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配置文件

apt-get install python-profiler 

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

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

 pip install pycallgraph

run:

 pycallgraph mine.py args

视图:

 gimp pycallgraph.png

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

dot:graph对于cairo渲染器位图太大。缩放0.257079以适合

这使我的图像变得难以使用。所以我通常创建svg文件:

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

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

pip install graphviz

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

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

最近,我为PyCharm创建了一个插件,使用该插件,您可以在PyCharm编辑器中轻松分析和可视化line_profiler的结果。

linepfiler在其他答案中也提到过,它是一个很好的工具,可以准确分析python解释器在某些行中花费了多少时间。

我创建的PyCharm插件可以在这里找到:https://plugins.jetbrains.com/plugin/16536-line-profiler

它需要一个python环境中的助手包,名为line profiler pycharm,可以使用pip或插件本身安装。

在PyCharm中安装插件后:

用line_profiler_pycharm.profile装饰器装饰您想要评测的任何函数使用“轮廓线”跑步器跑步

结果截图:

值得指出的是,使用探查器仅在主线程上有效(默认情况下),如果使用它们,您将无法从其他线程获得任何信息。这可能有点棘手,因为探查器文档中完全没有提到它。

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

您也可以创建自己的线程.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类而不是标准类。它可能会给你更多的灵活性,但我不确定它是否值得,特别是如果你使用的是不使用你的类的第三方代码。

我的方法是使用雅皮语(https://github.com/sumerc/yappi). 它与RPC服务器结合使用特别有用,在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方法可以随时间多次调用,以分析和比较例如不同的程序使用场景。

在较新版本的yappi中,以下代码将起作用:

@staticmethod
def printProfile():
    yappi.get_func_stats().print_all()

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

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