👋

Pythonのプロファイラ cProfile でコードのボトルネックを調べる際に使うコマンド集

に公開

リファレンス:
https://docs.python.org/3.14/library/profile.html

  1. 構文
python -m cProfile [-o output_file] [-s sort_order] (-m module | myscript.py)
<sort_order>に指定できる値
引数 enum 引数 意味
'calls' SortKey.CALLS 呼び出し回数
'cumulative' SortKey.CUMULATIVE 累積時間
'cumtime' 累積時間
'file' ファイル名
'filename' SortKey.FILENAME ファイル名
'module' ファイル名
'ncalls' 呼び出し回数
'pcalls' SortKey.PCALLS プリミティブ呼び出し回数
'line' SortKey.LINE 行番号
'name' SortKey.NAME 関数名
'nfl' SortKey.NFL 名前/ファイル/行番号
'stdname' SortKey.STDNAME 標準名
'time' SortKey.TIME 内部時間
'tottime' 内部時間

https://docs.python.org/3.14/library/profile.html#pstats.Stats.sort_stats

  1. 累積時間順に表示
python -m cProfile -s cumtime main.py
  1. 呼び出し回数順に表示(どの関数が何回呼ばれてるか確認)
python -m cProfile -s calls main.py
  1. ファイル名順でソート(特定ファイルのプロファイリング結果を俯瞰)
python -m cProfile -s filename main.py
  1. 関数名順でソート(アルファベット順でざっと見るときに)
python -m cProfile -s name main.py
  1. プリミティブ呼び出し回数順でソート(再帰やラッパー除外して純粋な呼び出し回数把握)
python -m cProfile -s pcalls main.py
  1. 結果をファイルに保存する
python -m cProfile -o out.prof main.py
  1. モジュールを直接プロファイリング(スクリプトじゃなくモジュールを測りたいとき)
python -m cProfile -s tottime -m your_module_name
  1. pstatsを使って分析
# 先頭20件を表示する
python - <<EOF
import pstats
ps = pstats.Stats('out.prof')
ps.strip_dirs().sort_stats('time').print_stats(20)
EOF

# 主要関数だけピンポイントで見る(正規表現でフィルタリング)
python -m cProfile -o out.prof main.py
python - <<EOF
import pstats
ps = pstats.Stats('out.prof')
ps.strip_dirs().sort_stats('cumtime').print_stats('<your_function>')
EOF

# リストに挙げた関数だけを、呼び出し回数順に表示する
python - <<'EOF'
import pstats
ps = pstats.Stats('out.prof')
ps.strip_dirs() \
  .sort_stats('calls') \
  .print_stats(('funcA', 'funcB', 'Class.method'))
EOF

# Callers: 関数 my_module.my_func をどこから呼んでいるかを調べる
python - <<'EOF'
import pstats
ps = pstats.Stats('out.prof')
ps.strip_dirs() \
  .sort_stats('time') \
  .print_callers('my_module.my_func')
EOF

# Callees: 関数 my_module.my_func の内部でさらにどの関数を呼んでいるか調べる
python - <<'EOF'
import pstats
ps = pstats.Stats('out.prof')
ps.strip_dirs() \
  .sort_stats('time') \
  .print_callees('my_module.my_func')
EOF

# 再帰呼び出しの除外
python - <<'EOF'
import pstats
ps = pstats.Stats('out.prof').strip_dirs()
ps.sort_stats('cumtime', 'ncalls')
ps.print_stats(50)
EOF
  1. プロファイルをCSV/TSV 形式でエクスポートする
python - <<'EOF' > stats.tsv
import pstats, csv
ps = pstats.Stats('out.prof').strip_dirs().sort_stats('cumulative')
writer = csv.writer(open('stats.tsv','w',newline=''), delimiter='\t')
writer.writerow(['func','ncalls','tottime','cumtime'])
for func, (nc, pn, tt, ct, callers) in ps.stats.items():
    writer.writerow([f"{func[1]}:{func[2]}", nc, f"{tt:.6f}", f"{ct:.6f}"])
EOF
  1. ワンライナー引数対応スクリプト
python - <<'EOF'
import sys, pstats
key = sys.argv[1] if len(sys.argv) > 1 else 'cumtime'
n   = int(sys.argv[2]) if len(sys.argv) > 2 else 20
ps = pstats.Stats('out.prof').strip_dirs().sort_stats(key)
ps.print_stats(n)
EOF cumtime 30
  1. snakevizを使ってブラウザUIでプロファイリング結果を可視化する
# poetry add --dev snakeviz # 標準モジュールではないので追加が必要
snakeviz out.prof

alt text

以上です。

Discussion