Jak mogę odmierzyć segment kodu do testowania wydajności za pomocą pythons timeit?

Mam skrypt Pythona, który działa tak, jak powinien, ale muszę napisać czas wykonania. Wygooglowałem, że powinienem użyć timeit ale nie mogę tego uruchomić.

Mój skrypt w Pythonie wygląda tak:

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)

Potrzebuję czasu na wykonanie zapytania i zapisanie go do pliku results_update.txt. Celem jest przetestowanie instrukcji aktualizacji dla mojej bazy danych z różnymi indeksami i mechanizmami strojenia.

Author: Adam, 2010-05-19

8 answers

Możesz użyć time.time() lub time.clock() przed i po bloku chcesz czasu.

import time

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

total = t1-t0
[[9]}ta metoda nie jest tak dokładna jak timeit (Nie średnio kilka biegów), ale jest to proste.

time.time() (W Windows i Linuksie) i time.clock() (w Linuksie) nie są wystarczająco precyzyjne dla szybkich funkcji (otrzymujesz total = 0). W tym przypadku lub jeśli chcesz uśrednić czas, jaki upłynął o kilka uruchomień, musisz ręcznie wywołać funkcję wiele razy (jak myślę wykonujesz już w swoim przykładzie kod i timeit robi to automatycznie po ustawieniu jego argumentu numer )

import time

def myfast():
   code

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

total_n = t1-t0

W Windows, jak stwierdził Corey w komentarzu, time.clock() ma znacznie większą precyzję (mikrosekundę zamiast sekundy) i jest preferowany przez time.time().

 302
Author: joaquin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-12 19:14:42

Jeśli profilujesz swój kod i możesz używać Ipythona, posiada on magiczną funkcję %timeit.

%%timeit działa na komórki.

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
 44
Author: munk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-02 23:26:59

Pomijając timing, ten kod, który pokazujesz, jest po prostu niepoprawny: wykonujesz 100 połączeń (całkowicie ignorując wszystkie oprócz ostatniego), a następnie wykonując pierwsze wywołanie execute przekazujesz mu zmienną lokalną query_stmt, którą inicjalizujesz tylko Po wywołaniu execute.

Najpierw popraw kod, nie martwiąc się jeszcze o czas: tzn. funkcja, która tworzy lub odbiera połączenie i wykonuje 100 lub 500 lub dowolną liczbę aktualizacji na tym połączeniu, a następnie zamyka połączenie. Gdy twój kod działa poprawnie, jest właściwym punktem, w którym można pomyśleć o użyciu timeit na nim!

W szczególności, jeśli funkcja, którą chcesz time jest bez parametru o nazwie foobar, możesz użyć timeit.timeit (2.6 lub później - jest bardziej skomplikowany w 2.5 i wcześniej):

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

Lepiej określ liczbę uruchomień, ponieważ domyślna wartość, milion, może być wysoka dla Twojego przypadku użycia (co prowadzi do spędzenia dużo czasu w tym kodzie;-).

 37
Author: Alex Martelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-19 14:33:06

Skup się na jednej konkretnej rzeczy . Wejście/Wyjście dysku jest powolne, więc wziąłbym to z testu, jeśli wszystko, co zamierzasz poprawić, to zapytanie do bazy danych.

A jeśli potrzebujesz czasu na wykonanie bazy danych, poszukaj zamiast tego narzędzi bazodanowych, takich jak Zapytanie o plan zapytań, i zauważ, że wydajność zależy nie tylko od dokładnego zapytania i indeksów, które masz, ale także od obciążenia danych (ile danych przechowałeś).

To powiedziawszy, możesz po prostu umieścić swój kod w funkcji i uruchom tę funkcję z timeit.timeit():

def function_to_repeat():
    # ...

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

Spowoduje to wyłączenie funkcji garbage collection, wielokrotne wywołanie funkcji function_to_repeat() oraz czas całkowitego czasu trwania tych wywołań przy użyciu timeit.default_timer(), który jest najdokładniejszym dostępnym zegarem dla konkretnej platformy.

Powinieneś przenieść kod setup z powtarzanej funkcji; na przykład najpierw powinieneś połączyć się z bazą danych, a potem tylko z zapytaniami. Użyj argumentu setup, Aby zaimportować lub utworzyć te zależności i przekazać je do funkcji:

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

duration = timeit.timeit(
    'function_to_repeat(var1, var2)',
    'from __main__ import function_to_repeat, var1, var2', 
    number=1000)
/ Align = "left" / function_to_repeat, var1 i var2 z twojego skryptu i przekaż je do funkcji każde powtórzenie.
 9
Author: Martijn Pieters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-26 16:07:31

Widzę, że pytanie zostało już udzielone, ale nadal chcę dodać moje 2 centy za to samo.

Miałem również do czynienia z podobnym scenariuszem, w którym muszę przetestować czas wykonania dla kilku podejść i dlatego napisałem mały skrypt, który wywołuje timeit na wszystkich funkcjach w nim napisanych.

Skrypt jest również dostępny jako github gist tutaj .

Mam nadzieję, że pomoże Tobie i innym.
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
Author: Abhijit Mamarde,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-18 21:09:04

Oto proste opakowanie na odpowiedź Stevena. Ta funkcja nie wykonuje powtarzanych uruchomień / uśredniania, po prostu oszczędza Ci konieczności powtarzania kodu czasowego wszędzie:)

'''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))
 2
Author: information_interchange,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-22 13:33:56

Pakiet testowy nie próbuje użyć zaimportowanego timeit, więc trudno powiedzieć, jaki był zamiar. Niemniej jednak jest to odpowiedź kanoniczna, więc kompletny przykład timeit wydaje się w porządku, opracowując odpowiedź Martijna.

Docs for timeit zaproponuj wiele przykładów i flag, które warto sprawdzić. Podstawowe użycie w wierszu poleceń to:

$ 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

Uruchom z -h, aby zobaczyć wszystkie opcje. Python MOTW ma świetny dział na timeit, który pokazuje jak uruchamiać Moduły poprzez import i Wielowierszowe ciągi kodu z wiersza poleceń.

W formie skryptu zazwyczaj używam go w ten sposób:

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")

Możesz dość łatwo upuścić funkcje i argumenty, których potrzebujesz. Należy zachować ostrożność podczas używania nieczystych funkcji i dbać o stan.

Przykładowe wyjście:

$ 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
------------------------------
 0
Author: ggorlen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-07-12 19:53:18

Kolejny prosty przykład czasu:

def your_function_to_test():
   # do some stuff...

time_to_run_100_times = timeit.timeit(lambda: your_function_to_test, number=100)
 0
Author: sam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-07-23 07:48:31