Python デコレーター は、祝福にも呪いにもなり得ます。DRYでモジュール化されたコードを書くのに役立つ一方で、特にチェーン化したり、本番システムで盲目的に適用したりすると、パフォーマンスの問題 を隠してしまう可能性があります。

この記事では、デコレーターのプロファイリング方法、使用するツール、探すべきシグナルについて説明します。これにより、Pythonアプリケーションでのデコレーターによる速度低下を回避できます。

プロファイリング デコレーターが重要な理由

単一のデコレーターは、ほんの数マイクロ秒のオーバーヘッドしか追加しない可能性があります。しかし、以下の場合:

  • 複数のデコレーターをチェーンする
     

  • 1秒間に数千回も関数を呼び出す
     

  • ロギング、トレース、再試行、メトリクスなどを追加する

…影響は急速に大きくなります。

データベースやCPUのせいにする前に、デコレーターを確認してください。それがサイレントボトルネックになっている可能性があります。すべてがこの記事で詳しく説明されています。

使用できるツール

1. timeit:迅速で簡単なベンチマーク

マイクロベンチマークに最適です。

import timeit

def raw():
    return 42

@my_custom_decorator
def decorated():
    return 42

print("Raw:", timeit.timeit(raw, number=100000))
print("Decorated:",
timeit.timeit(decorated, number=100000))

2. cProfile + Snakeviz:時間がどこで使われているかを確認する

python -m cProfile -o output.prof myscript.py
snakeviz output.prof

関数呼び出しのタイミングの視覚的な内訳が表示されます。デコレーターが高価な場合は、呼び出しツリーに表示されます。

3. line_profiler:特定の行を詳細に調べる

pipでインストールします。

pip install line_profiler

@profileを使用して関数をマークします。

@profile
@log_execution
def process_data(): ...

次に実行します。

kernprof -l -v myscript.py

デコレートされた関数内の遅い行を特定するのに最適です。

4. Py-Spy:非侵入型のリアルタイムプロファイリング

アプリケーションを実行して、以下を使用します。

py-spy top --pid

または、フレームグラフを生成します。

py-spy record -o profile.svg --pid

デコレーターがオーバーヘッドを追加するが、コードベースに直接表示されない場合(メタプログラミングなど)に役立ちます。

注意すべき危険信号

  • I/O、ロギング、またはデータベースへのアクセスを実行するデコレーター。
     

  • 大きなキャッシュサイズでfunctools.lru_cacheを過剰に使用すること。
     

  • 例外をサイレントに無視するtry/exceptを持つデコレーター。
     

  • タイムアウトが切れるのを待って実行をブロックする@retryまたは@timeout デコレーター。
     

  • *args, **kwargsを効率的に渡さないラッパー。

プロのヒント

  • ホットパス(HTTPハンドラーやタイトループなど)でのデコレーターを最小限に抑えます。
     

  • デコレーターの有無でテストして、実際のオーバーヘッドを確認します。
     

  • ローカルと本番環境で、実際のトラフィックパターンを使用してプロファイルします。