Respuestas:
La forma en que funciona timeit es ejecutar el código de configuración una vez y luego realizar llamadas repetidas a una serie de declaraciones. Por lo tanto, si desea probar la clasificación, es necesario tener cuidado para que una pasada en una ordenación en el lugar no afecte a la siguiente pasada con datos ya ordenados (eso, por supuesto, haría que el Timsort realmente brille porque funciona mejor) cuando los datos ya están parcialmente ordenados).
Aquí hay un ejemplo de cómo configurar una prueba para ordenar:
>>> 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 pase.
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 debidas a otros procesos que se ejecutan en su sistema.
Esos son mis consejos para usar Timeit correctamente. Espero que esto ayude :-)
.repeat(7,1000)
ya lo haces (usando la misma semilla)! Entonces su solución es IMO perfecta.
.repeat(7, 1000)
vs .repeat(2, 3500)
vs .repeat(35, 200
) debería depender de cómo el error debido a la carga del sistema se compara con el error debido a la variabilidad de entrada. En el caso extremo, si su sistema está siempre bajo una carga pesada, y ve una cola larga y delgada a la izquierda de la distribución del tiempo de ejecución (cuando lo atrapa en un estado inactivo raro), incluso podría .repeat(7000,1)
ser más útil que .repeat(7,1000)
si no puede presupuestar más de 7000 ejecuciones.
Si desea usar timeit
en una sesión interactiva de Python, hay dos opciones convenientes:
Use el shell de IPython . Cuenta con la conveniente %timeit
función especial:
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
En un intérprete estándar de Python, puede acceder a las funciones y otros nombres que definió anteriormente durante la sesión interactiva importándolos desde __main__
la declaración 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]
from __main__ import f
técnica. No creo que esto sea tan conocido como debería ser. Es útil en casos como este donde se está cronometrando una llamada a una función o método. En otros casos (cronometrar una serie de pasos), es menos útil porque introduce sobrecarga de llamadas de función.
%timeit f(x)
sys._getframe(N).f_globals
) deberían haber sido los predeterminados desde el principio.
Te contaré un secreto: la mejor manera de usarlo timeit
es en la línea de comando.
En la línea de comando, 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. Entonces, el menor tiempo 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 calcular!
Entonces, 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?
Puedes configurar cosas:
%~> python -m timeit -s "x = range(10000)" "sum(x)"
1000 loops, best of 3: 543 usec per loop
¡lo cual también es útil!
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 secuencias de comandos más largas, puede sentirse tentado a moverse timeit
dentro de una secuencia de comandos de Python. Sugiero evitar eso porque el análisis y el tiempo son simplemente mejores en la línea de comando. En cambio, tiendo a hacer scripts de shell:
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 eso no es gran cosa.
Pero que pasa si quieres usartimeit
dentro de tu módulo?
Bueno, la forma simple es hacer:
def function(...):
...
timeit.Timer(function).timeit(number=NUMBER)
y eso te da 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 debe combinar esto con en functools.partial
lugar de lambda: ...
reducir los gastos generales. Por lo tanto, podría 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)
lo que le daría algo más cercano a la interfaz desde la línea de comandos, pero de una manera mucho menos genial. Le "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 este es un envoltorio conveniente Timer(...).timeit(...)
y, por lo tanto, no es particularmente bueno en el momento. Yo personalmente prefiero usarTimer(...).repeat(...)
como he mostrado anteriormente.
Hay algunas advertencias timeit
que se mantienen en todas partes.
Los gastos generales no se contabilizan. Digamos que quiere tiempo x += 1
, para averiguar cuánto tiempo lleva la suma:
>>> python -m timeit -s "x = 0" "x += 1"
10000000 loops, best of 3: 0.0476 usec per loop
Bueno, es que no 0,0476 mu s. Solo sabes que es menos que eso. Todo error es positivo.
Así que trata de encontrar gastos generales puros :
>>> python -m timeit -s "x = 0" ""
100000000 loops, best of 3: 0.014 usec per loop
¡Es un buen 30% de gastos generales solo por el tiempo! Esto puede sesgar masivamente los tiempos relativos. Pero sólo se preocupaba por las añadiendo tiempos; los tiempos de búsqueda x
también deben incluirse en los gastos generales:
>>> 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 está completamente mal! x
es la lista vacía después de la primera iteración. Deberá 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 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 fracción pequeña del tiempo.
Para su ejemplo, vale la pena señalar que tanto el orden de inserción como el orden de tiempo tienen comportamientos de tiempo completamente inusuales para las listas ya ordenadas. Esto significa que necesitará un random.shuffle
tipo intermedio si desea evitar arruinar sus tiempos.
timeit
de un programa pero que funciona de la misma manera que la línea de comandos. .
timeit
ejecuta una pass
declaración cuando no se dan argumentos, lo que, por supuesto, lleva algún tiempo. Si no se da ningún argumento, pass
se no se ejecutan, por lo que resta de algunos 0.014
usecs de cada momento sería incorrecto.
Para mí, esta es la forma más rápida:
import timeit
def foo():
print("here is my code to time...")
timeit.timeit(stmt=foo, number=1234567)
# Генерация целых чисел
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)
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
El número es para ejecutar el código 1000000 veces. No es la configuración sino el stmt
Cuando ejecuta esto, puede ver que el índice es mucho más rápido que get. Puede ejecutarlo varias veces para ver.
El código básicamente trata de 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 suyos serán diferentes.
por índice: 0.20900007452246427
por get: 0.54841166886888
simplemente pase su código completo como argumento de tiempo:
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))
import timeit
def oct(x):
return x*x
timeit.Timer("for x in range(100): oct(x)", "gc.enable()").timeit()
gc.enable()
?
El módulo timeit incorporado funciona mejor desde la línea de comandos de IPython.
Para cronometrar funciones desde 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
Crearía dos funciones y luego ejecutaría 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.
Aquí está el código para facilitar la 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")
timsort(a)
y tome la diferencia :-)