2009-05-20 3 views
55

Я программирую на python на окнах и хочу точно измерить время, необходимое для запуска функции. Я написал функцию «time_it», которая выполняет другую функцию, запускает ее и возвращает время, затраченное на выполнение.Точная синхронизация функций в python

def time_it(f, *args): 
    start = time.clock() 
    f(*args) 
    return (time.clock() - start)*1000 

Я называю это 1000 раз и усредняет результат. (константа 1000 в конце - дать ответ в миллисекундах.)

Эта функция работает, но у меня есть это чувство, когда я делаю что-то неправильно, и что, делая это таким образом, я использую больше времени, чем функция фактически использует при ее запуске.

Есть ли более стандартный или общепринятый способ сделать это?

Когда я изменил свою тестовую функцию, чтобы вызвать печать, чтобы она заняла больше времени, моя функция time_it возвращает среднее значение в 2,5 мс, тогда как cProfile.run ('f()') возвращается и в среднем составляет 7,0 мс. Я полагал, что моя функция будет переоценивать время, если что-нибудь, что здесь происходит?

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

ответ

33

Вместо того, чтобы писать собственный код профилирование, я предлагаю вам проверить встроенные профайлеры Python (profile или cProfile, в зависимости от ваших потребностей): http://docs.python.org/library/profile.html

+0

Игнорировать меня - эта строка не является именем функции, это блок кода eval'd. Таким образом, вы можете использовать его для быстрого выбора времени. Это правильный ответ. А в других новостях - «нет» значительно быстрее, чем «! =» - но может иметь и другие последствия. –

+1

И от этого касания - перед использованием «не» для чего-нибудь умного - помните об этом - http://stackoverflow.com/questions/1392433/python-why-is-hello-is-hello –

65

Используйте из стандартной библиотеки Python.

Базовое использование:

from timeit import Timer 

# first argument is the code to be run, the second "setup" argument is only run once, 
# and it not included in the execution time. 
t = Timer("""x.index(123)""", setup="""x = range(1000)""") 

print t.timeit() # prints float, for example 5.8254 
# ..or.. 
print t.timeit(1000) # repeat 1000 times instead of the default 1million 
+2

Мне нужна моя функция для вызова с разными аргументами, но когда я вызываю t = timeit.Timer («f()», «from ___main___ import f») с разными аргументами и снова запускаю t.timeit (10000), я получаю те же результаты, разные аргументы должны приводить к очень разным временам работы. –

20

Этот код очень неточный

total= 0 
for i in range(1000): 
    start= time.clock() 
    function() 
    end= time.clock() 
    total += end-start 
time= total/1000 

Этого код менее неточный

start= time.clock() 
for i in range(1000): 
    function() 
end= time.clock() 
time= (end-start)/1000 

Очень неточное страдает от смещения измерения, если во время выполнения функции близка к точности часов. Большинство измеренных времен - это просто случайные числа от 0 до нескольких тиков часов.

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

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

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

+1

Почему/1000? Метод time.clock() возвращает секунды как значение с плавающей запятой. Если бы вы ожидали, что он вернет миллисекунды, это будет иметь смысл, однако деление на 1000 преобразований на килосекунды, единица, которую я никогда раньше не видел. – pixelgrease

+0

@pixelgrease milli/1000 = micro, not kilo :) – stenci

+0

@stenci Он предполагает, что результирующее значение находится в секундах, например, 1000 секунд. Если вы разделите его на 1000, вы получите 1 "килосекунды". – zehelvion

13

Если вы хотите использовать метод python, даже если блокировка, которую вы измеряете, может бросить, один хороший подход - использовать оператор with. Определить некоторые Timer класса как

import time 

class Timer:  
    def __enter__(self): 
     self.start = time.clock() 
     return self 

    def __exit__(self, *args): 
     self.end = time.clock() 
     self.interval = self.end - self.start 

Тогда вы можете время способа подключения, который может бросить.Использование

import httplib 

with Timer() as t: 
    conn = httplib.HTTPConnection('google.com') 
    conn.request('GET', '/') 

print('Request took %.03f sec.' % t.interval) 

__exit()__ Метод будет вызван, даже если запрос на соединение будет. Точнее, вы должны использовать tryfinally, чтобы увидеть результат в случае, если он бросает, как и с

try: 
    with Timer() as t: 
     conn = httplib.HTTPConnection('google.com') 
     conn.request('GET', '/') 
finally: 
    print('Request took %.03f sec.' % t.interval) 

More details here.

23

Вы можете создать «TimeMe» декоратор, как так

import time             

def timeme(method): 
    def wrapper(*args, **kw): 
     startTime = int(round(time.time() * 1000)) 
     result = method(*args, **kw) 
     endTime = int(round(time.time() * 1000)) 

     print(endTime - startTime,'ms') 
     return result 

    return wrapper 

@timeme 
def func1(a,b,c = 'c',sleep = 1): 
    time.sleep(sleep) 
    print(a,b,c) 

func1('a','b','c',0) 
func1('a','b','c',0.5) 
func1('a','b','c',0.6) 
func1('a','b','c',1) 
+1

+ n для этого ответа. Хотелось бы, чтобы был такой вариант. Красота - я могу экспортировать результаты журнала во внешний файл и добавлять это везде, где мне нужно! Спасибо, миллион. – curlyreggie

+5

Это отлично работает для нерекурсивных функций. Для рекурсивных функций он возвращает время для каждой итерации функции. – tachijuan

6

Это neater

from contextlib import contextmanager 

import time 
@contextmanager 
def timeblock(label): 
    start = time.clock() 
    try: 
     yield 
    finally: 
     end = time.clock() 
     print ('{} : {}'.format(label, end - start)) 



with timeblock("just a test"): 
      print "yippee" 
+0

Приятный ответ, простой и пока единственный, который я нашел, позволяет отправлять ярлык функции таймера. – Gabriel

+0

Удобное решение, но немного сложнее. Провел час, пытаясь выяснить, почему 'time.sleep (10)' всего лишь потратил 0,002 секунды для выполнения в соответствии с этим кодом. (btw, большая разница между 'time.clock()' и 'time.time()' в python) – swdev

4

Подобно @ AlexMartelli ответ

import timeit 
timeit.timeit(fun, number=10000) 

может сделать трюк.

Смежные вопросы