Python是否有统计分析器?如果没有,我该如何编写一个?
我需要运行一个Python脚本,随机运行一段时间,然后暂停它,获取一个堆栈跟踪信息,再继续运行。为了实现这个,我在网上查了很多资料,但没有找到明显的解决办法。
8 个回答
Python已经具备了你所需要的所有功能,不用去改动解释器。
你只需要使用traceback
模块,配合sys._current_frames()
这个函数。你只需要找到一种方法,按照你想要的频率来输出需要的追踪信息,比如可以使用UNIX信号,或者用另一个线程。
如果你想快速开始你的代码,可以参考这个提交中的做法:
从那个提交中复制
threads.py
模块,或者至少复制堆栈追踪输出的函数(ZPL许可证,非常宽松):把它连接到一个信号处理器,比如
SIGUSR1
。
然后你只需要运行你的代码,并根据需要频繁地用SIGUSR1来“杀死”它。
如果你想用相同的技术,偶尔对单个线程中的单个函数进行“采样”,可以使用另一个线程来计时,我建议你研究一下Products.LongRequestLogger的代码和测试(这是我在Nexedi工作时开发的):
无论这是否算是“统计”分析,Mike Dunlavey的回答被intuited引用,提出了一个很有说服力的观点,认为这是一种非常强大的“性能调试”技术,我个人也有经验,确实能帮助快速找到性能问题的真正原因。
我想到几种方法可以做到这一点:
与其在程序运行时尝试获取堆栈跟踪,不如直接给它发个中断,然后解析输出。你可以用一个shell脚本,或者用另一个Python脚本来调用你的应用程序作为子进程。基本的想法在这个针对C++问题的回答中有详细解释和支持。
- 其实,你也可以注册一个后处理例程(使用
sys.excepthook
),这样可以记录堆栈跟踪,而不需要解析输出。不幸的是,Python没有办法在异常发生的地方继续执行,所以在记录后你不能恢复执行。
- 其实,你也可以注册一个后处理例程(使用
如果你想从正在运行的程序中获取堆栈跟踪,你可能需要对实现进行一些修改。所以如果你真的想这样做,可以看看pypy,这是一个主要用Python编写的Python实现。我不知道在pypy中这样做会有多方便。我猜这不会特别方便,因为这涉及到在几乎每条指令中引入一个钩子,这样做效率可能会非常低下。而且,我觉得这与第一种方法相比没有太大优势,除非你需要很长时间才能达到想要开始做堆栈跟踪的状态。
有一套宏是为
gdb
调试器设计的,目的是方便调试Python本身。gdb可以连接到一个外部进程(在这种情况下,就是执行你应用程序的Python实例),并且可以对它做几乎任何事情。似乎宏pystack
可以让你在当前执行点获取Python堆栈的回溯。我觉得自动化这个过程会很简单,因为你可以(在最坏的情况下)通过expect
或其他方式将文本输入到gdb
中。
这里有一个叫做 statprof
模块
你可以通过 pip install statprof
(或者 easy_install statprof
)来安装它,安装后就可以使用了:
import statprof
statprof.start()
try:
my_questionable_function()
finally:
statprof.stop()
statprof.display()
关于这个模块,有一些背景信息可以参考 这篇博客:
那么,这个模块有什么用呢?其实,Python 已经有两个内置的性能分析工具:lsprof 和已经不再使用的 hotshot。lsprof 的问题在于,它只跟踪函数调用。如果你在某个函数里有几个运行很快的循环,lsprof 对于找出哪些循环真正重要几乎没什么帮助。
几天前,我正好遇到了 lsprof 无法解决的情况:它告诉我有一个运行很快的函数,但这个函数我不熟悉,而且函数的代码比较长,问题所在并不明显。
经过一番在 Twitter 和 Google+ 上的求助,有人推荐了 statprof。不过有个问题:虽然它在进行统计采样(太棒了!),但它只在采样时跟踪函数的第一行(这是什么鬼!?)。所以我对这个问题进行了修复,改进了文档,现在它既好用又不会误导人。以下是它输出的一个例子,更准确地找出了那个运行快的函数中的问题行:
% cumulative self time seconds seconds name 68.75 0.14 0.14 scmutil.py:546:revrange 6.25 0.01 0.01 cmdutil.py:1006:walkchangerevs 6.25 0.01 0.01 revlog.py:241:__init__ [...blah blah blah...] 0.00 0.01 0.00 util.py:237:__get__ --- Sample count: 16 Total time: 0.200000 seconds
我已经把 statprof 上传到 Python 包索引,所以安装起来几乎是小菜一碟:“easy_install statprof”,你就可以开始使用了。
因为 代码在 GitHub 上,欢迎大家提交 bug 报告和改进建议。希望你喜欢!