"timeit" --- 小さなコード断片の実行時間計測
*******************************************

**ソースコード:** Lib/timeit.py

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

このモジュールは小さい Python コードをの時間を計測するシンプルな手段を
提供しています。コマンドラインインターフェイス の他 呼び出しも可能 で
す。このモジュールは実行時間を計測するときに共通するいくつかの罠を回避
します。O'Reilly 出版の *Python Cookbook* 第2版にある、Tim Peter によ
る "Algorithms" 章も参照してください。


基本的な例
==========

次の例は コマンドラインインターフェイス を使って 3 つの異なる式の時間
を測定する方法を示しています。

   $ python3 -m timeit '"-".join(str(n) for n in range(100))'
   10000 loops, best of 5: 30.2 usec per loop
   $ python3 -m timeit '"-".join([str(n) for n in range(100)])'
   10000 loops, best of 5: 27.5 usec per loop
   $ python3 -m timeit '"-".join(map(str, range(100)))'
   10000 loops, best of 5: 23.2 usec per loop

同じ事を Python インターフェイス を使って実現することもできます:

   >>> import timeit
   >>> timeit.timeit('"-".join(str(n) for n in range(100))', number=10000)
   0.3018611848820001
   >>> timeit.timeit('"-".join([str(n) for n in range(100)])', number=10000)
   0.2727368790656328
   >>> timeit.timeit('"-".join(map(str, range(100)))', number=10000)
   0.23702679807320237

呼び出し可能オブジェクトは Python インターフェイス から渡すこともでき
ます:

   >>> timeit.timeit(lambda: "-".join(map(str, range(100))), number=10000)
   0.19665591977536678

ただし、"timeit()" はコマンドラインインターフェイスを使った時だけ繰り
返し回数を自動で決定する事に注意してください。使用例 節でより高度な例
を説明しています。


Python インターフェイス
=======================

このモジュールは 3 つの有用な関数と 1 つの公開クラスを持っています:

timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None)

   与えられた文、*setup* コードおよび *timer* 関数で "Timer" インスタ
   ンスを作成し、その "timeit()" メソッドを *number* 回実行します。 任
   意の *globals* 引数はコードを実行する名前空間を指定します。

   バージョン 3.5 で変更: 任意の *globals* 引数が追加されました。

timeit.repeat(stmt='pass', setup='pass', timer=<default timer>, repeat=5, number=1000000, globals=None)

   与えられた文、*setup* コードおよび *timer* 関数で "Timer" インスタ
   ンスを作成し、その "repeat()" メソッドを *repeat* 回繰り返すのを
   *number* 回実行します。 任意の *globals* 引数はコードを実行する名前
   空間を指定します。

   バージョン 3.5 で変更: 任意の *globals* 引数が追加されました。

   バージョン 3.7 で変更: *repeat* のデフォルト値が 3 から 5 へ変更さ
   れました。

timeit.default_timer()

   デフォルトのタイマーです。常に "time.perf_counter()" です。

   バージョン 3.3 で変更: デフォルトのタイマーが "time.perf_counter()"
   になりました。

class timeit.Timer(stmt='pass', setup='pass', timer=<timer function>, globals=None)

   小さなコード片の実行時間を計測するためのクラスです。

   コンストラクターは計測する文、セットアップのための追加の文、ならび
   にタイマー関数を取ります。 両文のデフォルトは "'pass'" です。 タイ
   マー関数はプラットフォーム依存です (モジュールの doctring を参照)。
   *stmt* および  *setup* には、複数行の文字列リテラルを含まない限り、
   ";" や改行で区切られた複数の文でも構いません。 デフォルトでは文は
   timeit の名前空間内で実行されます。 この挙動は名前空間を *globals*
   に渡すことで制御出来ます。

   最初の命令文の実行時間を計測するには、"timeit()" メソッドを使用しま
   す。"repeat()" と "autorange()" メソッドは "timeit()" を複数回呼び
   出したい時に使用します。

   *setup* の実行時間は全実行時間から除外されています。

   *stmt* および *setup* パラメータは、引数なしの呼び出し可能オブジェ
   クトをとることもできます。呼び出し可能オブジェクトを指定すると、そ
   のオブジェクトの呼び出しがタイマー関数に埋め込まれ、その関数が
   "timeit()" メソッドによって実行されます。この場合、関数呼び出しが増
   えるためにタイミングのオーバーヘッドが少し増える点に注意してくださ
   い。

   バージョン 3.5 で変更: 任意の *globals* 引数が追加されました。

   timeit(number=1000000)

      メイン文を *number* 回実行した時間を計測します。このメソッドはセ
      ットアップ文を1回だけ実行し、メイン文を指定回数実行するのにかか
      った秒数を浮動小数で返します。引数はループを何回実行するかの指定
      で、デフォルト値は 100万回です。メイン文、セットアップ文、タイマ
      ー関数はコンストラクターで指定されたものを使用します。

      注釈:

        デフォルトでは、"timeit()" は計測中、一時的に *ガベージコレク
        ション* を停止します。 この手法の利点は個々の計測結果がより比
        較しやすくなることです。 欠点は、ガベージコレクションが計測さ
        れる関数の性能の重要な要素である場合があることです。 その場合
        、*setup* 文字列の最初の文でガベージコレクションを有効にできま
        す。 以下に例を示します:

           timeit.Timer('for i in range(10): oct(i)', 'gc.enable()').timeit()

   autorange(callback=None)

      "timeit()" を呼び出す回数を自動的に決定します。

      これは総時間が0.2秒以上になるように "timeit()" を繰り返し呼び出
      す便利な関数で、最終的な結果（ループ回数、ループ回数に要した時間
      ）を返します。 要した時間が少なくとも0.2秒になるまで、シーケンス
      1, 2, 5, 10, 20, 50, ... から増加する回数で "timeit()" を呼び出
      します。

      *callback* が与えられ、Noneでない場合は、 callback(number,
      time_taken) という2つの引数を指定して試行された後に呼び出されま
      す。

      バージョン 3.6 で追加.

   repeat(repeat=5, number=1000000)

      "timeit()" を複数回繰り返します。

      これは "timeit()" を繰り返し呼び出したい時に有用で、結果をリスト
      にして返します。最初の引数で何回 "timeit()" を呼ぶか指定します。
      第 2 引数で "timeit()" の引数 *number* を指定します。

      注釈:

        結果のベクトルから平均値や標準偏差を計算して出力させたいと思う
        かもしれませんが、それはあまり意味がありません。多くの場合、最
        も低い値がそのマシンが与えられたコード断片を実行する場合の下限
        値です。結果のうち高めの値は、Python のスピードが一定しないた
        めに生じたものではなく、その他の計測精度に影響を及ぼすプロセス
        によるものです。したがって、結果のうち "min()" だけが見るべき
        値となるでしょう。この点を押さえた上で、統計的な分析よりも常識
        的な判断で結果を見るようにしてください。

      バージョン 3.7 で変更: *repeat* のデフォルト値が 3 から 5 へ変更
      されました。

   print_exc(file=None)

      計測対象コードのトレースバックを出力するためのヘルパーです。

      利用例:

         t = Timer(...)       # outside the try/except
         try:
             t.timeit(...)    # or t.repeat(...)
         except Exception:
             t.print_exc()

      これの標準のトレースバックより優れた点は、コンパイルしたテンプレ
      ートのソース行が表示されることです。オプションの引数 *file* には
      トレースバックの出力先を指定します。デフォルトは "sys.stderr" に
      なります。


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

コマンドラインからプログラムとして呼び出す場合は、次の書式を使います:

   python -m timeit [-n N] [-r N] [-u U] [-s S] [-h] [statement ...]

以下のオプションが使用できます:

-n N, --number=N

   'statement' を実行する回数

-r N, --repeat=N

   timer を繰り返す回数（デフォルトは5）

-s S, --setup=S

   最初に1回だけ実行する文 (デフォルトは "pass")

-p, --process

   デフォルトの "time.perf_counter()" の代わり "time.process_time()"
   を利用して、実時間ではなくプロセス時間を計測します

   バージョン 3.3 で追加.

-u, --unit=U

   timer 出力の時間の単位を指定します。 nsec、usec、msec、secを選択で
   きます。

   バージョン 3.5 で追加.

-v, --verbose

   時間計測の結果をそのまま詳細な数値でくり返し表示する

-h, --help

   簡単な使い方を表示して終了する

文は複数行指定することもできます。その場合、各行は独立した文として引数
に指定されたものとして処理します。クォートと行頭のスペースを使って、イ
ンデントした文を使うことも可能です。この複数行のオプションは  "-s" に
おいても同じ形式で指定可能です。

オプション "-n" でループの回数が指定されていない場合、1, 2, 5, 10, 20,
50, ... という数列で、少なくとも総時間が 0.2 秒になるまで回数を増やし
ていって、適切なループ回数が計算されます。

"default_timer()" の測定値は、同じマシン上で実行されている他のプログラ
ムの影響を受ける可能性があるため、正確な時間計測が必要な場合は、計測を
数回繰り返し、最適な時間を使用することをおすすめします。 "-r" オプショ
ンはこれに適しています。ほとんどの場合、デフォルトの5回の繰り返しで十
分でしょう。 "time.process_time()" を使用すればCPU時間を測定できます。

注釈:

  pass 文の実行には基本的なオーバーヘッドが存在します。ここにあるコー
  ドはこの事実を隠そうとはしていませんが、注意する必要があります。基本
  的なオーバーヘッドは引数なしでプログラムを起動することにより計測でき
  、それは Python のバージョンによって異なるでしょう。


使用例
======

最初に 1 回だけ実行されるセットアップ文を指定することが可能です:

   $ python -m timeit -s 'text = "sample string"; char = "g"'  'char in text'
   5000000 loops, best of 5: 0.0877 usec per loop
   $ python -m timeit -s 'text = "sample string"; char = "g"'  'text.find(char)'
   1000000 loops, best of 5: 0.342 usec per loop

出力には、3つのフィールドがあります。ループ回数は、ループの反復ごとに
何回文の内容が実行されたかを示します。反復回数 ('best of 5') は計時ル
ープが何回繰り返されたかを示し、最後に文の内容が平均して計時ループの中
の最良の反復で要した時間を示します。つまり、最速の反復をループ回数で割
った時間です。

   >>> import timeit
   >>> timeit.timeit('char in text', setup='text = "sample string"; char = "g"')
   0.41440500499993504
   >>> timeit.timeit('text.find(char)', setup='text = "sample string"; char = "g"')
   1.7246671520006203

同じことは "Timer" クラスとそのメソッドを使用して行うこともできます:

   >>> import timeit
   >>> t = timeit.Timer('char in text', setup='text = "sample string"; char = "g"')
   >>> t.timeit()
   0.3955516149999312
   >>> t.repeat()
   [0.40183617287970225, 0.37027556854118704, 0.38344867356679524, 0.3712595970846668, 0.37866875250654886]

以下の例は、複数行を含んだ式を計測する方法を示しています。ここでは、オ
ブジェクトの存在する属性と存在しない属性に対してテストするために
"hasattr()" と "try"/"except" を使用した場合のコストを比較しています:

   $ python -m timeit 'try:' '  str.__bool__' 'except AttributeError:' '  pass'
   20000 loops, best of 5: 15.7 usec per loop
   $ python -m timeit 'if hasattr(str, "__bool__"): pass'
   50000 loops, best of 5: 4.26 usec per loop

   $ python -m timeit 'try:' '  int.__bool__' 'except AttributeError:' '  pass'
   200000 loops, best of 5: 1.43 usec per loop
   $ python -m timeit 'if hasattr(int, "__bool__"): pass'
   100000 loops, best of 5: 2.23 usec per loop

   >>> import timeit
   >>> # attribute is missing
   >>> s = """\
   ... try:
   ...     str.__bool__
   ... except AttributeError:
   ...     pass
   ... """
   >>> timeit.timeit(stmt=s, number=100000)
   0.9138244460009446
   >>> s = "if hasattr(str, '__bool__'): pass"
   >>> timeit.timeit(stmt=s, number=100000)
   0.5829014980008651
   >>>
   >>> # attribute is present
   >>> s = """\
   ... try:
   ...     int.__bool__
   ... except AttributeError:
   ...     pass
   ... """
   >>> timeit.timeit(stmt=s, number=100000)
   0.04215312199994514
   >>> s = "if hasattr(int, '__bool__'): pass"
   >>> timeit.timeit(stmt=s, number=100000)
   0.08588060699912603

定義した関数に "timeit" モジュールがアクセスできるようにするために、
import 文の入った *setup* パラメータを渡すことができます:

   def test():
       """Stupid test function"""
       L = [i for i in range(100)]

   if __name__ == '__main__':
       import timeit
       print(timeit.timeit("test()", setup="from __main__ import test"))

他のオプションは "globals()" を *globals* 引数に渡すことです。 これに
よりコードはあなたのグローバル名前空間内で実行されます。 これはそれぞ
れでインポートするより 便利です:

   def f(x):
       return x**2
   def g(x):
       return x**4
   def h(x):
       return x**8

   import timeit
   print(timeit.timeit('[func(42) for func in (f,g,h)]', globals=globals()))
