0Pricing
Python Academy · レッスン

cProfile と line_profiler によるプロファイリング

cProfile と行単位のプロファイリングで CPU のボトルネックを見つけます。

「cProfile と line_profiler によるプロファイリング」はCoddyKit上の無料Python Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPython Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Python Academyコースには全4レッスンが含まれています。

プロファイリングの目的

プロファイリングによって、どの関数が最も多くの時間を消費しているかを特定し、最適化の方針を決められます。最適化の前には必ずプロファイリングを行い、決して推測だけで進めないでください。

import cProfile

def slow():
    total = 0
    for i in range(1_000_000):
        total += i
    return total

cProfile.run("slow()")

CLIからのcProfileの実行

スクリプト全体を変更せずにプロファイルするには、python -m cProfile -s cumtime script.pyを実行します。cumtime、tottime、またはcallsで並べ替えます。

# python -m cProfile -s cumtime my_script.py
#
# ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#   1000    0.500    0.001    2.100    0.002 utils.py:10(process)

コード内でのcProfile

cProfile.Profileを作成して有効化・無効化し、その後pstatsで統計情報を出力します。

import cProfile, pstats, io

pr = cProfile.Profile()
pr.enable()

# ... code to profile ...

pr.disable()
stream = io.StringIO()
ps = pstats.Stats(pr, stream=stream).sort_stats("cumulative")
ps.print_stats(10)   # top 10
print(stream.getvalue())

pstatsによるフィルタリング

print_stats(pattern)を使って、プロファイラの出力を特定のモジュールまたは関数名のパターンに絞り込みます。

import cProfile, pstats

cProfile.run("my_function()", "profile.out")
stats = pstats.Stats("profile.out")
stats.sort_stats("tottime")
stats.print_stats("mymodule")   # only mymodule functions

line_profiler

line_profilerは関数単位だけでなく、行ごとの実行時間を表示します。関数内で最も時間のかかる行を見つけるために不可欠です。

# pip install line_profiler
# Decorate target function:
from line_profiler import profile

@profile
def process(data):
    result = []
    for item in data:          # <- which line is slow?
        result.append(item*2)
    return result

# Run: kernprof -l -v script.py

kernprof CLI

kernprof -l script.pyは行プロファイリングを有効にしてスクリプトを実行します。-vを指定すると、レポートをすぐに表示します。

# kernprof -l -v script.py
#
# Line #  Hits  Time  Per Hit  % Time  Line Contents
# ======================================================
#     4    1  2.0   2.0       1.0    result = []
#     5  1000  120.0  0.1     60.0    for item in data:
#     6  1000   80.0  0.1     39.0        result.append(item*2)

Jupyterでのプロファイリング

Jupyterでは、%prun(cProfile)と%lprun(line_profiler)のマジックコマンドを利用できます。

# In a Jupyter cell:
# %prun -s cumulative my_function(data)

# %load_ext line_profiler
# %lprun -f my_function my_function(data)

ホットスポットの特定

累積時間(呼び出しの連鎖全体)が最も大きい関数と、合計時間(呼び出し先を除いた関数自身の時間)が最も大きい関数に注目します。

# cumtime = total time including callees (find root cause)
# tottime = time in function itself (find where work happens)
#
# Optimise the function with highest tottime first

早すぎる最適化を避ける

まずプロファイリングを行い、測定で特定したボトルネックを最適化します。Pythonでよく使われる高速化手法には、組み込み関数の利用、ループのNumPyへの移行、繰り返し行う検索のキャッシュ、ctypesまたはcffiを介したCの呼び出しがあります。

# Before optimising:
# profile shows: process_row() 95% of time

# Speedup: vectorise with NumPy
import numpy as np
arr = np.array(data)
result = arr * 2   # 100x faster than Python loop

py-spy:サンプリングプロファイラ

py-spyはコードを変更せずに実行中のプロセスをプロファイリングできます。実行中のPIDにアタッチして使用します。

# pip install py-spy

# Profile for 30 s and show flamegraph:
# py-spy top --pid 12345
# py-spy record -o profile.svg --pid 12345 --duration 30

timeitによるベンチマーク

特定の式のマイクロベンチマークにはtimeitを使用します。

import timeit

result = timeit.timeit(
    "[x*2 for x in range(1000)]",
    number=10_000
)
print(f"{result:.3f} s for 10k runs")

確認問題

cProfileのレポートでtottimeは何を示しますか。

まとめ

cProfileで時間のかかる関数を見つけ、line_profilerで時間のかかる行を見つけます。根本原因を特定するには-s cumtimeを指定してプロファイリングします。最適化するのは実際にホットスポットだと確認できた箇所だけにし、最大の効果が得られるNumPyのベクトル化、キャッシュ、C拡張などを利用します。

よくある質問

「cProfile と line_profiler によるプロファイリング」レッスンは無料ですか?

はい。「cProfile と line_profiler によるプロファイリング」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Python Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Python Academyコースには全4レッスンが含まれています。

「cProfile と line_profiler によるプロファイリング」で何を学びますか?

cProfile と行単位のプロファイリングで CPU のボトルネックを見つけます。 ブラウザで直接実行するハンズオンコードでPython Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Python Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのPython Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「cProfile と line_profiler によるプロファイリング」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このPython Academyレッスンでコードを書いて実行できますか?

はい。すべてのPython Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. CPython の参照カウント
  2. ガベージコレクターと循環参照
  3. cProfile と line_profiler によるプロファイリング
  4. tracemalloc によるメモリープロファイリング
← Python Academyに戻る