martes, 13 de octubre de 2015

Implementación de Semáforos y Barreras en Python.

Barreras

Los locks, también llamados mutex son objetos con dos estados posibles: adquirido o libre. Cuando un thread adquiere la barrera, los demás threads que pidan adquirirlo se bloquearán hasta que el thread que lo ha adquirido libere la barrera, momento en el cual podrá entrar otro thread.

La barrera se representa mediante la clase Lock. Llamando a Lock.acquire() el hilo bloqueará el Lock, de forma que el siguiente hilo que llame a Lock.acquire() se quedará a la espera de que el Lock se desbloquee. La llamada a Lock.release() desbloquea el Lock, haciendo que el hilo que estaba en espera continúe. Un ejemplo se muestra a continuación:
from threading import Thread, Lock
import time

class MiHilo(Thread):

    
    def __init__ (self, inicio, fin, bloqueo):
        Thread.__init__(self)
        self.inicio = inicio
        self.fin = fin
        self.bloqueo = bloqueo
      
    def run (self):
        bloqueo.acquire()
        for i in range(self.inicio,self.fin):
           print ("contador = "+str(i))
           time.sleep(0.2)
        bloqueo.release()
            
if __name__ == '__main__':
    bloqueo = Lock()
    bloqueo.acquire()
    hilo = MiHilo(0,10, bloqueo)
    hilo.start()
    time.sleep(1)
    for i in range (10,20):
       print ("main = "+str(i))
       time.sleep(0.1)
       
    bloqueo.release()

Esta clase Lock es muy simple. Cualquier hilo, puede liberar a otro sin importar si lo haya bloquedado o no, además si un hilo llama él mismo dos veces a acquire(), se queda bloqueado en la segunda llamada. Una mejor opción es RLock. En esta barrera solo el que haya llamado al método acquire() puede liberarlo con el método release() y un mismo hilo puede llamar varias veces a acquire() sin quedarse bloqueado, pero tiene que hacer el mismo número de llamadas a release() para desbloquearlo. El ejemplo siguiente muestra su funcionamiento:

from threading import Thread, RLock
import time

class MiHilo(Thread):
    def __init__ (self, inicio, fin, bloqueo):
        Thread.__init__(self)
        self.inicio = inicio
        self.fin = fin
        self.bloqueo = bloqueo

    def run (self):
        bloqueo.acquire()
        for i in range(self.inicio,self.fin):
           print ("contador = "+str(i))
           time.sleep(0.2)
        bloqueo.release()
            
if __name__ == '__main__':
    bloqueo = RLock()
    bloqueo.acquire()
    
    hilo = MiHilo(0,10, bloqueo)
    hilo.start()
    time.sleep(1)
    for i in range (10,20):
       print ("main = "+str(i))
       bloqueo.acquire()
       time.sleep(0.1)
       
    bloqueo.release()
    print ("El hilo todavia no ha comenzado")
    for i in range(10,20):
       bloqueo.release()

Semáforos

Un semáforo permite acceder a un determinado recurso a un número máximo de hilos simultáneamente. Si hay más hilos que el máximo permitido, los pone en espera y los va dejando pasar según van terminando los que están activos. Un semáforo actúa como un contador con un valor inicial. Cada vez que un hilo llama a Semaphore.acquire(), el contador se decrementa en 1 y se deja pasar al hilo. En el momento que el contador se hace cero, NO se deja pasar al siguiente hilo que llame a acquire(), sino que lo deja bloqueado. Cada vez que se llama a Semaphore.release(), el contador se incrementa en 1. Si se hace igual a cero, libera al siguiente hilo en la cola de espera. El ejemplo de su funcionamiento se muestra a continuación: 
from threading import Thread, Semaphore
import time, random


class MiHilo(Thread):
    def __init__(self, numero_hilo, semaforo):
        Thread.__init__(self)
        self.semaforo=semaforo
        self.numero_hilo = numero_hilo
        
    def run(self):
        semaforo.acquire()
        print ("Entra hilo "+str(self.numero_hilo))
        time.sleep(random.randrange(1,10,1))
        print ("Fin hilo " + str(self.numero_hilo))
        semaforo.release()          
        
if __name__ == '__main__':
    random.seed()
    semaforo = Semaphore(5)
    for i in range(0,10):
       hilo=MiHilo(i,semaforo)
       hilo.start()
       print ("Arrancado hilo "+str(i))
La información y los ejemplos se obtuvieron de las siguientes fuentes:
Los ejemplos se pueden descargar del siguiente link.

martes, 1 de septiembre de 2015

Practica No.4 Programación de Hilos en Python

¿QUÉ SON LOS THREADS?

Para que una computadora sea útil es necesario que podamos hacer más de una cosa a la vez. La cuestión es cómo lograr esto en computadoras con una sola CPU, la solución fue que en realidad no ejecutar varios procesos a la vez, sino que los procesos se van turnando y, dada la velocidad a la que ejecutan las instrucciones, nosotros tenemos la impresión de que las tareas se ejecutan de forma paralela como si tuviéramos multitarea real.


Cada vez que un proceso distinto pasa a ejecutarse es necesario realizar lo que se llama un cambio de contexto, durante el cual se salva el estado del programa que se estaba ejecutando a memoria y se carga el estado del programa que va a entrar a ejecutarse, sin embargo esto puede ser relativamente lento, por lo que a veces es mucho más eficaz utilizar lo que se conoce como threads, hilos de ejecución, o procesos ligeros.


Los threads son un concepto similar a los procesos: también se trata de código en ejecución. Sin embargo los threads se ejecutan dentro de un proceso, y los threads del proceso comparten recursos entre sí. El sistema operativo necesita menos recursos para crear y gestionar los threads, y el cambio de contexto es más rápido. Además, dado que los threads comparten el mismo espacio de memoria global, es sencillo compartir información entre ellos: cualquier variable global que tengamos en nuestro programa es vista por todos los threads.

EL GIL

La ejecución de los threads en Python está controlada por el GIL (Global Interpreter Lock) de forma que sólo un thread puede ejecutarse a la vez, independientemente del número de procesadores con el que cuente la máquina.


Por defecto el cambio de thread se realiza cada 10 instrucciones de bytecode, aunque se puede modificar mediante la función sys.setcheckinterval. También se cambia de thread cuando el hilo se pone a dormir con time.sleep o cuando comienza una operación de entrada/salida, las cuales pueden tardar mucho en finalizar.


Para minimizar un poco el efecto del GIL en el rendimiento de nuestra aplicación es conveniente llamar al intérprete con el flag -O, lo que hará que se genere un bytecode optimizado con menos instrucciones, y, por lo tanto, menos cambios de contexto.

THREADS EN PYTHON

El trabajo con threads se lleva a cabo en Python mediante el módulo thread. Este módulo es opcional y dependiente de la plataforma, también contamos con el módulo threading que se apoya en el primero para proporcionarnos una API orientada a objetos.

El módulo threading contiene una clase Thread que debemos extender para crear nuestros propios hilos de ejecución. El método run contendrá el código que queremos que ejecute el thread. Si queremos especificar nuestro propio constructor, este deberá llamar a threading.Thread.__init__(self) para inicializar el objeto correctamente.


Para que el thread comience a ejecutar su código basta con crear una instancia de la clase que acabamos de definir y llamar a su método start. El código del hilo principal y el del que acabamos de crear se ejecutarán de forma concurrente.


El método join se utiliza para que el hilo que ejecuta la llamada se bloquee hasta que finalice el thread sobre el que se llama. El método join puede tomar como parámetro un número en coma flotante indicando el número máximo de segundos a esperar. Podemos observar estos elementos en el siguiente ejemplo:
import threading  
  
class MiThread(threading.Thread):  
      def __init__(self, num):  
          threading.Thread.__init__(self)  
          self.num = num  
  
      def run(self):  
          print ("Soy el hilo", self.num)


print ("Soy el hilo principal") 
  
for i in range(0, 10):  
    t = MiThread(i)  
    t.start()  
    t.join() 

SINCRONIZACIÓN

Uno de los mayores problemas a los que tenemos que enfrentarnos al utilizar threads es la necesidad de sincronizar el acceso a ciertos recursos por parte de los threads. Entre los mecanismos de sincronización que tenemos disponibles en el módulo threading se encuentran los siguientes:


Candado


Los locks, también llamados mutex son objetos con dos estados posibles: adquirido o libre. Cuando un thread adquiere el candado, los demás threads que pidan adquirirlo se bloquearán hasta que el thread que lo ha adquirido libere el candado, momento en el cual podrá entrar otro thread.


El candado se representa mediante la clase Lock. Llamando a Lock.acquire() el hilo bloqueará el Lock, de forma que el siguiente hilo que llame a Lock.acquire() se quedará a la espera de que el Lock se desbloquee. La llamada a Lock.release() desbloquea el Lock, haciendo que el hilo que estaba en espera continúe. Un ejemplo se muestra a continuación:
from threading import Thread, Lock
import time

class MiHilo(Thread):

    
    def __init__ (self, inicio, fin, bloqueo):
        Thread.__init__(self)
        self.inicio = inicio
        self.fin = fin
        self.bloqueo = bloqueo
      
    def run (self):
        bloqueo.acquire()
        for i in range(self.inicio,self.fin):
           print ("contador = "+str(i))
           time.sleep(0.2)
        bloqueo.release()
            
if __name__ == '__main__':
    bloqueo = Lock()
    bloqueo.acquire()
    hilo = MiHilo(0,10, bloqueo)
    hilo.start()
    time.sleep(1)
    for i in range (10,20):
       print ("main = "+str(i))
       time.sleep(0.1)
       
    bloqueo.release()

Esta clase Lock es muy simple. Cualquier hilo, puede liberar a otro sin importar si lo haya bloquedado o no, además si un hilo llama él mismo dos veces a acquire(), se queda bloqueado en la segunda llamada. Una mejor opción es RLock. En este candado solo el que haya llamado al método acquire() puede liberarlo con el método release() y un mismo hilo puede llamar varias veces a acquire() sin quedarse bloqueado, pero tiene que hacer el mismo número de llamadas a release() para desbloquearlo. El ejemplo siguiente muestra su funcionamiento:

from threading import Thread, RLock
import time

class MiHilo(Thread):
    def __init__ (self, inicio, fin, bloqueo):
        Thread.__init__(self)
        self.inicio = inicio
        self.fin = fin
        self.bloqueo = bloqueo

    def run (self):
        bloqueo.acquire()
        for i in range(self.inicio,self.fin):
           print ("contador = "+str(i))
           time.sleep(0.2)
        bloqueo.release()
            
if __name__ == '__main__':
    bloqueo = RLock()
    bloqueo.acquire()
    
    hilo = MiHilo(0,10, bloqueo)
    hilo.start()
    time.sleep(1)
    for i in range (10,20):
       print ("main = "+str(i))
       bloqueo.acquire()
       time.sleep(0.1)
       
    bloqueo.release()
    print ("El hilo todavia no ha comenzado")
    for i in range(10,20):
       bloqueo.release()

Semáforos


Un semáforo permite acceder a un determinado recurso a un número máximo de hilos simultáneamente. Si hay más hilos que el máximo permitido, los pone en espera y los va dejando pasar según van terminando los que están activos. Un semáforo actúa como un contador con un valor inicial. Cada vez que un hilo llama a Semaphore.acquire(), el contador se decrementa en 1 y se deja pasar al hilo. En el momento que el contador se hace cero, NO se deja pasar al siguiente hilo que llame a acquire(), sino que lo deja bloqueado. Cada vez que se llama a Semaphore.release(), el contador se incrementa en 1. Si se hace igual a cero, libera al siguiente hilo en la cola de espera. El ejemplo de su funcionamiento se muestra a continuación: 
from threading import Thread, Semaphore
import time, random


class MiHilo(Thread):
    def __init__(self, numero_hilo, semaforo):
        Thread.__init__(self)
        self.semaforo=semaforo
        self.numero_hilo = numero_hilo
        
    def run(self):
        semaforo.acquire()
        print ("Entra hilo "+str(self.numero_hilo))
        time.sleep(random.randrange(1,10,1))
        print ("Fin hilo " + str(self.numero_hilo))
        semaforo.release()          
        
if __name__ == '__main__':
    random.seed()
    semaforo = Semaphore(5)
    for i in range(0,10):
       hilo=MiHilo(i,semaforo)
       hilo.start()
       print ("Arrancado hilo "+str(i))


Condiciones


Algo muy común es tener un hilo esperando por unos datos para tratarlos. Otro hilo es el encargado de proporcionar esos datos y avisar al primer hilo de que ya están disponibles. Para facilitar este tipo de uso tenemos la clase threading.Condition.
En primer lugar, creamos la Condition. El hilo que debe esperar por los datos, debe llamar al método Condition.acquire() y luego al Condition.wait(). Para llamar a wait() es obligatorio ser el propietario de la Condition, cosa que se consigue llamando a acquire(). La llamada a wait() libera la Condition, pero deja al hilo bloqueado hasta que alguien llame a Condition.notify().
El hilo encargado de suministrar los datos, debe llamar a Condition.acquire() para hacerse dueño de la Condition y cuando los datos estén disponibles, llamar a Condition.notify() y luego aCondition.release(). Estas dos llamadas juntas despertarán al hilo a la espera de datos. La llamada a notify() no libera la Condition, por lo que el hilo que está en el wait() será notifiado, pero no comenzará su ejecución hasta que se llame a release(). El ejemplo se muestra enseguida:
from threading import Thread, Condition
import time

class MiHilo (Thread):
    
    def __init__(self,lista,condicion):
        Thread.__init__(self)
        self.lista = lista
        self.condicion = condicion
        self.fin=False
        
    def run(self):
        self.condicion.acquire()
        while not self.fin:
            self.condicion.wait()
            if not self.fin:
               while len(lista)>0:
                  print (self.lista.pop(0))
        self.condicion.release()

if __name__ == '__main__':
    lista =[]
    condicion = Condition()
    hilo = MiHilo(lista, condicion)
    hilo.start()
    
    for i in range (0,10):
       condicion.acquire()
       lista.append(i)
       lista.append("numero "+str(i))
       condicion.notify()
       condicion.release()
       time.sleep(1)

    hilo.fin=True
    condicion.acquire()
    condicion.notify()
    condicion.release()

Eventos

La forma más fácil de hacer que un hilo espere a que otro hilo le avise es por medio de Event. El Event tiene un flag interno que indica si un hilo puede continuar o no. Un hilo llama al método Event.wait() y se queda bloqueado en espera hasta que el flag interno de Event se ponga a True. Otro hilo llame a Event.set() para poner el flag a True o bien a Event.clear() para ponerlo a False.

En realidad se trata de un wrapper por encima de Condition y sirven principalmente para coordinar threads mediante señales que indican que se ha producido un evento. Los eventos nos abstraen del hecho de que estemos utilizando un Lock por debajo, por lo que carece de métodos acquire y release. El ejemplo se muestra a continuación:

from threading import Thread, Event
import time

class MiHilo(Thread):
    def __init__(self, evento):
        Thread.__init__(self)
        self.evento=evento
    
    def run(self):
        self.evento.wait()
        print ("Entra hilo ")
        
if __name__ == '__main__':
    evento = Event()
    hilo=MiHilo(evento)
    hilo.start()
    time.sleep(2)
    print ("Hago evento.set()")
    evento.set()

La información y los ejemplos se obtuvieron de las siguientes fuentes:
Los ejemplos se pueden descargar del siguiente link.

Practica No.4 Programación Orientada a Objetos en Python

Conceptos Generales

La programación orientada a objetos (POO) es un paradigma de programación en el que los conceptos del mundo real relevantes para nuestro problema se modelan a través de clases y objetos, y nuestro programa consiste en una serie de interacciones entre estos objetos.

Los objetos se crean a partir de las clases, y pueden interactuar entre sí mediante mensajes. Algunos de los lenguajes que permiten la programación orientada a objetos son: Smalltalk, C++, Objective-C, C#, Java, Javascript, Python, entre otros.

El concepto básico para entender el funcionamiento de la POO son los conceptos de Clase y Objetos, los cuales podemos definirlos brevemente de la siguiente manera:

Las clases permiten agrupar en un nuevo tipo los datos y las funcionalidades asociadas a dichos datos, favoreciendo la separación entre los detalles de la implementación de las propiedades esenciales para su uso. Una clase define propiedades y comportamiento que se muestran en los entes llamados objetos (o instancias de una clase). La clase actúa como molde de un conjunto de objetos, de los que se dice que pertenecen a la clase.

En términos más abstractos, podemos pensar en las clases como definiciones y en los objetos como expresiones concretas de dichas definiciones.

Implementación en Python.

Antes que nada veamos el siguiente ejemplo
class Gato:
    def __init__(self, energia, hambre):
        self.energia = energia
        self.hambre = hambre
        print ('Se creo un gato')
 
    def tomar_leche(self, leche_en_litros):
        self.hambre += leche_en_litros
        print ('el gato toma su leche')
 
    def acariciar(self):
        print ('prrrrr...')
 
    def jugar(self):
        if self.energia <= 0 or self.hambre <=1:
            print ('el gato no quiero jugar')
        else:
            self.energia -=1
            self.hambre -= 2
            print ('al gato le encanta jugar')
 
    def dormir(self, horas):
        self.energia += horas
        print ('el gato tomo una siesta')


gato = Gato(5, 5)
gato.acariciar()
gato.jugar()
gato.jugar()
gato.jugar()
print (gato.energia)
print (gato.hambre)
gato.tomar_leche(4) 
print (gato.hambre)
gato.jugar()
print (gato.hambre)
print (gato.energia)

Como podemos ver creamos una clase a la que llamamos Gato con sus métodos y atributos propios, hay que fijarnos primero en la función _init_, como podemos observar este es el método que funciona como constructor del método y es el primero que se ejecuta al instanciar un objeto.

Aquí otra cosa a resaltar es que todos los métodos de la clase deben recibir como parámetro el atributo self, aunque nosotros al llamar a la función no lo indicamos, esto es para que cada método pueda acceder a los atributos y métodos propios de la clase. Para instanciar un objeto lo hacemos usando de la instrucción siguiente:
gato = Gato(5, 5)
Es importante pasar los parámetros que solicita la función _init_ al momento de realizar la instancia, y una vez que se realiza correctamente, ya podemos hacer referencia a los métodos y atributos de la clase usando el operador “.”.

Otro concepto importante es la herencia es decir generar una clase nueva a partir de otra, de la que recibe su comportamiento y estado (métodos y atributos), adaptándolos o ampliándolos según sea necesario. En el siguiente ejemplo vemos la clase Felino y la clase Gato que hereda de ella, esto se hace desde la declaración de la clase Gato como vemos en la siguiente sección de código:
class Felino:
    def __init__(self):
        print ('se creo el felino')
 
    def rugido(self):
        print ('El felino dio un rugido')
 
class Gato(Felino):
    def __init__(self, energia, hambre):
        self.energia = energia
        self.hambre = hambre
        print ('Se creo un gato')
 
    def tomar_leche(self, leche_en_litros):
        self.hambre += leche_en_litros
        print ('el gato toma su leche')
 
    def acariciar(self):
        print ('prrrrr...')
 
    def jugar(self):
        if self.energia <= 0 or self.hambre <=1:
            print ('el gato no quiero jugar')
        else:
            self.energia -=1
            self.hambre -= 2
            print ('al gato le encanta jugar')
 
    def dormir(self, horas):
        self.energia += horas
        print ('el gato tomo una siesta')

gato = Gato(3,3) 
gato.rugido()
De este modo el método rugido que se definió en la clase Felino puede ser accedido desde la clase Gato.

Algo que no existe en otros lenguajes de programación es el de poder heredar de múltiples clases al mismo tiempo, sin embargo esto si existe en Python, y el siguiente ejemplo es una muestra de ello, ya que nuestra clase Gato hereda tanto de la clase Felino como de la clase Mascota, por lo que puede acceder a métodos de ambas clases:
class Mascota:
    def __init__(self):
        print ('se creo la mascota')
 
    def sientate(self):
        print ('La mascota se sentó')
 
class Felino:
    def __init__(self):
        print ('se creo el felino')
 
    def rugido(self):
        print ('El felino dio un rugido')
 
class Gato(Felino, Mascota):
    def __init__(self, energia, hambre):
        self.energia = energia
        self.hambre = hambre
        print ('Se creo un gato')
 
    def tomar_leche(self, leche_en_litros):
        self.hambre += leche_en_litros
        print ('el gato toma su leche')
 
    def acariciar(self):
        print ('prrrrr...')
 
    def jugar(self):
        if self.energia <= 0 or self.hambre <=1:
            print ('el gato no quiero jugar')
        else:
            self.energia -=1
            self.hambre -= 2
            print ('al gato le encanta jugar')
 
    def dormir(self, horas):
        self.energia += horas
        print ('el gato tomo una siesta')

gato = Gato(3,3)
gato.rugido() 
gato.sientate()
Es importante también repasar el concepto de polimorfimo, es decir el poder realizar algo de muchas maneras diferentes o bien, poder responder a la misma función de varias maneras, en el ejemplo siguiente utilizamos una sola función para acceder a dos funciones distintas en diferentes clases.
class Gato:
   def  ruge(self):
       print ('El gato maulla')
 
class Perro:
   def ruge(self):
       print ('El perro ladra')
 
def rugir(animal):
    animal.ruge()

gato = Gato()
perro = Perro()
 
rugir(gato)
rugir(perro)

El último concepto que vamos a tratar en este post es sobre el encapsulamiento de los métodos y variables, esto se refiere a impedir la visualización o acceso de las variables de manera directa. Para declarar una variable o función como privada, el nombre de la función o variable a ser declarado debe comenzar con doble guion abajo. Esto bastará para que lo declarado sea reconocido como privado.

El siguiente ejemplo muestra cómo utilizar el encapsulamiento en métodos y variables.
class Encapsulamiento:
   def __init__(self):
       self.publico = 'variable publica'
       self.__privado = 'variable privada'
 
   def obtener_privado(self):
       print (self.__privado)

Ejemplo = Encapsulamiento()
print (Ejemplo.publico)
#print (Ejemplo.__privado)  Error
Ejemplo.obtener_privado()
Todos los ejemplos fueron tomados del siguiente link. Puede descargarlos desde el siguiente link

miércoles, 26 de agosto de 2015

Definiciones de Programación Paralela y Distribuida

Procesamiento Pipeline o Segmentado:


Es una técnica por medio de la cual se puede traslapar la ejecución de instrucciones. En la actualidad la segmentación es una de las tecnologías utilizadas para hacer procesadores más rápidos. Permite aumentar el rendimiento del funcionamiento en computadoras y dispositivos similares aplicándolo al microprocesador.

Consiste en la descomposición de la ejecución de un programa en varias etapas especializadas en realizar una tarea en específico, si bien estas etapas pueden requerir de que se ejecute una antes de ellos para ellas realizar su función, funcionan independientemente por lo cual, cuando una instrucción es procesada en la primera etapa y pasa a la segunda etapa, la primera etapa puede procesar inmediatamente la siguiente instrucción, sin esperar que la anterior sea procesada por todas las etapas.

Gráficamente se puede observar en la siguiente imagen:



Ley de Amdahl


La ley de Amdhal pone un límite superior a la ganancia en velocidad, y por tanto también a la eficiencia, de un sistema paralelo atendiendo al hecho, de que los procesos suelen tener partes que no pueden ser ejecutadas en paralelo, sino solo de forma secuencial pura.

Del tiempo total de ejecución del proceso, llamaremos s a la parte que no puede ser paralelizable y p al resto (paralelizable). Suponiendo condiciones ideales en la parte paralelizable del proceso p, tendremos que los tiempos mínimos de ejecución del proceso con un solo procesador y en el sistema paralelo con N procesadores.

De forma sencilla podemos enunciar la ley de Amdahl de la siguiente manera:

$$F_m = F_a ((1 - F_m)+ \frac{F_m}{A_m})$$
Donde:
$F_m$ = Tiempo de Ejecución mejorado
$F_a$ = Tiempo de Ejecución antiguo
$A_m$ = Factor de Mejora 

Lo más rescatable de la ley de Amdahl es el hecho de que el rendimiento de un sistema no aumenta por incrementarse indefinidamente el número de procesadores.

Ley de Gustafson


Establece que cualquier problema lo suficientemente grande puede ser eficientemente paralelizado, Se refiere a problemas con volumen de cálculo fijo en que se aumenta el número de procesadores, en la mayoría de los casos, cuando el volumen del problema crece, lo hace solo en su parte paralela, no en su parte secuencial. Ello hace que el cuello de botella secuencial tienda a cero cuando el volumen del problema aumenta.

Al aumentar el número de procesadores, el volumen de cálculo es mayor, esto afecta más a la parte paralela del problema, que a la parte secuencial. Esta ley puede quedar enunciada de la siguiente manera:

$$ S(N) = N - (N - 1)a$$
Donde:

$N = $ es el numero de procesadores
$S = $ es el aumento en velocidad
$a = $ es la parte no paralelizable del proceso

Esta ley indica que si existe una mejora en el procesamiento de la parte paralela de un proceso, al aumentar el número de procesadores, la cual se mantiene al momento de seguir aumentado el número de procesadores, siendo esto contradictorio a la ley de Amdhal, pero esto se debe a que parten de un fundamento diferente, mientras la de Amdahl considera procesos con un volumen de procesamiento fijo, la ley de Gustafson se refiere a problemas cuyo volumen de procesamiento puede aumentar según el número de procesadores, siendo esta última una situación más real.

Referencias:


lunes, 24 de agosto de 2015

Practica No.3: Programación en Android con Python

Actualmente los teléfonos están viviendo un boom tecnológico, lo que permite que evolucionen a un ritmo increíblemente acelerado, es por esto que la gran mayoría de lenguajes de programación han tratado de acercarse a esta nueva plataforma y poder crear aplicaciones que corran sobre ella.

Existen muchos S.O para celulares pero el gran dominador del mercado actualmente es Android, debido a esto no es raro que hayan surgido diversas librerías que permitan programar en Android. Y entre todos ellos Python no es la excepción y en la actualidad ya cuenta con varias formas de acércanos a la plataforma Android.

Algunas de las más reconocidas son Kivy, SL4A y QPython, sin embargo la opción QPython está descartada en este post debido a que está hecho para ejecutarse y programar directamente sobre un dispositivo Android, lo mismo que SL4A por lo tanto nos vamos a enfocar a la programación con Kivy al ser multiplataforma.

¿Cómo instalar Kivy?

Para instalar Kivy basta con ir a la página oficial y en ella bajar la versión adecuada de acuerdo a nuestro sistema, en este caso, la versión para Windows, después de bajarla y descomprimirla copiamos el archivo kivy-3.4.bat y lo mandamos a la carpeta Shell:sendto como acceso directo y le cambiamos el nombre a kivy-1.9.

De esta manera al tener nuestro archivo *.py le damos con click derecho y luego a “enviar a” y elegimos como opción kivy-1.9, así podemos probar nuestras aplicaciones.

Programando con Kivy

Realizaremos un ejemplo sencillo en kivy el cual simplemente desplegara el mensaje hola mundo en pantalla usando un botón, que en este ejemplo no realizara ninguna acción, el código es el siguiente:

from kivy.app import App
from kivy.uix.button import Button
 
class Hello(App):
    def build(self):
        btn = Button(text='Hello World')
        return  btn
 
Hello().run()

Como podemos observar lo único que hace esta clase es regresar un botón al que se le agrega la palabra “Hola Mundo” y ejecutar la aplicación con el método run(). Este ejemplo se puede descargar desde este link.

El siguiente ejemplo es tomado de los ejemplos de prueba de la propia herramienta Kivy, la cual obtiene la posición en pantalla cuando damos clic sobre ella, además nos indica el tipo de evento que es llamado y el número de eventos que han ocurrido, todo esto en colores generados aleatoriamente:

import kivy
kivy.require('1.0.6')
 
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.graphics import Color, Rectangle, Point, GraphicException
from random import random
from math import sqrt
 
 
def calculate_points(x1, y1, x2, y2, steps=5):
    dx = x2 - x1
    dy = y2 - y1
    dist = sqrt(dx * dx + dy * dy)
    if dist < steps:
        return None
    o = []
    m = dist / steps
    for i in xrange(1, int(m)):
        mi = i / m
        lastx = x1 + dx * mi
        lasty = y1 + dy * mi
        o.extend([lastx, lasty])
    return o
 
 
class Touchtracer(FloatLayout):
 
    def on_touch_down(self, touch):
        win = self.get_parent_window()
        ud = touch.ud
        ud['group'] = g = str(touch.uid)
        with self.canvas:
            ud['color'] = Color(random(), 1, 1, mode='hsv', group=g)
            ud['lines'] = (
                Rectangle(pos=(touch.x, 0), size=(1, win.height), group=g),
                Rectangle(pos=(0, touch.y), size=(win.width, 1), group=g),
                Point(points=(touch.x, touch.y), source='particle.png',
                      pointsize=5, group=g))
 
        ud['label'] = Label(size_hint=(None, None))
        self.update_touch_label(ud['label'], touch)
        self.add_widget(ud['label'])
        touch.grab(self)
        return True
 
    def on_touch_move(self, touch):
        if touch.grab_current is not self:
            return
        ud = touch.ud
        ud['lines'][0].pos = touch.x, 0
        ud['lines'][1].pos = 0, touch.y
 
        points = ud['lines'][2].points
        oldx, oldy = points[-2], points[-1]
        points = calculate_points(oldx, oldy, touch.x, touch.y)
        if points:
            try:
                lp = ud['lines'][2].add_point
                for idx in xrange(0, len(points), 2):
                    lp(points[idx], points[idx+1])
            except GraphicException:
                pass
 
        ud['label'].pos = touch.pos
        import time
        t = int(time.time())
        if t not in ud:
            ud[t] = 1
        else:
            ud[t] += 1
        self.update_touch_label(ud['label'], touch)
 
    def on_touch_up(self, touch):
        if touch.grab_current is not self:
            return
        touch.ungrab(self)
        ud = touch.ud
        self.canvas.remove_group(ud['group'])
        self.remove_widget(ud['label'])
 
    def update_touch_label(self, label, touch):
        label.text = 'ID: %s\nPos: (%d, %d)\nClass: %s' % (
            touch.id, touch.x, touch.y, touch.__class__.__name__)
        label.texture_update()
        label.pos = touch.pos
        label.size = label.texture_size[0] + 20, label.texture_size[1] + 20
 
 
class TouchtracerApp(App):
    title = 'Touchtracer'
    icon = 'icon.png'
 
    def build(self):
        return Touchtracer()
 
    def on_pause(self):
        return True
 
if __name__ in ('__main__', '__android__'):
    TouchtracerApp().run()

El ejemplo se puede descargar del siguiente link. Como podemos observar Kivy es una herramienta poderosa, que nos permite hacer cosas muy interesantes, que si bien no tiene métodos tan eficientes como otras opciones al ser multiplataforma, es una buena forma de comenzar en la programación de aplicaciones para Android.

Practica No. 3: Programación Google App Engine con Python

¿Qué es Google App Engine?

Google App Engine es un servicio de la familia de Google Cloud Platform que ofrece un servicio del tipo plataforma como servicio (PaaS), es decir permite que realicemos aplicaciones web sin necesidad de preocuparnos por la parte de infraestructura y centrarnos 100% en la aplicación, ya que la infraestructura utilizada es la propia de Google.

Esta plataforma facilita construir, mantener y escalar nuestras aplicaciones fácilmente, además de que soporta varios lenguajes como Java, Python, PHP, etc.  Además no tenemos que preocuparnos de la escalabilidad, la misma plataforma cuenta con un balanceador de cargas, es decir que nuestra aplicación automáticamente será atendida por las maquinas necesarias para que tenga un tiempo de respuesta óptimo.

Ofrece también herramientas seguras de almacenamiento persistente y veloz, especialmente recomendado es utilizar Google Cloud Datastore, la cual es una base de datos NoSQL, una de sus principales características es que es eventualmente consistente, es decir los cambios hechos no son registrados inmediatamente, sino que se realizara eventualmente.

Restricciones de Google App Engine

Todo esto puede sonar muy bien y es válido hacernos la pregunta de cuanto nos costara utilizar una infraestructura de este tipo, y la respuesta es nada, es gratis, mientras nos ajustemos a ciertos límites que se nos imponen, estos son:
  • 1.300.000 solicitudes diarias o 7.400 por minuto. 
  • 10 GB de subida y de bajada de datos diaria o 56 MB por minuto.
  • 1 GB de almacenamiento en el almacén de datos (el datastore).
  • 10.000.000 llamadas al almacén de datos diarias o 57.000 por minuto.
  • 7.000 llamadas al API de correo diarios o 32 llamadas por minuto.
  •  2.000 destinatarios de correo electrónico diarios u 8 por minuto.
Entre otras cosas, la lista completa de cuotas la podremos encontrar aquí, y hay que revisarla cuidadosamente para evitar meternos en problemas posteriormente. Como podemos observar esta plataforma ofrece características muy interesantes que merecen ser vistas a profundidad, y nos permite explorar un nuevo mundo de posibilidades sin ningún costo, la última parte de este post es la integración del GAE con Python.

Como utilizar Python con Google App Engine

Antes que nada hay que aclarar que todavía no tenemos compatibilidad con Python 3 por lo que es necesario utilizar la versión 2.7, en este post estaremos usando el framework de desarrollo oficial de Google, el webapp2. Primero que nada vamos a descargar el SDK de GAE desde la página principal en este link e instalarlo.

Después ejecutaremos el Google App Engine Launcher y en esta aplicación dentro de la pestaña file crearemos una nueva aplicación a la que nombraremos ejemplo, seleccionamos con que lenguaje queremos programar, la ruta donde queremos que se cree el proyecto y los puertos de administración, dentro de esta carpeta se crearan los siguientes archivos main.py el cual tendrá el código que ejecutara nuestra aplicación al recibir una petición, para nuestro caso lo llamaremos ejemplo.py; y por otro lado tenemos un archivo de configuración app.yaml.



La aplicación crea otras cosas también pero para nuestros fines solo nos importa lo anterior, por lo cual eliminaremos lo demás y modificaremos el archivo app.yaml para que se va de la siguiente manera:

application: ejemplo
version: 1
runtime: python27
api_version: 1
threadsafe: false

handlers:
- url: .*
  script: main.py

De este sencillo ejemplo lo único interesante es que en application indicamos el nombre de la carpeta que creamos, en runtime el lenguaje de programación a utilizar y en scripts el archivo que responderá a las peticiones. Una vez que tenemos configurado nuestro archivo de configuración, procedemos a modificar nuestro archivo main.py en este caso es como se muestra a continuación:

# -*- encoding: utf-8 -*-
import sys
import random
print 'Content-Type: text/html'
print ''
print '
'
# Leemos la entrada desde el formulario
data = sys.stdin.read()
try:
    resp = int(data[data.find('=')+1:])
except:
    resp = -1
print 'Tu número es ', resp
answer = random.randint(1, 50)
if resp < answer:
    print 'Tu número es demasiado bajo'
if resp == answer:
    print 'Lo has conseguido los numeros son iguales '
if resp > answer:
    print 'Tu número es demasiado alto'
print '
' print '''
Introduce un número entre 1 y 50:
'''

Como podemos observar este sencillo ejemplo lo único que hace es pedir un numero entre el 1 y el 100 y regresar si acertamos o no, en esta parte hay que destacar que con “print 'Content-Type: text/html'” indicamos que estamos creando una página web y que con la propia instrucción print podemos ir creando nuestra página usando php, además con el método sys.stdin podemos obtener la respuesta del método post. Si queremos correr nuestra aplicación desde el Google App Engine Launcher corremos nuestra aplicación y después vamos a la acción browse que nos abrirá nuestro navegador para probar nuestra aplicación.

En este ejemplo sin embargo no utilizamos el framework webapp2, así que para finalizar mostraremos otra aplicación que ahora si utiliza este framework en su funcionamiento, los elementos básicos son los mismos, sin embargo los archivos deberán modificarse, el archivo app.yaml cambia de la siguiente manera:

application: ejemplo2
version: 1
runtime: python27
api_version: 1
threadsafe: false

handlers:

- url: .*
  script: main.py

libraries:
- name: webapp2
  version: "2.5.2"

Mientras que el archivo main.py es el que se muestra a continuación:

# -*- encoding: utf-8 -*-
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
import random
class MainHandler(webapp.RequestHandler):
    formdata = '''
Introduce un número entre 1 y 100:
''' def get(self): self.response.headers['Content-Type'] = 'text/html' self.response.out.write(self.formdata) def post(self): resp = self.request.get('resp') try: resp = int(resp) except: resp = -1 answer = random.randint(1, 100) if resp < answer: msg = 'Tu número es demasiado bajo' if resp is answer: msg = 'Has acertado campeón, toma un pin!' if resp > answer: msg = 'Tu número es demasiado alto' self.response.out.write('Tu número es : '+str(resp)+' \n') self.response.out.write(msg+'\n') self.response.out.write(self.formdata) def main(): application = webapp.WSGIApplication([('/.*', MainHandler)],debug=True) run_wsgi_app(application) if __name__ is '__main__': main()
Si bien los cambios en el archivo app.yaml no son muchos y queda claro lo que se agregó, vale la pena mencionar que cambios ocurren el manejar el framework webapp2. Los elementos principales son:

·         Una o más clases ResquestHandler para procesar las peticiones y devolver una respuesta
·         Una instancia de WSGIApplication que encaminar la peticiones hacia sus manejadores basándose en su URL
·         Una función main que ejecuta el bucle principal utilizando el adaptador CGI

Dentro del archivo lo primero que vemos es el MainHandler esta clase es la encargada de responder a los solicitudes, a diferencia del ejercicio anterior donde solo escribíamos el componente form, en este lo debemos asiganar a una variable que en este caso denominamos formdata, después se definen dos métodos uno para responder al método post y otro al método get, en este caso el método post se encarga de mandar a pantalla el form que creamos en formato html para que podamos interactuar con el usuario.


Por su lado la función post responde cuando se ingresa el numero en la página y realiza la comparación, y después de desplegar el resultado de igual manera mandar a pantalla de nuevo el form.

Por último la función main crea una WSGIApplication en la cual se le manda a llamar al manejador principal y ejecuta la aplicación en un ciclo propio de wsgi. Si desea puede bajar el primer ejemplo desde aquí y el segundo desde aquíLos ejemplos fueron tomados desde la siguiente fuente

martes, 18 de agosto de 2015

Practica No.2 Programación Web en Python

Actualmente el poder realizar aplicaciones web es un requisito indispensable en cualquier lenguaje que quiera tener un lugar en la industria y Python no es la excepción, existen una gran cantidad de frameworks que permiten realizar aplicaciones web en Python, desde los más sencillos hasta los más complicados, pero entre todos ellos el que se considera el más completo y robusto es el framework Django. 

En este post vamos a realizar la instalación del framework bajo la plataforma de Windows y hacer un ejemplo sencillo para asegurarnos de que todo esté en orden, primero que nada vamos a hacer a darnos una pequeña idea de lo que es Django. 

¿Qué es Django? 

Django es un framework web de alto nivel, escrito en Python, que ayuda al desarrollo rápido y a un diseño limpio y pragmático. Construido por desarrolladores experimentados, resuelve una buena parte de los problemas del desarrollo web de tal manera que uno se pueda enfocar en escribir su app sin necesidad de reinventar la rueda. Es gratis y de código abierto. 

Django pone énfasis en el re-uso, la conectividad y extensibilidad de componentes, el desarrollo rápido y el principio No te repitas (DRY, del inglés Don’t Repeat Yourself). Python es usado en todas las partes del framework, incluso en configuraciones, archivos, y en los modelos de datos. Al igual que otros frameworks de desarrollo web, Django también trabaja con el patrón MVC (Modelo Vista Controlador). 

Instalación del Django 

Para instalar Django en la plataforma de Windows primero necesitamos tener instalado Python, para esto solo hay que bajar el instalador apropiado del sitio oficial aquí, de acuerdo a nuestro sistema operativo, una vez que hemos terminado la instalación de Python necesitamos verificar que las variables de entorno de del sistema estén correctamente agregadas y aparezcan las siguientes:

"C:\Python27;C:\Python27\Scripts"

Si las variables no están correctas, podemos agregarlas de forma manual con cuidado, después de verificar lo anterior hay que descargar la paquetería setuptools, este paquete permite gestionar los paquetes propios de Python, para ello entramos a la página oficial aquí y buscamos la opción Windows (simplified), descargamos el archivo ez.setup.py  y lo ejecutamos, si nuestras variables de entorno son correctas, se ejecutara la línea de comandos de Python y se instalara el paquete.

Una vez instalado el paquete anterior simplemente ingresamos en una terminal el siguiente comando y la instalación se realizara rapidamente:

pip install Django

Para verificar que todo esté en orden verificamos con el siguiente comando desde la línea de comando de Python y la salida debera ser parecida a la imagen solo cambiando la version de Django instalada.


Ejemplo del Funcionamiento del Django

Primero que todo tenemos realizar una configuración inicial. Esto es, necesitamos auto-generar código que define un proyecto Django (conjunto de configuraciones para una instancia de Django, incluyendo configuración de base de datos). Para realizar esto desde línea de comando nos ubicamos en el directorio donde queramos crear nuestro proyecto y ejecutamos el comando:
 
django-admin.py startproject ejemplo
Starproject crea dos directorios y un conjunto de archivos distribuidos de la siguiente manera:
  • ejemplo/: El directorio externo ejemplo/ es solo el contenedor del proyecto. No importa para Django por lo que podemos renombrarlo si queremos.
  • manage.py: Una utilidad en línea de comando que nos permite interactuar con el proyecto Django de diversas formas. No debemos editarlo de ninguna manera.
  • ejemplo/ejemplo/: El directorio interno ejemplo/ es el paquete Python actual del Proyecto, es usado para importar cualquier cosa dentro de él.
  •  __init__.py: Un archiva requerido por Python para tratar el directorio ejemplo como paquete.
  • settings.py: Configuración para este proyecto Django.
  •  urls.py: Las URLs para este proyecto Django; una "tabla de contenidos" de tu sitio basado en Django.
  • wsgi.py: Un punto de entrada para servidores web compatibles con WSGI.
Para asegurarnos de que nuestro proyecto esté funcionando entrar al directorio ejemplo/ y ejecutar el comando:
 
python manage.py runserver
.
Con lo cual debemos ver una salida como la siguiente si es que todo salió bien


Finalmente accedemos a la dirección IP http://127.0.0.1:8000/ con la cual accederemos a la página de bienvenida del servidor Django.


Aquí hay que aclarar algo importante este servidor tiene como objetivo solo ser un entorno de desarrollo, no de producción, por lo que no debe ser utilizado a nivel empresarial.

Ahora vamos a crear el archivo que necesitamos para implementar una parte del modelo MVC con el que trabaja el framework Django, para esto creamos un archivo llamado views.py en el directorio ejemplo/ejemplo el contenido de este archivo es el siguiente:
 
from django.http import HttpResponse

def hello(request):
    return HttpResponse("Hello world")
El objetivo del archivo views.py es tener funciones de Python que reciban un HttpRequest como primer parámetro (puede tener más parámetros) y regrese una instancia de HttpResponse, que se despliega como una pagina web.

Para que podamos observar nuestro Hello World en el navegador necesitamos asignarle una dirección URL a la función hello de la clase views, de esta manera a cada URL o página desplegada se le asigna una función que desplegara su contenido. Vamos a modificar el archivo urls.py de la siguiente manera:
 

from django.conf.urls import include, url
from django.contrib import admin
from Ejemplo import views

urlpatterns = [
   url(r'^admin/', include(admin.site.urls)),
   url(r'^$', views.hello, name='hello')
]
De esta manera al acceder a la dirección IP http://127.0.0.1:8000/ en vez de desplegarse en pantalla el mensaje de bienvenida de Django se desplegara nuestro Hello World. Con esto terminamos nuestro pequeño ejemplo con Django hay muchas más cosas que podemos realizar con el pero esto es solo una primera aproximación a todo lo que nos brinda.