"profiling.tracing" --- 決定論的プロファイラ
********************************************

Added in version 3.15.

**ソースコード:** Lib/profiling/tracing/

======================================================================

The "profiling.tracing" module provides deterministic profiling of
Python programs. It monitors every function call, function return, and
exception event, recording precise timing for each. This approach
provides exact call counts and complete visibility into program
execution, making it ideal for development and testing scenarios.

注釈:

  後方互換性のため、このモジュールは "cProfile" としても利用できます。
  "cProfile" という名前は、今後のすべての Python バージョンでも引き続
  き使えます。 コードベースに合ったインポート方法を使ってください。

     # 推奨する新しい形式
     import profiling.tracing
     profiling.tracing.run('my_function()')

     # こちらも動作する (後方互換性あり)
     import cProfile
     cProfile.run('my_function()')


決定論的プロファイリングとは
============================

*Deterministic profiling* captures every function call, function
return, and exception event during program execution. The profiler
measures the precise time intervals between these events, providing
exact statistics about how the program behaves.

In contrast to statistical profiling, which samples the call stack
periodically to estimate where time is spent, deterministic profiling
records every event. This means you get exact call counts rather than
statistical approximations. The trade-off is that instrumenting every
event introduces overhead that can slow down program execution.

Python はインタープリタで実行する言語なので、決定論的プロファイリング
を実用的に行えます。 インタープリタは関数の呼び出しとリターンのイベン
トをすでに通知しているため、プロファイラはコードを変更せずにこの仕組み
を利用できます。 インタープリタによる実行自体のコストと比べるとオーバ
ーヘッドは比較的小さく、ほとんどの開発作業に適しています。

決定論的プロファイリングは、次のような疑問への答えを得るのに役立ちます
。

* この関数は何回呼び出されたか。

* プログラム全体の呼び出しグラフはどうなっているか。

* ある関数はどの関数を呼び出しているか。

* 予期しない関数呼び出しが起きていないか。

呼び出し回数の統計は、想定外の回数からバグを見つけたり、回数が多い箇所
からインライン展開の候補を見つけたりするのに役立ちます。 関数内部の実
行時間の統計は、最適化すべき「ホットループ」を明らかにします。 累積時
間の統計は、アルゴリズムの非効率な箇所を見つけるのに役立ちます。 この
プロファイラの累積時間の扱い方により、再帰的な実装と反復的な実装を直接
比較できます。


コマンドライン・インターフェース
================================

"profiling.tracing" モジュールをスクリプトとして実行し、別のスクリプト
やモジュールをプロファイリングできます。

   python -m profiling.tracing [-o output_file] [-s sort_order] (-m module | script.py)

指定したスクリプトまたはモジュールをプロファイラの下で実行し、結果を標
準出力に表示します (またはファイルに保存します)。

-o <output_file>

   プロファイリング結果を標準出力の代わりにファイルに書き込みます。 こ
   のファイルは "pstats" モジュールで読み込み、後から分析できます。

-s <sort_order>

   指定したキーで出力をソートします。 "cumulative"、"time"、"calls"、
   "name" など、"pstats.Stats.sort_stats()" が認識するすべてのソートキ
   ーを指定できます。 このオプションは "-o" を指定していない場合だけ適
   用されます。

-m <module>

   スクリプトの代わりにモジュールをプロファイリングします。 モジュール
   は標準のインポートの仕組みで探します。

   Added in version 3.7: "cProfile" の "-m" オプション。

   Added in version 3.8: "profile" の "-m" オプション。


プログラムからの使用例
======================

プロファイリングをより細かく制御するには、モジュールの関数とクラスを直
接使います。


基本的なプロファイリング
------------------------

最も簡単なのは "run()" 関数を使う方法です。

   import profiling.tracing
   profiling.tracing.run('my_function()')

指定したコード文字列をプロファイリングし、概要を標準出力に表示します。
後で分析するために結果を保存するには、次のようにします。

   profiling.tracing.run('my_function()', 'output.prof')


"Profile" クラスを使う
----------------------

"Profile" クラスを使うと、細かく制御できます。

   import profiling.tracing
   import pstats
   from io import StringIO

   pr = profiling.tracing.Profile()
   pr.enable()
   # ... プロファイリングするコード ...
   pr.disable()

   # 結果を表示する。
   s = StringIO()
   ps = pstats.Stats(pr, stream=s).sort_stats(pstats.SortKey.CUMULATIVE)
   ps.print_stats()
   print(s.getvalue())

"Profile" クラスはコンテキストマネージャーとしても使えます。

   import profiling.tracing

   with profiling.tracing.Profile() as pr:
       # ... プロファイリングするコード ...

   pr.print_stats()


モジュールリファレンス
======================

profiling.tracing.run(command, filename=None, sort=-1)

   コマンドの実行をプロファイリングし、結果を表示または保存します。

   この関数は "exec()" を使い、"__main__" モジュールの名前空間で
   *command* 文字列を実行します。

      exec(command, __main__.__dict__, __main__.__dict__)

   *filename* を指定しない場合、この関数は "pstats.Stats" インスタンス
   を作成して概要を標準出力に表示します。 *filename* を指定した場合は
   、生のプロファイリングデータをそのファイルに保存し、後で "pstats"
   を使って分析できます。

   *sort* 引数は表示する出力のソート順を指定します。
   "pstats.Stats.sort_stats()" が認識する任意の値を指定できます。

profiling.tracing.runctx(command, globals, locals, filename=None, sort=-1)

   名前空間を明示的に指定して、コマンドの実行をプロファイリングします
   。

   "run()" と同様ですが、"__main__" モジュールの名前空間の代わりに、指
   定した *globals* と *locals* のマッピングを使ってコマンドを実行しま
   す。

      exec(command, globals, locals)

class profiling.tracing.Profile(timer=None, timeunit=0.0, subcalls=True, builtins=True)

   実行統計を収集するプロファイラオブジェクトです。

   省略可能な *timer* 引数は、カスタムの時刻取得用関数を指定します。
   指定しない場合、プロファイラはプラットフォームに適したデフォルトの
   タイマーを使います。 カスタムの時刻取得用関数は、現在時刻を表す単一
   の数値を返さなければなりません。 整数を返す場合は、*timeunit* で 1
   単位の長さを指定します (例えばミリ秒なら "0.001")。

   *subcalls* 引数は、関数間の呼び出し関係を追跡するかどうかを制御しま
   す。 *builtins* 引数は、組み込み関数をプロファイリングするかどうか
   を制御します。

   バージョン 3.8 で変更: コンテキストマネージャーサポートが追加されま
   した。

   enable()

      プロファイリングデータの収集を開始します。

   disable()

      プロファイリングデータの収集を停止します。

   create_stats()

      データの収集を停止し、結果を現在のプロファイルとして内部に記録し
      ます。

   print_stats(sort=-1)

      現在のプロファイルから "pstats.Stats" オブジェクトを作成し、結果
      を標準出力に表示します。

      *sort* 引数はソート順を指定します。 単一のキーまたは複数段階のソ
      ート用のキータプルを指定でき、値は "pstats.Stats.sort_stats()"
      と同じものを使います。

      Added in version 3.13: ソートキーのタプルをサポートしました。

   dump_stats(filename)

      現在のプロファイルデータを *filename* に書き込みます。 このファ
      イルは "pstats.Stats" で読み込み、後から分析できます。

   run(cmd)

      "exec()" を通じてコマンド文字列をプロファイリングします。

   runctx(cmd, globals, locals)

      指定した名前空間を使い、"exec()" を通じてコマンド文字列をプロフ
      ァイリングします。

   runcall(func, /, *args, **kwargs)

      関数呼び出しをプロファイリングします。 *func* が返す値をそのまま
      返します。

         result = pr.runcall(my_function, arg1, arg2, keyword=value)

注釈:

  Profiling requires that the profiled code returns normally. If the
  interpreter terminates (for example, via "sys.exit()") during
  profiling, no results will be available.


カスタムな時刻取得用関数を使う
==============================

"Profile" クラスはカスタムの時刻取得用関数を受け取れるため、実経過時間
や CPU 時間など、実行のさまざまな側面を測定できます。 コンストラクタに
時刻取得用関数を渡してください。

   pr = profiling.tracing.Profile(my_timer_function)

時刻取得用関数は、現在時刻を表す単一の数値を返さなければなりません。
整数を返す場合は、1 単位の長さを表す *timeunit* も指定してください。

   # 時刻取得用関数はミリ秒単位の時刻を返す。
   pr = profiling.tracing.Profile(my_ms_timer, 0.001)

最高の性能を得るには、時刻取得用関数をできるだけ高速にしてください。
プロファイラはこの関数を頻繁に呼び出すため、そのオーバーヘッドはプロフ
ァイリングのオーバーヘッドに直接影響します。

"time" モジュールは、カスタムの時刻取得用関数に適した関数をいくつか提
供しています。

* "time.perf_counter()": 高分解能の実経過時間

* "time.process_time()": CPU 時間 (スリープを除く)

* "time.monotonic()": 単調増加するクロックの時刻


制限事項
========

決定論的プロファイリングには、時間測定の精度に関する本質的な制約があり
ます。

The underlying timer typically has a resolution of about one
millisecond. Measurements cannot be more accurate than this
resolution. With enough measurements, timing errors tend to average
out, but individual measurements may be imprecise.

There is also latency between when an event occurs and when the
profiler captures the timestamp. Similarly, there is latency after
reading the timestamp before user code resumes. Functions called
frequently accumulate this latency, which can make them appear slower
than they actually are. This error is typically less than one clock
tick per call but can become significant for functions called many
times.

"profiling.tracing" モジュール (およびその別名 "cProfile") は、オーバ
ーヘッドの小さい C 拡張として実装されています。 そのため、非推奨の純粋
な Python による "profile" モジュールよりも、こうした時間測定の問題は
目立ちません。

参考:

  "profiling"
     Python のプロファイリングツールの概要と、プロファイラの選び方。

  "profiling.sampling"
     本番環境向けの統計的サンプリングプロファイラ。

  "pstats"
     プロファイリングデータの統計分析と書式化。

  "profile"
     非推奨の純粋な Python によるプロファイラ (補正方法のドキュメント
     を含む)。
