🎃 Grandes descuentos en libros en línea, eformaciones y vídeos*. Código CALABAZA30. Pulse aquí
¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
  1. Libros
  2. Python 3
  3. Iniciación a la programación concurrente
Extrait - Python 3 Tratamiento de los datos y técnicas de programación
Extractos del libro
Python 3 Tratamiento de los datos y técnicas de programación
1 opinión
Volver a la página de compra del libro

Iniciación a la programación concurrente

Noción de rendimiento

Antes de hablar de rendimiento, hay que saber analizar cuáles son los paradigmas utilizados, cuál es la complejidad del programa y para terminar, cuál es la arquitectura de hardware necesaria para que el programa funcione.

En efecto, hay muchos detalles sobre los que el programa no tiene ningún control. Se puede citar, por ejemplo, la naturaleza del sistema operativo sobre el que funciona, la cantidad y el consumo de otros programas que funcionan al mismo tiempo, la cantidad de memoria, el procesador o incluso el ancho de banda de la red.

Sepa que en esta parte no hablamos del rendimiento en el sentido de optimización a nivel de la algoritmia del código, sino de soluciones a nivel de la arquitectura de la aplicación en sí misma.

1. Programación bloqueante

Todo programa pasa por fases en las que está inactivo. Esto se puede deber al hecho de que espera alguna cosa del usuario:

data = read("Indique un dato") 

O porque esperamos un recurso cualquiera: I/O, disco duro, periférico o incluso de la red:

import requests  
response = requests.get('http://inspyration.org') 

En este ejemplo, el programa espera la resolución de nombres (DNS), después de la respuesta del servidor que potencialmente va a poner la consulta en la cola de espera, y después trabajar por su parte durante un pequeño instante, antes de empezar a enviar su respuesta.

Tanto en un caso como en el otro, mientras que su programa espere el recurso, está bloqueado: no hará nada más. Según el contexto, esto puede ser aceptable, porque se puede permitir perder hasta 300, incluso 500 milisegundos, sin que el usuario se vea afectado.

Sin embargo, en otros contextos, esto es peor:

from bs4 import BeautifulSoup  
soup = BeautifulSoup(response.content, "html.parser")  
image_urls = [img.get("src") for img in soup.find_all("img")]  
image_contents = [requests.get("http://inspyration.org/{}".format(url))  ...

Terminología

En esta parte se van a presentar algunas nociones técnicas que es necesario entender, para que pueda hacer sus elecciones.

1. Proceso

Un proceso es la ejecución de un conjunto de instrucciones a través del uso de recursos físicos, siendo los dos principales la memoria RAM, en la que se almacena su entorno de ejecución y el procesador que tiene asignado.

Las operaciones de creación, gestión y destrucción de un proceso, se gestionan por el sistema operativo, no directamente por Python. Python ofrece herramientas de alto nivel para crear, gestionar y destruir estos procesos, que siguen siendo relativamente sencillos pero que usan en realidad el sistema operativo, que hace la parte principal del trabajo.

Y afortunadamente, porque este último es extremamente complejo e implica nociones de programación de sistemas muy específicos. El proceso se gestiona por un planificador que depende del sistema operativo. Este último se encarga de poner a disposición los recursos (memoria, tiempo de procesador, etc.) y vigilar que cada proceso acceda a estos recursos de manera equitativa. Si hay varios procesadores, los procesos también se distribuyen entre ellos de manera equitativa.

Un programa lanzado por un usuario, se puede corresponder con un único proceso o varios. Por ejemplo, durante el lanzamiento, Apache crea seis procesos:

$ ps ax | grep apache   
 1569 ?        Ss     0:00 /usr/sbin/apache2 -k start   
 1584 ?        S      0:00 /usr/sbin/apache2 -k start   
 1585 ?        S      0:00 /usr/sbin/apache2 -k start   
 1586 ?        S      0:00 /usr/sbin/apache2 -k start   
 1587...

Presentación de los paradigmas

1. Programación asíncrona

Cuando la problemática principal es el tiempo I/O, la solución ideal es programar de modo asíncrono. La idea es que nuestro programa continúa funcionando con un proceso que solo contiene un hilo de ejecución, no nos molestamos en gestionar tareas o procesos, que son una herramienta relativamente pesada de implementar.

La programación asíncrona consiste en declarar que una de las tareas que el programa debe ejecutar, potencialmente se puede bloquear (quedar en espera de una I/O y no tener nada que hacer mientras tanto) y el programa puede continuar su ejecución por el código de espera.

Por ejemplo, es uno de los puntos fuertes de JavaScript, un lenguaje que está diseñado con objetivos particulares: es ejecutado para un navegador y es el navegador el que administra los hilos de ejecución y procesos. Por lo tanto, JavaScript siempre tendrá en este contexto, un único hilo de ejecución y un único proceso y dando por hecho que ejecuta instrucciones en un contexto de red, forzosamente hay mucha espera I/O.

Para esto se usa la asincronía, que para este lenguaje es la mejor opción para mejorar el rendimiento. Por esta razón destacan todos los esfuerzos dirigidos a este objetivo.

De esta manera, cuando se alardea de los méritos de Node.js, solo se alardea del buen uso de las capacidades del lenguaje en sí mismo. Node.js solo hace un buen uso de la asincronía y sabe aprovecharlo. También esto es lo que otros servidores Python muy antiguos y conocidos hacen perfectamente desde hace años, como Tornado o Twisted, pero cada uno a su manera.

Uno de los ejes de mejora de la rama 3.x de Python es justamente el desarrollo de estas capacidades de gestión de la asincronía. El capítulo siguiente se dedica a la presentación de estas funcionalidades y presenta algunos ejemplos concretos.

2. Programación en paralelo

La programación en paralelo es el hecho de añadir la potencia de la CPU de la misma máquina, creando varios hilos de ejecución o varios procesos, para repartir la carga de trabajo.

Utilizar la programación en paralelo hace necesario un poco de trabajo, pero en Python esto no es una tarea insuperable. Sin embargo, esto no aportará tanto como deseamos...