Точное время выполнения функций в python


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

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

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

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

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

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

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

7 60

7 ответов:

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

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

вы можете создать декоратор "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)

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

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 раз, чтобы заглушить планирование ОС и другие накладные расходы.

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

если вы хотите, чтобы время 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()__ метод будет вызван, даже если запрос соединения thows. Точнее, вы бы использовали tryfinally чтобы увидеть результат в случае, если он бросает, а с

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

более детально здесь.

это аккуратнее

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"

похоже на ответ @AlexMartelli

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

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