如何逐行分析Python代码?

2024-10-16 22:27:06 发布

您现在位置:Python中文网/ 问答频道 /正文

我一直在使用cProfile来分析我的代码,它工作得很好。我还使用gprof2dot.py来可视化结果(使结果更清晰)。

然而,cProfile(和我迄今为止看到的大多数其他Python剖析器)似乎只在函数调用级别进行剖析。当从不同的地方调用某些函数时,这会导致混乱——我不知道call#1或call#2是否占用了大部分时间。当所讨论的函数有六个层次的深度时,情况会变得更糟,这是从其他七个地方调用的。

如何获得逐行分析?

而不是这个:

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(c)”调用来查看该调用占用的时间,与“func(a)”调用分开。

这有道理吗?是否有收集此类信息的分析库?我错过了什么很棒的工具吗?


Tags: 函数代码frompymain地方时间情况
3条回答

我相信这就是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

希望能有帮助!

您也可以使用pprofilepypi)。 如果要分析整个执行过程,则不需要修改源代码。 您还可以通过两种方式分析较大程序的子集:

  • 在到达代码中的特定点时切换分析,例如:

    import pprofile
    profiler = pprofile.Profile()
    with profiler:
        some_code
    # Process profile content: generate a cachegrind file and send it to user.
    
    # You can also write the result to the console:
    profiler.print_stats()
    
    # Or to a file:
    profiler.dump_stats("/tmp/profiler_stats.txt")
    
  • 通过使用统计分析从调用堆栈异步切换分析(需要在考虑的应用程序中触发此代码的方法,例如信号处理程序或可用的工作线程):

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

代码注释输出格式非常类似于line profiler:

$ 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不依赖于代码修改,因此它可以分析顶级模块语句,从而可以分析程序启动时间(导入模块、初始化globals等所需的时间)。

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

披露:我是一个合适的作者。

您可以使用line_profiler软件包来完成此操作

1。首先安装软件包:

    pip install line_profiler

2。使用magic命令将包加载到python/notebook环境中

    %load_ext line_profiler

3。如果要分析函数的代码,则
请执行以下操作:
%lprun-f函数名函数调用

    %lprun -f function_defined_by_you function_defined_by_you(arg1, arg2)

如果遵循以下步骤,您将获得一个格式良好的输出,其中包含所有详细信息:)

相关问题 更多 >