Python – Medindo tempo de execução

cleitonbueno_python_time_timeitQuem nunca preciso medir o tempo de algo? Digo, em foco a programação e principalmente quando se aplica Profiling, tempo as vezes é muito importante medir. Uma coisa é fazer algo funcionar, outra coisa é com eficiência e outra é ter desempenho, muitas vezes fazemos o primeiro e esquecemos o resto, eu já quebrei a cabeça em cima de uma função tentando otimização e aplicando tudo o que eu sabia e o problema não era meu e sim de uma função de uma lib pronta. Enfim, vamos ver como podemos medir tempo de execução de programas ou trechos de códigos em python.

Em C, quem já precisou, usando a library time.h podemos criar um tipo com time_t ou clock_t e utilizando duas variáveis, chamar no inicio e no fim, no final realiza uma matemática simples, normalmente substração e temos o tempo gasto, normalmente em ms(milissegundos). E quem disse que em Python é diferente? Vamos ao código exemplo, abaixo e logo em seguida adicionar a forma de medir o tempo.

my_list_name = [
"Cleiton", "Maria", "Carlos", "Pedro", "Oscar", "Renata", "Reginaldo",
"Rafael", "Tereza", "Aparecido", "Dias", "Fred", "Ronaldo", "Pamela",
"Vanessa", "Ingrid", "Suellen", "Ana", "Priscila", "Willian", "Manoel",
"Diego", "Henrique", "Sergio", "Cleidson", "Luiza", "Antonio", "Rafaela",
"Souza", "Bueno", "Luiz", "Lucas", "Matheus", "Evandro", "Murilo", "Vinicius",
"Cassio", "Bruno", "Domingues", "Miguel", "Gabriel", "Fernando", "Jane", "Luiz",
"Fabio"
]

my_list_sorted = sorted(my_list_name)

""" Opcao 1 """
ids = 0
while ids < len(my_list_name):
print("Nome: %s") % (my_list_sorted[ids])
ids+=1

""" Opcao 2 """
"""
print "n".join(["Nome: %s" % x for x in my_list_sorted])
"""

Minimizei ao máximo para um exemplo legal, para os testes, visto que foi tirado de um programa real a ideia do artigo, tenho uma lista com nomes my_list_name um pouco mais abaixo na linha12 eu uso sorted() para ordenar minha lista e armazenar em my_list_sorted o que quero medir aqui é entre as duas opções qual é a mais rápida, pode haver outras maneiras de tonar mais rápida ou eficiente, mas para o exemplo as duas vão apresentar resultados interessantes ;)

Primeiramente, vamos “medir” com o time do Linux.

cleiton@linuxVM:~/projetos/python/measure_time_code$ time python my_code.py
Nome: Ana
Nome: Antonio
Nome: Aparecido
Nome: Bruno
Nome: Bueno
Nome: Carlos
Nome: Cassio
Nome: Cleidson
Nome: Cleiton
Nome: Dias
Nome: Diego
Nome: Domingues
Nome: Evandro
Nome: Fabio
Nome: Fernando
Nome: Fred
Nome: Gabriel
Nome: Henrique
Nome: Ingrid
Nome: Jane
Nome: Lucas
Nome: Luiz
Nome: Luiz
Nome: Luiza
Nome: Manoel
Nome: Maria
Nome: Matheus
Nome: Miguel
Nome: Murilo
Nome: Oscar
Nome: Pamela
Nome: Pedro
Nome: Priscila
Nome: Rafael
Nome: Rafaela
Nome: Reginaldo
Nome: Renata
Nome: Ronaldo
Nome: Sergio
Nome: Souza
Nome: Suellen
Nome: Tereza
Nome: Vanessa
Nome: Vinicius
Nome: Willian

real 0m0.081s
user 0m0.052s
sys 0m0.020s

Uma breve explicação do que o Linux esta nos dizendo:
real: Tempo total ocorrido do inicio ao encerramento da aplicação
user: Tempo gasto de CPU no user-space, no modo usuario dentro do processo
sys: Tempo gasto de CPU dentro do kernel, funções e chamadas especificas do kernel

Uma referencia legal pode ser visto man do time.

OBS: Não é boa pratica dizer que real é user + sys, faça um teste com uma aplicação com multithreading e veja o resultado.

Vamos agora comentar a Opção 1 e ver o tempo da Opção 2:

my_list_name = [
"Cleiton", "Maria", "Carlos", "Pedro", "Oscar", "Renata", "Reginaldo",
"Rafael", "Tereza", "Aparecido", "Dias", "Fred", "Ronaldo", "Pamela",
"Vanessa", "Ingrid", "Suellen", "Ana", "Priscila", "Willian", "Manoel",
"Diego", "Henrique", "Sergio", "Cleidson", "Luiza", "Antonio", "Rafaela",
"Souza", "Bueno", "Luiz", "Lucas", "Matheus", "Evandro", "Murilo", "Vinicius",
"Cassio", "Bruno", "Domingues", "Miguel", "Gabriel", "Fernando", "Jane", "Luiz",
"Fabio"
]

my_list_sorted = sorted(my_list_name)

""" Opcao 1 """
"""
ids = 0
while ids < len(my_list_name):
print("Nome: %s") % (my_list_sorted[ids])
ids+=1
"""

""" Opcao 2 """
print "n".join(["Nome: %s" % x for x in my_list_sorted])

Vamos novamente ao terminal e ver o tempo agora.

cleiton@linuxVM:~/projetos/python/measure_time_code$ time python my_code.py
Nome: Ana
Nome: Antonio
Nome: Aparecido
Nome: Bruno
Nome: Bueno
Nome: Carlos
Nome: Cassio
Nome: Cleidson
Nome: Cleiton
Nome: Dias
Nome: Diego
Nome: Domingues
Nome: Evandro
Nome: Fabio
Nome: Fernando
Nome: Fred
Nome: Gabriel
Nome: Henrique
Nome: Ingrid
Nome: Jane
Nome: Lucas
Nome: Luiz
Nome: Luiz
Nome: Luiza
Nome: Manoel
Nome: Maria
Nome: Matheus
Nome: Miguel
Nome: Murilo
Nome: Oscar
Nome: Pamela
Nome: Pedro
Nome: Priscila
Nome: Rafael
Nome: Rafaela
Nome: Reginaldo
Nome: Renata
Nome: Ronaldo
Nome: Sergio
Nome: Souza
Nome: Suellen
Nome: Tereza
Nome: Vanessa
Nome: Vinicius
Nome: Willian

real 0m0.078s
user 0m0.056s
sys 0m0.016s

Ok, analisando nossa segunda opção, pareceu que se saiu melhor, mas nossa aplicação é pequena, e se antes disso executasse outras funções e queremos por exemplo, ver o que é mais rápido a Opção 1 ou a Opção 2, vamos ver agora o Time, modifique o nosso código conforme abaixo.

import time

my_list_name = [
"Cleiton", "Maria", "Carlos", "Pedro", "Oscar", "Renata", "Reginaldo",
"Rafael", "Tereza", "Aparecido", "Dias", "Fred", "Ronaldo", "Pamela",
"Vanessa", "Ingrid", "Suellen", "Ana", "Priscila", "Willian", "Manoel",
"Diego", "Henrique", "Sergio", "Cleidson", "Luiza", "Antonio", "Rafaela",
"Souza", "Bueno", "Luiz", "Lucas", "Matheus", "Evandro", "Murilo", "Vinicius",
"Cassio", "Bruno", "Domingues", "Miguel", "Gabriel", "Fernando", "Jane", "Luiz",
"Fabio"
]

my_list_sorted = sorted(my_list_name)

""" Opcao 1 """
inicio=time.time()
ids = 0
while ids < len(my_list_name):
print("Nome: %s") % (my_list_sorted[ids])
ids+=1

fim=time.time()
print("nTempo total Op.1: %f") % (fim-inicio)
print

""" Opcao 2 """
inicio=time.time()
print "n".join(["Nome: %s" % x for x in my_list_sorted])
fim=time.time()
print("nTempo total Op.2: %f") % (fim-inicio)
print

Hum, legal! Agora vou medir antes de entrar de executar a Opção 1 e parar quando sair e o mesmo na Opção 2 e imprimir na tela o tempo, vamos ver.

cleiton@linuxVM:~/projetos/python/measure_time_code$ time python my_code.py
Nome: Ana
Nome: Antonio
Nome: Aparecido
Nome: Bruno
Nome: Bueno
Nome: Carlos
Nome: Cassio
Nome: Cleidson
Nome: Cleiton
Nome: Dias
Nome: Diego
Nome: Domingues
Nome: Evandro
Nome: Fabio
Nome: Fernando
Nome: Fred
Nome: Gabriel
Nome: Henrique
Nome: Ingrid
Nome: Jane
Nome: Lucas
Nome: Luiz
Nome: Luiz
Nome: Luiza
Nome: Manoel
Nome: Maria
Nome: Matheus
Nome: Miguel
Nome: Murilo
Nome: Oscar
Nome: Pamela
Nome: Pedro
Nome: Priscila
Nome: Rafael
Nome: Rafaela
Nome: Reginaldo
Nome: Renata
Nome: Ronaldo
Nome: Sergio
Nome: Souza
Nome: Suellen
Nome: Tereza
Nome: Vanessa
Nome: Vinicius
Nome: Willian

Tempo total Op.1: 0.011110

Nome: Ana
Nome: Antonio
Nome: Aparecido
Nome: Bruno
Nome: Bueno
Nome: Carlos
Nome: Cassio
Nome: Cleidson
Nome: Cleiton
Nome: Dias
Nome: Diego
Nome: Domingues
Nome: Evandro
Nome: Fabio
Nome: Fernando
Nome: Fred
Nome: Gabriel
Nome: Henrique
Nome: Ingrid
Nome: Jane
Nome: Lucas
Nome: Luiz
Nome: Luiz
Nome: Luiza
Nome: Manoel
Nome: Maria
Nome: Matheus
Nome: Miguel
Nome: Murilo
Nome: Oscar
Nome: Pamela
Nome: Pedro
Nome: Priscila
Nome: Rafael
Nome: Rafaela
Nome: Reginaldo
Nome: Renata
Nome: Ronaldo
Nome: Sergio
Nome: Souza
Nome: Suellen
Nome: Tereza
Nome: Vanessa
Nome: Vinicius
Nome: Willian

Tempo total Op.2: 0.000851

Nossa! Neste caso List Comprehensions mandou muito bem heim.

Maravilha, vimos como medir o tempo de execução de um trecho de código no Python, pode estender fácil para funções e afins, mas descobri algo muito interessante neste aspecto no Python, quando passeando encontrei no Debugging and Profiling o Timeit.

A função timeit.timeit() e timeit.repeat(), segundo a documentação segue sua estrutura:

timeit.timeit(stmt=’pass’, setup=’pass’, timer=, number=1000000)
timeit.repeat(stmt=’pass’, setup=’pass’, timer=, repeat=3, number=1000000)

Os parâmetros importantes e bom saber são:

Statement ou stmt: A função que sera “testada”
setup: O código básico para iniciar e realizar os testes
number: Numero de loops a realizar com a função, valor padrão é 1000000
repeat: Quantas baterias de number serão feitas (valido somente no timeit.repeat()

Vamos agora, passar nossos dois códigos em duas funções opcao1() e opcao2() e implementar os testes com timeit.

import timeit

my_list_name = [e
"Cleiton", "Maria", "Carlos", "Pedro", "Oscar", "Renata", "Reginaldo",
"Rafael", "Tereza", "Aparecido", "Dias", "Fred", "Ronaldo", "Pamela",
"Vanessa", "Ingrid", "Suellen", "Ana", "Priscila", "Willian", "Manoel",
"Diego", "Henrique", "Sergio", "Cleidson", "Luiza", "Antonio", "Rafaela",
"Souza", "Bueno", "Luiz", "Lucas", "Matheus", "Evandro", "Murilo", "Vinicius",
"Cassio", "Bruno", "Domingues", "Miguel", "Gabriel", "Fernando", "Jane", "Luiz",
"Fabio"
]

my_list_sorted = sorted(my_list_name)

def opcao1():
ids = 0
while ids < len(my_list_name):
print("Nome: %s") % (my_list_sorted[ids])
ids+=1

def opcao2():
print "n".join(["Nome: %s" % x for x in my_list_sorted])

tempo_op1 = timeit.timeit("opcao1()", "from __main__ import opcao1", number=10)
tempo_op2 = timeit.timeit("opcao2()", "from __main__ import opcao2", number=10)

print("Opcao1 com 10 = %f") % (tempo_op1)
print("Opcao2 com 10 = %f") % (tempo_op2)

Importamos o timeit, colocamos em duas funções o que queremos “testar” e inserimos duas variaveis tempo_op1 e tempo_op2 que recebem timeit.timeit(), a estrutura é primeiro a função, segundo setup basico para inicializar e utilizar a função e no caso, coloquei numero de 10 loops, visto que 1.000.000 com esses prints seria um pouco exaustivo não?

Vamos ver a saída.

cleiton@linuxVM:~/projetos/python/otimizacao$ python my_code_def_final.py
Nome: Ana
Nome: Antonio
Nome: Aparecido
Nome: Bruno
Nome: Bueno
Nome: Carlos
Nome: Cassio
Nome: Cleidson
Nome: Cleiton
...
Nome: Vanessa
Nome: Vinicius
Nome: Willian
Opcao1 com 10 = 0.057217
Opcao2 com 10 = 0.000649

Hum, agora vamos fazer com 10, 100 e 1000 loops:

import timeit

my_list_name = [
"Cleiton", "Maria", "Carlos", "Pedro", "Oscar", "Renata", "Reginaldo",
"Rafael", "Tereza", "Aparecido", "Dias", "Fred", "Ronaldo", "Pamela",
"Vanessa", "Ingrid", "Suellen", "Ana", "Priscila", "Willian", "Manoel",
"Diego", "Henrique", "Sergio", "Cleidson", "Luiza", "Antonio", "Rafaela",
"Souza", "Bueno", "Luiz", "Lucas", "Matheus", "Evandro", "Murilo", "Vinicius",
"Cassio", "Bruno", "Domingues", "Miguel", "Gabriel", "Fernando", "Jane", "Luiz",
"Fabio"
]

my_list_sorted = sorted(my_list_name)

def opcao1():
ids = 0
while ids < len(my_list_name):
print("Nome: %s") % (my_list_sorted[ids])
ids+=1

def opcao2():
print "n".join(["Nome: %s" % x for x in my_list_sorted])

""" Primeira Bateria """
tempo_op1 = timeit.timeit("opcao1()", "from __main__ import opcao1", number=10)
tempo_op2 = timeit.timeit("opcao2()", "from __main__ import opcao2", number=10)

""" Segunda Bateria """
tempo_op11 = timeit.timeit("opcao1()", "from __main__ import opcao1", number=100)
tempo_op21 = timeit.timeit("opcao2()", "from __main__ import opcao2", number=100)

""" Terceira Bateria """
tempo_op12 = timeit.timeit("opcao1()", "from __main__ import opcao1", number=1000)
tempo_op22 = timeit.timeit("opcao2()", "from __main__ import opcao2", number=1000)

print("Opcao1 com 10 = %f") % (tempo_op1)
print("Opcao2 com 10 = %f") % (tempo_op2)
print
print("Opcao1 com 100 = %f") % (tempo_op11)
print("Opcao2 com 100 = %f") % (tempo_op21)
print
print("Opcao1 com 1000 = %f") % (tempo_op12)
print("Opcao2 com 1000 = %f") % (tempo_op22)

Vamos ver a saída:

cleiton@linuxVM:~/projetos/python/otimizacao$ python my_code_def_final.py
Nome: Ana
Nome: Antonio
Nome: Aparecido
Nome: Bruno
Nome: Bueno
Nome: Carlos
Nome: Cassio
Nome: Cleidson
Nome: Cleiton
...
Nome: Renata
Nome: Ronaldo
Nome: Sergio
Nome: Souza
Nome: Suellen
Nome: Tereza
Nome: Vanessa
Nome: Vinicius
Nome: Willian
Opcao1 com 10 = 0.046940
Opcao2 com 10 = 0.002445

Opcao1 com 100 = 0.248750
Opcao2 com 100 = 0.217057

Opcao1 com 1000 = 2.481773
Opcao2 com 1000 = 1.960809

Para encerrar, agora uma amostra usando time.repeat().

import timeit

my_list_name = [
"Cleiton", "Maria", "Carlos", "Pedro", "Oscar", "Renata", "Reginaldo",
"Rafael", "Tereza", "Aparecido", "Dias", "Fred", "Ronaldo", "Pamela",
"Vanessa", "Ingrid", "Suellen", "Ana", "Priscila", "Willian", "Manoel",
"Diego", "Henrique", "Sergio", "Cleidson", "Luiza", "Antonio", "Rafaela",
"Souza", "Bueno", "Luiz", "Lucas", "Matheus", "Evandro", "Murilo", "Vinicius",
"Cassio", "Bruno", "Domingues", "Miguel", "Gabriel", "Fernando", "Jane", "Luiz",
"Fabio"
]

my_list_sorted = sorted(my_list_name)

def opcao1():
ids = 0
while ids < len(my_list_name):
print("Nome: %s") % (my_list_sorted[ids])
ids+=1

def opcao2():
print "n".join(["Nome: %s" % x for x in my_list_sorted])

tempo_op1 = timeit.repeat("opcao1()", "from __main__ import opcao1", repeat=5, number=10)
tempo_op2 = timeit.repeat("opcao2()", "from __main__ import opcao2", repeat=5, number=10)

print("Opcao1 com repeat 5 = %s") % tempo_op1
print("Opcao2 com repeat 5 = %s") % tempo_op2

Vamos fazer 10 loops com 5 baterias, vamos ver a saída:

cleiton@linuxVM:~/projetos/python/otimizacao$ python my_code_def_final.py
Nome: Ana
Nome: Antonio
Nome: Aparecido
Nome: Bruno
Nome: Bueno
Nome: Carlos
Nome: Cassio
Nome: Cleidson
Nome: Cleiton
...
Nome: Suellen
Nome: Tereza
Nome: Vanessa
Nome: Vinicius
Nome: Willian
Opcao1 com repeat 5 = [0.04651808738708496, 0.031491994857788086, 0.032666921615600586, 0.016332149505615234, 0.05835700035095215]
Opcao2 com repeat 5 = [0.00022292137145996094, 0.0015659332275390625, 0.0009109973907470703, 0.001811981201171875, 0.001252889633178711]

Ele nos devolve uma lista com o tempo de cada bateria.

Gostaram? Viu que Python não é só uma linguagem fácil e excelente para prototipar, quanto mais você mergulha mais ela se torna poderosa e mostra recursos. Isso porque os testes foram focado em uma ideia fixa, não abordei cProfile, profile, trace, pstats, hotshot, line_profiler, memory_profiler, pypy, psyco da pra ver que tem bastante coisa pra falar ainda né?

Espero que tenham gostado e até a próxima!

 

Referências

http://man7.org/linux/man-pages/man1/time.1.html
https://docs.python.org/2/library/timeit.html
https://docs.python.org/2/library/time.html
https://docs.python.org/2/tutorial/datastructures.html#list-comprehensions
https://docs.python.org/2/library/debug.html
Medindo tempo de execução de código Python

Share Button

CC BY-NC-SA 4.0 Python – Medindo tempo de execução by Cleiton Bueno is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.