Funkcja pomiaru czasu w Pythonie


123

Chcę utworzyć funkcję Pythona, aby przetestować czas spędzony w każdej funkcji i wydrukować jej nazwę wraz z czasem, jak mogę wydrukować nazwę funkcji i jeśli jest inny sposób, aby to zrobić, powiedz mi

def measureTime(a):
    start = time.clock() 
    a()
    elapsed = time.clock()
    elapsed = elapsed - start
    print "Time spent in (function name) is: ", elapsed

Narzędzia do profilowania w Pythonie mogą pokazywać nazwy funkcji i czas spędzony w każdej z nich. Przeczytaj tutaj: docs.python.org/library/profile.html
Roadmaster

Lepsze wykorzystanie timeitdo pomiaru. Nie jest doskonały, ale zdecydowanie przebija twoje pchnięcie i jest o wiele łatwiejszy w użyciu timeitniż samodzielne przygotowanie czegoś lepszego.

Odpowiedzi:


241

Przede wszystkim zdecydowanie sugeruję użycie profilera lub przynajmniej użycie timeit .

Jeśli jednak chcesz napisać własną metodę pomiaru czasu, której dokładnie chcesz się nauczyć, tutaj jest miejsce, w którym możesz zacząć korzystać z dekoratora.

Python 2:

def timing(f):
    def wrap(*args):
        time1 = time.time()
        ret = f(*args)
        time2 = time.time()
        print '%s function took %0.3f ms' % (f.func_name, (time2-time1)*1000.0)
        return ret
    return wrap

Użycie jest bardzo proste, wystarczy użyć dekoratora @timing:

@timing
def do_work():
  #code

Python 3:

def timing(f):
    def wrap(*args, **kwargs):
        time1 = time.time()
        ret = f(*args, **kwargs)
        time2 = time.time()
        print('{:s} function took {:.3f} ms'.format(f.__name__, (time2-time1)*1000.0))

        return ret
    return wrap

Uwaga Dzwonię, f.func_nameaby uzyskać nazwę funkcji jako ciąg (w Pythonie 2) lub f.__name__ w Pythonie 3.


4
dokładnie to, czego chcę :) ... ale przekonaliście mnie, żebym użył profilera Pythona
Wazery

3
Wygląda na to, że założono, że time.time () podaje czas w mikrosekundach od epoki? Dokumentacja podaje, że podaje czas w sekundach docs.python.org/2/library/time.html#time.time .
Rahul Jha

To nie może przynieść efektu po użyciu yield w func. Jak mogę nadal korzystać z tej metody i używać wydajności?
jiamo

def timing (f): def wrap (* args, ** kwargs): time1 = time.time () ret = f (* args, ** kwargs) time2 = time.time () print 'Funkcja% s zajęła% 0.3 f ms '% (f.func_name, (time2-time1) * 1000) return ret return wrap
Vivek Bagaria

1
Jaka jest wada samodzielnego pisania? Czy przechowywanie listy czasów, które upłynęło, i badanie ich dystrybucji nie jest wystarczająco proste?
3pitt

51

Po zabawie z timeitmodułem nie podoba mi się jego interfejs, który nie jest tak elegancki w porównaniu do dwóch poniższych metod.

Poniższy kod jest w języku Python 3.

Metoda dekoratora

To jest prawie to samo z metodą @ Mike'a. Tutaj dodaję kwargsi functoolszawijam, żeby było lepiej.

def timeit(func):
    @functools.wraps(func)
    def newfunc(*args, **kwargs):
        startTime = time.time()
        func(*args, **kwargs)
        elapsedTime = time.time() - startTime
        print('function [{}] finished in {} ms'.format(
            func.__name__, int(elapsedTime * 1000)))
    return newfunc

@timeit
def foobar():
    mike = Person()
    mike.think(30)

Metoda menedżera kontekstu

from contextlib import contextmanager

@contextmanager
def timeit_context(name):
    startTime = time.time()
    yield
    elapsedTime = time.time() - startTime
    print('[{}] finished in {} ms'.format(name, int(elapsedTime * 1000)))

Na przykład możesz go używać tak, jak:

with timeit_context('My profiling code'):
    mike = Person()
    mike.think()

Kod w withbloku będzie mierzony w czasie.

Wniosek

Korzystając z pierwszej metody, możesz codziennie skomentować dekorator, aby uzyskać normalny kod. Jednak może tylko mierzyć czas funkcji. Jeśli masz jakąś część kodu, której nie możesz uczynić z funkcji, możesz wybrać drugą metodę.

Na przykład teraz masz

images = get_images()
bigImage = ImagePacker.pack(images, width=4096)
drawer.draw(bigImage)

Teraz chcesz ustawić czas na bigImage = ...linii. Jeśli zmienisz to na funkcję, będzie to:

images = get_images()
bitImage = None
@timeit
def foobar():
    nonlocal bigImage
    bigImage = ImagePacker.pack(images, width=4096)
drawer.draw(bigImage)

Wygląda niezbyt dobrze ... A co jeśli jesteś w Pythonie 2, który nie ma nonlocalsłowa kluczowego.

Zamiast tego użycie drugiej metody pasuje tutaj bardzo dobrze:

images = get_images()
with timeit_context('foobar'):
    bigImage = ImagePacker.pack(images, width=4096)
drawer.draw(bigImage)

Ciekawy wkład, jednak uważam za bezużyteczne to, że we wspomnianej metodzie dekoratora trzeba było zmienić timeitinterfejs i skorzystać z wraps()funkcji functoolsmodułu. Chodzi mi o to, że cały ten dodatkowy kod nie jest potrzebny.
Billal Begueradj

1
Potrzebyimport functools
Guillaume Chevalier

1
Zwróć uwagę, że dekorator traci wartość zwracaną przez oryginalną funkcję
Marc Van Daele,

11

Nie wiem w czym jest problem z timeitmodułem. To prawdopodobnie najprostszy sposób na zrobienie tego.

import timeit
timeit.timeit(a, number=1)

Możliwe jest również przesyłanie argumentów do funkcji. Wszystko, czego potrzebujesz, to otoczyć swoją funkcję dekoratorami. Więcej wyjaśnień tutaj: http://www.pythoncentral.io/time-a-python-function/

Jedynym przypadkiem, w którym możesz być zainteresowany pisaniem własnych instrukcji dotyczących czasu, jest to, że chcesz uruchomić funkcję tylko raz i chcesz również uzyskać jej wartość zwracaną.

Zaletą korzystania z timeitmodułu jest to, że pozwala na powtórzenie liczby wykonań. Może to być konieczne, ponieważ inne procesy mogą wpływać na dokładność taktowania. Powinieneś więc uruchomić go wiele razy i spojrzeć na najniższą wartość.


3
Wysyłanie argumentów do funkcji za pomocą opakowań i dekoratorów? Dlaczego nie timeit.timeit(lambda: func(a,b,c), number=1)? Używam tego podczas testowania hipotetycznego rozwiązania w terminalu.
Jack

11

Timeit ma dwie duże wady: nie zwraca wartości zwracanej funkcji i używa funkcji eval, która wymaga przekazania dodatkowego kodu konfiguracyjnego do importu. To rozwiązuje oba problemy w prosty i elegancki sposób:

def timed(f):
  start = time.time()
  ret = f()
  elapsed = time.time() - start
  return ret, elapsed

timed(lambda: database.foo.execute('select count(*) from source.apachelog'))
(<sqlalchemy.engine.result.ResultProxy object at 0x7fd6c20fc690>, 4.07547402381897)

Dzięki! timeit nie działa dobrze z Apache Spark, ponieważ musisz zaimportować wszystkie zależności Spark, a kto chce utworzyć duży stary ciąg, który to robi? To rozwiązanie jest znacznie prostsze i bardziej elastyczne.
Paul

4

Istnieje proste narzędzie do pomiaru czasu. https://github.com/RalphMao/PyTimer

Może działać jak dekorator :

from pytimer import Timer
@Timer(average=False)      
def matmul(a,b, times=100):
    for i in range(times):
        np.dot(a,b)        

Wynik:

matmul:0.368434
matmul:2.839355

Może również działać jak zegar wtyczki z kontrolą przestrzeni nazw (przydatne, jeśli wstawiasz go do funkcji, która ma dużo kodów i może być wywołana gdziekolwiek indziej).

timer = Timer()                                           
def any_function():                                       
    timer.start()                                         

    for i in range(10):                                   

        timer.reset()                                     
        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
        timer.checkpoint('block1')                        

        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
        timer.checkpoint('block2')                        
        np.dot(np.ones((100,1000)), np.zeros((1000,1000)))

    for j in range(20):                                   
        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
    timer.summary()                                       

for i in range(2):                                        
    any_function()                                        

Wynik:

========Timing Summary of Default Timer========
block2:0.065062
block1:0.032529
========Timing Summary of Default Timer========
block2:0.065838
block1:0.032891

Mam nadzieję, że to pomoże


3

Metoda dekoratora przy użyciu biblioteki dekoratora w języku Python:

import decorator

@decorator
def timing(func, *args, **kwargs):
    '''Function timing wrapper
        Example of using:
        ``@timing()``
    '''

    fn = '%s.%s' % (func.__module__, func.__name__)

    timer = Timer()
    with timer:
        ret = func(*args, **kwargs)

    log.info(u'%s - %0.3f sec' % (fn, timer.duration_in_seconds()))
    return ret

Zobacz post na moim blogu:

post na blogu mobilepro.pl

mój post w Google Plus


1

Mój sposób na zrobienie tego:

from time import time

def printTime(start):
    end = time()
    duration = end - start
    if duration < 60:
        return "used: " + str(round(duration, 2)) + "s."
    else:
        mins = int(duration / 60)
        secs = round(duration % 60, 2)
        if mins < 60:
            return "used: " + str(mins) + "m " + str(secs) + "s."
        else:
            hours = int(duration / 3600)
            mins = mins % 60
            return "used: " + str(hours) + "h " + str(mins) + "m " + str(secs) + "s."

Ustaw zmienną tak jak start = time()przed wykonaniem funkcji / pętli i printTime(start)zaraz po bloku.

i masz odpowiedź.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.