代码之家  ›  专栏  ›  技术社区  ›  rocketmonkeys

如何逐行分析python代码?

  •  98
  • rocketmonkeys  · 技术社区  · 14 年前

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

    但是,cprofile(以及我迄今为止看到的大多数其他python profiler)似乎只在函数调用级别进行概要分析。当从不同的地方调用某些函数时,这会导致混淆-我不知道调用1或调用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)”调用分开。

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

    3 回复  |  直到 6 年前
        1
  •  108
  •   martineau    6 年前

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

    希望有帮助!

        2
  •  32
  •   vpelletier    10 年前

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

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

      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
      

    代码注释输出格式与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不依赖于代码修改,因此它可以对顶级模块语句进行配置,从而允许配置程序启动时间(导入模块、初始化全局变量等所需的时间)。

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

    披露:我是权威作家。

        3
  •  1
  •   Fabio Zadrozny    10 年前

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

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