26.4. Python プロファイラ
*************************

**ソースコード:** Lib/profile.py と Lib/pstats.py

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


26.4.1. プロファイラとは
========================

"cProfile" と "profile" は *決定論的プロファイリング (deterministic
profiling)* を行います。 *プロファイル (profile)* とは、プログラムの各
部分がどれだけ頻繁に呼ばれたか、そして実行にどれだけ時間がかかったかと
いう統計情報です。 "pstats" モジュールを使ってこの統計情報をフォーマッ
トし表示することができます。

Python 標準ライブラリは同じインターフェイスを提供するプロファイラの実
装を3つ提供しています。

1. "cProfile" はほとんどのユーザーに推奨されるモジュールです。 C言
   語で 書かれた拡張モジュールで、オーバーヘッドが少ないため長時間実行
   され るプログラムのプロファイルに適しています。 Brett Rosen と Ted
   Czotter によって提供された "lsprof" に基づいています。

   バージョン 2.5 で追加.

2. "profile" はピュア Python モジュールで、 "cProfile" モジュールは
   こ のモジュールのインタフェースを真似ています。対象プログラムに相当
   の オーバーヘッドが生じます。もしプロファイラに何らかの拡張をしたい
   の であれば、こちらのモジュールを拡張する方が簡単でしょう。このモジ
   ュ ールはもともと Jim Roskind により設計、実装されました。

   バージョン 2.4 で変更: 組み込みの関数やメソッドで消費された時間も報
   告するようになりました。

3. "hotshot" は、後処理時間の長さと引き換えにプロファイル中のオーバ
   ー ヘッドを小さくすることに主眼を置いた実験的な C モジュールでした
   。こ のモジュールはもう保守されておらず、将来のバージョンの Python
   から は外されるかもしれません。

   バージョン 2.5 で変更: 以前より意味のある結果が得られているはずです
   。かつては時間計測の中核部分に致命的なバグがありました.

"profile" と "cProfile" の両モジュールは同じインタフェースを提供してい
るので、ほぼ取り替え可能です。 "cProfile" はずっと小さなオーバーヘッド
で動きますが、まだ新しく、全てのシステムで使えるとは限らないでしょう。
"cProfile" は実際には内部モジュール "_lsprof" に被せられた互換性レイヤ
です。 "hotshot" モジュールは特別な使い道のために取っておいてあります
。

注釈: 2つのプロファイラモジュールは、与えられたプログラムの実行時プ
  ロファ イルを取得するために設計されており、ベンチマークのためのもの
  ではあり ません (ベンチマーク用途には "timeit" のほうが正確な計測結
  果を求めら れます)。これは Python コードと C で書かれたコードをベン
  チマークする ときに特に大きく影響します。プロファイラは Python コー
  ドに対してオー バーヘッドを発生しますが、C言語レベルの関数に対しては
  オーバーヘッド を生じません。なのでC言語で書かれたコードが、実際の速
  度と関係なく、 Pythonで書かれたコードより速く見えるでしょう。


26.4.2. かんたんユーザマニュアル
================================

この節は "マニュアルなんか読みたくない人"のために書かれています。ここ
ではきわめて簡単な概要説明とアプリケーションのプロファイリングを手っ取
り早く行う方法だけを解説します。

1つの引数を取る関数をプロファイルしたい場合、次のようにできます:

   import cProfile
   import re
   cProfile.run('re.compile("foo|bar")')

(お使いのシステムで "cProfile" が使えないときは代わりに "profile" を使
って下さい)

上のコードは "re.compile()" を実行して、プロファイル結果を次のように表
示します:

         197 function calls (192 primitive calls) in 0.002 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.001    0.001 <string>:1(<module>)
        1    0.000    0.000    0.001    0.001 re.py:212(compile)
        1    0.000    0.000    0.001    0.001 re.py:268(_compile)
        1    0.000    0.000    0.000    0.000 sre_compile.py:172(_compile_charset)
        1    0.000    0.000    0.000    0.000 sre_compile.py:201(_optimize_charset)
        4    0.000    0.000    0.000    0.000 sre_compile.py:25(_identityfunction)
      3/1    0.000    0.000    0.000    0.000 sre_compile.py:33(_compile)

最初の行は 197 回の呼び出しを測定したことを示しています。その中で 192
回は *プリミティブ* です。すなわち呼び出しが再帰によってなされてはいま
せん。次の行は "Ordered by: standard name" は一番右の列の文字列が出力
のソートに用いられたことを示します。列の見出しは以下を含みます:

ncalls
   呼び出し回数、

tottime
   与えられた関数に消費された合計時間 (sub-function の呼び出しで消費さ
   れた時間は除外されています)

percall
   "tottime" を "ncalls" で割った値

cumtime
   この関数と全ての subfunction に消費された累積時間 (起動から終了まで
   )。この数字は再帰関数に *ついても* 正確です。

percall
   "cumtime" をプリミティブな呼び出し回数で割った値

filename:lineno(function)
   その関数のファイル名、行番号、関数名

最初の行に 2 つの数字がある場合 (たとえば "3/1") は、関数が再帰的に呼
び出されたということです。2 つ目の数字はプリミティブな呼び出しの回数で
、1 つ目の数字は総呼び出し回数です。関数が再帰的に呼び出されなかった場
合、それらは同じ値で数字は 1 つしか表示されないことに留意してください
。

"run()" 関数でファイル名を指定することで、プロファイル実行の終了時に出
力を表示する代わりに、ファイルに保存することが出来ます。

   import cProfile
   import re
   cProfile.run('re.compile("foo|bar")', 'restats')

"pstats.Stats" クラスはファイルからプロファイルの結果を読み込んで様々
な書式に整えます。

ファイル "cProfile" は他のスクリプトをプロファイルするためのスクリプト
として起動することも出来ます。例えば:

   python -m cProfile [-o output_file] [-s sort_order] myscript.py

"-o" はプロファイルの結果を標準出力の代わりにファイルに書き出します。

"-s" は出力を "sort_stats()" で出力をソートする値を指定します。"-o" が
ない場合に有効です。

"pstats" モジュールの "Stats" クラスにはプロファイル結果のファイルに保
存されているデータを処理して出力するための様々なメソッドがあります。

   import pstats
   p = pstats.Stats('restats')
   p.strip_dirs().sort_stats(-1).print_stats()

"strip_dirs()" メソッドによって全モジュール名から無関係なパスが取り除
かれました。"sort_stats()" メソッドにより、出力される標準的な モジュー
ル/行/名前 文字列にしたがって全項目がソートされました。"print_stats()"
メソッドによって全統計が出力されました。以下のようなソート呼び出しを試
すことができます:

   p.sort_stats('name')
   p.print_stats()

最初の行ではリストを関数名でソートしています。2行目で情報を出力してい
ます。さらに次の内容も試してください:

   p.sort_stats('cumulative').print_stats(10)

このようにすると、関数が消費した累計時間でソートして、さらにその上位10
件だけを表示します。どのアルゴリズムが時間を多く消費しているのか知りた
いときは、この方法が役に立つはずです。

ループで多くの時間を消費している関数はどれか調べたいときは、次のように
します:

   p.sort_stats('time').print_stats(10)

上記はそれぞれの関数で消費された時間でソートして、上位10件の関数の情報
が表示されます。

次の内容も試してください:

   p.sort_stats('file').print_stats('__init__')

このようにするとファイル名でソートされ、そのうちクラスの初期化メソッド
(メソッド名 "__init__") に関する統計情報だけが表示されます:

   p.sort_stats('time', 'cum').print_stats(.5, 'init')

上記は時間 (time) をプライマリキー、累計時間 (cumulative time) をセカ
ンダリキーにしてソートした後でさらに条件を絞って統計情報を出力します。
".5" は上位 50% だけを選択することを意味し、さらにその中から文字列
"init" を含むものだけが表示されます。

どの関数がどの関数を呼び出しているのかを知りたければ、次のようにします
("p" は最後に実行したときの状態でソートされています):

   p.print_callers(.5, 'init')

このようにすると、関数ごとの呼び出し側関数の一覧が得られます。

さらに詳しい機能を知りたければマニュアルを読むか、次の関数の実行結果か
ら内容を推察してください:

   p.print_callees()
   p.add('restats')

スクリプトとして起動した場合、 "pstats" モジュールはプロファイルのダン
プを読み込み、分析するための統計ブラウザとして動きます。シンプルな行指
向のインタフェース ("cmd" を使って実装) とヘルプ機能を備えています。


26.4.3. リファレンスマニュアル -- "profile" と "cProfile"
=========================================================

"profile" および "cProfile" モジュールは以下の関数を提供します:

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

   この関数は "exec" 文に渡せる一つの引数と、オプション引数としてファ
   イル名を指定できます。全ての場合でこのルーチンは以下を実行します:

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

   そして実行結果からプロファイル統計情報を収集します。ファイル名が指
   定されていない場合は "Stats" のインスタンスが自動的に作られ、簡単な
   レポートが表示されます。 *sort* が指定されている場合これはこの
   "Stats" インスタンスに渡され、結果をどのように並び替えるかを制御し
   ます。

profile.runctx(command, globals, locals, filename=None)

   この関数は "run()" に似ていますが、 globals、 locals 辞書を
   *command* のために与えるための追加引数を取ります。このルーチンは以
   下を実行します:

      exec(command, globals, locals)

   そしてプロファイル統計情報を "run()" 同様に収集します。

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

   このクラスは普通、プロファイリングを "cProfile.run()" 関数が提供す
   るもの以上に正確に制御したい場合にのみ使われます。

   *timer* 引数に、コードの実行時間を計測するためのカスタムな時刻取得
   用関数を渡せます。これは現在時刻を表す単一の数値を返す関数でなけれ
   ばなりません。もしこれが整数を返す関数ならば、 *timeunit* には単位
   時間当たりの実際の持続時間を指定します。たとえば関数が 1000 分の 1
   秒単位で計測した時間を返すとすると、 *timeunit* は ".001" でしょう
   。

   "Profile" クラスを直接使うと、プロファイルデータをファイルに書き出
   すことなしに結果をフォーマット出来ます:

      import cProfile, pstats, StringIO
      pr = cProfile.Profile()
      pr.enable()
      # ... do something ...
      pr.disable()
      s = StringIO.StringIO()
      sortby = 'cumulative'
      ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
      ps.print_stats()
      print s.getvalue()

   enable()

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

   disable()

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

   create_stats()

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

   print_stats(sort=-1)

      現在のプロファイルに基づいて "Stats" オブジェクトを作成し、結果
      を標準出力に出力します。

   dump_stats(filename)

      現在のプロファイルの結果を *filename* に書き出します。

   run(cmd)

      "exec()" を用いて cmd をプロファイルします。

   runctx(cmd, globals, locals)

      "exec()" を用いて指定されたグローバルおよびローカルな環境で cmd
      をプロファイルします。

   runcall(func, *args, **kwargs)

      "func(*args, **kwargs)" をプロファイルします。


26.4.4. "Stats" クラス
======================

"Stats" クラスを使用してプロファイラデータを解析します。

class pstats.Stats(*filenames or profile, stream=sys.stdout)

   このコンストラクタは *filename* で指定した (単一または複数の) ファ
   イルもしくは "Profile" のインスタンスから "統計情報オブジェクト" の
   インスタンスを生成します。出力は *stream* で指定したストリームに出
   力されます。

   The file selected by the above constructor must have been created
   by the corresponding version of "profile" or "cProfile".  To be
   specific, there is *no* file compatibility guaranteed with future
   versions of this profiler, and there is no compatibility with files
   produced by other profilers, or the same profiler run on a
   different operating system.  If several files are provided, all the
   statistics for identical functions will be coalesced, so that an
   overall view of several processes can be considered in a single
   report.  If additional files need to be combined with data in an
   existing "Stats" object, the "add()" method can be used.

   プロファイルデータをファイルから読み込む代わりに、
   "cProfile.Profile" または "profile.Profile" オブジェクトをプロファ
   イルデータのソースとして使うことができます。

   "Stats" には次のメソッドがあります:

   strip_dirs()

      "Stats" クラスのこのメソッドは、ファイル名の前に付いているすべて
      のパス情報を取り除くためのものです。出力の幅を80文字以内に収めた
      いときに重宝します。このメソッドはオブジェクトを変更するため、取
      り除いたパス情報は失われます。パス情報除去の操作後、オブジェクト
      が保持するデータエントリは、オブジェクトの初期化、ロード直後と同
      じように "ランダムに" 並んでいます。 "strip_dirs()" を実行した結
      果、2つの関数名が区別できない (両者が同じファイルの同じ行番号で
      同じ関数名となった) 場合、一つのエントリに合算されされます。

   add(*filenames)

      "Stats" クラスのこのメソッドは、既存のプロファイリングオブジェク
      トに情報を追加します。引数は対応するバージョンの "profile.run()"
      または "cProfile.run()" によって生成されたファイルの名前でなくて
      はなりません。関数の名前が区別できない (ファイル名、行番号、関数
      名が同じ) 場合、一つの関数の統計情報として合算されます。

   dump_stats(filename)

      "Stats" オブジェクトに読み込まれたデータを、ファイル名
      *filename* のファイルに保存します。ファイルが存在しない場合は新
      たに作成され、すでに存在する場合には上書きされます。このメソッド
      は "profile.Profile" クラスおよび "cProfile.Profile" クラスの同
      名のメソッドと等価です。

   バージョン 2.3 で追加.

   sort_stats(*keys)

      このメソッドは "Stats" オブジェクトを指定した基準に従ってソート
      します。典型的には引数にソートのキーにしたい項目を示す文字列を指
      定します (例: "'time'" や "'name'" など)。

      2つ以上のキーが指定された場合、2つ目以降のキーは、それ以前のキー
      で等価となったデータエントリの再ソートに使われます。たとえば
      "sort_stats('name', 'file')" とした場合、まずすべてのエントリが
      関数名でソートされた後、同じ関数名で複数のエントリがあればファイ
      ル名でソートされます。

      キー名には他のキーと判別可能である限り綴りを省略して名前を指定で
      きます。現在のバージョンで定義されているキー名は以下の通りです:

      +--------------------+------------------------+
      | 有効な引数         | 意味                   |
      +====================+========================+
      | "'calls'"          | 呼び出し数             |
      +--------------------+------------------------+
      | "'cumulative'"     | 累積時間               |
      +--------------------+------------------------+
      | "'cumtime'"        | 累積時間               |
      +--------------------+------------------------+
      | "'file'"           | ファイル名             |
      +--------------------+------------------------+
      | "'filename'"       | ファイル名             |
      +--------------------+------------------------+
      | "'module'"         | ファイル名             |
      +--------------------+------------------------+
      | "'ncalls'"         | 呼び出し数             |
      +--------------------+------------------------+
      | "'pcalls'"         | プリミティブな呼び出し |
      |                    | 回数                   |
      +--------------------+------------------------+
      | "'line'"           | 行番号                 |
      +--------------------+------------------------+
      | "'name'"           | 関数名                 |
      +--------------------+------------------------+
      | "'nfl'"            | 関数名/ファイル名/行番 |
      |                    | 号                     |
      +--------------------+------------------------+
      | "'stdname'"        | 標準名                 |
      +--------------------+------------------------+
      | "'time'"           | 内部時間               |
      +--------------------+------------------------+
      | "'tottime'"        | 内部時間               |
      +--------------------+------------------------+

      すべての統計情報のソート結果は降順 (最も多く時間を消費したものが
      一番上に来る) となることに注意してください。ただし、関数名、ファ
      イル名、行数に関しては昇順 (アルファベット順) になります。
      "'nfl'" と "'stdname'" には微妙な違いがあります。標準名
      (standard name) とは表示された名前によるソートで、埋め込まれた行
      番号のソート順が特殊です。たとえば、 (ファイル名が同じで) 3、20
      、40という行番号のエントリがあった場合、20、3、40 の順に表示され
      ます。一方 "'nfl'" は行番号を数値として比較します。要するに、
      "sort_stats('nfl')" は "sort_stats('name', 'file', 'line')" と指
      定した場合と同じになります。

      後方互換性のため、数値を引数に使った "-1", "0", "1", "2" の形式
      もサポートしています。それぞれ "'stdname'", "'calls'", "'time'",
      "'cumulative'" として処理されます。引数をこの旧スタイルで指定し
      た場合、最初のキー (数値キー) だけが使われ、複数のキーを指定して
      も2番目以降は無視されます。

   reverse_order()

      "Stats" クラスのこのメソッドは、オブジェクト内の情報のリストを逆
      順にソートします。デフォルトでは選択したキーに応じて昇順、降順が
      適切に選ばれることに注意してください。

   print_stats(*restrictions)

      "Stats" クラスのこのメソッドは、 "profile.run()" の項で述べたプ
      ロファイルのレポートを出力します。

      出力するデータの順序はオブジェクトに対し最後に行った
      "sort_stats()" による操作に基づきます ("add()" と "strip_dirs()"
      による制限にも支配されます)。

      引数は (もし与えられると) リストを重要なエントリのみに制限するた
      めに使われます。初期段階でリストはプロファイルした関数の完全な情
      報を持っています。制限の指定は、 (行数を指定する) 整数、 (行のパ
      ーセンテージを指定する) 0.0 から 1.0 までの割合を指定する小数、
      (出力する standard name にマッチする) 正規表現のいずれかを使って
      行います。複数の制限が指定された場合、指定の順に適用されます。た
      とえば次のようになります:

         print_stats(.1, 'foo:')

      上記の場合まず出力するリストは全体の10%に制限され、さらにファイ
      ル名の一部に文字列 ".*foo:" を持つ関数だけが出力されます:

         print_stats('foo:', .1)

      こちらの例の場合、リストはまずファイル名に ".*foo:" を持つ関数だ
      けに制限され、その中の最初の 10% だけが出力されます。

   print_callers(*restrictions)

      "Stats" クラスのこのメソッドは、プロファイルのデータベースの中か
      ら何らかの関数呼び出しを行った関数をすべて出力します。出力の順序
      は "print_stats()" によって与えられるものと同じです。出力を制限
      する引数も同じです。各呼び出し側関数についてそれぞれ一行ずつ表示
      されます。フォーマットは統計を作り出したプロファイラごとに微妙に
      異なります:

      * "profile" の場合、呼び出し側関数の後に括弧で囲まれて表示され
        る 数値はその呼び出しが何回行われたかを示しています。利便性の
        ため 、 2番目の括弧なしで表示される数値によって、関数が消費し
        た累積 時間を表しています。

      * "cProfile" の場合、各呼び出し側関数の後に3つの数字が付きます
        。 呼び出しが何回行われたかと、この呼び出し側関数からの呼び出
        しに よって現在の関数内で消費された合計時間および累積時間です
        。

   print_callees(*restrictions)

      "Stats" クラスのこのメソッドは、指定した関数から呼び出された関数
      のリストを出力します。呼び出し側、呼び出される側の方向は逆ですが
      、引数と出力の順序に関しては "print_callers()" と同じです。


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

*決定論的プロファイリング* とは、すべての *関数呼び出し*, *関数からの
リターン*, *例外発生* をモニターし、正確なタイミングを記録することで、
イベント間の時間、つまりどの時間にユーザコードが実行されているのかを計
測するやり方です。もう一方の *統計的プロファイリング* (このモジュール
でこの方法は採用していません) とは、有効なインストラクションポインタか
らランダムにサンプリングを行い、プログラムのどこで時間が使われているか
を推定する方法です。後者の方法は、オーバヘッドが少ないものの、プログラ
ムのどこで多くの時間が使われているか、その相対的な示唆に留まります。

Python の場合、実行中は必ずインタプリタが動作しているため、決定論的プ
ロファイリングを行うにあたり、計測用にコードを追加する必要はありません
。 Python は自動的に各イベントに *フック* (オプションのコールバック)
を提供します。加えて Python のインタプリタという性質によって、実行時に
大きなオーバーヘッドを伴う傾向がありますが、それに比べると一般的なアプ
リケーションでは決定論的プロファイリングで追加される処理のオーバーヘッ
ドは少ない傾向にあります。結果的に、決定論的プロファイリングは少ないコ
ストで Python プログラムの実行時間に関する詳細な統計を得られる方法とな
っているのです。

呼び出し回数はコード中のバグ発見にも使用できます (とんでもない数の呼び
出しが行われている部分)。インライン拡張の対象とすべき部分を見つけるた
めにも使えます (呼び出し頻度の高い部分)。内部時間の統計は、注意深く最
適化すべき"ホットループ"の発見にも役立ちます。累積時間の統計は、アルゴ
リズム選択に関連した高レベルのエラー検知に役立ちます。なお、このプロフ
ァイラは再帰的なアルゴリズム実装の累計時間を計ることが可能で、通常のル
ープを使った実装と直接比較することもできるようになっています。


26.4.6. 制限事項
================

一つの制限はタイミング情報の正確さに関するものです。決定論的プロファイ
ラには正確さに関する根本的問題があります。最も明白な制限は、 (一般に)
"クロック"は .001 秒の精度しかないということです。これ以上の精度で計測
することはできません。仮に充分な精度が得られたとしても、"誤差"が計測の
平均値に影響を及ぼすことがあります。この最初の誤差を取り除いたとしても
、それがまた別の誤差を引き起こす原因となります。

もう一つの問題として、イベントを検知してからプロファイラがその時刻を実
際に *取得* するまでに "いくらかの時間がかかる" ことです。同様に、イベ
ントハンドラが終了する時にも、時刻を取得して (そしてその値を保存して)
から、ユーザコードが処理を再開するまでの間に遅延が発生します。結果的に
多く呼び出される関数または多数の関数から呼び出される関数の情報にはこの
種の誤差が蓄積する傾向にあります。このようにして蓄積される誤差は、典型
的にはクロックの精度を下回ります (1クロック以下) が、一方でこの時間が
累計して非常に大きな値になることも *あり得ます* 。

この問題はオーバーヘッドの小さい "cProfile" よりも "profile" において
より重要です。そのため、 "profile" は誤差が確率的に (平均値で) 減少す
るようにプラットフォームごとに補正する機能を備えています。プロファイラ
に補正を施すと (最小二乗の意味で) 正確さが増しますが、ときには数値が負
の値になってしまうこともあります (呼び出し回数が極めて少なく、確率の神
があなたに意地悪をしたとき :-) )。プロファイルの結果に負の値が出力され
ても *驚かないでください* 。これは補正を行った場合にのみ生じることで、
補正を行わない場合に比べて計測結果は実際にはより正確になっているはずだ
からです。


26.4.7. キャリブレーション (補正)
=================================

"profile" のプロファイラは time 関数呼び出しおよびその値を保存するため
のオーバーヘッドを補正するために、各イベントの処理時間から定数を引きま
す。デフォルトでこの定数の値は 0 です。以下の手順で、プラットフォーム
に合った、より適切な定数が得られます (制限事項 参照)。

   import profile
   pr = profile.Profile()
   for i in range(5):
       print pr.calibrate(10000)

calibrate メソッドは引数として与えられた数だけ Python の呼び出しを行い
ます。直接呼び出す場合と、プロファイラを使って呼び出す場合の両方が実施
され、それぞれの時間が計測されます。その結果、プロファイラのイベントに
隠されたオーバーヘッドが計算され、その値は浮動小数として返されます。た
とえば、 1.8 GHz の Intel Core i5 で Mac OS X を使用、 Python の
time.clock() をタイマとして使った場合、値はおよそ 4.04e-6 となります。

この手順で使用しているオブジェクトはほぼ一定の結果を返します。 *非常に
* 早いコンピュータを使う場合、もしくはタイマの性能が貧弱な場合は、一定
の結果を得るために引数に 100000 や 1000000 といった大きな値を指定する
必要があるかもしれません。

一定の結果が得られたら、それを使う方法には3通りあります。 [1]

   import profile

   # 1. Apply computed bias to all Profile instances created hereafter.
   profile.Profile.bias = your_computed_bias

   # 2. Apply computed bias to a specific Profile instance.
   pr = profile.Profile()
   pr.bias = your_computed_bias

   # 3. Specify computed bias in instance constructor.
   pr = profile.Profile(bias=your_computed_bias)

選択肢がある場合は、補正値は小さめに設定した方が良いでしょう。プロファ
イルの結果に負の値が表われる"頻度が低く"なるはずです。


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

プロファイラが時刻を取得する方法を変更したいなら (たとえば、実測時間や
プロセスの経過時間を使いたい場合)、時刻取得用の関数を "Profile" クラス
のコンストラクタに指定することができます:

   pr = profile.Profile(your_time_func)

この結果生成されるプロファイラは時刻取得に "your_time_func()" を呼び出
すようになります。 "profile.Profile" と "cProfile.Profile" のどちらを
使っているかにより、 "your_time_func" の戻り値は異なって解釈されます:

"profile.Profile"
   "your_time_func()" は単一の数値、あるいは ("os.times()" と同じよう
   に) その合計が累計時間を示すリストを返すようになっていなければなり
   ません。関数が1つの数値、あるいは長さ2の数値のリストを返すようにな
   っていれば、ディスパッチルーチンには特別な高速化バージョンが使われ
   ます。

   あなたが選択した時刻取得関数のために、プロファイラのクラスを補正す
   べきであることを警告しておきます (キャリブレーション (補正) 参照)。
   ほとんどのマシンでは、プロファイル時のオーバヘッドの小ささという意
   味においては、時刻取得関数が長整数値を返すのが最も良い結果を生みま
   す。 ("os.times()" は浮動小数点数のタプルを返すので *かなり悪い* で
   す。) 綺麗な手段でより良い時刻取得関数に置き換えたいと望むなら、ク
   ラスを派生して、ディスパッチメソッドを置き換えてあなたの関数を一番
   いいように処理するように固定化してしまってください。補正定数の調整
   もお忘れなく。

"cProfile.Profile"
   "your_time_func()" は単一の数値を返すようになっていなければなりませ
   ん。単一の整数を返すのであれば、クラスコンストラクタの第二引数に単
   位時間当たりの実際の持続時間を渡せます。例えば
   "your_integer_time_func" が 1000 分の 1 秒単位で計測した時間を返す
   とすると、 "Profile" インスタンスを以下のように構築出来ます:

      pr = cProfile.Profile(your_integer_time_func, 0.001)

   "cProfile.Profile" クラスはキャリブレーションができないので、自前の
   タイマ関数は注意を払って使う必要があり、またそれは可能な限り速くな
   ければなりません。自前のタイマ関数で最高の結果を得るには、
   "_lsprof" 内部モジュールの C ソースファイルにハードコードする必要が
   あるかもしれません。

-[ 注記 ]-

[1] Python 2.2 より前のバージョンではプロファイラのソースコードに
    補正 値として埋め込まれた定数を直接編集する必要がありました。今で
    も同じ ことは可能ですが、その方法は説明しません。なぜなら、もうソ
    ースを編 集する必要がないからです。
