【问题标题】:Profiling in Python: Who called the function?Python 中的分析:谁调用了该函数?
【发布时间】:2010-10-25 00:38:50
【问题描述】:

我正在使用cProfile 在 Python 中进行分析。我发现了一个占用大量 CPU 时间的函数。如何找出哪个函数调用这个繁重的函数最多?

编辑:

我会找到一个解决方法:我可以在那个繁重的函数中写一行 Python 代码来打印调用它的函数的名称吗?

【问题讨论】:

    标签: python profiling


    【解决方案1】:

    我几乎总是使用Gprof2dot查看cProfile模块的输出,基本上它将输出转换为graphvis图(.dot文件),例如:

    它可以很容易地确定哪个函数最慢,以及哪个函数调用了它。

    用法是:

    python -m cProfile -o output.pstats path/to/your/script arg1 arg2
    gprof2dot.py -f pstats output.pstats | dot -Tpng -o output.png
    

    【讨论】:

    • +1 这他妈的太棒了,太棒了!谢谢你给我看这个..哇..
    • 如果你想像 cProfile.run(exp) 一样分析表达式,你可以使用它(仍然创建一个临时 pstats 文件): def run(exp,output="profile.png", statsFileName="stats.pstats"): 导入 cProfile cProfile.run(exp,statsFileName) os.system("python gprof2dot.py -f pstats %s | dot -Tpng -o %s > /dev/null 2>&1" %(statsFileName,输出))
    • 谢谢。这也是最便携的方式。
    【解决方案2】:

    可以使用标准库中的分析器cProfile 来实现。
    pstats.Stats(分析器结果)中有方法print_callees(或者print_callers)。


    示例代码:

    import cProfile, pstats
    pr = cProfile.Profile()
    pr.enable()
    
    # ... do something ...
    
    pr.disable()
    ps = pstats.Stats(pr).strip_dirs().sort_stats('cumulative')
    ps.print_callees()
    

    结果将类似于:

    Function                           called...
                                           ncalls  tottime  cumtime
    ElementTree.py:1517(_start_list)   ->   24093    0.048    0.124  ElementTree.py:1399(start)
                                            46429    0.015    0.041  ElementTree.py:1490(_fixtext)
                                            70522    0.015    0.015  ElementTree.py:1497(_fixname)
    ElementTree.py:1527(_data)         ->   47827    0.017    0.026  ElementTree.py:1388(data)
                                            47827    0.018    0.053  ElementTree.py:1490(_fixtext)
    

    左边是调用者,右边是被调用者。
    (例如,_fixtext_data 调用了 47827 次,_start_list 被调用了 46429 次)


    另见:


    几点说明:

    • 需要为此编辑您的代码(插入那些配置文件语句)。
      (即不能从命令行使用,如python -m cProfile myscript.py。虽然可以为此编写单独的脚本)
    • 有点不相关,但strip_dirs()必须在sort_stats()之前(否则排序不起作用)

    【讨论】:

    • 在其中一个 cmets 中提到了这一点,但我认为它应该是一个单独的答案,因为有时外部工具/依赖项是不可能的。 (即使输出不如其他答案那么好)
    【解决方案3】:

    Pycscope 就是这样做的。我今天才发现它,所以我不能说它有多好,但我尝试过的几个例子都很好(虽然并不完美)。

    https://pypi.python.org/pypi/pycscope/

    您将使用它来生成一个 cscope 文件,然后从编辑器(特别是 VIM)生成一个 cscope 插件。我尝试将它与 vanilla cscope 一起使用,似乎普通的 cscope 会混淆。

    【讨论】:

      【解决方案4】:

      这可能无法直接回答您的问题,但肯定会有所帮助。如果使用带有选项的分析器 --sort 累积,它将按累积时间对函数进行排序。这不仅有助于检测繁重的函数,还有助于检测调用它们的函数。

      python -m cProfile --sort cumulative myScript.py
      

      有一个解决方法可以获取调用者函数:

      import inspect
      print inspect.getframeinfo(inspect.currentframe().f_back)[2]
      

      您可以根据需要添加任意数量的 f_back,以防您需要调用者调用者等 如果你想计算频繁调用,你可以这样做:

      record = {}
      
      caller = inspect.getframeinfo(inspect.currentframe().f_back)[2]
      record[caller] = record.get(caller, 0) + 1
      

      然后按频率顺序打印:

      print sorted(record.items(), key=lambda a: a[1])
      

      【讨论】:

      • 如果将 cProfile 的结果保存到文件并使用pstats 模块加载配置文件,则可以直接查询重函数的调用者:loaded_stats_object.print_callers('heavy_function')
      【解决方案5】:

      对不起,我不熟悉 Python,但有一个 general method 可以工作,假设您可以在随机时间手动中断执行。

      这样做,并显示调用堆栈。它会以很高的概率告诉你你想知道什么。如果你想更确定,就多做几次。

      它之所以有效,是因为有罪的调用者必须在调用堆栈中浪费的部分时间,这会在大部分时间将其暴露给您的中断,无论它是分散在许多短调用还是几个长调用中那些。

      注意:这个过程更像是诊断而不是测量。假设糟糕的调用浪费了 90% 的时间。然后每次你停止它,错误的调用语句就在调用堆栈上供你查看的概率是 90%,你将能够看到它是错误的。但是,如果您想准确测量损耗,那就是另一个问题了。为此,您将需要更多样本,以查看其中包含该调用的百分比。或者,只需修复有罪的呼叫,计时加速,这将告诉您确切的浪费是什么。

      【讨论】:

        【解决方案6】:

        您可能想看看pycallgraph

        【讨论】:

          【解决方案7】:

          inspect.stack() 会给你当前的调用栈。

          【讨论】:

            【解决方案8】:

            我自己没有使用过 cProfile,但大多数分析器都会为您提供调用层次结构。
            谷歌搜索我发现这个slides 是关于 cProfile 的。也许这有帮助。第 6 页看起来 cProfile 确实提供了层次结构。

            【讨论】:

              猜你喜欢
              • 2011-02-05
              • 2020-10-08
              • 1970-01-01
              • 2014-01-09
              • 1970-01-01
              • 1970-01-01
              • 2011-07-19
              • 2014-05-03
              • 1970-01-01
              相关资源
              最近更新 更多