py-spy のすすめ

## [py-spy](https://github.com/benfred/py-spy) について
Rustで書かれた軽量のPythonのprofiling toolsです。
プログラムがどこで重い処理をしているか関数ごとに確認できるのでパフォーマンス改善に役に立ちます。

Pythonのbuildin profilerの[cProfileやprofile](https://docs.python.org/ja/3.13/library/profile.html)もありますが、プログラム全体で何%その関数が使用しているかわかりにくかったのでpy-spyを試したところ全体で何%関数が使用しているとわかりやすく出力してくれたのでありがたかったです。

Linux, Windows, Mac OS でも動作するようです。

## [install](https://github.com/benfred/py-spy#installation)
- pip
- github releases page
- Homebrew

などさまざまな方法でinstallできるようです。
私はDocker内のモジュールを調べたかったので pip で入れました。

## 機能
py-spyでhelpを実行
```sh
py-spy --help
py-spy 0.4.0
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
プログラムの実行前に起動しprofile結果を記録してsvgなどで出力してくれる。

```sh
py-spy record -o {任意のファイル名}.svg --pid 12345
# OR
py-spy record -o {任意のファイル名}.svg -- python {something}.py
```
#### --help
```sh
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, chrometrace]
    -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
```
`ps aux`などで pid を調べて入力

### top
リアルタイムにプログラム内で使用している関数がどの程度CPUを使用しているか出力してくれます。Unixのtopコマンドのようです。
```sh
py-spy top --pid 12345
# OR
py-spy top -- python myprogram.py
```
#### --help
```sh
py-spy-top 
Displays a top like view of functions consuming CPU

USAGE:
    py-spy top [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
    -r, --rate <rate>        The number of samples to collect per second [default: 100]
    -s, --subprocesses       Profile subprocesses of the original process
        --full-filenames     Show full Python filenames, instead of shortening to show only the package part
    -g, --gil                Only include traces that are holding on to the GIL
    -i, --idle               Include stack traces for idle threads
        --delay <seconds>    Delay between 'top' refreshes. [default: 1.0]
        --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
```
### dump
各スレッドのコールスタックやその他の基本的なプロセス情報をコンソールに出力してくれる。
```sh
py-spy dump --pid 12345
```

#### --help
```sh
py-spy-dump 
Dumps stack traces for a target program to stdout

USAGE:
    py-spy dump [OPTIONS] --pid <pid>

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
    -l, --locals            Show local variables for each frame. Passing multiple times (-ll) increases verbosity
    -j, --json              Format output as JSON
    -s, --subprocesses      Profile subprocesses of the original process
        --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
```
## [Dockerでの使用](https://github.com/benfred/py-spy?tab=readme-ov-file#how-do-i-run-py-spy-in-docker)
Dockerで使用する場合は
```sh
runの時に --cap-add SYS_PTRACE
```
compose ymal file
```sh
your_service:
   cap_add:
     - SYS_PTRACE
```
を追加する

## [FrameGraph](https://github.com/brendangregg/FlameGraph)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643403/86da558b-242a-48d3-84ad-8a6203f0fb44.png)
これは上記リンクのサンプル

(https://github.com/brendangregg/FlameGraph)
recordの機能で出力されるfileはsvgだけでなく、[default: flamegraph] [possible values: flamegraph, raw, speedscope, chrometrace]の中から選べます。

svgで出力されるflamegraphが便利で、svgを開くとブラウザで確認できます。
上から親関数でスタック上に子関数が下に積み上がっており、深いほど処理が複雑で、横幅が多いほどCPU使用率が高いです。
関数をクリックすると全体で何%使用しているのかやズームして確認できます。

### まとめ
チームでシェアする場面や、先方に報告する場面でとても重宝します。プロファイリングは初めて行いましたが、内部で意識できない関数が動作していることが可視化されて興味深かったです。

お試しください。