timeitモジュールの使用方法


351

何をするかという概念は理解timeitしていますが、コードに実装する方法がわかりません。

どのように私は言う、二つの機能を比較することができますinsertion_sortし、tim_sortで、timeit

回答:


266

timeitが機能する方法は、セットアップコードを一度実行してから、一連のステートメントを繰り返し呼び出すことです。したがって、並べ替えをテストする場合は、インプレース並べ替えの1つのパスが既に並べ替えられたデータの次のパスに影響を与えないように注意する必要があります(もちろん、Timsortは最高のパフォーマンスを発揮するため、非常に優れています)データがすでに部分的に順序付けられている場合)。

並べ替えのテストを設定する方法の例を次に示します。

>>> import timeit

>>> setup = '''
import random

random.seed('slartibartfast')
s = [random.random() for i in range(1000)]
timsort = list.sort
'''

>>> print min(timeit.Timer('a=s[:]; timsort(a)', setup=setup).repeat(7, 1000))
0.334147930145

一連のステートメントは、すべてのパスでソートされていないデータの新しいコピーを作成することに注意してください。

また、測定スイートを7回実行して最高の時間のみを維持するタイミング手法にも注意してください。これは、システムで実行されている他のプロセスによる測定の歪みを減らすのに役立ちます。

これらは、timeitを正しく使用するための私のヒントです。お役に立てれば :-)


8
はい、リストのコピーが含まれます(並べ替え自体に比べて非常に高速です)。ただし、コピーしない場合は、最初のパスでリストが並べ替えられ、残りのパスでは何もする必要はありません。並べ替えの時間だけを知りたい場合は、上記を実行して、実行せずにtimsort(a)、違いを確認してください:-)
Raymond Hettinger

セットアップごとに7回繰り返してから平均することをお勧めします。逆ではなく、このように、他のプロセスによる各スパイクが平均化されるのではなく、完全に無視される可能性が高い場合。
最大

75
@maxタイミングの平均ではなくmin()を使用します。それは私、ティムピーターズ、そしてグイドファンロッサムからの推薦です。最速の時間は、キャッシュが読み込まれ、システムが他のタスクでビジー状態でないときにアルゴリズムが実行できる最高の時間を表します。すべてのタイミングは騒々しいです-最速の時間が最も騒々しいです。最速のタイミングが最も再現性が高く、2つの異なる実装のタイミングをとる場合に最も有用であることを示すのは簡単です。
レイモンドヘッティンガー2012

4
1000個の入力の平均(まあ、合計ですが、同等です)を計算します。次に、7回繰り返し、最小値を取ります。平均(最良のケースではない)アルゴリズムの複雑さが必要なため、1000入力以上の平均が必要です。あなたが与えた理由のために、あなたは最小限が必要です。1つの入力を選択し、アルゴリズムを7回実行して、最小値を取ることで、アプローチを改善できると思いました。それを1000の異なる入力に対して繰り返し、平均を取ります。私が気付かなかったことは、あなたが.repeat(7,1000)すでに(同じシードを使用して)これを行っているということです!したがって、あなたのソリューションは完璧なIMOです。
最大

5
私はあなたが7000の実行の予算を配分する方法(例えば、ことを追加することができます.repeat(7, 1000).repeat(2, 3500)Vsが.repeat(35, 200)によるシステム負荷にエラーが原因の入力変動にエラーと比較する方法に依存しなければなりません。お使いのシステムに大きな負荷がかかっ常にあり、あなたは(あなたが稀アイドル状態でそれをキャッチしたときに)実行時間分布の左側に細長い尾を見れば、極端なケースでは、あなたも見つけるかもしれない.repeat(7000,1)よりも有用であることが.repeat(7,1000)、あなたの場合7000ランを超える予算を組むことはできません。
最大

277

timeitインタラクティブなPythonセッションで使用する場合、2つの便利なオプションがあります。

  1. IPythonシェルを使用します。便利%timeitな特殊機能を備えています。

    In [1]: def f(x):
       ...:     return x*x
       ...: 
    
    In [2]: %timeit for x in range(100): f(x)
    100000 loops, best of 3: 20.3 us per loop
  2. 標準のPythonインタープリターで__main__は、セットアップセッションでインポートすることにより、インタラクティブセッション中に以前に定義した関数やその他の名前にアクセスできます。

    >>> def f(x):
    ...     return x * x 
    ... 
    >>> import timeit
    >>> timeit.repeat("for x in range(100): f(x)", "from __main__ import f",
                      number=100000)
    [2.0640320777893066, 2.0876040458679199, 2.0520210266113281]

97
from __main__ import f技術を示すための+1 。これは、広く知られているとは思いません。これは、関数やメソッドの呼び出しがタイミングをとられているこのような場合に役立ちます。その他の場合(一連のステップのタイミングをとる)、関数呼び出しのオーバーヘッドが発生するため、あまり役に立ちません。
レイモンドヘッティンガー

15
あなたはただすることができます%timeit f(x)
2014

注: "import f"設定では、fa高速ローカル読み取りにアクセスできます。これは、通常の通常のコードでの(短い高速関数の)グローバル関数呼び出しを正確に反映していません。Py3.5 +では、実際のグローバルを提供できます: "バージョン3.5で変更:オプションのグローバルパラメータが追加されました。"; 避けられないtimeitモジュールのグローバルの前(あまり意味がありません)。おそらく、呼び出しコードのグローバル(sys._getframe(N).f_globals)が最初からデフォルトになっているはずです。
kxr

140

私は秘密を打ち明けましょう:使用する最良の方法timeitはコマンドラインです。

コマンドラインで、timeit適切な統計分析を実行します。これにより、最短の実行にかかった時間がわかります。タイミングのエラーはすべて正なのでこれは良いことです。したがって、最短の時間でエラーが最小になります。コンピュータは計算することができるより速く計算することができないので、負のエラーを取得する方法はありません!

したがって、コマンドラインインターフェイス:

%~> python -m timeit "1 + 2"
10000000 loops, best of 3: 0.0468 usec per loop

それは非常に簡単です。

次のものを設定できます。

%~> python -m timeit -s "x = range(10000)" "sum(x)"
1000 loops, best of 3: 543 usec per loop

これも便利です!

複数の行が必要な場合は、シェルの自動継続を使用するか、個別の引数を使用できます。

%~> python -m timeit -s "x = range(10000)" -s "y = range(100)" "sum(x)" "min(y)"
1000 loops, best of 3: 554 usec per loop

それはのセットアップを与えます

x = range(1000)
y = range(100)

と回

sum(x)
min(y)

より長いスクリプトが必要な場合はtimeit、Pythonスクリプト内に移動したくなるかもしれません。コマンドラインの方が分析とタイミングが優れているだけなので、これは避けることをお勧めします。代わりに、私はシェルスクリプトを作成する傾向があります。

 SETUP="

 ... # lots of stuff

 "

 echo Minmod arr1
 python -m timeit -s "$SETUP" "Minmod(arr1)"

 echo pure_minmod arr1
 python -m timeit -s "$SETUP" "pure_minmod(arr1)"

 echo better_minmod arr1
 python -m timeit -s "$SETUP" "better_minmod(arr1)"

 ... etc

複数の初期化のため、これには少し時間がかかる場合がありますが、通常、それは大した問題ではありません。


しかし、モジュール内で使用したい場合はどうでしょtimeitか?

簡単な方法は次のとおりです。

def function(...):
    ...

timeit.Timer(function).timeit(number=NUMBER)

これにより、その回数実行するための累積(最小ではない!)時間を得ることができます。

適切な分析を行うには、以下を使用.repeatしてください。

min(timeit.Timer(function).repeat(repeat=REPEATS, number=NUMBER))

オーバーヘッドを下げるfunctools.partial代わりに、通常これを組み合わせる必要がありますlambda: ...。したがって、あなたは次のようなものを持つことができます:

from functools import partial

def to_time(items):
    ...

test_items = [1, 2, 3] * 100
times = timeit.Timer(partial(to_time, test_items)).repeat(3, 1000)

# Divide by the number of repeats
time_taken = min(times) / 1000

あなたも行うことができます:

timeit.timeit("...", setup="from __main__ import ...", number=NUMBER)

コマンドラインからインターフェイスに近いものを提供しますが、はるかにクールではありません。"from __main__ import ..."あなたはによって作成された人工的な環境内で、あなたのメインモジュールからのコードを使用することができますtimeit

これはの便利なラッパーであるためTimer(...).timeit(...)、タイミングに関しては特に優れているわけではないことに注意してください。個人的Timer(...).repeat(...)には、上記で示したように使用することを好みます。


警告

timeitどこにでもあるいくつかの注意点があります。

  • オーバーヘッドは考慮されていません。x += 1追加にかかる時間を調べるために、時間をかけたいとしましょう。

    >>> python -m timeit -s "x = 0" "x += 1"
    10000000 loops, best of 3: 0.0476 usec per loop

    まあ、それ 0.0476 µsではありません。あなたはそれがそれ以下であることを知っているだけです。すべてのエラーは正です。

    だから純粋なオーバーヘッドを見つけてみてください:

    >>> python -m timeit -s "x = 0" ""      
    100000000 loops, best of 3: 0.014 usec per loop

    これは、タイミングからちょうど30%のオーバーヘッドです。これにより、相対的なタイミングが大幅にずれることがあります。しかし、あなたは本当にタイミングを追加することだけを気にしました。のルックアップタイミングxもオーバーヘッドに含める必要があります。

    >>> python -m timeit -s "x = 0" "x"
    100000000 loops, best of 3: 0.0166 usec per loop

    違いはそれほど大きくありませんが、それはあります。

  • メソッドの変更は危険です。

    >>> python -m timeit -s "x = [0]*100000" "while x: x.pop()"
    10000000 loops, best of 3: 0.0436 usec per loop

    しかし、それは完全に間違っています! x最初の反復後の空のリストです。再初期化する必要があります:

    >>> python -m timeit "x = [0]*100000" "while x: x.pop()"
    100 loops, best of 3: 9.79 msec per loop

    しかし、それからあなたはたくさんのオーバーヘッドを持っています。個別に説明してください。

    >>> python -m timeit "x = [0]*100000"                   
    1000 loops, best of 3: 261 usec per loop

    オーバーヘッドは時間のごくわずかな部分であるため、オーバーヘッドを差し引くことはここは妥当であることに注意してください。

    あなたの例では、挿入ソートとティムソートの両方が、すでにソートされたリストに対して完全に異常なタイミング動作を持っていることに注目する価値があります。これはrandom.shuffle、タイミングの破壊を回避したい場合は、ソートの間にソートが必要になることを意味します。


1
usecの意味?マイクロ秒ですか?
Hasan Iqbal 2017

2
@HasanIqbalAnikはい。
Veedrac 2017

@StefanPochmannそれは複数回サンプリングを試みないためです。
Veedrac


@Veedrac純粋なタイミングのオーバーヘッドを差し引くことについてのステートメントを考慮して、引数が指定されていないときにステートメントをtimeit実行しpassます。もちろん、これには時間がかかります。任意の引数を指定している場合は、passなりません実行されますので、いくつかの減算0.014タイミング毎からUSECSすることは正しくないだろう。
Arne

99

コード/関数の2つのブロックをすばやく比較する場合は、次のようにします。

import timeit

start_time = timeit.default_timer()
func1()
print(timeit.default_timer() - start_time)

start_time = timeit.default_timer()
func2()
print(timeit.default_timer() - start_time)

43

timeitを使用する最も簡単な方法はコマンドラインからです。

与えられたtest.py

def InsertionSort(): ...
def TimSort(): ...

次のようにtimeitを実行します:

% python -mtimeit -s'import test' 'test.InsertionSort()'
% python -mtimeit -s'import test' 'test.TimSort()'

18

私にとっては、これが最速の方法です:

import timeit
def foo():
    print("here is my code to time...")


timeit.timeit(stmt=foo, number=1234567)

12
# Генерация целых чисел

def gen_prime(x):
    multiples = []
    results = []
    for i in range(2, x+1):
        if i not in multiples:
            results.append(i)
            for j in range(i*i, x+1, i):
                multiples.append(j)

    return results


import timeit

# Засекаем время

start_time = timeit.default_timer()
gen_prime(3000)
print(timeit.default_timer() - start_time)

# start_time = timeit.default_timer()
# gen_prime(1001)
# print(timeit.default_timer() - start_time)


3

次のそれぞれに同じ辞書を設定し、実行時間をテストします。

セットアップ引数は基本的に辞書をセットアップすることです

数はコードを1000000回実行することです。セットアップではなく、stmt

これを実行すると、インデックスが取得よりもはるかに高速であることがわかります。複数回実行して確認できます。

このコードは基本的に、辞書のcの値を取得しようとします。

import timeit

print('Getting value of C by index:', timeit.timeit(stmt="mydict['c']", setup="mydict={'a':5, 'b':6, 'c':7}", number=1000000))
print('Getting value of C by get:', timeit.timeit(stmt="mydict.get('c')", setup="mydict={'a':5, 'b':6, 'c':7}", number=1000000))

これが私の結果です。あなたの結果は異なります。

インデックス別:0.20900007452246427

取得:0.54841166886888


どのバージョンのpythonを使用していますか?
エドゥアルド

3

コード全体をtimeitの引数として渡すだけです。

import timeit

print(timeit.timeit(

"""   
limit = 10000
prime_list = [i for i in range(2, limit+1)]

for prime in prime_list:
    for elem in range(prime*2, max(prime_list)+1, prime):
        if elem in prime_list:
            prime_list.remove(elem)
"""   
, number=10))


0

組み込みのtimeitモジュールは、IPythonコマンドラインから最適に機能します。

モジュール内から関数の時間を計るには:

from timeit import default_timer as timer
import sys

def timefunc(func, *args, **kwargs):
    """Time a function. 

    args:
        iterations=3

    Usage example:
        timeit(myfunc, 1, b=2)
    """
    try:
        iterations = kwargs.pop('iterations')
    except KeyError:
        iterations = 3
    elapsed = sys.maxsize
    for _ in range(iterations):
        start = timer()
        result = func(*args, **kwargs)
        elapsed = min(timer() - start, elapsed)
    print(('Best of {} {}(): {:.9f}'.format(iterations, func.__name__, elapsed)))
    return result

0

パラメーターを受け入れる関数でPython REPLインタープリターを使用する方法の例。

>>> import timeit                                                                                         

>>> def naive_func(x):                                                                                    
...     a = 0                                                                                             
...     for i in range(a):                                                                                
...         a += i                                                                                        
...     return a                                                                                          

>>> def wrapper(func, *args, **kwargs):                                                                   
...     def wrapper():                                                                                    
...         return func(*args, **kwargs)                                                                  
...     return wrapper                                                                                    

>>> wrapped = wrapper(naive_func, 1_000)                                                                  

>>> timeit.timeit(wrapped, number=1_000_000)                                                              
0.4458435332577161                                                                                        

0

2つの関数を作成してから、これに似たものを実行します。リンゴとリンゴを比較するために、同じ数の実行/実行を選択したいことに注意してください。
これはPython 3.7でテストされました。

ここに画像の説明を入力してください これは簡単にコピーするためのコードです

!/usr/local/bin/python3
import timeit

def fibonacci(n):
    """
    Returns the n-th Fibonacci number.
    """
    if(n == 0):
        result = 0
    elif(n == 1):
        result = 1
    else:
        result = fibonacci(n-1) + fibonacci(n-2)
    return result

if __name__ == '__main__':
    import timeit
    t1 = timeit.Timer("fibonacci(13)", "from __main__ import fibonacci")
    print("fibonacci ran:",t1.timeit(number=1000), "milliseconds")
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.