Python コードを 1 行ずつプロファイリングする
 
この記事では、Python コードを 1 行ずつプロファイリングし、コードの実行に関する有用な情報を取得する方法について説明します。
まず、プロファイリングについて簡単に紹介します。 次に、関数ベースのプロファイリングよりも行ごとのプロファイリングを使用する方が適している場合について説明します。 その後、Python でのプロファイリングの実装について説明します。
プロファイリングとは
プロファイリングは、コードのさまざまな部分で使用されるリソースをチェックするプロセスです。 効率的なコーディングの実践では、通常、時間の複雑さ (さまざまなコーディング ユニット/関数にかかる時間) またはメモリの複雑さ (さまざまなコーディング ユニット/関数によるメモリ使用量) に主に関心があります。
前者はプログラムにかかる時間を短縮することに集中するのに役立ち、後者はメモリ使用量を最適化するのに役立ちます。
関数プロファイリング
主にどの言語でも、関数レベルのプロファイリングは、プログラムのさまざまな関数によって消費される時間を調べるために使用されます。 したがって、Python の既定のパッケージには、関数プロファイリング用のライブラリ cProfile と Profile が既に含まれています。
ラインプロファイラー
関数ベースのプロファイラーは、さまざまな関数にかかった時間に関する情報を提供します。 ただし、関数またはプログラムの全体的な時間にどの行がより多く貢献しているかを確認するために、行ベースの時間の複雑さが必要になる場合があります。
そのような場合のために、Python には line_profiler ライブラリがあります。
line_profiler モジュールを使用した Python コードのプロファイリング
LineProfiler モジュールを使用すると、コードを行ごとまたは関数ごとにプロファイリングできます。
次の Python コード スニペットでは、slow_avg_producer() と fast_average_producer() という名前の 2つの関数を作成しました。 これらの関数はどちらも同じ平均を生成しますが、実行時間は異なります。
まず、次のモジュールをインストールして、ライン プロファイラーを使用してコードをプロファイリングします。
!pip install line_profiler
import time
import random
def slow_avg_producer():
    time.sleep(6)  # Do nothing for 5 time units
    # generate random numbers array
    arr = [
        random.randint(1, 100) for i in range(10000)
    ]  # generate random numbers array
    return sum(arr) / len(arr)
def fast_average_producer():
    time.sleep(1)  # Do nothing for 1 time units
    # generate random numbers array
    arr = [random.randint(1, 100) for i in range(10000)]
    return sum(arr) / len(arr)
def main_func():
    average = slow_avg_producer()
    print(average)
    result = fast_average_producer()
    print(average)
タイミング統計を取得するには、まず LineProfiler オブジェクトを作成し、次に main_func() を渡してラッパーを取得します。 最後に、プロファイリング統計を取得できます。
from line_profiler import LineProfiler
line_prof = LineProfiler()
line_prof_wrapper = line_prof(main_func)
line_prof_wrapper()
# print the profiling details
line_prof.print_stats()
上記のコードは、次の出力を生成します。 時間の単位はマイクロ秒です。
Timer unit: 1e-06 s
Total time: 7.10521 s
File: <ipython-input-14-67ae2a9633ee>
Function: main_func at line 17
Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    17                                           def main_func():
    18         1    6054801.0 6054801.0     85.2      average = slow_avg_producer()
    19         1        676.0    676.0      0.0      print(average)
    20
    21         1    1049070.0 1049070.0     14.8      result = fast_average_producer()
    22         1        659.0    659.0      0.0      print(average)
統計によると、メイン関数では、slow_avg_producer() に 6.054 秒かかり、次の print ステートメントに 0.000676 秒かかります。 一方、fast_average_producer() は非常に短い時間しかかかりません。
コマンド ライン経由で Python コードをプロファイリングする
コマンド ライン インターフェイスを使用して、関数の行ごとのプロファイリングの詳細を取得できます。 この方法では、kernprof コマンドを使用します。
ここでも同じ関数を使用しますが、各関数の先頭にデコレータ @profile を追加します。 このスクリプトに average_producer.py という名前を付けます。
import time
import random
@profile
def slow_avg_producer():
    time.sleep(6)  # Do nothing for 5 time units
    # generate random numbers array
    arr = [
        random.randint(1, 100) for i in range(10000)
    ]  # generate random numbers array
    return sum(arr) / len(arr)
@profile
def fast_average_producer():
    time.sleep(1)  # Do nothing for 1 time units
    # generate random numbers array
    arr = [random.randint(1, 100) for i in range(10000)]
    return sum(arr) / len(arr)
@profile
def main_func():
    average = slow_avg_producer()
    print(average)
    result = fast_average_producer()
    print(average)
main_func()
kernprof コマンドを使用して、次のコマンドを使用して line-profiler モジュールをインストールする必要があります。
pip install line-profiler
Colab または Jupyter Notebook ターミナルの場合、上記のコマンドの先頭に感嘆符 (!) を使用します。

インストール後、kernprof.exe がインストールされているディレクトリを変更する必要があります。 次に、次のコマンドを実行します。
kernprof は cProfile を実行するオプションを提供しますが、ここではライン プロファイラーを使用します。 そのために、-l 引数が使用されます。
kernprof -l average_producer.py

次のコマンドを実行して、プロファイリングの結果を確認します。
python -m line_profiler average_producer.py.lprof
以下に示すように、プロファイリング結果をコマンド ライン インターフェイスに出力します。

