Medindo tempo de execução de código Python

Muitas vezes, é necessário que comparemos duas implementações diferentes de uma mesma função em nosso código para saber qual apresenta melhor performance, com relação ao tempo de resposta. Por exemplo, escrevemos duas funções que fazem a soma de todos os números inteiros entre x e y. Ambas funcionam corretamente, mas, gostaríamos de descobrir qual delas nos fornece o menor tempo de resposta. As duas implementações para a mesma função são apresentadas abaixo:

def soma1(x, y):
    return sum(range(x, y+1))

def soma2(x, y):
    soma = 0
    for i in range(x, y+1):
        soma += i
    return soma

Tanto a função soma1, quanto a função soma2 realizam a soma de todos os elementos entre x e y. Como descobrir qual delas nos fornece o menor tempo de resposta? A primeira solução que vem em mente é marcar o tempo antes de chamar a função e marcar o tempo depois de chamar a função. Assim o tempo de resposta seria a diferença entre os dois tempos marcados. Veja o exemplo:

import time

# verifica o tempo de resposta da função soma1
ini = time.time()
soma1(1, 1000000)
fim = time.time()
print "Função soma1: ", fim-ini

# verifica o tempo de resposta da função soma2
ini = time.time()
soma2(1, 1000000)
fim = time.time()
print "Função soma2: ", fim-ini

Assim, você obtém o tempo de execução de cada uma das funções. O problema dessa abordagem é a precisão do tempo medido. A função time.time() retorna o tempo atual em segundos passados desde 1/1/1970. Apesar de ser um número de ponto flutuante, a precisão desse número não é muito grande, além de a própria chamada da função time() já trazer consigo um overhead.

Uma alternativa mais precisa é a utilização do módulo timeit. Esse módulo é incluído com Python e foi projetado especificamente para fazer a medição de tempo de execução de programas/trechos de código Python. Por isso, é mais recomendado que utilizemos o timeit no lugar de uma solução “caseira”. Vejamos a seguir como utilizar o timeit.

Pela linha de comando

O timeit pode ser utilizado para medir o tempo de execução de código python diretamente na linha de comando. O exemplo abaixo ilustra isso:

$ python -m timeit "range(1,10000)"

A linha de comando acima executa o módulo timeit como um script e passa como entrada a expressão Python que está entre aspas. A execução do comando acima em meu sistema retornou o seguinte:

10000 loops, best of 3: 156 usec per loop

Ou seja, o timeit realizou 3 baterias compostas de um conjunto de 10000 repetições cada, executando o código range(1,10000) e, dessas 3 tentativas, a melhor delas obteve uma média de 156 microsegundos por execução do código. Através dele, podemos, por exemplo, verificar na prática que o uso do xrange() ao invés do range() nos dá um tempo de execução melhor. Veja e compare:

$ python -m timeit "xrange(1,10000)"
1000000 loops, best of 3: 0.357 usec per loop

Além do recurso de teste pela linha de comando, também podemos executar o timeit dentro de scripts Python.

Importando o módulo e usando dentro do código Python

Voltando ao exemplo das funções soma1 e soma2, no qual queremos descobrir qual delas nos dá melhor tempo de resposta, podemos fazer o seguinte, no mesmo arquivo onde estão definidas as funções:

import timeit

def soma1(x, y):
    return sum(range(x, y+1))

def soma2(x, y):
    soma = 0
    for i in range(x, y+1):
        soma += i
    return soma

t = timeit.Timer("soma1(1,1000)", "from __main__ import soma1")
print t.repeat()

t = timeit.Timer("soma2(1,1000)", "from __main__ import soma2")
print t.repeat()

O programa, quando executado, irá fazer o teste de ambas as funções. Vamos analisar as duas chamadas a funções do módulo timeit:

t = timeit.Timer("soma1(1,1000)", "from __main__ import soma1")
print t.repeat()

Na primeira linha, passamos duas entradas para o método Timer(). A primeira string indica o código a ser testado, ou seja, o código do qual estamos interessados em saber o tempo de resposta. A segunda string indica um código de inicialização, necessário para que o timeit consiga executar o código que queremos. Chamando a função repeat() sem parâmetros, são executadas 3 baterias de 1000000 execuções e, como retorno, recebemos uma lista contendo os tempos médios de execução das 3 baterias de testes. Poderíamos especificar, como no exemplo abaixo, a quantidade de baterias (2) e o número de execuções por bateria (1000).

t = timeit.Timer("soma1(1,1000)", "from __main__ import soma1")
print t.repeat(2, 1000)

Assim, podemos utilizar o módulo timeit para descobrir qual o tempo de resposta de nosso código, sem a necessidade de construir o nosso próprio módulo de testes de tempo de execução. Uma das vantagens do timeit é que, por exemplo, ele desabilita o Garbage Collector, para evitar que este interfira nos resultados.

Curioso em saber qual das duas funções (soma1 e soma2) apresentou melhor tempo de execução? Faça o teste. 🙂

Anúncios

Acentuação em programas Python

Python, por padrão, interpreta seus programas usando a codificação de caracteres ASCII. Tal codificação não é capaz de representar os caracteres acentuados (á, á, â, ã, …), que são muito utilizados na nossa língua portuguesa. Então, como escrever um comentário no código utilizando a grafia correta, sem precisar escrever ‘é’ como ‘eh’? Como escrever mensagens a serem passadas ao usuário utilizando acentuação nos caracteres? Como evitar que a mensagem de erro abaixo apareça como saída do programa?

File "foo.py", line 2
SyntaxError: Non-ASCII character '\xc3' in file foo.py on line 2, but no
encoding declared;see http://www.python.org/peps/pep-0263.html for details

É preciso indicar ao Python qual é a codificação de caracteres que nosso arquivo de código está utilizando. Isso pode ser feito incluindo a seguinte linha de código no cabeçalho do arquivo.

# coding=<nome da codificação>

ou:

# -*- coding: <nome da codificação> -*-

As distribuições Linux, em sua maioria, utilizam a codificação UTF-8 para representação de caracteres. Assim, para utilizar caracteres com acentuação em um arquivo Python, usando Linux, é preciso adicionar ao começo* do arquivo:

# coding=UTF-8

ou:

# -*- coding: UTF-8 -*-

* A linha que indica a codificação pode ser a primeira linha do arquivo, ou a segunda, caso o arquivo contenha a indicação do binário do interpretador na primeira linha. Ex.:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

Mais informações em: http://www.python.org/dev/peps/pep-0263