Python Profiling

Page content

コードを実際に動作させながら、どこで遅くなるかを計測するのだ。

プロファイラとは?

良好なパフォーマンスを確保するための開発者のツールボックスの中で最も優れたツールの一つがプロファイリングです。本番のコードがどこで遅くなるかを正確に予測するのは非常に困難です。プロファイリングツールを使えば、コードの中で遅くなっている行を正確に示すことができます。また、特定の最適化を行い、テストすることも可能です。 Sentry 日本語公式ブログ - プロファイリング入門101:プロファイリングとは何か? - Ichizoku

Python のプロファイラ?

cProfile と profile は 決定論的プロファイリング (deterministic profiling) を行います。 プロファイル (profile) とは、プログラムの各部分がどれだけ頻繁に呼ばれたか、そして実行にどれだけ時間がかかったかという統計情報です。 pstats モジュールを使ってこの統計情報をフォーマットし表示することができます。 (https://docs.python.org/ja/3/library/profile.html)

  • cProfile: ほとんどのユーザーに推奨されるモジュールです。 C言語で書かれた拡張モジュールで、オーバーヘッドが少ないため長時間実行されるプログラムのプロファイルに適しています。 Brett Rosen と Ted Czotter によって提供された lsprof に基づいています。
  • profile: cProfile の低レベル版、ピュア Python モジュールで、 cProfile モジュールはこのモジュールのインターフェースを真似ています。対象プログラムに相当のオーバーヘッドが生じます。もしプロファイラに何らかの拡張をしたいのであれば、こちらのモジュールを拡張する方が簡単でしょう。このモジュールはもともと Jim Roskind により設計、実装されました。
  • py-spy: https://github.com/benfred/py-spy

py-spy

py-spy は Python のプロファイラ。 2024-03-25 時点では python v3.11 までしかサポートしていないので、その点だけ注意!

以下のようにインストールできる。

pip install py-spy

ヘルプを見ると良い

$ py-spy --help

py-spy 0.3.14
Sampling profiler for Python programs

USAGE:
    py-spy <SUBCOMMAND>

OPTIONS:
    -h, --help       Print help information
    -V, --version    Print version information

SUBCOMMANDS:
    record    Records stack trace information to a flamegraph, speedscope or raw file
    top       Displays a top like view of functions consuming CPU
    dump      Dumps stack traces for a target program to stdout
    help      Print this message or the help of the given subcommand(s)

サブコマンド record の使い方

$ py-spy record -h

py-spy-record
Records stack trace information to a flamegraph, speedscope or raw file

USAGE:
    py-spy record [OPTIONS] [python_program]...

ARGS:
    <python_program>...    commandline of a python program to run

OPTIONS:
    -p, --pid <pid>              PID of a running python program to spy on
        --full-filenames         Show full Python filenames, instead of shortening to show only the package part
    -o, --output <filename>      Output filename
    -f, --format <format>        Output file format [default: flamegraph] [possible values: flamegraph, raw, speedscope]
    -d, --duration <duration>    The number of seconds to sample for [default: unlimited]
    -r, --rate <rate>            The number of samples to collect per second [default: 100]
    -s, --subprocesses           Profile subprocesses of the original process
    -F, --function               Aggregate samples by function's first line number, instead of current line number
        --nolineno               Do not show line numbers
    -t, --threads                Show thread ids in the output
    -g, --gil                    Only include traces that are holding on to the GIL
    -i, --idle                   Include stack traces for idle threads
        --nonblocking            Don't pause the python process when collecting samples. Setting this option will reduce the performance impact of sampling, but may lead to inaccurate results
    -h, --help                   Print help information

この辺に従って、 v0.3.14 での実行方法はコレ。

$ sudo py-spy record -- python summarize.py

py-spy> Sampling process 100 times a second. Press Control-C to exit.

^C
Traceback (most recent call last):
  File "/Users/george/dev/take4mats/cidrs/summarize.py", line 5, in <module>
    for l in sys.stdin:
KeyboardInterrupt

py-spy> Stopped sampling because Control-C pressed
py-spy> Wrote flamegraph data to 'python-2024-03-25T09:27:23+09:00.svg'. Samples: 2020 Errors: 0
python-2024-03-25T09:27:23+09:00.svg

すると、 flamegraph が svg で出力されるのでブラウザで開いてみると、こんな感じ。

py-spy flamegraph