web-dev-qa-db-ja.com

Pythonコードを行ごとにプロファイルするにはどうすればよいですか?

私はコードをプロファイリングするためにcProfileを使用してきましたが、うまく機能しています。 gprof2dot.py を使用して、結果を視覚化します(わかりやすくします)。

ただし、cProfile(および他のほとんどのPython私がこれまで見たプロファイラー)は、関数呼び出しレベルでのみプロファイルするようです。これにより、特定の関数が異なる場所から呼び出されると混乱が生じます。呼び出し#1または呼び出し#2がほとんどの時間を占めているかどうかはわかりませんが、問題の関数が他の7つの場所から呼び出された6レベルの深さである場合、さらに悪化します。

行ごとのプロファイリングを取得するにはどうすればよいですか?

これの代わりに:

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

次に、2番目の「func(c)」呼び出しをクリックして、「func(a)」呼び出しとは別に、その呼び出しで何が時間を消費しているかを確認できます。

それは理にかなっていますか?このタイプの情報を収集するプロファイリングライブラリはありますか?見逃した素晴らしいツールはありますか?

103
rocketmonkeys

Robert Kernの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

お役に立てば幸いです!

110
Joe Kington

pprofilepypi )を使用することもできます。実行全体のプロファイルを作成する場合、ソースコードの変更は必要ありません。次の2つの方法で、より大きなプログラムのサブセットをプロファイルすることもできます。

  • 次のようなコード内の特定のポイントに到達したときにプロファイルを切り替えます。

    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の著者です。

36
vpelletier

PyVmMonitorには、そこで役立つライブビューがあります(実行中のプログラムに接続して、そこから統計を取得できます)。

参照: http://www.pyvmmonitor.com/

1
Fabio Zadrozny

あなたはline_profilerパッケージの助けを借りることができます

1。パッケージの最初のインストール:

    pip install line_profiler

2。magicコマンドを使用して、python/notebook環境にパッケージをロードします

    %load_ext line_profiler

。関数のコードをプロファイルする場合は、
次のようにします。
%lprun -f function_name function_call

    %lprun -f function_defined_by_you function_defined_by_you(arg1, arg2)

上記の手順を実行すると、すべての詳細を含むフォーマットされた素敵な出力が得られます

1
Sateesh