Cómo usar el módulo timeit

Entiendo el concepto de lo que hace el timeit , pero no estoy seguro de cómo implementarlo en mi código.

¿Cómo puedo comparar dos funciones, por ejemplo, insertion_sort y tim_sort , con timeit ?

La forma en que funciona timeit es ejecutar el código de configuración una vez y luego hacer llamadas repetidas a una serie de declaraciones. Por lo tanto, si desea probar la clasificación, se requiere algo de cuidado para que una pasada en una clasificación en el lugar no afecte la próxima pasada con datos ya clasificados (eso, por supuesto, haría que el Timsort realmente brille porque funciona mejor cuando los datos ya parcialmente ordenados).

Aquí hay un ejemplo de cómo configurar una prueba para clasificar:

 >>> import timeit >>> setup = ''' import random random.seed('slartibartfast') s = [random.random() for i in range(1000)] timsort = list.sort ''' >>> print min(timeit.Timer('a=s[:]; timsort(a)', setup=setup).repeat(7, 1000)) 0.334147930145 

Tenga en cuenta que la serie de declaraciones hace una copia nueva de los datos sin clasificar en cada paso.

Además, tenga en cuenta la técnica de sincronización de ejecutar el conjunto de mediciones siete veces y mantener solo el mejor momento; esto realmente puede ayudar a reducir las distorsiones de medición debido a otros procesos que se ejecutan en su sistema.

Esos son mis consejos para usar timeit correctamente. Espero que esto ayude 🙂

Si desea usar timeit en una sesión interactiva de Python, hay dos opciones convenientes:

  1. Usa el shell de IPython . Cuenta con la conveniente función especial %timeit :

     In [1]: def f(x): ...: return x*x ...: In [2]: %timeit for x in range(100): f(x) 100000 loops, best of 3: 20.3 us per loop 
  2. En un intérprete de Python estándar, puede acceder a las funciones y otros nombres que definió anteriormente durante la sesión interactiva importándolos desde __main__ en la statement de configuración:

     >>> def f(x): ... return x * x ... >>> import timeit >>> timeit.repeat("for x in range(100): f(x)", "from __main__ import f", number=100000) [2.0640320777893066, 2.0876040458679199, 2.0520210266113281] 

Te timeit un secreto: la mejor forma de usar timeit es en la línea de comandos.

En la línea de comandos, timeit realiza un análisis estadístico adecuado: le indica cuánto tiempo tomó la ejecución más corta. Esto es bueno porque todo error en el tiempo es positivo. Así que el tiempo más corto tiene el menor error. ¡No hay forma de obtener un error negativo porque una computadora nunca puede calcular más rápido de lo que puede!

Por lo tanto, la interfaz de línea de comandos:

 %~> python -m timeit "1 + 2" 10000000 loops, best of 3: 0.0468 usec per loop 

Eso es bastante simple, ¿eh?

Puede configurar cosas:

 %~> python -m timeit -s "x = range(10000)" "sum(x)" 1000 loops, best of 3: 543 usec per loop 

que es útil, también!

Si desea varias líneas, puede usar la continuación automática del shell o usar argumentos separados:

 %~> python -m timeit -s "x = range(10000)" -s "y = range(100)" "sum(x)" "min(y)" 1000 loops, best of 3: 554 usec per loop 

Eso da una configuración de

 x = range(1000) y = range(100) 

y tiempos

 sum(x) min(y) 

Si desea tener scripts más largos, puede tener la tentación de pasar al timeit dentro de un script de Python. Sugiero evitar eso porque el análisis y la sincronización son simplemente mejores en la línea de comandos. En cambio, tiendo a hacer shell scripts:

  SETUP=" ... # lots of stuff " echo Minmod arr1 python -m timeit -s "$SETUP" "Minmod(arr1)" echo pure_minmod arr1 python -m timeit -s "$SETUP" "pure_minmod(arr1)" echo better_minmod arr1 python -m timeit -s "$SETUP" "better_minmod(arr1)" ... etc 

Esto puede tardar un poco más debido a las múltiples inicializaciones, pero normalmente no es un gran problema.


¿Pero qué timeit si quieres usar timeit dentro de tu módulo?

Bueno, la forma más sencilla es hacer:

 def function(...): ... timeit.Timer(function).timeit(number=NUMBER) 

y eso le da tiempo acumulativo (¡ no mínimo!) para ejecutar ese número de veces.

Para obtener un buen análisis, use .repeat y tome el mínimo:

 min(timeit.Timer(function).repeat(repeat=REPEATS, number=NUMBER)) 

Normalmente debería combinar esto con functools.partial lugar de lambda: ... para reducir los gastos generales. Así podrías tener algo como:

 from functools import partial def to_time(items): ... test_items = [1, 2, 3] * 100 times = timeit.Timer(partial(to_time, test_items)).repeat(3, 1000) # Divide by the number of repeats time_taken = min(times) / 1000 

También puedes hacer:

 timeit.timeit("...", setup="from __main__ import ...", number=NUMBER) 

que le daría algo más cercano a la interfaz desde la línea de comandos, pero de una manera mucho menos interesante. El "from __main__ import ..." permite usar el código de su módulo principal dentro del entorno artificial creado por timeit .

Vale la pena señalar que esta es una envoltura de conveniencia para Timer(...).timeit(...) y por lo tanto no es particularmente buena en el momento Personalmente prefiero usar el Timer(...).repeat(...) como se muestra arriba.


Advertencias

Hay algunas advertencias con el timeit que se mantienen en todas partes.

  • La sobrecarga no se tiene en cuenta. Digamos que quieres calcular el tiempo x += 1 , para saber cuánto tarda la sum:

     >>> python -m timeit -s "x = 0" "x += 1" 10000000 loops, best of 3: 0.0476 usec per loop 

    Bueno, no es 0.0476 µs. Solo sabes que es menos que eso. Todo error es positivo.

    Así que trata de encontrar pura sobrecarga:

     >>> python -m timeit -s "x = 0" "" 100000000 loops, best of 3: 0.014 usec per loop 

    ¡Eso es un buen 30% de sobrecarga solo por tiempo! Esto puede sesgar masivamente los tiempos relativos. Pero solo te importaban los tiempos añadidos; los tiempos de búsqueda para x también deben incluirse en la sobrecarga:

     >>> python -m timeit -s "x = 0" "x" 100000000 loops, best of 3: 0.0166 usec per loop 

    La diferencia no es mucho mayor, pero está ahí.

  • Los métodos de mutación son peligrosos.

     >>> python -m timeit -s "x = [0]*100000" "while x: x.pop()" 10000000 loops, best of 3: 0.0436 usec per loop 

    ¡Pero eso es completamente incorrecto! x es la lista vacía después de la primera iteración. Tendrá que reinicializar:

     >>> python -m timeit "x = [0]*100000" "while x: x.pop()" 100 loops, best of 3: 9.79 msec per loop 

    Pero entonces tienes muchos gastos generales. Cuenta para eso por separado.

     >>> python -m timeit "x = [0]*100000" 1000 loops, best of 3: 261 usec per loop 

    Tenga en cuenta que restar la sobrecarga es razonable aquí solo porque la sobrecarga es una pequeña fracción del tiempo.

    Para su ejemplo, vale la pena señalar que tanto la ordenación de inserción como la ordenación de Tim tienen comportamientos de tiempo completamente inusuales para listas ya ordenadas. Esto significa que necesitarás un random.shuffle entre clases para evitar arruinar tus tiempos.

Si quieres comparar dos bloques de código / funciones rápidamente, puedes hacer:

 import timeit start_time = timeit.default_timer() func1() print(timeit.default_timer() - start_time) start_time = timeit.default_timer() func2() print(timeit.default_timer() - start_time) 

Encuentro que la forma más fácil de usar timeit es desde la línea de comandos:

Dado test.py :

 def InsertionSort(): ... def TimSort(): ... 

corre el tiempo de esta manera:

 % python -mtimeit -s'import test' 'test.InsertionSort()' % python -mtimeit -s'import test' 'test.TimSort()' 
 # Генерация целых чисел def gen_prime(x): multiples = [] results = [] for i in range(2, x+1): if i not in multiples: results.append(i) for j in range(i*i, x+1, i): multiples.append(j) return results import timeit # Засекаем время start_time = timeit.default_timer() gen_prime(3000) print(timeit.default_timer() - start_time) # start_time = timeit.default_timer() # gen_prime(1001) # print(timeit.default_timer() - start_time) 

Para mí, esta es la manera más rápida:

 import timeit def foo(): print("here is my code to time...") timeit.timeit(stmt=foo, number=1234567) 

Esto funciona muy bien:

  python -m timeit -c "$(cat file_name.py)" 

permite configurar el mismo diccionario en cada uno de los siguientes y probar el tiempo de ejecución.

El argumento de configuración es básicamente configurar el diccionario.

Número es ejecutar el código 1000000 veces. No la configuración sino el stmt

Cuando ejecutas esto puedes ver que el índice es mucho más rápido que obtener. Puedes ejecutarlo varias veces para ver.

El código básicamente intenta obtener el valor de c en el diccionario.

 import timeit print('Getting value of C by index:', timeit.timeit(stmt="mydict['c']", setup="mydict={'a':5, 'b':6, 'c':7}", number=1000000)) print('Getting value of C by get:', timeit.timeit(stmt="mydict.get('c')", setup="mydict={'a':5, 'b':6, 'c':7}", number=1000000)) 

Aquí están mis resultados, los tuyos serán diferentes.

por índice: 0.20900007452246427

por obtener: 0.54841166886888

simplemente pasa tu código completo como un argumento de timeit:

 import timeit print(timeit.timeit(""" limit = 10000 prime_list = [i for i in range(2, limit+1)] for prime in prime_list: for elem in range(prime*2, max(prime_list)+1, prime): if elem in prime_list: prime_list.remove(elem)""" , number=10)) 

El módulo timeit incorporado funciona mejor desde la línea de comandos de IPython.

Para progtwigr funciones desde dentro de un módulo:

 from timeit import default_timer as timer import sys def timefunc(func, *args, **kwargs): """Time a function. args: iterations=3 Usage example: timeit(myfunc, 1, b=2) """ try: iterations = kwargs.pop('iterations') except KeyError: iterations = 3 elapsed = sys.maxsize for _ in range(iterations): start = timer() result = func(*args, **kwargs) elapsed = min(timer() - start, elapsed) print(('Best of {} {}(): {:.9f}'.format(iterations, func.__name__, elapsed))) return result 

Ejemplo de cómo usar el intérprete REPL de Python con una función que acepta parámetros.

 >>> import timeit >>> def naive_func(x): ... a = 0 ... for i in range(a): ... a += i ... return a >>> def wrapper(func, *args, **kwargs): ... def wrapper(): ... return func(*args, **kwargs) ... return wrapper >>> wrapped = wrapper(naive_func, 1_000) >>> timeit.timeit(wrapped, number=1_000_000) 0.4458435332577161 

Deberías crear dos funciones y luego ejecutar algo similar a esto. Tenga en cuenta que desea elegir el mismo número de ejecución / ejecución para comparar manzana con manzana.
Esto fue probado bajo Python 3.7.

introduzca la descripción de la imagen aquí Aquí está el código para facilitar su copia.

 !/usr/local/bin/python3 import timeit def fibonacci(n): """ Returns the n-th Fibonacci number. """ if(n == 0): result = 0 elif(n == 1): result = 1 else: result = fibonacci(n-1) + fibonacci(n-2) return result if __name__ == '__main__': import timeit t1 = timeit.Timer("fibonacci(13)", "from __main__ import fibonacci") print("fibonacci ran:",t1.timeit(number=1000), "milliseconds") 
 import timeit def oct(x): return x*x timeit.Timer("for x in range(100): oct(x)", "gc.enable()").timeit()