2009-10-26 1 views
44

Я пытаюсь ввести код. Во-первых, я использовал синхронизации декоратора:timeit против декодера времени

#!/usr/bin/env python 

import time 
from itertools import izip 
from random import shuffle 

def timing_val(func): 
    def wrapper(*arg, **kw): 
     '''source: http://www.daniweb.com/code/snippet368.html''' 
     t1 = time.time() 
     res = func(*arg, **kw) 
     t2 = time.time() 
     return (t2 - t1), res, func.__name__ 
    return wrapper 

@timing_val 
def time_izip(alist, n): 
    i = iter(alist) 
    return [x for x in izip(*[i] * n)] 

@timing_val 
def time_indexing(alist, n): 
    return [alist[i:i + n] for i in range(0, len(alist), n)] 

func_list = [locals()[key] for key in locals().keys() 
      if callable(locals()[key]) and key.startswith('time')] 
shuffle(func_list) # Shuffle, just in case the order matters 

alist = range(1000000) 
times = [] 
for f in func_list: 
    times.append(f(alist, 31)) 

times.sort(key=lambda x: x[0]) 
for (time, result, func_name) in times: 
    print '%s took %0.3fms.' % (func_name, time * 1000.) 

дающий

% test.py 
time_indexing took 73.230ms. 
time_izip took 122.057ms. 

И здесь я использую timeit:

% python - m timeit - s '' 'alist=range(1000000);[alist[i:i+31] for i in range(0, len(alist), 31)]' 
10 loops, best of 3: 
    64 msec per loop 
% python - m timeit - s 'from itertools import izip' 'alist=range(1000000);i=iter(alist);[x for x in izip(*[i]*31)]' 
10 loops, best of 3: 
    66.5 msec per loop 

Использование timeit результаты практически одинаковы, но с использованием синхронизации декоратора его появляется time_indexing быстрее, чем time_izip.

В чем заключается эта разница?

Должен ли считаться какой-либо метод?

Если да, то какой?

+1

Кстати, атрибут 'func_name' не надежно доступен для методов, только для функций, поэтому вместо этого вместо« __name__ »может быть предпочтительнее. Попробуйте использовать 'time.sleep'. –

+0

@ A-B-B: Спасибо за исправление. – unutbu

ответ

15

Использовать тайм-аут. Выполнение теста более одного раза дает мне гораздо лучшие результаты.

func_list=[locals()[key] for key in locals().keys() 
      if callable(locals()[key]) and key.startswith('time')] 

alist=range(1000000) 
times=[] 
for f in func_list: 
    n = 10 
    times.append(min( t for t,_,_ in (f(alist,31) for i in range(n)))) 

for (time,func_name) in zip(times, func_list): 
    print '%s took %0.3fms.' % (func_name, time*1000.) 

->

<function wrapper at 0x01FCB5F0> took 39.000ms. 
<function wrapper at 0x01FCB670> took 41.000ms. 
+0

Да, это, по-видимому, причина. Благодаря! – unutbu

+18

Как FYI, timeit также отключает сбор мусора в течение всего теста. Это может быть другой вопрос. –

2

Просто догадаться, но может ли разница быть величиной разницы в значениях диапазона()?

От оригинального источника:

alist=range(1000000) 

Ваш timeit Например:

alist=range(100000) 

Для чего это стоит, вот результаты на моей системе с диапазоном, установленным на 1 млн:

$ python -V 
Python 2.6.4rc2 

$ python -m timeit -s 'from itertools import izip' 'alist=range(1000000);i=iter(alist);[x for x in izip(*[i]*31)]' 
10 loops, best of 3: 69.6 msec per loop 

$ python -m timeit -s '' 'alist=range(1000000);[alist[i:i+31] for i in range(0, len(alist), 31)]' 
10 loops, best of 3: 67.6 msec per loop 

Мне не удалось запустить ваш другой код, так как я не мог импортировать модуль «декоратор» в моей системе.


Update - Я вижу то же самое расхождение вы делаете, когда я запускаю код без декоратора вовлеченного.

$ ./test.py 
time_indexing took 84.846ms. 
time_izip took 132.574ms. 

Благодарим за сообщение об этом вопросе; Сегодня я чему-то научился. =)

+0

Я удалил модуль декоратора, чтобы мой код был легче запускать. Не могли бы вы попробовать? Вы видите разницу в скорости при запуске скрипта? Кроме того, я изменил диапазон от 10^5 -> 10^6, поэтому сравнение более равно. Спасибо. – unutbu

+0

Обновлено, для чего это стоит, но похоже, что вы получили свой ответ сейчас. Без проблем. – mpontillo

0

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

6

Я устал от from __main__ import foo, теперь использую это - для простых аргументов, для которых% г работает, и не в IPython.
(Почему timeit работает только со строками, а не с трюками/закрываниями, то есть timefunc (f, произвольные args)?)


import timeit 

def timef(funcname, *args, **kwargs): 
    """ timeit a func with args, e.g. 
      for window in (3, 31, 63, 127, 255): 
       timef("filter", window, 0) 
    This doesn't work in ipython; 
    see Martelli, "ipython plays weird tricks with __main__" in Stackoverflow   
    """ 
    argstr = ", ".join([ "%r" % a for a in args]) if args else "" 
    kwargstr = ", ".join([ "%s=%r" % (k,v) for k,v in kwargs.items()]) \ 
     if kwargs else "" 
    comma = ", " if (argstr and kwargstr) else "" 
    fargs = "%s(%s%s%s)" % (funcname, argstr, comma, kwargstr) 
     # print "test timef:", fargs 
    t = timeit.Timer(fargs, "from __main__ import %s" % funcname) 
    ntime = 3 
    print "%.0f usec %s" % (t.timeit(ntime) * 1e6/ntime, fargs) 

#............................................................................... 
if __name__ == "__main__": 
    def f(*args, **kwargs): 
     pass 

    try: 
     from __main__ import f 
    except: 
     print "ipython plays weird tricks with __main__, timef won't work" 
    timef("f") 
    timef("f", 1) 
    timef("f", """ a b """) 
    timef("f", 1, 2) 
    timef("f", x=3) 
    timef("f", x=3) 
    timef("f", 1, 2, x=3, y=4) 

Добавлено: смотрите также «IPython играет странные трюки с главного», Мартелли в running-doctests-through-ipython

+0

Спасибо! Это, безусловно, облегчает падение функций во времени. Вы можете опустить 'argstr',' kwargstr', 'comma', если вы используете' fargs = '% s (*% s, **% s)'% (funcname, args, kwargs) ', но, возможно, это делает fargs a немного труднее читать. – unutbu

+1

Это не работает с большими массивами NumPy. –

24

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

import time 

def timeit(f): 

    def timed(*args, **kw): 

     ts = time.time() 
     result = f(*args, **kw) 
     te = time.time() 

     print 'func:%r args:[%r, %r] took: %2.4f sec' % \ 
      (f.__name__, args, kw, te-ts) 
     return result 

    return timed 

Использование декоратора легко использовать аннотации.

@timeit 
def compute_magic(n): 
    #function definition 
    #.... 

Или переименовать функцию, которую вы хотите использовать.

compute_magic = timeit(compute_magic) 
+6

Я считаю, что использование functools.wraps здесь будет небольшим улучшением. – kuzzooroo

+2

Из любопытства был этот ответ скопирован отсюда: https://www.andreas-jung.com/contents/a-python-decorator-for-measuring-the -проведение-время-методов – emschorsch

30

Используйте обертывание от functools, чтобы улучшить ответ Мэтта Олкока.

from functools import wraps 
from time import time 

def timing(f): 
    @wraps(f) 
    def wrap(*args, **kw): 
     ts = time() 
     result = f(*args, **kw) 
     te = time() 
     print 'func:%r args:[%r, %r] took: %2.4f sec' % \ 
      (f.__name__, args, kw, te-ts) 
     return result 
    return wrap 

В качестве примера:

@timing 
def f(a): 
    for _ in range(a): 
     i = 0 
    return -1 

метода ссылающихся на f обернутого @timing:

func:'f' args:[(100000000,), {}] took: 14.2240 sec 
f(100000000) 

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