enchufado
   RSS
#
Calcular el rendimiento de scripts Python (Programación) 2012-03-28 01:54:04

Rendimiento en algoritmos o funciones

En ocasiones he tenido la necesidad de medir cuánto tiempo tarda una función u operación dada en Python, y siempre acabo recurriendo al módulo time. He aquí un sencillo y representativo ejemplo de lo que estoy hablando:

import time

t0 = time.time() # O bien: t0 = time.clock()
# Bucle con operación de ejemplo:
for i in xrange(1000000):
 i += 1
print time.time()-t0 # O bien: # O bien: print time.clock()-t0

Usar una u otra función (time o clock) dependerá de gustos, puesto que de todos modos (la primera con más precisión, al menos en GNU/Linux), la salida de ambas indican el tiempo (en segundos) entre la primera y la segunda toma de muestra de tiempo. Evidentemente, este es un ejemplo pequeño, pero para operaciones más largas, es recomendable organizar el código a benchmarkear en una función y llamarla.

También hay un módulo (timeit) que te evita alguna que otra linea de código y que está precisamente diseñado para estos menesteres, pero a pesar de rezar que uno no debe crear su propia función de medición de tiempo, tampoco he visto motivos para no hacerlo.

Estos son otros ejemplos personales más ilustrativos/interesantes, que comparan distintos modos de hacer las cosas:

Rendimiento en aplicaciones

Esto es útil para medir el rendimiento de funciones o algoritmos de modo aislado, pero no demasiado para aplicaciones medianamente complejas. Para estos casos, conviene tirar de profilers o perfiladores (esto no tiene nada que ver con hacerse las cejas). ¿Qué herramientas tenemos en Python para esto? Pues está el módulo profile (escrito en Python), cProfile (escrito en C), hotshot (el reemplazo de profile) y uno que he descubierto últimamente apto para aplicaciones multi-threaded: yappi. Veamos un ejemplo de preparación y uso de este último:

  1. Bajamos el tarball y lo descomprimimos.
  2. Instalamos el paquete con las cabeceras de desarrollo de Python (en Debian GNU/Linux, el paquete python-dev).
  3. Compilamos los fuentes: python setup.py build
  4. Realizamos la instalación del módulo (como root): python setup.py install
  5. Ya sólo queda usarlo :)

Una instanciación básica sería como la que sigue:

import yappi

def add10():
 i = 0
 for i in xrange(10):
  i += i

yappi.start() # Iniciamos la monitorización de yappi
add10() # Llamada a la funcion de ejemplo
yappi.print_stats() # Mostramos las stats de ejecución
yappi.stop() # Paramos la monitorización de yappi

Y la salida constaría de 3 bloques identificables. La documentación que versa sobre la materia es concisa pero suficiente. El primer bloque son las estadísticas de las funciones, mostrando su nombre (name), el número de llamadas que realiza (#n), el tiempo de ejecución de la función sin contar las llamadas (tsub), el tiempo total de ejecución de la función (ttot) y el tiempo medio (ttot / #n = tavg).

name                                                 #n     tsub           ttot             tavg
..dist-packages/yappi.py.get_stats:5 1        0.000000   0.000000   0.000000   
..st-packages/yappi.py.print_stats:5  1        0.000000   0.000000   0.000000   
test.py.add10:3                                 1        0.000023   0.000023   0.000023   

El segundo bloque son las estadísticas del thread: nombre, id, nombre de la última función ejecutada por el hilo (fname), numero de veces que es llamado (scnt) y tiempo total de ejecución (ttot).

name               tid      fname                                                scnt   ttot
_MainThread    -121.. ..dist-packages/yappi.py.get_stats:5 1        0.000000   

El tercer y último bloque da información general del perfilado: estado del perfilador (status), cuando inició su ejecución (tstart), número total de funciones perfiladas (fcnt), número total de hilos perfilados (tcnt) y uso de memoria por parte del perfilador (mem), que no de la aplicación perfilada.

status     tstart                                      fcnt    tcnt    mem(bytes)
running    Wed Mar 16 22:56:06 2011   4        1        45868

Explicado qué significa cada cosa que muestra, ¿qué significan los datos de salida de nuestro anterior ejemplo? En el primer bloque del perfilado de nuestra pequeña aplicación vemos que se ejecutan 3 funciones: 2 de ellas del propio perfilador y una nuestra, que es la que acapara prácticamente la totalidad del tiempo de ejecución. En el segundo bloque del perfilado, vemos que sólo lanzamos una vez el hilo principal de la aplicación (_MainThread) y la última función que lanzó fue la del perfilador (get_stats). En el tercer y último bloque vemos cuándo lanzamos el perfilado, que se perfilaron 4 funciones, 1 hilo y que el perfilador usó unos 45Kb.

Referencias:


Comentarios (2)


Volver al indice

login, admin, form, register