Anuncios

Bienvenidos sean a este post, hoy hablaremos sobre los perfilados.

Anuncios

Pero que es un perfilado? Es basicamente ejecutar la aplicacion mientras se realiza un seguimiento de varios parametros diferentes, tales como la cantidad de veces que se llamo a la funcion y la cantidad de tiempo que se pasa dentro de ella, nos puede ayudar a encontrar los cuellos de botella de nuestras aplicaciones, para poder mejorar aquello que realmente nos frena, en este tema tenemos un par de implementaciones para la misma interface del perfilado:

  • cProfile, es la mas recomendada para los usuarios, es una extension de C con una sobrecarga razonable que la hace adecuada para crear perfiles de programas de larga duracion
  • profile, es un modulo puro de python cuya interfaz es imitada por cProfile pero que agrega una sobrecarga significativa a los programas perfilados
Anuncios
Anuncios

Esta interfaz hace un perfilado determinista lo cual significa que se monitorea todas las llamadas de funciones, devoluciones de las funciones y eventos de excepciones y tambien se establecen los intervalos precisos de tiempos entre todos estos eventos, otro enfoque llamado perfilado estadistico toma muestra de manera aleatoria el puntero de instruccion efectivo y deduce donde se esta perdiendo el tiempo, este ultimo implica menos sobrecarga pero provee solo resultados aproximados, esto es debido a la forma en que el interprete ejecuta el codigo y este tipo de perfilado no agrega tanta sobrecarga como uno creeria, para entender el concepto vamos a crear un archivo al cual llamaremos triples.py y le agregaremos el siguiente codigo:

triples.py

def calc_triples(max):
	triples = []
	for a in range(1, max + 1):
		for b in range(a, max + 1):
			hipotenusa = calc_hipot(a, b)
			if se_inicio(hipotenusa):
				triples.append((a, b, int(hipotenusa)))
	return triples

def calc_hipot(a, b):
	return (a**2 + b**2) ** .5

def se_inicio(n):
	return n.is_integer()

triples = calc_triples(1000)
Anuncios
Anuncios

Primero crearemos una funcion para calcular triples pitagoricos, este recibira un valor que sera nuestro limite para calcular, tendremos un bucle donde contaremos desde 1 hasta el maximo informado mas 1 y lo almacenaremos en a despues haremos otro bucle exactamente igual pero que comenzara desde el valor de a y lo almacenaremos en b, dentro de este segundo bucle calcularemos la hipotenusa por medio de una funcion (que luego definiremos) y por ultimo tenemos un condicional donde verificamos si hipotenusa esta iniciada por medio de otra funcion agregaremos los tres valores a triples, despues tenemos dos funciones:

  • calc_hipot sera para obtener el valor de la hipotenusa en base a los dos lados informados
  • se_inicio para devolver a hipotenusa como un valor entero
Anuncios

Esto es debido a que el valor obtenido sera de tipo float y nosotros lo necesitamos entero, por ultimo hacemos un calculo y lo almacenamos en triples, con esto tenemos nuestro archivo creado pasemos a ver como ejecutar el perfilado, para ello debemos usar el ejecutable con las siguientes opciones:

$ python3 -m cProfile triples.py
Anuncios

En lugar de ejecutarlo directo le pasaremos el modificador -m y le indicaremos que use a cProfile y luego ira el archivo en cuestion, tardara un poco pero nos devovlera lo siguiente:

tinchicus@dbn001vrt:~/lenguajes/python$ python3 -m cProfile triples.py 
         1502538 function calls in 0.864 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.864    0.864 triples.py:1(<module>)
        1    0.200    0.200    0.864    0.864 triples.py:1(calc_triples)
   500500    0.498    0.000    0.498    0.000 triples.py:10(calc_hipot)
   500500    0.118    0.000    0.166    0.000 triples.py:13(se_inicio)
        1    0.000    0.000    0.864    0.864 {built-in method builtins.exec}
     1034    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
   500500    0.048    0.000    0.048    0.000 {method 'is_integer' of 'float' objects}


tinchicus@dbn001vrt:~/lenguajes/python$
Anuncios

Vean toda la informacion destallada que nos devuelve de los tiempos de cada funcion, eventos y demas elementos del codigo, vamos a ver un par de datos como son calc_hipot el cual fue llamado 500500 y le llevo un total de 0.498 segundos de ejecucion, si lo comparamos con se_inicio en la misma cantidad de llamadas desperdicio mucho menos tiempo, esta bien que hacen procesos muy distintos pero es abismal la diferencia por lo tanto vamos a hacer una modificacion en el archivo, tomemos la funcion calc_hipot acutal:

def calc_hipot(a, b):
	return (a**2 + b**2) ** .5
Anuncios

Lo cambiaremos de la siguiente manera:

def calc_hipot(a, b):
	return (a*a + b*b) ** .5
Anuncios

En este caso cambiamos el uso de potenciacion por el metodo tradicional de potenciacion pero antes de volver a chequearlo vamos a ver la funcion se_inicio:

def se_inicio(n):
        return n.is_integer()
Anuncios

La cambiaremos de la siguiente manera:

def se_inicio(n):
        return n == int(n)
Anuncios

En este caso cambiamos la forma de devolver el valor si es entero, en lugar de usar la funcion lo hacemos por condicion, si volvemos a ejecutar el cProfile obtendremos los siguientes tiempos:

tinchicus@dbn001vrt:~/lenguajes/python$ python3 -m cProfile triples.py 
         1002038 function calls in 0.521 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.521    0.521 triples.py:1(<module>)
        1    0.182    0.182    0.521    0.521 triples.py:1(calc_triples)
   500500    0.196    0.000    0.196    0.000 triples.py:10(calc_hipot)
   500500    0.143    0.000    0.143    0.000 triples.py:13(se_inicio)
        1    0.000    0.000    0.521    0.521 {built-in method builtins.exec}
     1034    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


tinchicus@dbn001vrt:~/lenguajes/python$
Anuncios
Anuncios

Como podemos ver se mejoro bastane el tiempo de ejecucion de la funcion calc_hipot, se acuerdan porque? En este post habiamos mencionado que cuando usabamos un metodo de potenciacion como este implicaba el uso de otras interfaces lo cual hace que se desperdicie mucho tiempo en el procesamiento pero en cambio con el metodo tradicional es simplemente una multiplicacion lo cual tarda en menos en procesarlo, en cambio la funcion se_inicio ahora tarda mas tiempo porque en lugar de usar una funcion utilizamos una condicion que debe verificar esto y nos llevara msa ciclos que una funcion donde solamente ve eso, es decir que debemos tener cuidado a la hora de armar nuestros codigos y esta es una gran herramienta para descubrir posibles perdidas de tiempo y solucionar los mismos pero cuando es necesario hacer un perfilado?

Anuncios

Como vimos esta muy bueno utilizar esta herramienta pero necesitamos saber cuando se debe hacer y cuales son las mediciones que necesitamos como Donald Knuth una vez dijo:

La optimizacion prematura es la raiz de todo mal

Donald Knuth
Anuncios

Primero y principal, «correccion» dado que queremos un codigo para entregar los resultados correctos, por lo tanto escribe tests, encuentra casos extremos y estresa tu codigo en todas las formas que tienen sentido, no subestimes cosas porque no sean posibles que sucedan, se minucioso.

Anuncios

Segundo, siempre programa con las mejores practicas, repasemos algunas:

  • Legibilidad
  • Extensibilidad
  • Acoplamiento flexible
  • Modularidad
  • Diseño
Anuncios

Pero tambien debemos aplicar los principios de OOP:

  • Encapsulacion
  • Abstraccion
  • Responsabilidad unica
  • Abierto/Cerrado
  • etc
Anuncios

Leer y aplicar estos conceptos te abriran nuevos horizontes y ampliaran tu forma de programar, cuando nuestros codigo ya tienen todo lo mencionado anteriormente podemos ejecutar el perfilado y ver cual es el cuello de botella de este, como ultimo consejo siempre comiencen con el cuello de botella mas grande porque esto puede generar un efecto domino y al solucionarlo puede solucionar otros o no pero siempre es mas recomendable comenzar por el mayor problema de todos.

Anuncios

En resumen, hoy hemos visto como es el perfilado, hemos hablado sobre los tipos posibles de la misma interfaz, hemos mencionado sus diferencias, hemos creado un ejemplo para ver como funciona, tambien lo hemos modificado para ver como en algunos casos puede beneficiarnos de una manera y como puede perjudicarnos por ultimo hemos visto cuando podemos aplicarlo, espero les haya sido de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios
pp258

Donación

Es para mantenimento del sitio, gracias!

$1.50