我有一个庞大的代码库,现在需要花费很长时间才能执行。我不知道是什么。
代码永远不会引发异常,它似乎继续处理某些事情。
我想做的是在某些功能周围放置计时器来测试哪一个是罪魁祸首。但我不确定这是否是正确的方法,或者如何做到这一点。
我不能轻易地在代码周围的点处引发异常,因为有各种循环调用相同的函数,这可能有时只需要太长时间。
最佳策略是什么?
答案 0 :(得分:8)
一种解决方案是使用内置于Python中的cProfile
来说明您的代码花费最多时间的函数。重要的是,即使您使用<停止代码,此分析也会起作用 / em> KeyboardInterrupt
。因此,您可以开始运行和分析代码,在一两分钟后停止它,然后查看它花费的时间。
使用这些额外的-m
和-o
参数运行代码:
python -m cProfile -o profile.txt myscript.py
然后程序运行完毕后,运行以下代码(例如,从另一个脚本):
import pstats
p = pstats.Stats('profile.txt')
p.strip_dirs().sort_stats("time").print_stats()
这将打印按照您在其中花费的总时间排序的功能列表。
这是使用分析来调试无限循环的演示。假设myscript.py
有以下代码。
def f():
while True:
g(100000)
def g(n):
x = []
for i in range(n):
x.append(n)
f()
当然这会导致无限循环运行很多次。所以我运行上面的profiling命令,但是我在大约30-40秒后停止它(甚至可以短得多)。其简介将打印为:
Wed Jan 30 10:58:50 2013 profile.txt
115414854 function calls in 37.705 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1155 25.787 0.022 37.020 0.032 test3.py:5(g)
115412541 10.060 0.000 10.060 0.000 {method 'append' of 'list' objects}
1155 1.173 0.001 1.173 0.001 {range}
1 0.685 0.685 37.705 37.705 test3.py:1(f)
1 0.000 0.000 37.705 37.705 test3.py:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
请注意,我们的无限循环函数g
就函数所花费的时间而言位于列表的顶部。
注意:仅仅因为代码在一个函数中花费所有时间并不意味着循环直接围绕该函数 - 它可以由函数调用的函数调用(等)处于无限循环中(注意append
靠近列表顶部,因为它在g
内调用。另一种方法是使用.sort_stats("cum")
根据每个函数的累计时间对它们进行排序。这两种方法的结合,以及一些小的侦探工作(查看代码和添加调试消息),应该能够找出罪魁祸首。
答案 1 :(得分:1)
我建议使用logging module将调试语句添加到代码中的各个点。使用日志记录模块,您可以轻松打开和关闭调试语句,以及控制显示的信息。
另一种选择是使用IDE,您可以在其中轻松添加断点,从而可以确定代码进入长循环的位置。
答案 2 :(得分:1)
[编辑:我看到您添加了python-2.7
标记,因此这不会对您有所帮助,但对其他人可能有用或有趣。]
在Python 3中,您可以通过键入 Control-C 进入调试器。然后从调试器中检查堆栈以查看程序花费时间的位置。
Python 3.3.0 (default, Nov 23 2012, 10:26:01)
>>> import time, pdb
>>> def foo(): time.sleep(1); foo()
...
>>> pdb.run('foo()')
> <string>(1)<module>()
(Pdb) c
^C
Program interrupted. (Use 'cont' to resume).
--Call--
> <stdin>(1)foo()
(Pdb) w
<stdin>(1)<module>()
/.../pdb.py(1556)run()
-> Pdb().run(statement, globals, locals)
/.../bdb.py(405)run()
-> exec(cmd, globals, locals)
<string>(1)<module>()
<stdin>(1)foo()
<stdin>(1)foo()
<stdin>(1)foo()
<stdin>(1)foo()
<stdin>(1)foo()
> <stdin>(1)foo()
您可以将此技术视为一种基于样本的分析,只需一个样本!对于许多问题,您只需要一个样本。