Функция измерения времени Python


123

Я хочу создать функцию Python, чтобы проверить время, затраченное на каждую функцию, и распечатать ее имя с указанием времени, как я могу распечатать имя функции, и если есть другой способ сделать это, скажите мне

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

Инструменты профилирования Python могут показать вам имена функций и время, потраченное на каждую из них. Читайте здесь: docs.python.org/library/profile.html
Roadmaster,

Лучше использовать timeitдля измерения. Он не идеален, но он намного timeitлучше, чем вы, и его намного проще использовать, чем самому придумать что-то лучшее.

Ответы:


241

Прежде всего, я настоятельно рекомендую использовать профилировщик или, по крайней мере, timeit .

Однако, если вы хотите написать свой собственный метод синхронизации, чтобы научиться, вот где можно начать использовать декоратор.

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

И использование очень простое, просто используйте декоратор @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

Обратите внимание: я звоню, f.func_nameчтобы получить имя функции в виде строки (в Python 2) или f.__name__ в Python 3.


4
именно то, что я хочу :) ... но вы, ребята, убедили меня использовать профилировщик Python
Wazery

3
Похоже, это предполагает, что time.time () сообщает время в микросекундах с начала эпохи? В документации указано, что время указывается в секундах docs.python.org/2/library/time.html#time.time .
Рахул Джа

Это не может вступить в силу после использования yield в func. Как я могу использовать этот метод и использовать yield?
jiamo

def time (f): def wrap (* args, ** kwargs): time1 = time.time () ret = f (* args, ** kwargs) time2 = time.time () print '% s функция заняла% 0,3 f ms '% (f.func_name, (time2-time1) * 1000) return ret return wrap
Вивек Багария

1
в чем недостаток написания самому? Разве недостаточно просто хранить список прошедшего времени и изучать их распределение?
3pitt

51

После игры с timeitмодулем мне не нравится его интерфейс, который не так элегантен по сравнению с двумя следующими способами.

Следующий код находится на Python 3.

Метод декоратора

Это почти то же самое с методом @Mike. Здесь я добавляю kwargsи functoolsоборачиваю, чтобы было лучше.

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)

Метод диспетчера контекста

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

Например, вы можете использовать это как:

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

И код в withблоке будет синхронизирован.

Вывод

Используя первый метод, вы можете каждый день закомментировать декоратор, чтобы получить нормальный код. Однако это может только время функции. Если у вас есть часть кода, которую вы не знаете, чтобы сделать ее функцией, вы можете выбрать второй метод.

Например, теперь у вас есть

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

Теперь вы хотите измерить время bigImage = ...линии. Если вы измените его на функцию, это будет:

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

Выглядит не очень хорошо ... Что делать, если вы используете Python 2, в котором нет nonlocalключевого слова.

Вместо этого здесь очень подходит второй метод:

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

Интересный вклад, однако я считаю бесполезным то, что в упомянутом вами методе декоратора вам пришлось изменить timeitинтерфейс и использовать wraps()функцию functoolsмодуля. Я имею в виду, что весь этот дополнительный код не нужен.
Billal Begueradj

1
Потребностиimport functools
Гийом Шевалье

1
Обратите внимание, что ваш декоратор теряет возвращаемое значение исходной функции
Марк Ван

11

Я не понимаю, в чем проблема с timeitмодулем. Это, наверное, самый простой способ сделать это.

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

Также можно отправлять аргументы функциям. Все, что вам нужно, это завершить вашу функцию с помощью декораторов. Дополнительные объяснения здесь: http://www.pythoncentral.io/time-a-python-function/

Единственный случай, когда вы можете быть заинтересованы в написании собственных операторов времени, - это если вы хотите запустить функцию только один раз и также хотите получить ее возвращаемое значение.

Преимущество использования timeitмодуля в том, что он позволяет вам повторять количество выполнений. Это может быть необходимо, потому что другие процессы могут повлиять на точность синхронизации. Итак, вы должны запустить его несколько раз и посмотреть на наименьшее значение.


3
Отправка аргументов функции с помощью оберток и декораторов? А почему бы и нет timeit.timeit(lambda: func(a,b,c), number=1)? Я использую это при тестировании гипотетического решения в терминале.
Джек

11

Timeit имеет два больших недостатка: он не возвращает возвращаемое значение функции и использует eval, что требует передачи дополнительного кода настройки для импорта. Это решает обе проблемы просто и элегантно:

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)

Спасибо! timeit плохо работает с Apache Spark, потому что вам нужно импортировать все зависимости Spark, а кто хочет создать большую старую строку, которая это делает? Это решение намного проще и гибче.
Пол

4

Есть простой инструмент для расчета времени. https://github.com/RalphMao/PyTimer

Может работать как декоратор :

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

Вывод:

matmul:0.368434
matmul:2.839355

Он также может работать как таймер подключаемого модуля с контролем пространства имен (полезно, если вы вставляете его в функцию, которая имеет много кодов и может быть вызвана где угодно).

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

Вывод:

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

Надеюсь, это поможет


3

Метод декоратора с использованием библиотеки декоратора 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

См. Сообщение в моем блоге:

разместить в блоге mobilepro.pl

мой пост в Google Plus


1

Мой способ сделать это:

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

Установите переменную, как start = time()перед выполнением функции / циклов, и printTime(start)сразу после блока.

и вы получили ответ.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.