Respostas:
A maneira como o timeit funciona é executar o código de instalação uma vez e depois fazer chamadas repetidas para uma série de instruções. Portanto, se você deseja testar a classificação, são necessários alguns cuidados para que uma passagem em uma classificação no local não afete a próxima passagem com dados já classificados (que, é claro, fariam o Timsort realmente brilhar, porque apresenta melhor desempenho quando os dados já estiverem parcialmente solicitados).
Aqui está um exemplo de como configurar um teste para classificação:
>>> 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
Observe que a série de instruções faz uma nova cópia dos dados não classificados em cada passagem.
Além disso, observe a técnica de tempo de execução do conjunto de medições sete vezes e mantendo apenas o melhor tempo - isso pode realmente ajudar a reduzir distorções de medição devido a outros processos em execução no seu sistema.
Essas são minhas dicas para usar o timeit corretamente. Espero que isto ajude :-)
.repeat(7,1000)
já faz isso (usando a mesma semente)! Portanto, sua solução é perfeita IMO.
.repeat(7, 1000)
vs. .repeat(2, 3500)
vs .repeat(35, 200
) deve depender de como o erro devido à carga do sistema se compara ao erro devido à variabilidade de entrada. No caso extremo, se o seu sistema estiver sempre sob carga pesada e você vir uma cauda longa e fina à esquerda da distribuição do tempo de execução (quando você o pega em um raro estado inativo), você pode até achar .repeat(7000,1)
mais útil do que .repeat(7,1000)
se não pode orçar mais de 7000 execuções.
Se você deseja usar timeit
em uma sessão interativa do Python, há duas opções convenientes:
Use o shell IPython . Possui a %timeit
função especial conveniente :
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
Em um intérprete padrão do Python, você pode acessar funções e outros nomes que você definiu anteriormente durante a sessão interativa importando-os da __main__
instrução setup:
>>> 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. Eu não acho que isso seja tão conhecido como deveria ser. É útil em casos como este em que uma chamada de função ou método está sendo cronometrada. Em outros casos (cronometrando uma série de etapas), é menos útil porque introduz uma sobrecarga de chamada de função.
%timeit f(x)
sys._getframe(N).f_globals
) deveriam ter sido o padrão desde o início.
Vou contar um segredo: a melhor maneira de usar timeit
é na linha de comando.
Na linha de comando, timeit
faz uma análise estatística adequada: informa quanto tempo levou a menor execução. Isso é bom porque todo erro no tempo é positivo. Portanto, o menor tempo tem o menor erro. Não há como obter um erro negativo, porque um computador nunca pode calcular mais rápido do que ele pode calcular!
Portanto, a interface da linha de comandos:
%~> python -m timeit "1 + 2"
10000000 loops, best of 3: 0.0468 usec per loop
Isso é bem simples, né?
Você pode configurar as coisas:
%~> python -m timeit -s "x = range(10000)" "sum(x)"
1000 loops, best of 3: 543 usec per loop
o que também é útil!
Se você desejar várias linhas, poderá usar a continuação automática do shell ou 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
Isso fornece uma configuração de
x = range(1000)
y = range(100)
e tempos
sum(x)
min(y)
Se você deseja ter scripts mais longos, pode ser tentado a mudar para timeit
dentro de um script Python. Sugiro evitar isso porque a análise e o tempo são simplesmente melhores na linha de comando. Em vez disso, costumo criar 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
Isso pode demorar um pouco mais devido às múltiplas inicializações, mas normalmente isso não é grande coisa.
Mas e se você quiser usar timeit
dentro do seu módulo?
Bem, a maneira mais simples é fazer:
def function(...):
...
timeit.Timer(function).timeit(number=NUMBER)
e isso dá a você tempo cumulativo ( não mínimo!) para executar esse número de vezes.
Para obter uma boa análise, use .repeat
e use o mínimo:
min(timeit.Timer(function).repeat(repeat=REPEATS, number=NUMBER))
Você normalmente deve combinar isso com, em functools.partial
vez de lambda: ...
diminuir a sobrecarga. Assim, você pode ter 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
Você também pode fazer:
timeit.timeit("...", setup="from __main__ import ...", number=NUMBER)
o que proporcionaria algo mais próximo da interface na linha de comando, mas de uma maneira muito menos interessante. O "from __main__ import ..."
permite usar o código do seu módulo principal dentro do ambiente artificial criado por timeit
.
Vale a pena notar que este é um invólucro de conveniência Timer(...).timeit(...)
e, portanto, não é particularmente bom no momento. Pessoalmente, prefiro usar Timer(...).repeat(...)
como mostrei acima.
Existem algumas ressalvas com timeit
isso em todos os lugares.
As despesas gerais não são contabilizadas. Digamos que você queira tempo x += 1
para descobrir quanto tempo leva a adição:
>>> python -m timeit -s "x = 0" "x += 1"
10000000 loops, best of 3: 0.0476 usec per loop
Bem, não são 0,0476 µs. Você só sabe que é menos que isso. Todo erro é positivo.
Portanto, tente encontrar uma sobrecarga pura :
>>> python -m timeit -s "x = 0" ""
100000000 loops, best of 3: 0.014 usec per loop
Essa é uma boa sobrecarga de 30% apenas por tempo! Isso pode distorcer enormemente os tempos relativos. Mas você realmente se importava apenas com a adição de horários; os tempos de pesquisa x
também precisam ser incluídos nas despesas gerais:
>>> python -m timeit -s "x = 0" "x"
100000000 loops, best of 3: 0.0166 usec per loop
A diferença não é muito maior, mas está lá.
Métodos de mutação são perigosos.
>>> python -m timeit -s "x = [0]*100000" "while x: x.pop()"
10000000 loops, best of 3: 0.0436 usec per loop
Mas isso está completamente errado! x
é a lista vazia após a primeira iteração. Você precisará reinicializar:
>>> python -m timeit "x = [0]*100000" "while x: x.pop()"
100 loops, best of 3: 9.79 msec per loop
Mas então você tem muita sobrecarga. Conta isso separadamente.
>>> python -m timeit "x = [0]*100000"
1000 loops, best of 3: 261 usec per loop
Observe que subtrair a sobrecarga é razoável aqui apenas porque a sobrecarga é uma fração pequena do tempo.
No seu exemplo, vale a pena notar que tanto a Insertion Sort quanto a Tim Sort têm comportamentos de tempo completamente incomuns para listas já classificadas. Isso significa que você precisará de uma random.shuffle
classificação intermediária, se quiser evitar prejudicar seus horários.
timeit
em um programa, mas funcionando da mesma maneira que a linha de comando? .
timeit
executa uma pass
declaração quando nenhum argumento é fornecido, o que, é claro, leva algum tempo. Se algum argumento for fornecido, nãopass
será executado, portanto, subtrair alguns usecs de cada momento seria incorreto. 0.014
para mim, esta é a maneira mais 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)
Isso funciona muito bem:
python -m timeit -c "$(cat file_name.py)"
permite configurar o mesmo dicionário em cada um dos itens a seguir e testar o tempo de execução.
O argumento de configuração é basicamente configurar o dicionário
Número é para executar o código 1000000 vezes. Não é a configuração, mas o stmt
Quando você executa isso, pode ver que o índice é muito mais rápido que o obtido. Você pode executá-lo várias vezes para ver.
O código basicamente tenta obter o valor de c no dicionário.
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))
Aqui estão os meus resultados, os seus serão diferentes.
por índice: 0.20900007452246427
por get: 0.54841166886888
basta passar todo o seu código como 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))
import timeit
def oct(x):
return x*x
timeit.Timer("for x in range(100): oct(x)", "gc.enable()").timeit()
gc.enable()
?
O módulo timeit interno funciona melhor na linha de comando do IPython.
Para cronometrar funções de dentro de um 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
Exemplo de como usar o interpretador Python REPL com função que aceita 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
Você criaria duas funções e depois executaria algo semelhante a isso. Observe que você deseja escolher o mesmo número de execução / execução para comparar apple com apple.
Isso foi testado no Python 3.7.
Aqui está o código para facilitar a cópia
!/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)
e tirar a diferença :-)