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

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


当前回答

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

其他回答

如果你想做一个累积分析器,意思是连续运行函数几次并观察结果的总和。

您可以使用此cumulative_profiler装饰器:

它是python>=3.6特定的,但您可以删除非本地的,因为它可以在旧版本上工作。

import cProfile, pstats

class _ProfileFunc:
    def __init__(self, func, sort_stats_by):
        self.func =  func
        self.profile_runs = []
        self.sort_stats_by = sort_stats_by

    def __call__(self, *args, **kwargs):
        pr = cProfile.Profile()
        pr.enable()  # this is the profiling section
        retval = self.func(*args, **kwargs)
        pr.disable()

        self.profile_runs.append(pr)
        ps = pstats.Stats(*self.profile_runs).sort_stats(self.sort_stats_by)
        return retval, ps

def cumulative_profiler(amount_of_times, sort_stats_by='time'):
    def real_decorator(function):
        def wrapper(*args, **kwargs):
            nonlocal function, amount_of_times, sort_stats_by  # for python 2.x remove this row

            profiled_func = _ProfileFunc(function, sort_stats_by)
            for i in range(amount_of_times):
                retval, ps = profiled_func(*args, **kwargs)
            ps.print_stats()
            return retval  # returns the results of the function
        return wrapper

    if callable(amount_of_times):  # incase you don't want to specify the amount of times
        func = amount_of_times  # amount_of_times is the function in here
        amount_of_times = 5  # the default amount
        return real_decorator(func)
    return real_decorator

实例

剖析函数baz

import time

@cumulative_profiler
def baz():
    time.sleep(1)
    time.sleep(2)
    return 1

baz()

baz跑了5次并打印了以下内容:

         20 function calls in 15.003 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       10   15.003    1.500   15.003    1.500 {built-in method time.sleep}
        5    0.000    0.000   15.003    3.001 <ipython-input-9-c89afe010372>:3(baz)
        5    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

指定次数

@cumulative_profiler(3)
def baz():
    ...

我发现cprofiler和其他资源更多地用于优化目的,而不是调试。

我制作了自己的测试模块,用于简单的python脚本速度测试。(在我的例子中,使用ScriptProfilerPy测试了1K+行py文件,并在几分钟内将代码速度提高了10倍。

模块ScriptProfilerPy()将运行代码,并向其添加时间戳。我把模块放在这里:https://github.com/Lucas-BLP/ScriptProfilerPy

Use:

from speed_testpy import ScriptProfilerPy

ScriptProfilerPy("path_to_your_script_to_test.py").Profiler()

输出:

最近,我为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装饰器装饰您想要评测的任何函数使用“轮廓线”跑步器跑步

结果截图:

我发现,如果您不想使用命令行选项,该功能快速且易于使用。

要使用,只需在要分析的每个函数上方添加@profile。

def profile(fnc):
    """
    Profiles any function in following class just by adding @profile above function
    """
    import cProfile, pstats, io
    def inner (*args, **kwargs):
        pr = cProfile.Profile()
        pr.enable()
        retval = fnc (*args, **kwargs)
        pr.disable()
        s = io.StringIO()
        sortby = 'cumulative'   #Ordered
        ps = pstats.Stats(pr,stream=s).strip_dirs().sort_stats(sortby)
        n=10                    #reduced the list to be monitored
        ps.print_stats(n)
        #ps.dump_stats("profile.prof")
        print(s.getvalue())
        return retval
    return inner 

每个函数的输出如下

   Ordered by: cumulative time
   List reduced from 38 to 10 due to restriction <10>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.002    0.002 3151212474.py:37(get_pdf_page_count)
        1    0.000    0.000    0.002    0.002 fitz.py:3604(__init__)
        1    0.001    0.001    0.001    0.001 {built-in method fitz._fitz.new_Document}
        1    0.000    0.000    0.000    0.000 fitz.py:5207(__del__)
        1    0.000    0.000    0.000    0.000 {built-in method fitz._fitz.delete_Document}
        1    0.000    0.000    0.000    0.000 fitz.py:4816(init_doc)
        1    0.000    0.000    0.000    0.000 fitz.py:5197(_reset_page_refs)
        1    0.000    0.000    0.000    0.000 fitz.py:4821(<listcomp>)
       11    0.000    0.000    0.000    0.000 fitz.py:4054(_getMetadata)
        1    0.000    0.000    0.000    0.000 weakref.py:241(values)

不久前,我制作了pycallgraph,它从您的Python代码生成可视化。编辑:我已经将示例更新为使用3.3,这是本文撰写时的最新版本。

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

pycallgraph graphviz -- ./mypythonscript.py

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

from pycallgraph import PyCallGraph
from pycallgraph.output import GraphvizOutput

with PyCallGraph(output=GraphvizOutput()):
    code_to_profile()

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