timeit —小さなコードスニペットの実行時間を測定します
ソースコード: :source: `Lib / timeit.py`
このモジュールは、Pythonコードの小さなビットの時間を計る簡単な方法を提供します。 コマンドラインインターフェイスと呼び出し可能の両方があります。 実行時間を測定するための多くの一般的なトラップを回避します。 O'Reillyが発行した Python Cookbook の「Algorithms」の章へのTimPetersの紹介も参照してください。
基本的な例
次の例は、コマンドラインインターフェイスを使用して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つの便利な関数とパブリッククラスを定義します。
- timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None)
指定されたステートメント、 setup コード、および timer 関数を使用して Timer インスタンスを作成し、 numberを使用して timeit()メソッドを実行します。 実行。 オプションの globals 引数は、コードを実行する名前空間を指定します。
バージョン3.5で変更:オプションの globals パラメーターが追加されました。
- timeit.repeat(stmt='pass', setup='pass', timer=<default timer>, repeat=5, number=1000000, globals=None)
指定されたステートメント、 setup コード、および timer 関数を使用して Timer インスタンスを作成し、指定された[X159X ] repeat countおよび number 実行。 オプションの globals 引数は、コードを実行する名前空間を指定します。
バージョン3.5で変更:オプションの globals パラメーターが追加されました。
バージョン3.7で変更: リピートのデフォルト値が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'
になります。 タイマー機能はプラットフォームに依存します(モジュールのドキュメント文字列を参照)。 stmt および setup には、複数行の文字列リテラルが含まれていない限り、;
または改行で区切られた複数のステートメントを含めることもできます。 ステートメントは、デフォルトでtimeitの名前空間内で実行されます。 この動作は、名前空間を globals に渡すことで制御できます。最初のステートメントの実行時間を測定するには、 timeit()メソッドを使用します。 repeat()および autorange()メソッドは、 timeit()を複数回呼び出す便利なメソッドです。
setup の実行時間は、時間指定された実行実行全体から除外されます。
stmt および setup パラメーターは、引数なしで呼び出すことができるオブジェクトを受け取ることもできます。 これにより、それらへの呼び出しがタイマー関数に埋め込まれ、 timeit()によって実行されます。 この場合、余分な関数呼び出しがあるため、タイミングのオーバーヘッドが少し大きくなることに注意してください。
バージョン3.5で変更:オプションの globals パラメーターが追加されました。
- timeit(number=1000000)
メインステートメントの number 実行時間。 これにより、セットアップステートメントが1回実行され、メインステートメントの実行にかかる時間がfloatとして秒単位で測定されて返されます。 引数はループを通過する回数であり、デフォルトは100万回です。 使用するmainステートメント、setupステートメント、timer関数がコンストラクターに渡されます。
ノート
デフォルトでは、 timeit()は、タイミング中にガベージコレクションを一時的にオフにします。 このアプローチの利点は、独立したタイミングをより比較できるようにすることです。 欠点は、GCが測定される機能のパフォーマンスの重要な要素である可能性があることです。 その場合、 setup 文字列の最初のステートメントとしてGCを再度有効にすることができます。 例えば:
timeit.Timer('for i in range(10): oct(i)', 'gc.enable()').timeit()
- autorange(callback=None)
timeit()を呼び出す回数を自動的に決定します。
これは、 timeit()を繰り返し呼び出して、合計時間が0.2秒以上になるようにし、最終的な値(ループの数、そのループの数にかかる時間)を返す便利な関数です。 timeit()を呼び出し、シーケンス1、2、5、10、20、50、…から、かかる時間が少なくとも0.2秒になるまで番号を増やしていきます。
コールバックが指定され、
None
でない場合、各試行の後にcallback(number, time_taken)
の2つの引数を使用して呼び出されます。バージョン3.6の新機能。
- repeat(repeat=5, number=1000000)
timeit()を数回呼び出します。
これは、 timeit()を繰り返し呼び出して、結果のリストを返す便利な関数です。 最初の引数は、 timeit()を呼び出す回数を指定します。 2番目の引数は、 timeit()の number 引数を指定します。
ノート
結果ベクトルから平均と標準偏差を計算し、これらを報告するのは魅力的です。 ただし、これはあまり役に立ちません。 通常、最小値は、マシンが特定のコードスニペットを実行できる速度の下限を示します。 結果ベクトルの値が高いのは、通常、Pythonの速度の変動ではなく、タイミングの精度を妨げる他のプロセスが原因です。 したがって、結果の min()は、おそらく関心があるはずの唯一の数値です。 その後、ベクトル全体を見て、統計ではなく常識を適用する必要があります。
バージョン3.7で変更: リピートのデフォルト値が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
- 'ステートメント'を実行する回数
- -r N, --repeat=N
- タイマーを繰り返す回数(デフォルトは5)
- -s S, --setup=S
- 最初に1回実行されるステートメント(デフォルト
pass
)
- -p, --process
デフォルトの time.perf_counter()の代わりに time.process_time()を使用して、実時間ではなく処理時間を測定します。
バージョン3.3の新機能。
- -u, --unit=U
タイマー出力の時間単位を指定します。 nsec、usec、msec、またはsecを選択できます
バージョン3.5の新機能。
- -v, --verbose
- 生のタイミング結果を出力します。 より多くの桁の精度のために繰り返す
- -h, --help
- 短い使用法メッセージを印刷して終了します
複数行のステートメントは、各行を個別のステートメント引数として指定することで指定できます。 インデントされた行は、引数を引用符で囲み、先頭にスペースを使用することで可能です。 複数の -s オプションは同様に扱われます。
-n が指定されていない場合、合計時間が少なくとも0.2秒になるまで、シーケンス1、2、5、10、20、50、…から数を増やして、適切なループ数が計算されます。
default_timer()の測定値は、同じマシンで実行されている他のプログラムの影響を受ける可能性があるため、正確なタイミングが必要な場合は、タイミングを数回繰り返し、最適な時間を使用することをお勧めします。 -r オプションはこれに適しています。 ほとんどの場合、デフォルトの5回の繰り返しで十分です。 time.process_time()を使用してCPU時間を測定できます。
ノート
passステートメントの実行に関連する特定のベースラインオーバーヘッドがあります。 ここのコードはそれを隠そうとはしていませんが、あなたはそれを知っているべきです。 ベースラインオーバーヘッドは、引数なしでプログラムを呼び出すことで測定でき、Pythonのバージョン間で異なる場合があります。
例
最初に一度だけ実行されるセットアップステートメントを提供することが可能です。
$ 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
>>> 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 / exception を使用して、オブジェクト属性の欠落と存在をテストします。
$ 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 モジュールに定義した関数へのアクセスを許可するには、インポートステートメントを含む 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"))
もう1つのオプションは、 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()))