Ответ 1
Вместо написания собственного кода профилирования я предлагаю вам проверить встроенные профилировщики Python (profile
или cProfile
, в зависимости от ваших потребностей): http://docs.python.org/library/profile.html
Я программирую в python на окнах и хочу точно измерить время, необходимое для запуска функции. Я написал функцию "time_it", которая выполняет другую функцию, запускает ее и возвращает время, затраченное на выполнение.
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 мс. Я полагал, что моя функция будет переоценивать время, если что-нибудь, что здесь происходит?
Еще одно примечание: это относительное время функций по сравнению друг с другом, о котором я забочусь, а не о абсолютном времени, поскольку это, очевидно, будет меняться в зависимости от аппаратного обеспечения и других факторов.
Вместо написания собственного кода профилирования я предлагаю вам проверить встроенные профилировщики 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()__
будет вызываться, даже если запрашивается запрос на соединение. Точнее, вы бы использовали try
finally
, чтобы увидеть результат в случае его броска, например, с помощью
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)
может сделать трюк.