"timeit" --- Mede o tempo de execução de pequenos trechos de código
*******************************************************************

**Código-fonte:** Lib/timeit.py

======================================================================

Este módulo fornece uma maneira simples de cronometrar pequenos
trechos do código Python. Ele tem uma Interface de linha de comando e
um chamável.  Ele evita uma série de armadilhas comuns para medir os
tempos de execução de um script. Veja também o capítulo "Algorithms"
de Tim Peters, na segunda edição do *Python Cookbook*, publicado pela
O'Reilly.


Exemplos básicos
================

O exemplo a seguir mostra como a Interface de linha de comando pode
ser usado para comparar três expressões diferentes:

   $ python3 -m timeit '"-".join(str(n) for n in range(100))'
   10000 loops, best of 5: 30.2 usec per loop
   $ python3 -m timeit '"-".join([str(n) for n in range(100)])'
   10000 loops, best of 5: 27.5 usec per loop
   $ python3 -m timeit '"-".join(map(str, range(100)))'
   10000 loops, best of 5: 23.2 usec per loop

Isso pode ser obtido da interface Interface em Python

   >>> import timeit
   >>> timeit.timeit('"-".join(str(n) for n in range(100))', number=10000)
   0.3018611848820001
   >>> timeit.timeit('"-".join([str(n) for n in range(100)])', number=10000)
   0.2727368790656328
   >>> timeit.timeit('"-".join(map(str, range(100)))', number=10000)
   0.23702679807320237

Um chamável também pode ser passado para a Interface em Python:

   >>> timeit.timeit(lambda: "-".join(map(str, range(100))), number=10000)
   0.19665591977536678

Observe, entretanto, que "timeit()" determinará automaticamente o
número de repetições somente quando a interface de linha de comando
for usada. Na seção Exemplos você encontrará exemplos mais avançados.


Interface em Python
===================

Este módulo define três funções e uma classe pública:

timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None)

   Cria uma instância de "Timer" com o código de *setup* e a função
   *timer* função para executar o método "timeit()" com o total de
   execuções informado em *number*. O argumento opcional *globals*
   especifica um espaço de nomes no qual o código será executado.

   Alterado na versão 3.5: O parâmetro opcional *globals* foi
   adicionado.

timeit.repeat(stmt='pass', setup='pass', timer=<default timer>, repeat=5, number=1000000, globals=None)

   Cria uma instância "Timer" com a instrução fornecida, o código de
   *setup* e a função *timer* e para executar o método "repeat()" com
   o total de execuções informando em *repeat* e o *número* de
   execuções fornecidos. O argumento opcional *globals* especifica um
   espaço de nomes no qual executar o código.

   Alterado na versão 3.5: O parâmetro opcional *globals* foi
   adicionado.

   Alterado na versão 3.7: Valor padrão de *repetição* mudou de 3 para
   5.

timeit.default_timer()

   O cronômetro padrão, que é sempre time.perf_counter(), retorna
   segundos com ponto flutuante. Uma alternativa,
   time.perf_counter_ns, retorna um inteiro em nanossegundos.

   Alterado na versão 3.3: "time.perf_counter()" é o cronômetro padrão
   agora.

class timeit.Timer(stmt='pass', setup='pass', timer=<timer function>, globals=None)

   Classe para cronometrar a velocidade de execução de pequenos
   trechos de código.

   O construtor recebe uma instrução a ser cronometrada, uma instrução
   adicional usada para configuração e uma função de timer. Ambas as
   instruções têm como padrão "'pass'"; a função de timer é dependente
   da plataforma (veja a string do documento do módulo). *stmt* e
   *setup* também podem conter múltiplas instruções separadas por ";"
   ou novas linhas, desde que não contenham literais de string
   multilinhas. A instrução será, por padrão, executada dentro do
   espaço de nomes do *timeit*; esse comportamento pode ser controlado
   passando um espaço de nomes para *globals*.

   Para medir o tempo de execução da primeira instrução use o método
   "timeit()". Os métodos "repeat()" e "autorange()" são convenientes
   para chamar "timeit()" várias vezes.

   O tempo de execução de *setup* é excluído do tempo total de
   execução cronometrado.

   Os parâmetros *stmt* e *setup* também podem receber objetos que
   podem ser chamados sem argumentos. Isso incorporará chamadas a eles
   em uma função de timer que será executada por "timeit()". Observe
   que a sobrecarga de temporização é um pouco maior neste caso por
   causa das chamadas de função extras.

   Alterado na versão 3.5: O parâmetro opcional *globals* foi
   adicionado.

   timeit(number=1000000)

      Mede o tempo para a quantidade *number* de execuções da
      instrução principal. Isso executa a instrução setup uma vez e,
      em seguida, retorna o tempo necessário para executar a instrução
      principal várias vezes. O temporizador padrão retorna segundos
      como um float. O argumento é o número de vezes que o laço passa,
      com padrão de um milhão. A instrução principal, a instrução de
      configuração e a função de timer a ser usada são passadas para o
      construtor.

      Nota:

        Por padrão, "timeit()" desativa temporariamente *coleta de
        lixo* durante a temporização. A vantagem dessa abordagem é que
        ela torna temporizações independentes mais comparáveis. A
        desvantagem é que o GC pode ser um componente importante do
        desempenho da função que está sendo medida. Se for assim, o GC
        pode ser reativado como a primeira instrução na string
        *setup*. Por exemplo:

           timeit.Timer('for i in range(10): oct(i)', 'gc.enable()').timeit()

   autorange(callback=None)

      Determina automaticamente quantas vezes chamar "timeit()".

      Esta é uma função de conveniência que chama "timeit()"
      repetidamente para que o tempo total seja >= 0,2 segundos,
      retornando o eventual (número de voltas, tempo gasto para esse
      número de voltas). Ela chama "timeit()" com números crescentes
      da sequência 1, 2, 5, 10, 20, 50, ... até que o tempo gasto seja
      de pelo menos 0,2 segundos.

      Se *callback* for fornecido e não for "None", ele será chamado
      após cada tentativa e tem dois argumento: "callback(number,
      time_taken)".

      Novo na versão 3.6.

   repeat(repeat=5, number=1000000)

      Chama "timeit()" algumas vezes.

      Esse é um função de conveniência que chama o "timeit()"
      repetidamente e retorna uma lista de resultados.  O primeiro
      argumento especifica quantas vezes deve chamar o "timeit()".  O
      segundo argumento especifica o argumento *number* para
      "timeit()".

      Nota:

        É tentador calcular a média e o desvio padrão do vetor de
        resultados e relatá-los. No entanto, isso não é muito útil. Em
        um caso típico, o menor valor fornece um limite inferior para
        a velocidade com que sua máquina pode executar o trecho de
        código fornecido; valores mais altos no vetor de resultados
        normalmente não são causados pela variabilidade na velocidade
        do Python, mas por outros processos que interferem na precisão
        do seu tempo. Portanto, o "min()" do resultado é provavelmente
        o único número no qual você deve se interessar. Depois disso,
        você deve analisar o vetor inteiro e aplicar o bom senso em
        vez de estatística.

      Alterado na versão 3.7: Valor padrão de *repetição* mudou de 3
      para 5.

   print_exc(file=None)

      Função auxiliar para imprimir um traceback do código
      cronometrado.

      Uso típico:

         t = Timer(...)       # outside the try/except
         try:
             t.timeit(...)    # or t.repeat(...)
         except Exception:
             t.print_exc()

      A vantagem em relação ao traceback padrão é que as linhas de
      origem no modelo compilado serão exibidas.  O argumento opcional
      *file* direciona para onde o traceback é enviado; o padrão é
      "sys.stderr".


Interface de linha de comando
=============================

Quando chamado como um programa a partir da linha de comando, as
seguintes opções estão disponíveis:

   python -m timeit [-n N] [-r N] [-u U] [-s S] [-h] [statement ...]

As seguintes opções são permitidas

-n N, --number=N

   Quantas vezes deve executar 'statement'

-r N, --repeat=N

   Quantidade de vezes para repetir o cronômetro (o valor padrão é 5)

-s S, --setup=S

   instrução a ser executada apenas uma vez e quando iniciada (padrão
   "pass" )

-p, --process

   mede apenas o tempo de processamento, e não o tempo total de
   execução, usando "time.process_time()" em vez de
   "time.perf_counter()", que é o padrão

   Novo na versão 3.3.

-u, --unit=U

   especifique uma unidade de tempo para a saída do cronômetro; pode
   selecionar "nsec", "usec", "msec", ou "sec"

   Novo na versão 3.5.

-v, --verbose

   imprime resultados brutos de tempo; repetir para obter mais
   precisão de dígitos

-h, --help

   imprime uma mensagem curta de uso e sai

Uma instrução multilinha pode ser fornecida especificando cada linha
como um argumento de instrução separado; linhas indentadas são
possíveis colocando um argumento entre aspas e usando espaços à
esquerda. Múltiplas opções "-s" são tratadas de forma semelhante.

Se "-n" não for informada, um número adequado de loops será calculado
tentando adicionar números numa sequência como 1, 2, 5, 10, 20, 50,
... até que o tempo total seja de pelo menos 0,2 segundos.

As medições de "default_timer()" podem ser afetadas por outros
programas em execução na mesma máquina, portanto, a melhor coisa a
fazer quando uma cronometragem precisa é repeti-la algumas vezes e
usar o melhor tempo. A opção "-r" é boa para isso; o padrão de 5
repetições provavelmente é suficiente na maioria dos casos. Você pode
usar "time.process_time()" para medir o tempo de CPU.

Nota:

  Há uma certa sobrecarga padrão associada à execução de uma instrução
  *pass*. O código aqui não tenta ocultá-lo, mas você deve estar
  ciente disso.  A sobrecarga padrão pode ser medida invocando pelo
  programa sem argumento, e pode ser diferente entre diferentes
  versões Python.


Exemplos
========

É possível fornecer uma instrução de configuração que é executada
apenas uma vez no início:

   $ python -m timeit -s 'text = "sample string"; char = "g"'  'char in text'
   5000000 loops, best of 5: 0.0877 usec per loop
   $ python -m timeit -s 'text = "sample string"; char = "g"'  'text.find(char)'
   1000000 loops, best of 5: 0.342 usec per loop

Na saída, existem três campos. A contagem de laços, que informa
quantas vezes o corpo da instrução foi executado por repetição do laço
de temporização. A contagem de repetições ('melhor de 5') que informa
quantas vezes o laço de temporização foi repetido e, finalmente, o
tempo que o corpo da instrução levou, em média, na melhor repetição do
laço de temporização. Ou seja, o tempo necessário para a repetição
mais rápida dividido pela contagem de interações.

   >>> import timeit
   >>> timeit.timeit('char in text', setup='text = "sample string"; char = "g"')
   0.41440500499993504
   >>> timeit.timeit('text.find(char)', setup='text = "sample string"; char = "g"')
   1.7246671520006203

O mesmo pode ser feito usando a classe "Timer" e seus métodos:

   >>> import timeit
   >>> t = timeit.Timer('char in text', setup='text = "sample string"; char = "g"')
   >>> t.timeit()
   0.3955516149999312
   >>> t.repeat()
   [0.40183617287970225, 0.37027556854118704, 0.38344867356679524, 0.3712595970846668, 0.37866875250654886]

Os exemplos a seguir mostram como cronometrar expressões que contêm
várias linhas. Aqui comparamos o custo de usar "hasattr()" vs.
"try"/"except" para testar atributos de objetos presentes e ausentes:

   $ python -m timeit 'try:' '  str.__bool__' 'except AttributeError:' '  pass'
   20000 loops, best of 5: 15.7 usec per loop
   $ python -m timeit 'if hasattr(str, "__bool__"): pass'
   50000 loops, best of 5: 4.26 usec per loop

   $ python -m timeit 'try:' '  int.__bool__' 'except AttributeError:' '  pass'
   200000 loops, best of 5: 1.43 usec per loop
   $ python -m timeit 'if hasattr(int, "__bool__"): pass'
   100000 loops, best of 5: 2.23 usec per loop

   >>> import timeit
   >>> # attribute is missing
   >>> s = """\
   ... try:
   ...     str.__bool__
   ... except AttributeError:
   ...     pass
   ... """
   >>> timeit.timeit(stmt=s, number=100000)
   0.9138244460009446
   >>> s = "if hasattr(str, '__bool__'): pass"
   >>> timeit.timeit(stmt=s, number=100000)
   0.5829014980008651
   >>>
   >>> # attribute is present
   >>> s = """\
   ... try:
   ...     int.__bool__
   ... except AttributeError:
   ...     pass
   ... """
   >>> timeit.timeit(stmt=s, number=100000)
   0.04215312199994514
   >>> s = "if hasattr(int, '__bool__'): pass"
   >>> timeit.timeit(stmt=s, number=100000)
   0.08588060699912603

Para dar ao módulo "timeit" acesso as funções que você definiu, você
pode passar o parâmetro *setup*, que contém um instrução de importar:

   def test():
       """Stupid test function"""
       L = [i for i in range(100)]

   if __name__ == '__main__':
       import timeit
       print(timeit.timeit("test()", setup="from __main__ import test"))

Outra opção é passar "globals()" para o parâmetro *globals*, o que
fará com que o código seja executado em seu espaço de nomes global.
Isso pode ser mais conveniente do que especificar individualmente
imports:

   def f(x):
       return x**2
   def g(x):
       return x**4
   def h(x):
       return x**8

   import timeit
   print(timeit.timeit('[func(42) for func in (f,g,h)]', globals=globals()))
