【发布时间】:2010-10-25 00:38:50
【问题描述】:
我正在使用cProfile 在 Python 中进行分析。我发现了一个占用大量 CPU 时间的函数。如何找出哪个函数调用这个繁重的函数最多?
编辑:
我会找到一个解决方法:我可以在那个繁重的函数中写一行 Python 代码来打印调用它的函数的名称吗?
【问题讨论】:
我正在使用cProfile 在 Python 中进行分析。我发现了一个占用大量 CPU 时间的函数。如何找出哪个函数调用这个繁重的函数最多?
编辑:
我会找到一个解决方法:我可以在那个繁重的函数中写一行 Python 代码来打印调用它的函数的名称吗?
【问题讨论】:
我几乎总是使用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
【讨论】:
可以使用标准库中的分析器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()之前(否则排序不起作用)【讨论】:
Pycscope 就是这样做的。我今天才发现它,所以我不能说它有多好,但我尝试过的几个例子都很好(虽然并不完美)。
https://pypi.python.org/pypi/pycscope/
您将使用它来生成一个 cscope 文件,然后从编辑器(特别是 VIM)生成一个 cscope 插件。我尝试将它与 vanilla cscope 一起使用,似乎普通的 cscope 会混淆。
【讨论】:
这可能无法直接回答您的问题,但肯定会有所帮助。如果使用带有选项的分析器 --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])
【讨论】:
pstats 模块加载配置文件,则可以直接查询重函数的调用者:loaded_stats_object.print_callers('heavy_function')
对不起,我不熟悉 Python,但有一个 general method 可以工作,假设您可以在随机时间手动中断执行。
这样做,并显示调用堆栈。它会以很高的概率告诉你你想知道什么。如果你想更确定,就多做几次。
它之所以有效,是因为有罪的调用者必须在调用堆栈中浪费的部分时间,这会在大部分时间将其暴露给您的中断,无论它是分散在许多短调用还是几个长调用中那些。
注意:这个过程更像是诊断而不是测量。假设糟糕的调用浪费了 90% 的时间。然后每次你停止它,错误的调用语句就在调用堆栈上供你查看的概率是 90%,你将能够看到它是错误的。但是,如果您想准确测量损耗,那就是另一个问题了。为此,您将需要更多样本,以查看其中包含该调用的百分比。或者,只需修复有罪的呼叫,计时加速,这将告诉您确切的浪费是什么。
【讨论】:
您可能想看看pycallgraph。
【讨论】:
inspect.stack() 会给你当前的调用栈。
【讨论】:
我自己没有使用过 cProfile,但大多数分析器都会为您提供调用层次结构。
谷歌搜索我发现这个slides 是关于 cProfile 的。也许这有帮助。第 6 页看起来 cProfile 确实提供了层次结构。
【讨论】: