Está en la página 1de 7

Modelos de programación paralela

Comparar el modelo de programación de memoria compartida con el modelo de memoria


distribuida. (No se pide explicar instrucciones de las interfaces de programación sino
analizar el procesamiento de los datos en cada uno de los modelos).

1. Explicar cómo se generan los distintos procesos en cada modelo de programación y


cómo se produce la comunicación entre ellos para cada caso.

En un primer lugar me parece clave aclarar qué implica el cómputo en Paralelo, para poder
comprender más claramente ambos modelos: el de programación de memoria compartida y
el de programación con memoria distribuida.

¿Qué implica el cómputo en Paralelo?

La Computación Paralela es el uso simultáneo de múltiples recursos de cómputo para


resolver un problema computacional. Esto significa:

- Es ejecutado utilizando múltiples procesadores


- El problema es divido en partes que pueden resolverse concurrentemente
- Cada parte es además dividida en una serie de instrucciones, que a su vez se
ejecutan simultáneamente en diferentes procesadores
- Debe emplearse un mecanismo de coordinación general

Ahora bien, debe también haber cierta comunicación para que todo esto pueda llevarse a
cabo.

Estaremos analizando dos modelos de programación: el de memoria compartida y el de


memoria distribuida.

Programación en memoria compartida

Un programa paralelo que se ejecuta en un multiprocesador de memoria compartida


generalmente consta de varios hilos de ejecución. A estos hilos de ejecución o ‘threads’ se
los considera procesos. Un procesador, entonces comienza la ejecución del programa en
paralelo en múltiples hilos de ejecución. Cada thread posee sus propios registros de control
y su propio stack de datos, además de ejecutar el código de manera independiente.
A continuación podemos notar que el programa comienza ejecutándose de manera
secuencial. Esto se da en lo que llamamos hilo maestro. Luego, comienza lo que es la
ejecución en paralelo, que da lugar a una zona crítica (hablaré de ello más tarde).
Finalmente, el programa vuelve a ejecutarse de manera secuencial.

Región Región
Secuencial Secuencial
Región Región
Paralela Paralela
Antes de continuar, me gustaría dejar en claro que los threads ejecutan en mismo código o
programa, pero al manejarse con identificadores distintos si se utilizan condiciones o su
identificador correctamente se puede lograr que cada thread realice una tarea distinta o que
cooperando con el resto de los threads cada uno realice una pequeña porción del trabajo.

Como ya dijimos el procesador es de memoria compartida, en otras palabras, todos los


procesos o ‘threads’ tienen acceso a la misma memoria. Entonces, es lógico que la
comunicación entre los procesos se dé a través de variables compartidas en dicha memoria,
como podemos ver en el esquema de a continuación,
Programación en memoria distribuida

El paradigma de programación en memoria distribuida, consiste de P procesos


(eventualmente procesadores) cada uno de ellos con su espacio de direcciones exclusivo. El
espacio de direccionamiento se encuentra particionado. Es decir que, cada proceso tiene su
memoria privada, y no comparten entre ellos una memoria compartida. ¿Cómo se
comunican? En lugar de comunicarse a través de variables compartidas, utilizan mensajes.
Por esta razón, este paradigma también se conoce como de Mensajes.

Vale aclarar que los mensajes no solo sirven para la comunicación entre los procesos, sino
que también ayudan a la sincronización. Además, toda interacción requiere la cooperación
de dos procesos.

Al arrancar una aplicación se lanzan en paralelo N copias del mismo programa y el


intercambio de información, así como la sincronización, como ya dije, se hace mediante
paso de mensajes. Loqs datos deben ser explícitamente particionados (explícitamente
escribir a quien le corresponde cada parte del programa, cada datos de los programa).

Un modelo que suele utilizarse es el de master-worker, donde hay un proceso maestro


(suele ser el de id 0) y procesos esclavos. El maestro funciona como cerebro del programa y
se encarga de distribuir el trabajo a todos los workers. Siendo los workers el resto de los
procesos. En la mayoría de los casos el maestro no solo reparte el trabajo y los datos a los
workers sino que también una vez que los workers terminaron de trabajar, junta o recopila
los resultados obtenidos de todos los workers. Ahora bien, se utilizarán mensajes y
condicionales para identificar que hace cada proceso puesto que todos los procesadores
recibe la misma copia del programa.
2. ¿Qué puede afirmar sobre el determinismo cuando se realiza el cómputo en cada
modelo?

Para responder esta pregunta primero explicaré a que nos referimos cuando hablamos de
determinismo en un programa. Decimos que un programa es determinístico cuando para
los mismos datos de entrada, ejecuta siempre la misma secuencia de instrucciones y obtiene
la misma salida. Contrariamente un programa no determinístico es cuando puede dar
distintos resultados al ejecutarse sobre los mismos datos de entrada.

Decimos entonces que, el determinismo consiste en la previsibilidad de los resultados a


obtener al ejecutar el programa. Es decir, tener conocimiento de lo que se va a devolver o
mostrar por pantalla y en qué orden.

Al dividir en procesos paralelos, no puede darse determinismo. Esto tiene que ver con la
naturaleza del paralelismo, que implica una competencia entre los procesos para acceder a
un recurso compartido o para enviar mensajes a otro proceso. En ambos casos, no es
posible saber de antemano el orden en que se llevará a cabo la ejecución, ya que se
obtendrán distintos resultados tras cada repetición. Asimismo, aplicar restricciones que
permitan dar desde el código un orden de ejecución, puede llevar a que se pierda la ventaja
del paralelismo y que se lleve al equivalente a una versión secuencial de programa,
perdiendo performance.

3. ¿Qué problema de sincronización se produce? Explicar si la sincronización es un


problema que se soluciona por hardware o por software, o con ambos. ¿Qué se sincroniza
en cada modelo de programación?

Ya expliqué que cundo hablamos de programación en memoria compartida los


subprocesos individuales pueden acceder a cualquier ubicación de la memoria principal y
ejecutar secuencias de instrucciones de forma independiente. Esto puede llevar a Race
Conditions o a que por problemas de sincronización se modifique una variable que no debía
se ser modificada hasta cierto punto. Para resolver el segundo des estos problemas aparecer
las barreras, que nos permiten sincronizar todos los procesos. Las Race Conditions también
se dan por problemas de sincronización, esta es una situación en la que el resultado depende
de la sincronización precisa de los accesos de lectura y escritura a la misma ubicación en la
memoria principal. Como solución a las mismas aparece el concepto de zona crítica. La
zona crítica es un punto de consistencia en memoria, al ser una región de exclusión mutua
en la que solamente se puede ejecutar un thread a la vez. Si volvemos al diagrama inicial,

Zona Crítica

Cuando hablamos de programación en memoria distribuida, si bien no hay problemas de


conflictos en memoria puede haber otros conflictos respecto la sincronización. Hay
mensajes que son bloqueantes y en caso de utilizarlos incorrectamente pueden hacer que el
programa se bloque y no continúe con su ejecución, es decir que podrían haber
DEADLOCKS. Durante la cursada, por ejemplo, vimos los mensajes de Send y Recv.
También vimos que ambos en MPI, son bloqueantes. Entonces, podría suceder que los
procesos 0 y 1, estén esperando con un Recv de información del respectivo, esto llevaría a
un ABRAZO MORTAL.

En ambos modelos la sincronización de los procesos puede llevar a que los procesos tengan
tiempo ocioso. En el modelo de programación con memoria compartida, si se utilizan
menos threads que los disponibles, si se utilizan las barreras dónde no corresponden o
demasiadas zonas críticas ciertos procesos pueden quedar ociosos. Por otro lado, en el
modelo de programación con memoria distribuida los mensajes bloqueantes pueden llevar a
que ciertos procesos tengan tiempo ocioso. Un Send debe esperar a que se ejecute el Recv
respectivo para que la ejecución puede seguir y viceversa. Una solución a esto es agregar
un buffer. Cuando hay un SEND, este copia el dato en el buffer y el programa sigue
adelante. Cuando el receptor realiza el Recv respectivo, encuentra el dato en el buffer.
Igualmente también aparece un tema de hardware. Recordemos que en memoria distribuida
cada procesador es una entidad independiente. Imaginemos, entonces que tenemos 4
procesadores: uno que hace de master y 3 que hacen de workers, según el modelo de
master-worker que hemos estudiado a lo largo de la cursada. ¿Qué sucede si distribuimos la
carga de trabajo de manera estática y hay un procesador que es muchísimo más veloz que
los otros? Este quedará rápidamente ocioso y estaremos desperdiciando un recurso. Como
solución deberíamos analizar distribuir la carga dinámicamente.

También podría gustarte