2010-10-13 111 views
72

我一直在使用cProfile来剖析我的代码,并且它一直在很好地工作。我还使用gprof2dot.py来显示结果(使其更清晰一些)。如何逐行分析python代码?

但是,cProfile(以及目前为止我所见过的大多数其他Python配置文件)似乎只能在函数调用级别进行配置。当某些功能从不同的地方被调用时,这会引起混淆 - 我不知道1号或2号呼叫是否占用大部分时间。当所讨论的功能是6个层次时,这会变得更糟,从另外7个地方调用。

所以我的问题是:如何获得一行一行的分析?取而代之的是:

function #12, total time: 2.0s 

我希望看到这样的事情:

function #12 (called from somefile.py:102) 0.5s 
function #12 (called from main.py:12) 1.5s 

CPROFILE并展示了如何的总时间要“转让”给父母,但这又连接丢失当你有一堆层和相互联系的电话。

理想情况下,我很想有一个可以解析数据的GUI,然后向我展示我的源文件,并给出每行的总时间。事情是这样的:

main.py: 

a = 1 # 0.0s 
result = func(a) # 0.4s 
c = 1000 # 0.0s 
result = func(c) # 5.0s 

然后,我可以点击第二个“FUNC(三)”打电话,看看有什么占用时间在这一号召,从“FUNC(一)”呼叫分开。

这有道理吗?是否有任何分析库收集此类信息?有没有我错过的一些很棒的工具?任何反馈意见。谢谢!!

+2

我的猜测是,你会感兴趣的'pstats.print_callers'。一个例子是[这里](http://www.doughellmann.com/PyMOTW/profile/)。 – 2010-10-13 20:18:59

+0

穆罕默德,这绝对有帮助!至少它解决了一个问题:根据原点分离函数调用。我认为乔金顿的答案更接近我的目标,但print_callers()肯定会让我获得一半。谢谢! – rocketmonkeys 2010-10-14 15:25:25

回答

84

我相信这就是Robert Kern's line_profiler的目的。从链接:

File: pystone.py 
Function: Proc2 at line 149 
Total time: 0.606656 s 

Line #  Hits   Time Per Hit % Time Line Contents 
============================================================== 
    149           @profile 
    150           def Proc2(IntParIO): 
    151  50000  82003  1.6  13.5  IntLoc = IntParIO + 10 
    152  50000  63162  1.3  10.4  while 1: 
    153  50000  69065  1.4  11.4   if Char1Glob == 'A': 
    154  50000  66354  1.3  10.9    IntLoc = IntLoc - 1 
    155  50000  67263  1.3  11.1    IntParIO = IntLoc - IntGlob 
    156  50000  65494  1.3  10.8    EnumLoc = Ident1 
    157  50000  68001  1.4  11.2   if EnumLoc == Ident1: 
    158  50000  63739  1.3  10.5    break 
    159  50000  61575  1.2  10.1  return IntParIO 

希望帮助!

+1

乔,这*完全是我在找的东西。我可以使用一个装饰器,将一个LineProfiler()对象附加到一些函数中,然后它将逐个分析该函数的轮廓。我真的希望有一个图形化的方式来看结果,但这是一个很好的开始!谢谢! – rocketmonkeys 2010-10-14 15:40:46

+1

作为一个后续:我已经使用过这几次了,我甚至还制作了一个django decorator @profiler来自动在这个逐行剖析器中包装视图,并将结果吐出。这是完美的!这是我在分析视图时真正需要的。它不能递归地显示我需要花费的时间,但我至少可以将它缩小到一行。这通常只是我需要的。再次感谢! – rocketmonkeys 2010-11-30 15:15:01

+3

line_profiler是否适用于Python 3?我无法获得有关这方面的任何信息。 – user1251007 2012-07-23 15:02:39

19

您也可以使用pprofilepypi)。 如果你想剖析整个执行过程,它不需要修改源代码。在代码中达到特定点时,如

  • 拨动分析:

    import pprofile 
    profiler = pprofile.Profile() 
    with profiler: 
        some_code 
    # Process profile content: generate a cachegrind file and send it to user. 
    
  • 拨动从调用堆栈异步剖析 您也可以分析一个更大的计划的一个子集在两个方面(需要通过使用统计分析来在所考虑的应用中触发此代码的方式,例如信号处理程序或可用的工作线程):

    import pprofile 
    profiler = pprofile.StatisticalProfile() 
    statistical_profiler_thread = pprofile.StatisticalThread(
        profiler=profiler, 
    ) 
    with statistical_profiler_thread: 
        sleep(n) 
    # Likewise, process profile content 
    

代码注释输出格式很像行探查:

$ pprofile --threads 0 demo/threads.py 
Command line: ['demo/threads.py'] 
Total duration: 1.00573s 
File: demo/threads.py 
File duration: 1.00168s (99.60%) 
Line #|  Hits|   Time| Time per hit|  %|Source code 
------+----------+-------------+-------------+-------+----------- 
    1|   2| 3.21865e-05| 1.60933e-05| 0.00%|import threading 
    2|   1| 5.96046e-06| 5.96046e-06| 0.00%|import time 
    3|   0|   0|   0| 0.00%| 
    4|   2| 1.5974e-05| 7.98702e-06| 0.00%|def func(): 
    5|   1|  1.00111|  1.00111| 99.54%| time.sleep(1) 
    6|   0|   0|   0| 0.00%| 
    7|   2| 2.00272e-05| 1.00136e-05| 0.00%|def func2(): 
    8|   1| 1.69277e-05| 1.69277e-05| 0.00%| pass 
    9|   0|   0|   0| 0.00%| 
    10|   1| 1.81198e-05| 1.81198e-05| 0.00%|t1 = threading.Thread(target=func) 
(call)|   1| 0.000610828| 0.000610828| 0.06%|# /usr/lib/python2.7/threading.py:436 __init__ 
    11|   1| 1.52588e-05| 1.52588e-05| 0.00%|t2 = threading.Thread(target=func) 
(call)|   1| 0.000438929| 0.000438929| 0.04%|# /usr/lib/python2.7/threading.py:436 __init__ 
    12|   1| 4.79221e-05| 4.79221e-05| 0.00%|t1.start() 
(call)|   1| 0.000843048| 0.000843048| 0.08%|# /usr/lib/python2.7/threading.py:485 start 
    13|   1| 6.48499e-05| 6.48499e-05| 0.01%|t2.start() 
(call)|   1| 0.00115609| 0.00115609| 0.11%|# /usr/lib/python2.7/threading.py:485 start 
    14|   1| 0.000205994| 0.000205994| 0.02%|(func(), func2()) 
(call)|   1|  1.00112|  1.00112| 99.54%|# demo/threads.py:4 func 
(call)|   1| 3.09944e-05| 3.09944e-05| 0.00%|# demo/threads.py:7 func2 
    15|   1| 7.62939e-05| 7.62939e-05| 0.01%|t1.join() 
(call)|   1| 0.000423908| 0.000423908| 0.04%|# /usr/lib/python2.7/threading.py:653 join 
    16|   1| 5.26905e-05| 5.26905e-05| 0.01%|t2.join() 
(call)|   1| 0.000320196| 0.000320196| 0.03%|# /usr/lib/python2.7/threading.py:653 join 

需要注意的是,因为pprofile不依赖于代码修改就可以分析顶层模块声明,允许个人主页程序启动时间(多久需要导入模块,初始化全局变量,...)。

它可以生成cachegrind格式的输出,因此您可以使用kcachegrind轻松浏览大型结果。

披露:我是pprofile的作者。

+0

+1感谢您的贡献。 - 完成我有一个不同的观点 - 测量语句和函数的包含时间是一个目标,找出可以做什么来使代码更快是一个不同的目标,当代码变得很大时,差异变得非常明显 - 就像10^6行代码,代码可能会浪费大量的时间,我发现它的方式是抽取少量非常详细的样本,并用一个人的眼睛 - 不是总结。问题由于浪费时间的一小部分而暴露出来。 – 2015-02-02 14:18:12

+1

你说得对,当我想要分析一个较小的子集时,我没有提及pprofile的用法。我编辑我的帖子来添加这个例子。 – vpelletier 2015-02-03 06:54:40

+0

这正是我所期待的:非侵入性和广泛性。 – egpbos 2016-10-05 06:30:16

0

PyVmMonitor有一个实时视图可以帮助你(你可以连接到正在运行的程序并从中获取统计信息)。

请参见:http://www.pyvmmonitor.com/