Pythonのtimeitでパフォーマンスをテストするためにコードセグメントの時間を計るにはどうすればよいですか?


161

正常に機能するpythonスクリプトがありますが、実行時間を記述する必要があります。私は使うべきだとググったが、うまく動かtimeitないようだ。

私のPythonスクリプトは次のようになります。

import sys
import getopt
import timeit
import random
import os
import re
import ibm_db
import time
from string import maketrans
myfile = open("results_update.txt", "a")

for r in range(100):
    rannumber = random.randint(0, 100)

    update = "update TABLE set val = %i where MyCount >= '2010' and MyCount < '2012' and number = '250'" % rannumber
    #print rannumber

    conn = ibm_db.pconnect("dsn=myDB","usrname","secretPWD")

for r in range(5):
    print "Run %s\n" % r        
    ibm_db.execute(query_stmt)
 query_stmt = ibm_db.prepare(conn, update)

myfile.close()
ibm_db.close(conn)

必要なのは、クエリを実行してファイルに書き込むのにかかる時間ですresults_update.txt。目的は、さまざまなインデックスとチューニングメカニズムを使用してデータベースの更新ステートメントをテストすることです。


/についての質問は具体的timeitですか?私はそうは思いません。その場合、タイトルから「with Pythons timeit」を削除する必要があります。
Martin Thoma、2015年

回答:


274

あなたは使用することができるtime.time()time.clock()、時間にしたいブロックの前と後。

import time

t0 = time.time()
code_block
t1 = time.time()

total = t1-t0

この方法はtimeit(複数回の実行を平均化するほどには)正確ではありませんが、簡単です。

time.time()(WindowsおよびLinuxの場合)およびtime.clock()(Linuxの場合)は、高速関数には十分正確ではありません(合計= 0になります)。この場合、または数回の実行で経過した時間を平均化したい場合は、関数を手動で複数回呼び出す必要があります(すでにサンプルコードで行っており、time 引数に数値を設定すると自動的に実行されます)。

import time

def myfast():
   code

n = 10000
t0 = time.time()
for i in range(n): myfast()
t1 = time.time()

total_n = t1-t0

Windowsでは、Coreyがコメントで述べたように、time.clock()精度がはるかに高く(秒ではなくマイクロ秒)、に優先しtime.time()ます。


8
Windowsではfyi、time.time()の代わりにtime.clock()を使用
Corey Goldberg

4
コーリー、どうして?時計の精度が高い(マイクロ秒)か、それ以外に何かあるからですか
joaquin

11
timeit.default_timer()を使用して、コードプラットフォームを独立させることができます。OSに応じて、time.clock()またはtime.time()を返します。
Marc Stober 2014年

6
手で時計を選択するのではなく、を使用しtimeit.default_timerます。Pythonはすでにあなたのために仕事をしてくれました。しかし、実際にはtimeit.timeit(myfast, number=n)、繰り返し呼び出しホイールを再発明する代わりに使用する必要があります(timeitコードを繰り返し実行している間、ガベージコレクターを無効にするという事実を見逃してください)。
Martijn Pieters

15
update:time.clock()は非推奨になりました。ここで、time.time()を使用する必要があります。実際には、バージョン3.3以降、最良のオプションは、time.perf_counter()だろう
Madlozoz

42

コードをプロファイリングしていて、IPythonを使用できる場合は、マジック関数が含まれています%timeit

%%timeit 細胞を操作します。

In [2]: %timeit cos(3.14)
10000000 loops, best of 3: 160 ns per loop

In [3]: %%timeit
   ...: cos(3.14)
   ...: x = 2 + 3
   ...: 
10000000 loops, best of 3: 196 ns per loop

36

タイミングとはかなり異なり、このコードは正しくありません。100の接続を実行し(最後の接続を除いてすべて無視)、次に最初の実行呼び出しを実行するとき、実行後にquery_stmtのみ初期化するローカル変数を渡しますコール。

まず、まだタイミングを気にせずにコードを正しくします。つまり、接続を作成または受信し、その接続で100または500または任意の数の更新を実行してから、接続を閉じる関数です。コードが正しく機能するようになったら、それを使用timeitすることを考える適切なポイントです!

具体的には、時間を計測したい関数がパラメーターなしの関数である場合は、timeit.timeit(2.6以降-2.5以前ではより複雑)foobarを使用できます。

timeit.timeit('foobar()', number=1000)

ユースケースではデフォルトの100万が高い可能性があるため、実行回数を指定する方がよいでしょう(このコードで多くの時間を費やすことになります;-)。


26
最後の数分間、これに苦労した後、将来の視聴者に、関数foobarがメインファイルにある場合はおそらくセットアップ変数も渡すことを知らせたいと思います。このように: timeit.timeit('foobar()','from __main__ import foobar',number=1000)
リッチ

3
Python 2.7.8では、次のように単純に使用できますtimeit.timeit( foobar, number=1000 )

9

1つの特定のことに焦点を当てます。ディスクI / Oが遅いので、調整するのがデータベースクエリだけの場合は、テストから除外します。

また、データベースの実行時間を計測する必要がある場合は、代わりにクエリプランを要求するなどのデータベースツールを探し、パフォーマンスは正確なクエリや使用しているインデックスだけでなく、データの負荷(データの量)によっても異なることに注意してくださいあなたが保存しました)。

つまり、コードを関数に入れて、その関数をtimeit.timeit()次のように実行するだけです。

def function_to_repeat():
    # ...

duration = timeit.timeit(function_to_repeat, number=1000)

これにより、ガベージコレクションが無効になり、function_to_repeat()関数が繰り返し呼び出さtimeit.default_timer()れ、特定のプラットフォームで利用可能な最も正確なクロックであるを使用して、それらの呼び出しの合計時間が計測されます。

繰り返しコードからセットアップコードを移動する必要があります。たとえば、最初にデータベースに接続してから、クエリのみの時間を計測する必要があります。setup引数を使用して、これらの依存関係をインポートまたは作成し、関数に渡します。

def function_to_repeat(var1, var2):
    # ...

duration = timeit.timeit(
    'function_to_repeat(var1, var2)',
    'from __main__ import function_to_repeat, var1, var2', 
    number=1000)

グローバルをつかむだろうfunction_to_repeatvar1var2あなたのスクリプトからと機能各繰り返しにそれらを渡します。


コードを関数に入れることは私が探していたステップです-単にコードを文字列にしてevalINGが完全に取るに足らないもののために飛ぶことはないからです。thx
javadba

2

質問には既に回答済みですが、それでも2セント追加したいと思います。

私はいくつかのアプローチの実行時間をテストしなければならないという同様のシナリオにも直面しているため、それに記述されているすべての関数でtimeitを呼び出す小さなスクリプトを記述しました。

スクリプトは、こちらの github gistからも入手できます

それがあなたと他の人を助けることを願っています。

from random import random
import types

def list_without_comprehension():
    l = []
    for i in xrange(1000):
        l.append(int(random()*100 % 100))
    return l

def list_with_comprehension():
    # 1K random numbers between 0 to 100
    l = [int(random()*100 % 100) for _ in xrange(1000)]
    return l


# operations on list_without_comprehension
def sort_list_without_comprehension():
    list_without_comprehension().sort()

def reverse_sort_list_without_comprehension():
    list_without_comprehension().sort(reverse=True)

def sorted_list_without_comprehension():
    sorted(list_without_comprehension())


# operations on list_with_comprehension
def sort_list_with_comprehension():
    list_with_comprehension().sort()

def reverse_sort_list_with_comprehension():
    list_with_comprehension().sort(reverse=True)

def sorted_list_with_comprehension():
    sorted(list_with_comprehension())


def main():
    objs = globals()
    funcs = []
    f = open("timeit_demo.sh", "w+")

    for objname in objs:
        if objname != 'main' and type(objs[objname]) == types.FunctionType:
            funcs.append(objname)
    funcs.sort()
    for func in funcs:
        f.write('''echo "Timing: %(funcname)s"
python -m timeit "import timeit_demo; timeit_demo.%(funcname)s();"\n\n
echo "------------------------------------------------------------"
''' % dict(
                funcname = func,
                )
            )

    f.close()

if __name__ == "__main__":
    main()

    from os import system

    #Works only for *nix platforms
    system("/bin/bash timeit_demo.sh")

    #un-comment below for windows
    #system("cmd timeit_demo.sh")

2

これがスティーブンの答えの簡単なラッパーです。この関数は繰り返し実行/平均化を行わず、どこでもタイミングコードを繰り返す必要をなくします:)

'''function which prints the wall time it takes to execute the given command'''
def time_func(func, *args): #*args can take 0 or more 
  import time
  start_time = time.time()
  func(*args)
  end_time = time.time()
  print("it took this long to run: {}".format(end_time-start_time))

0

テストスイートはインポートされtimeitたものを使用しようとしないため、意図が何であるかを知るのは困難です。それでも、これは標準的な回答timeitであるため、Martijnの回答について詳しく説明したように見えるの完全な例です。

ドキュメントにtimeitは、チェックする価値のある多くの例とフラグが用意されています。コマンドラインの基本的な使用方法は次のとおりです。

$ python -mtimeit "all(True for _ in range(1000))"
2000 loops, best of 5: 161 usec per loop
$ python -mtimeit "all([True for _ in range(1000)])"
2000 loops, best of 5: 116 usec per loop

で実行すると-h、すべてのオプションが表示されます。Python MOTWにtimeit、コマンドラインからインポートおよび複数行のコード文字列を使用してモジュールを実行する方法を示す素晴らしいセクションがあります。

スクリプト形式では、通常、次のように使用します。

import argparse
import copy
import dis
import inspect
import random
import sys
import timeit

def test_slice(L):
    L[:]

def test_copy(L):
    L.copy()

def test_deepcopy(L):
    copy.deepcopy(L)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--n", type=int, default=10 ** 5)
    parser.add_argument("--trials", type=int, default=100)
    parser.add_argument("--dis", action="store_true")
    args = parser.parse_args()
    n = args.n
    trials = args.trials
    namespace = dict(L = random.sample(range(n), k=n))
    funcs_to_test = [x for x in locals().values() 
                     if callable(x) and x.__module__ == __name__]
    print(f"{'-' * 30}\nn = {n}, {trials} trials\n{'-' * 30}\n")

    for func in funcs_to_test:
        fname = func.__name__
        fargs = ", ".join(inspect.signature(func).parameters)
        stmt = f"{fname}({fargs})"
        setup = f"from __main__ import {fname}"
        time = timeit.timeit(stmt, setup, number=trials, globals=namespace)
        print(inspect.getsource(globals().get(fname)))

        if args.dis:
            dis.dis(globals().get(fname))

        print(f"time (s) => {time}\n{'-' * 30}\n")

必要な関数と引数を簡単にドロップできます。不純な関数を使用するときは注意して、状態を管理してください。

出力例:

$ python benchmark.py --n 10000
------------------------------
n = 10000, 100 trials
------------------------------

def test_slice(L):
    L[:]

time (s) => 0.015502399999999972
------------------------------

def test_copy(L):
    L.copy()

time (s) => 0.01651419999999998
------------------------------

def test_deepcopy(L):
    copy.deepcopy(L)

time (s) => 2.136012
------------------------------
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.