• No se han encontrado resultados

Benemérita Universidad Autónoma de Puebla Facultad de Ciencias de la Computación

N/A
N/A
Protected

Academic year: 2021

Share "Benemérita Universidad Autónoma de Puebla Facultad de Ciencias de la Computación"

Copied!
54
0
0

Texto completo

(1)

Facultad de Ciencias de la Computación

Licenciatura en Ciencias de la Computación Ingeniería en Ciencias de la Computación

APUNTES DEL CURSO:

PROGRAMACION CONCURRENTE y PARALELA

Creación

Activación Terminación

Planificación por tiempo

Planificación por tiempo

Sincronización por Sincronización por

  Listo        En  ejecución

  Bloqueado

 Finalizado   Nuevo

Dr. Mario Rossainz López

Otoño de 2011

Puebla, Pue. México

(2)

 

1. INTRODUCCIÓN A LA PROGRAMACIÓN CONCURRENTE Y PARALELA   

1.1. HISTORIA DE LA PROGRAMACIÓN CONCURRENTE 

En  los  tiempos  actuales  la  construcción  de  sistemas  paralelos  y  concurrentes  tiene  cada vez menos limitantes y su existencia ha hecho posible obtener gran eficiencia en  el procesamiento de datos. La utilización de tales sistemas se ha extendido a muchas  áreas de la Ciencia Computacional e Ingeniería. Como es sabido, existen infinidad de  aplicaciones que tratan de obtener el máximo rendimiento del sistema al resolver un  problema  utilizando  máquinas  con  un  solo  procesador;  sin  embargo,  cuando  el  sistema no puede proporcionar el rendimiento esperado, una posible solución es optar  por  aplicaciones,  lenguajes  de  programación,  arquitecturas  e  infraestructuras  de  procesamiento  paralelo  y  concurrente.  Para  incrementar  el  rendimiento  de  determinados  sistemas,  el  procesamiento  paralelo  es,  por  tanto,  una  alternativa  al  procesamiento  secuencial.  Desde  el  punto  de  vista  práctico,  hoy  en  día  existen  arquitecturas de computadores implementadas en sistemas reales que proporcionan  auténtico  procesamiento  paralelo  que  justifican  el  interés  actual  en  llevar  a  cabo  investigaciones  dentro  del  procesamiento  paralelo  y  áreas  a  fines  (concurrencia,  sistemas  distribuidos,  sistemas  de  tiempo  real,  etc.).  Una  parte  importante  de  estas  investigaciones se refiere a mejorar el diseño de algoritmos paralelos y distribuidos, el  desarrollo  de  metodologías  y  modelos  de  programación  paralela  que  ayuden  a  la  programación  de  estos  algoritmos  sobre  sistemas  físicamente  distribuidos,  así  como  herramientas de razonamiento que permitan garantizar su corrección. 

 

Procesamiento Paralelo 

El  procesamiento  paralelo  ha  provocado  un  tremendo  impacto  en  muchas  áreas  donde  las  aplicaciones  computacionales  tienen  cabida.  Un  gran  número  de  aplicaciones computacionales en la ciencia, ingeniería, comercio o medicina requieren  de  una  alta  velocidad  de  cálculo  para  resolver  problemas  de  visualización,  bases  de  datos distribuidas, simulaciones, predicciones científicas, etc. 

Estas aplicaciones envuelven procesamiento de datos o ejecución de un largo número  de  iteraciones  de  cálculo.  El  procesamiento  paralelo  es  uno  de  los  enfoques  computacionales  que  hoy  en  día  puede  ayudarnos  a  procesar  estos  cálculos  de  una  manera  más  viable.  Este  incluye  el  estudio  de  arquitecturas  paralelas  y  algoritmos  paralelos. 

Las  tecnologías  del  procesamiento  paralelo  son  actualmente  explotadas  de  forma  comercial,  principalmente  para  el  trabajo  en  el  desarrollo  de  herramientas  y  ambientes. El desarrollo de esta área en las redes de computadoras ha dado lugar a la  llamada  Computación  Heterogénea  [Bacci  et  al.99]  (que  proporciona  un  ambiente 

 

(3)

 

donde  la  aplicación  paralela  es  ejecutada  utilizando  diferentes  computadoras  secuenciales y paralelas en las cuales la comunicación se lleva a cabo a través de una  red inteligente. Este enfoque proporciona alto rendimiento). 

Existen muchos factores que contribuyen al rendimiento de un sistema paralelo, entre  ellos están la integración entre procesos, es decir, el tener múltiples procesos activos  simultáneos resolviendo un problema dado; la arquitectura del hardware (arquitectura  superescalar, de vector o pipelinizada, etc.), y enfoques de programación paralela para  comunicar y sincronizar los procesos. 

 

Paralelismo 

El objetivo del paralelismo es conseguir ejecutar un programa inicialmente secuencial  en menos tiempo utilizando para ello varios procesadores. La idea central es dividir un  problema  grande  en  varios  más  pequeños  y  repartirlos  entre  los  procesadores  disponibles, pero, sin una estrategia adecuada de diseño, dicho reparto no produce los  resultados  esperados  respecto  del  aumento  de  eficiencia  del  programa,  así  como  puede complicar enormemente la programación y el mantenimiento posterior. Así, a  veces ocurre que no es posible llevar a cabo una paralelización de todo el programa,  debido a que aparecen elementos que no se pueden paralelizar, otras veces el reparto  de elementos del programa entre los procesadores no se hace de manera equitativa,  lo que se traduce en un aumento muy pobre de la eficiencia respecto del tiempo de  ejecución. 

                   

(a) 

4 procesadores

1 procesador

tiempo Tiempo en

paralelo

Fig. 1. (a) Esquema ideal del resultado de la paralelización 

Tiempo en

Secuencial

         

(4)

 

  (b) 

 

4 procesadores

1 procesador

           

  tiempo

  Tiempo en

paralelo

Tiempo en

Secuencial

 

Fig. 1. (b) esquema real, del resultado de la paralelización   

Si  suponemos  que  todos  los  procesadores  llevan  a  cabo  el  mismo  tipo  y  número  de  operaciones, la reducción ideal del tiempo de ejecución debería ser la que se muestra  en la fig. 1. (a); sin embargo, lo que realmente ocurre con frecuencia es lo mostrado en  la fig. 1 (b). 

Un ejemplo de paralelización puede ser un ciclo de 100 iteraciones que se reparte para  su  ejecución  entre  4  procesadores.  Cada  uno  de  ellos  sólo  ejecutará  25  repeticiones  del ciclo original, Fig. 2. 

  25 Iteraciones

Procesador 1

 

25 Iteraciones 25 Iteraciones 25 Iteraciones

   

  Procesador 2

 

  FOR i= 1, 100 {

a[i]= b[i]+c[i]

}

 

Procesador 3

     

  Procesador 4

     

Fig. 2. Reparticiones de las iteraciones de un ciclo en 4 procesadores     

 

(5)

 

Sin  embargo,  esto  no  suele  funcionar  de  esta  forma  en  el  caso  real,  ya  que  suelen  existir  relaciones  entre  las  variables  de  los  programas  que  establecen  dependencias  secuenciales que impiden la realización de repartos simples, como en el caso anterior. 

Es  por  esto  que  la  unidad  de  paralelización  en  los  lenguajes  de  programación  y  sus  aplicaciones  no  debiera  ser  las  instrucciones  de  los  programas,  sino  más  bien  entidades que encapsulen instrucciones así como los datos que modifican. 

 

Concurrencia 

La  programación  concurrente  tiene  sus  raíces  en  los  sistemas  operativos  y  en  la  programación de sistemas, no en  vano, los primeros programas concurrentes fueron  los propios sistemas operativos de multiprogramación en los que un solo procesador  de gran capacidad debía compartir su tiempo entre muchos usuarios. En los años 60’s  se  introdujeron  en  las  computadoras  dispositivos  controladores  independientes  de  entrada‐salida  llamados  canales.  Estos  canales  eran  programables  en  sí  mismos.  Los  sistemas operativos fueron organizados como una colección de procesos ejecutándose  concurrentemente (“al mismo tiempo”), algunos en los canales y otros ejecutándose  en  el  procesador  principal  o  CPU.  Por  otro  lado  en  esos  años  la  programación  de  sistemas con capacidades de concurrencia se hacía a bajo nivel, en ensamblador, pues  aparte de no disponer de lenguajes de alto nivel con capacidades de concurrencia, se  primaba  la  supuesta  eficiencia  del  código  escrito  directamente  en  ensamblador.  La  aparición en 1972 del lenguaje de alto nivel Concurrent Pascal, desarrollado por Brinch  Hansen abrió la puerta a otros lenguajes de alto nivel que incorporaban concurrencia. 

 

Desde entonces la programación concurrente ha ido ganando interés y actualmente se  utiliza  muy  a  menudo  en  la  implementación  de  numerosos  sistemas.  Tres  grandes  hitos fortalecen el hecho de que la programación concurrente sea tan importante: 

 

• La  aparición  del  concepto  de  Thread  o  hilo  que  hace  que los  programas  puedan  ejecutarse con mayor velocidad comparados con aquellos que utilizan el concepto  de proceso 

• La aparición más reciente de lenguajes como JAVA, lenguaje orientado a objetos de  propósito  general  que  da  soporte  directamente  a  la  programación  concurrente  mediante la inclusión de primitivas específicas. 

• La  aparición  del  INTERNET  que  es  un  campo  abonado  para  el  desarrollo  y  la  utilización de programas concurrentes. Cualquier programa de Internet en el que  podamos  pensar  tales  como  un  navegador,  un  chat,  etc.,  están  programados  usando técnicas de programación concurrente. 

 

(6)

 

1.2. LENGUAJES DE PROGRAMACIÓN CONCURRENTE 

Los  lenguajes  de  programación  paralela/concurrente  se  basan  en  dos  categorías,  en  abstracciones de programación concurrente basadas en exclusión mutua de accesos a  una memoria individual; y en abstracciones de procesos que se comunican mediante  el envío de mensajes unos con otros. El envío de mensajes es una acción de alto nivel  que puede ser implementada físicamente mediante procesadores distribuidos. 

 

Cada  enfoque  de  programación  concurrente/paralela  sugiere  una  configuración  de  hardware particular. La mejor será aquella que empate con las primitivas del lenguaje  utilizado  en  la  programación  paralela/concurrente.  Actualmente  existen  muchos  lenguajes  de  programación  que  ya  tienen  diseñadas  primitivas  para  el  manejo  de  procesos de manera asíncrona o síncrona.  La programación asíncrona se utiliza para la  programación  de  multiprocesadores  o  sistemas  distribuidos;  mientras  que  las  soluciones  paralelas  síncronas  son  propias  del  uso  de  arrays  o  vectores  de  procesadores. 

 

En el caso de la programación orientada a objetos, ésta puede ser utilizada como un  buen  enfoque  de  programación  paralela/concurrente  ya  que  puede  encapsular  y  abstraer patrones comunes de comunicación paralela y llevar dicha abstracción hacia  un estilo estructurado de programación paralela/concurrente.  

 

El  lenguaje  de  programación  C++  es  un  ejemplo  de  un  excelente  lenguaje  de  programación  orientado  a  objetos  con  el  que  se  pueden  implementar  aplicaciones  paralelas mediante el uso de threads para el manejo de procesos ligeros o hilos en un  ambiente de memoria compartida. 

 

1.3. CONCEPTOS BÁSICOS DE PROGRAMACIÓN CONCURRENTE Y PARALELA 

En  un  modelo  general  de  programación,  un  programa  ordinario  se  forma  de  un  conjunto  de  instrucciones  y  datos,  escrito  en  algún  lenguaje  de  programación  (comúnmente  imperativo)  donde  las  instrucciones  son  ejecutadas  secuencialmente  (una  tras  otra).  La  ejecución  de  éste  código  viene  refrendada  entonces  por  su  linealidad,  es  decir,  el  procesador  ejecuta  un  conjunto  de  instrucciones  de  máquina  único,  que  pertenece  a  un  solo  proceso.  A  éste  tipo  de  programas  se  conoce  como  programa secuencial (ver Figura 3). 

 

(7)

 

Definimos  entonces  la  programación  secuencial  como  un  simple  hilo  de  ejecución  o  proceso ligero1 donde un procesador ejecuta un conjunto de instrucciones de manera  secuencial. 

int main() {

char *mensaje1 = "Hola";

char *mensaje2 = "Mundo";

imprimir_mensaje((void *)mensaje1);

imprimir_mensaje((void *)mensaje2);

printf("\n");

exit(0);

return 1; }

                 

Fig. 3. Un Simple hilo de Ejecución, Proceso Ligero o Programa Secuencial   

 

Por  el  contrario,  en  un  modelo  de  Programación  Concurrente,  existen  varios  programas  que  se  ejecutan  secuencialmente,  es  decir,  existen  múltiples  hilos  de  ejecución trabajando en paralelo (ver Figura 4), aparentemente, ya que se trata de un  paralelismo abstracto, pues no es necesario utilizar un procesador físico para ejecutar  cada  proceso.  En  otras  palabras,  si  observamos  en  un  intervalo  de  tiempo  lo  suficientemente  amplio  el  conjunto  de  instrucciones  de  máquina  ejecutado  por  el  procesador,  veremos  que  hay  instrucciones  que  pertenecen  a  diferentes  hilos  de  ejecución; pues los distintos procesos comparten el tiempo de ejecución de un único  procesador disponible, mediante alguna técnica de planificación2

En  conclusión  diremos  que,  aunque  el  programa  concurrente  sea  ejecutado  en  un  único  procesador,  supondremos  que  los  procesos  que  lo  integran  están  siendo  ejecutados simultáneamente, y no nos preocuparemos por los detalles del paralelismo  físico que proporciona nuestra computadora. 

   

      

1 En páginas posteriores se hará la distinción entre lo que es un proceso ligero y un proceso pesado.

2 Esto es así, ya que las instrucciones de los procesos se ejecutan intercalándose unas con otras (paralelismo

(8)

   

   

             

int main() {

char *mensaje1 = "Hola";

char *mensaje2 = "Mundo";

imprimir

*)mensaje1);

_mensaje((void

imprimir mensaje((void int main() {

char *mensaje1 = "Hola";

char *mensaje2 = "Mundo";

imprimir

*)mensaje1);

_mensaje((void

imprimir mensaje((void int main() {

char *mensaje1 = "Hola";

char *mensaje2 = "Mundo";

imprimir_mensaje((void*)mensaje1);

imprimir_mensaje((void*)mensaje2);

printf("\n");

Fig. 4. Programación Concurrente: Múltiples hilos de Ejecución trabajando en 

“paralelo” 

 

De forma física los procesos pueden: 

 

a) Multiplexar su ejecución en un único procesador (Multiprogramación) 

b) Multiplexar  su  ejecución  en  un  sistema  multiprocesador  de  memoria  compartida  (Multiproceso) 

c) Multiplexar  su  ejecución  en  varios  procesadores  que  no  comparten  memoria  (Procesamiento Distribuido) 

d) Ejecutarse según un modelo híbrido de los 3 anteriores   

Sólo en los casos (b), (c) y (d) se puede hablar de una ejecución paralela verdadera. El  término concurrente indica paralelismo potencial. 

Una propiedad fundamental de la programación concurrente es el no determinismo: 

dado un instante de tiempo, no es conocido que va a ocurrir en el instante siguiente. 

Para la implementación sobre un único procesador, no puede saberse si va a ocurrir o  no una interrupción que cause un intercambio del proceso que esta siendo ejecutado. 

En  el  caso  de  un  sistema  multiprocesador,  las  velocidades  de  los  procesadores  no  están sincronizadas, por lo que no puede saberse que procesador va a ser el primero  en ejecutar su siguiente instrucción. Comúnmente es deseable que el estado final de  un  cálculo  esté  determinado,  aunque  el  cálculo  mismo  no  lo  esté.  Por  ejemplo,  es  deseable  que  la  suma  de  dos  subexpresiones  sea  la  misma,  independientemente  de  cual de ellas sea evaluada en primer lugar. Esto podría llevarnos a concluir que: Sería 

(9)

 

necesario  disponer  de  un  modelo  abstracto  de  concurrencia,  que  permita  razonar  sobre la corrección o correctitud de programas y sistemas concurrentes. 

 En cuanto al tipo de interacción entre procesos, se pueden distinguir entre tres tipos  de conducta: 

 

a) Procesos  Independientes:  Aquellos  que  no  se  comunican  entre  sí  y  por  tanto  no  requieren sincronizarse (ponerse de acuerdo). 

b) Procesos  Cooperantes:  Aquellos  que  colaboran  en  la  realización  de  un  trabajo  común, y para ello, deben comunicarse entre sí y sincronizar sus actividades. 

c) Procesos en Competencia: Aquellos que comparten un número finito de recursos  de  un  sistema  computacional,  por  ejemplo,  dispositivos  periféricos,  memoria,  capacidad del procesador, etc. Los procesos deben competir “en una carrera” por  obtener  el  uso  de  los  recursos  del  sistema.  Esta  competencia  requiere  que  los  procesos  se  comuniquen  y/o  se  sincronicen,  aun  cuando  las  labores  que  realicen  sean independientes. 

 

Programación Concurrente 

La  programación  concurrente es  el  conjunto  de  notaciones  y  técnicas  utilizadas  para  describir  mediante  programas  el  “paralelismo  potencial”  de  los  problemas,  así  como  para  resolver  los  problemas  de  comunicación  y  sincronización  que  se  presentan  cuando varios procesos que se ejecutan concurrentemente comparten recursos. 

Cada problema concurrente presenta un tipo distinto de paralelismo; implementarlo,  es  una cuestión  ligada en  principio  a  la  arquitectura  de  la computadora  en  cuestión. 

Para poder trabajar de forma independiente de la arquitectura se requiere utilizar un  modelo  abstracto  de  la  concurrencia  que  permita  razonar  sobre  la  corrección  de  los  programas que implementen el paralelismo, con independencia de la máquina en que  estos  programas  se  ejecuten.  Un  lenguaje  de  Programación  por  ejemplo  es  una  abstracción que sirve al programador para comunicarse con la computadora sin tener  que considerar la arquitectura física de ésta o las instrucciones de máquina. 

La  programación  concurrente  como  abstracción,  está  diseñada  para  permitirnos  razonar sobre el comportamiento dinámico de los programas y sistemas concurrentes  cuando se ejecutan sobre un único procesador, utilizando el paralelismo abstracto ya  definido. 

     

(10)

 

Los Procesos 

Un  proceso  es  una  entidad  compuesta  de  un  código  ejecutable  secuencial,  un  conjunto  de  datos  y  una  pila  que  se  utiliza  para  la  transferencia  de  parámetros,  restauración  de  llamadas  recursivas  o  de  interrupciones,  etc.  Una  parte  de  la  pila  contiene el entorno del proceso (ver Figura5). 

 

Segmentos de memoria compartida,

pipes o archivos PILA

DATOS TEXTO

proceso 

Memoria Compartida 

proceso  PILA

DATOS TEXTO  

             

Fig. 5. Modelo básico de un Proceso   

Aun  cuando  el  concepto  de  proceso  es  “el  mismo”  en  todos  los  lenguajes  de  programación  concurrente,  existen  variaciones  en  cuanto  al  modelo  de  concurrencia  que adoptan. Estas variantes se dan en aspectos como: 

• La estructura:  

o Estática: Cuando el número de procesos del programa concurrente es fijo y  se conoce en tiempo de compilación 

o Dinámica: Cuando los procesos pueden ser creados en cualquier momento. 

El  número  de  procesos  existentes  en  el  programa  concurrente  sólo  se  conoce en tiempo de ejecución. 

• El Nivel de Paralelismo:  

o Anidado: Cuando un proceso puede ser definido dentro de otro proceso. 

o Plano:  Cuando  los  procesos  sólo  pueden  ser  definidos  en  el  nivel  más  externo del programa. 

• Relaciones  entre  Procesos:  Se  pueden  crear  jerarquías  de  procesos  e  interrelaciones entre ellos con el uso de los niveles de paralelismo anidados. 

o Relación Padre/Hijo: Un proceso (el padre) es el responsable de la creación  de otro, el hijo. El padre ha de esperar mientras el hijo está siendo creado e  inicializado. 

 

(11)

 

o Relación  Guardian/Dependiente:  El  proceso  guardián  no  puede  terminar  hasta que todos los procesos dependientes hayan terminado3.  

• Granularidad:  Se  puede  hacer  la  división  de  paralelismo  de  grano  fino  y  paralelismo de grano grueso. Un programa concurrente de grano grueso contiene  relativamente  pocos  procesos,  cada  uno  con  una  labor  significativa  que  realizar. 

Los programas de grano fino contienen un gran número de procesos, algunos de  los cuales pueden incluir una única acción. 

 

Estados y Operaciones de un Proceso 

Durante  el  tiempo  de  existencia  de  un  proceso,  éste  se  puede  encontrar  en  uno  de  varios estados posibles. El cambio de un estado a otro depende de ciertas operaciones  (ver Figura 6): 

• Nuevo:  El  proceso  ha  sido  creado  por  un  usuario  al  lanzar  un  programa  concurrente a ejecución. 

• En  Ejecución:  Las  instrucciones  de  máquina  que  conforman  el  código  fuente  del  proceso están siendo ejecutadas por el procesador. 

• Bloqueado:  El  proceso  está  esperando  a  que  ocurra  algún  tipo  de  evento,  por  ejemplo, la realización de una operación de entrada/salida. 

• Listo: El proceso está en espera de que sus instrucciones sean ejecutadas por el  procesador cuando el planificador del sistema operativo le dé el turno para ello. 

• Finalizado:  El  proceso  ha  terminado  y  todos  los  recursos  que  ha  utilizado  para  ejecutarse son liberados. 

                 

Nuevo

Listo ejecución En

Bloqueado

Finalizado

Sincronización por

Sincronización por Planificación por tiempo

Planificación por tiempo

Activación Terminación Creación

Fig. 6. Diagrama de Transición de Estados de un Proceso a través de operaciones 

      

3 En lenguajes que permiten estructuras dinámicas (como C o Ada), el padre y el guardián pueden no ser el

(12)

     

La acción que hace que un proceso cambie de un estado a otro es una operación. Los  procesos  llevan  asociadas  diversas  operaciones:  creación  y  activación,  terminación,  sincronización y planificación (scheduling). 

1. Creación y Activación: Se distingue entre creación y activación porque en algunos  lenguajes  y  sistemas,  los  procesos  pueden  ser  declarados  (creados),  pero  no  comienzan (se activan) hasta que se alcanza un punto determinado en el programa  que  los  crean.  En  algunos  casos,  la  activación  es  implícita  cuando  se  alcanza  ese  punto. 

2. Terminación:  La  forma  en  que  un  proceso  termina  difiere  entre  los  sistemas.  Un  proceso puede terminar implícitamente, cuando acaba de ejecutar sus tareas y no  tiene  procesos  dependientes,  o  explícitamente,  mediante  la  ejecución  de  una  operación. 

3. Planificación o Scheduling: La planificación es el método por el que los procesos  son asignados a los procesadores disponibles, es decir; es el medio por el que un  proceso pasa del estado listo al estado en ejecución. 

4. Sincronización:  Las  operaciones  de  sincronización  entre  procesos  tienen  que  ver  con  técnicas  de  comunicación  a  través  de  mecanismos  como  la  exclusión  mutua,  candados, variables de condición, semáforos o monitores; mecanismos de los que  más adelante hablaremos en apartados especiales para ello. 

 

Formas de crear y manejar procesos: 

1. Llamadas  al  Sistema  Operativo:  los  que  están  a  favor  de  esta  forma  de  hacer  programación  concurrente  dicen  que  es  la  manera  por  la que  se  consigue  mayor  eficiencia en la ejecución de los programas. Los que están en contra dicen que los  programas que utilizan servicios del sistema operativo son más complejos, menos  obvios y con un alto costo  para ser adaptados a otro sistema operativo. 

2. Lenguajes  de  Programación  que  soportan  Programación  Concurrente  con  Independencia  del  Sistema  Operativo:  Ejemplos  de  lenguajes  de  programación  que  incorporan  estructuras  de  soporte  de  concurrencia  son:  Ada,  Occam,  Pascal‐

Concurrente,  C‐Concurrente,  Java,  etc.  Los  que  están  a  favor  de  esta  manera  de  hacer concurrencia dicen que ésta se expresa de un modo claramente visible para  el programador, los programas son más fáciles de probar y mantener, se mejora el  poder de abstracción, la modelización del mundo físico es más clara y natural, los  programas son menos dependientes de la máquina. 

3. Bibliotecas  para  creación  y  operaciones  con  procesos,  independientes  del  S.O.: 

Según  éste método,  el  lenguaje  no conlleva  estructuras  sintácticas especiales.  En  su lugar, una biblioteca soporta la concurrencia mediante la definición de tipos y 

 

(13)

 

operaciones  que  pueden  ser  invocados  por  una  aplicación.  La  interfaz  entre  el  programador y dichos módulos es independiente de la máquina y del sistema. Un  ejemplo de esto es la forma de concurrencia con el uso de la biblioteca Pthreads  del lenguaje C. 

 

Multiprogramación, Multitarea y Procesos 

La multitarea se encuentra en el núcleo de casi cualquier sistema operativo moderno y  surge  debido  a  la  necesidad  de  aprovechar  los  tiempos  muertos  de  la  computadora  dejados por u proceso cuando efectúa una operación de E/S; la solución para llenarlos  es  dejar  que  otro  proceso  comience  a  ejecutarse.  De  ahí  que  la  multiprogramación  como ya se ha dicho en líneas anteriores se defina como la ejecución concurrente de  varios procesos independientes sobre un solo procesador. 

Para  lograr  la  multiprogramación  se  utiliza  un  programa  planificador  (scheduler)  que  ejecutado por el S.O. y quizás considerando algún esquema de prioridades, determina  a qué proceso le corresponde acceder al procesador. 

La generalización de la multiprogramación nos lleva al concepto de paralelismo, donde  se intenta resolver un problema descomponiéndolo en varios procesos concurrentes. 

Interfoliación: La interfoliación aparece en la concurrencia abstracta y se define como  el entrecruzamiento en un único código secuencial real de los códigos concurrentes de  varios  procesos,  a  nivel  de  instrucciones  atómicas.  Aparece  entonces  el  no‐

determinismo  de  la  aplicación,  que  como  ya  se  citó,  es  una  de  las  características  fundamentales  de  la  programación  concurrente  y  es  el  que  dificulta  el  análisis  de  la  corrección semántica del programa. Por ejemplo, suponga que se lanzan dos procesos  concurrentes que modifican una variable compartida x: 

 

Proceso 1:

Load(x);

Add(x,1);

Store(x);

Proceso 2:

Load(x);

Add(x,1);

Store(x);

   

 

Al lanzar ambos procesos de forma concurrente, es posible que se den dos secuencias  de  interfoliación  distintas  que  hacen  que  la  variable  x  pueda  tomar  distintos  valores  (uno  o  dos).  En  consecuencia,  dos  ejecuciones  distintas  de  un  mismo  programa  concurrente llevarán asociadas diferentes secuencias de interfoliación que darán lugar  a resultados distintos. 

 

(14)

 

Parece  entonces  que  la  depuración  de  un  programa  concurrente  se  torna  inviable,  siendo  inútil  el  uso  de  un  trazador,  pues  cada  ejecución  del  programa  origina  una  interfoliación  diferente.  Esto  origina  incertidumbre  sobre  el  resultado  final  y  no  consideraremos  correcto  a  un  programa  concurrente  a  menos  que  lo  sea  bajo  todas  las secuencias posibles de interfoliación4. Si consideramos una posible versión de alto  nivel del pseudocódigo anterior, tendríamos: 

 

Integer: N:=0;

 

Task body P1 is Begin

N:=N+1;

End P1;

Task body P2 is Begin

N:=N+1;

End P2;

     

Se pueden dar dos circunstancias: 

• Que el compilador traduzca el programa en una simple instrucción INC, en cuyo  caso el entrelazado no afecta el resultado. 

• Que  el  compilador  traduzca  el  programa  en  código  de  máquina,  en  cuyo  caso  utilizaría  registros  del  procesador  y  los  entrelazados  podrían  dar  lugar  a  distintas  salidas, algunas erróneas como la que se muestra en la siguiente tabla: 

 

PROCESO INSTRUCCION N REGISTRO 1 REGISTRO 2 Inicial 0

P1 Load(x) 0 0

P2 Load(x) 0 0 0

P1 Add(x,1) 0 1 0

P2 Add(x,1) 0 1 1

P1 Store(x) 1 1 1

P2 Store(x) 1 1 1

 

En  la  tabla  se  muestra  una  posible  interfoliación  que  da  un  error,  pues  cualquier  persona esperaría observar un valor de x=2. 

     

      

 

4 La interfoliación entre procesos concurrente se da a nivel de instrucciones atómicas.

(15)

   

Hilos o Threads 

Un método para lograr el paralelismo consiste en hacer que varios procesos cooperen  y se sincronicen mediante la compartición de memoria. Una alternativa para hacerlo  viable  es  la  de  emplear  múltiples  threads  (hilos)  de  ejecución  en  un  solo  espacio  de  direcciones.  Un  thread  es  una  secuencia  de  instrucciones  ejecutada  dentro  de  un  programa,  en  otras  palabras,  cuando  se  ejecuta  un  programa,  el  CPU  utiliza  el  contador de programa del proceso para determinar qué instrucción debe ejecutarse a  continuación.  

El flujo de instrucciones resultante se denomina hilo de ejecución del programa y se  puede ver como el flujo de control para el proceso, representado por una secuencia de  direcciones  de  instrucciones  que  el  contador  de  programa  va  indicando  durante  la  ejecución  [BUT97].  Desde  el  punto  de  vista  del  programa,  la  secuencia  de  instrucciones  de  un  hilo  de  ejecución  es  un  flujo  ininterrumpido  de  direcciones;  en  cambio,  desde  el  punto  de  vista  del  procesador,  los  hilos  de  ejecución  de  diferentes  procesos están entremezclados y el punto en el que la ejecución cambia de un proceso  a otro es denominado conmutación de contextos. Además, una extensión natural del  modelo  de  proceso  concurrente  es  el  permitir  la  ejecución  de  varios  threads  dentro  del  mismo  proceso,  que  recibe  el  nombre  de  multithreading,  (ver  figura  7),  que  constituye  un  mecanismo  eficiente  para  controlar  hilos  de  ejecución  que  comparten  tanto  código  como  datos  en  una  aplicación  paralela,  con  lo  cual  se  evitan  las  conmutaciones de contexto. Esta estrategia también mejora el rendimiento, pues, en  una  máquina  multiprocesador,  a  los  procesadores  se  les  puede  asignar  múltiples  threads de tal forma que estas consiguen ser ejecutadas simultáneamente.  

 

THREAD PILA

THREAD DATOS

THREAD TEXTO MEMORIA

COMPARTIDA

           

Fig. 7. Threads dentro de un proceso (todos los threads son parte de un proceso) 

(16)

   

Cada  hilo  de  ejecución  se  asocia  con  un  thread,  es  decir,  con  un  tipo  de  datos  abstracto que representa el flujo de control dentro de un proceso. Cada thread tiene  su  propia  pila  de  ejecución,  valor  de  contador  de  programa,  conjunto  de  registros  y  estado (ver Figura 8).  

 

         

 

    Ejecución 

Thread

Contador de Programa Estado  Registros

 

Figura 8. Modelo genérico de un Thread   

Al declarar muchos threads dentro de los confines de un solo proceso, el programador  puede  lograr  paralelismo  con  un  bajo  costo5;  sin  embargo,  los  threads  también  presentan  ciertas  complicaciones  en  cuanto  a  la  necesidad  de  sincronización.  Los  Threads  son  frecuentemente  llamados  procesos  ligeros  y  puede  decirse  que  son 

“primos” de los procesos UNIX. En UNIX cualquier proceso ha de poseer los siguientes  elementos constitutivos: 

 

1. Un espacio de direcciones (pila, datos y segmento de código). 

2. Una tabla de descriptores de archivos (archivos abiertos). 

3. Un hilo o programa de ejecución. 

 

Características de los Threads 

• Los threads son más pequeños comparados con los procesos. 

• La creación de un thread es relativamente menos costosa. 

• Los threads comparten los recursos mientras que los procesos requieren su propio  conjunto de recursos. 

• Los threads ocupan menos memoria (es decir, son más económicos respecto del  gasto de recursos computacionales en un sistema). 

 

5 La figura 5 muestra el modelo básico de un proceso con estas características.

(17)

 

• Los  threads  proporcionan  a  los  programadores  la  posibilidad  de  escribir  aplicaciones  concurrentes  que  se  pueden  ejecutar  tanto  en  sistemas  monoprocesador, como en sistemas multiprocesador de forma transparente. 

• Los threads pueden incrementar el rendimiento en entornos monoprocesador. 

 

Con Threads o sin Threads 

Los threads no necesariamente proporcionan la mejor solución a cualquier problema  de programación. No siempre son fáciles de usar y no siempre proporcionan el mejor  rendimiento.  Existen  muchos  problemas  que  son  inherentemente  no  concurrentes,  por  lo  que,  añadirles  threads  puede  incrementar  innecesariamente  el  código  aumentado,  el  tiempo  de  programación  y  también  complicar  la  programación  de  las  aplicaciones. Si cualquier paso o secuencia en un programa depende directamente del  resultado  del  paso  previo,  entonces  el  usar  threads  probablemente  no  ayudará  en  nada,  ya  que  cada  thread  tendría  que  esperar  a  otro  thread  para  poder  ser  completado.  Los  candidatos  más  obvios  para  codificar  threads  son  aplicaciones  que  contemplen: 

• realización  de  gran  cantidad  de  cálculos  que  puedan  ser  paralelizables  (o  descompuestos)  en  múltiples  threads,  en  donde  dichos  cálculos  puedan  intentar  ejecutarse en multiprocesadores de hardware,  

• bien  la  necesidad  de  realizar  muchas  operaciones  de  E/S  que  puedan  ser  superpuestas  (es  decir,  donde  muchos  threads  puedan  esperar  a  diferentes  peticiones  de  E/S  al  mismo  tiempo).  Los  sistemas  servidores  o  las  aplicaciones  distribuidas son buenos candidatos para ello. 

 

Programación con Threads 

Un  paquete  o  biblioteca  de  threads  junto  con  algún  lenguaje  de  programación  específico  permite  escribir  programas  con  varios  puntos  simultáneos  de  ejecución  sincronizados  a  través  de  memoria  compartida.  Sin  embargo,  la  programación  con  threads introduce nuevas dificultades, ya que la programación concurrente y paralela  acomete  problemas  que  no  se  suscitan  en  la  programación  secuencial,  así  como  también  se  utilizan  técnicas  diferentes  para  resolverlos.  Existen  problemas  simples,  por ejemplo, el interbloqueo entre procesos que se adaptan mejor a ser resueltos con  un ambiente de ejecución con threads, pero para otros problemas el rendimiento de  las aplicaciones resulta ser penalizado. 

(18)

 

Un  thread  es  un  concepto  sencillo:  un  simple  flujo  de  control  secuencial  [KLE96].  El  programador  no  necesita  aprender  nada  nuevo  para  emplear  un  único  thread  en  el  desarrollo  de  un  programa.  Cuando  se  tienen  múltiples  threads  en  un  programa,  significa  que  en  cualquier  instante  el  programa  tiene  múltiples  puntos  de  ejecución,  uno  en  cada  uno  de  sus  threads;  por  lo  que  el  programador  ha  de  decidir  cuándo  y  dónde crear los múltiples threads. Las operaciones de creación vienen proporcionadas  mediante la correcta utilización de un paquete de librería o de un sistema con threads,  pero  las  directrices  del  diseño  no  suelen  ser  evidentes  para  un  programador  no  acostumbrado al desarrollo de aplicaciones paralelas. Por otro lado, en un lenguaje de  alto nivel, las variables globales son compartidas por todos los threads del programa,  es  decir,  leen  y  escriben  en  las  mismas  posiciones  de  memoria;  sin  embargo,  el  programador  es  el  responsable  de  emplear  los  mecanismos  de  sincronización  del  paquete de threads para garantizar que la memoria compartida se acceda de manera  correcta,  lo  cual  hace  más  propensas  a  las  aplicaciones  a  contener  errores  de  difícil  depuración que afectan a la seguridad de los programas. 

Las facilidades proporcionadas por  un paquete del tipo anterior son conocidas como  primitivas ligeras, lo que significa que las primitivas de: 

• Creación, 

• Mantenimiento, 

• Sincronización y 

• Destrucción, 

Debieran  ser  lo  suficientemente  accesibles  para  limitar  el  esfuerzo  necesario  para  la  correcta utilización que cubra las necesidades de concurrencia del programador6 

Los Threads en la Concurrencia 

• En el uso de un sistema multiprocesador: Los threads son una herramienta atractiva  para permitir a un programa sacar provecho de las facilidades para mejorar el speedup  o  rendimiento  de  las  aplicaciones  paralelas  que  presenta  este  tipo  de  hardware. 

Empleando un paquete de threads, el programador puede utilizar los procesadores de  forma  económica  en  cuanto  al  aprovechamiento  de  la  capacidad  computacional  de  estos. Parece funcionar bien en sistemas que van de 10 a 1000 procesadores. 

• En  la  gestión  de  dispositivos  de  entrada/salida:  Estos  dispositivos  pueden  ser  programados  fácilmente  mediante  threads,  de  tal  forma  que  las  peticiones  a  un  servicio sean secuenciales y el thread que realiza la petición quede suspendido hasta  que  la  solicitud  sea  completada;  mientras,  el  programa  principal  puede  realizar  más  trabajo con otros threads solapando la ejecución de varios hilos.  

        

6 Esto normalmente no es así, ya que las librerías de threads presentan muchas funciones, con un gran número de parámetros algunas de ellas, que no hacen intuitiva, ni mucho menos, su utilización.

(19)

 

• Con los  usuarios como  fuente de concurrencia: A veces un usuario necesita realizar  dos o tres tareas simultáneamente, la utilización de los Threads en las aplicaciones son  una buena forma de atender esta necesidad. 

• En la constitución de un sistema distribuido: Servidores de red compartidos, donde el  servidor  se  encarga  de  recibir  peticiones  de  múltiples  clientes.  El  uso  de  múltiples  threads permite al servidor gestionar las solicitudes de los clientes en paralelo, en vez  de  procesarlas  en  serie,  lo  que  hubiera  significado  la  creación  de  un  proceso  de  servicio  para  cada  cliente  con  un  enorme  desperdicio  de  la  capacidad  de  procesamiento. 

• En la reducción de la latencia7 de las operaciones de un programa: Emplear threads  para  diferir  el  trabajo  es  una  técnica  que  proporciona  muy  buenos  resultados.  La  reducción de la latencia puede mejorar los tiempos de respuesta de un programa. 

 

Finalmente, uno de los problemas de utilizar múltiples threads de ejecución es que hasta  hace poco no existía un estándar. Algunos modelos populares de threads son: 

• Mach C threads, CMU 

• Sun OS LWP threads, Sun Microsystems 

• PARAS CORE threads, C‐DAC 

• Java‐Threads, Sun Microsystems 

• Chorus threads, Paris 

• OS/2 threads, IBM 

• Windows NT/95 threads, Microsoft 

• POSIX, ISO/IEEE standard   

La  extensión  POSIX.1c  se  aprobó  en  Junio  de  1995.  El  estándar  POSIX‐Thread  significa  técnicamente  el  API‐Thread  especificado  por  el  estándar  formal  internacional  POSIX 1003.1c‐1995.  Con  la  adopción  de  un  estándar  POSIX  para  los  threads,  las  aplicaciones  comerciales actuales se han ido construyendo con este API y seguramente en un futuro no  muy lejano dicha estandarización será adoptada por todos. 

         

      

7La latencia es el tiempo empleado entre la llamada a un procedimiento y la finalización de éste.

(20)

 

2. ARQUITECTURAS PARALELAS Y DISTRIBUIDAS   

La arquitectura de computadoras es el estudio de la organización e interconexión de  los componentes que integran un sistema computacional. La computadora puede ser  constituida  a  partir  de  la  construcción  de  bloques  tales  como  memorias,  unidades  aritméticas,  elementos  de  procesamiento  y  buses.  Con  estos  elementos  se  pueden  conformar  un  número  indeterminado  de  diferentes  tipos  de  máquinas  computacionales,  desde  las  más  pequeñas  o  básicas  hasta  las  llamadas  supercomputadoras.  El  comportamiento  funcional  de  los  componentes  de  las  diferentes  computadoras  son  similares  unos  con  otros.    Por  ejemplo,  el  sistema  de  memoria  realiza  funciones  de  almacenamiento,  la  unidad  de  procesamiento  central  lleva a cabo operaciones, y las interfaces de entrada y salida transfieren datos desde  un procesador a los dispositivos apropiados. 

Las mayores diferencias sobre los distintos tipos de computadoras estriban en la forma  en  que  sus  módulos  son  conectados  entre  sí,  las  características  de  rendimiento  de  dichos módulos, y el método mediante el cual un sistema computacional es controlado  por  sus  operaciones.  Los  dos  elementos  principales  de  un  sistema  computacional  convencional son el procesador y la memoria. 

Un  procesador  manipula  datos  almacenados  en  la  memoria  mediante  instrucciones. 

Las instrucciones son colocadas en los módulos de memoria y el flujo siempre va de la  memoria al procesador. El movimiento de datos en un sistema es bidireccional; pues  estos pueden ser leídos desde, o escritos hacia los módulos de memoria. La figura 9  representa  la  interconexión  memoria‐procesador  conocida  como  el  modelo  de  computación Von Newmann. 

                 

Fig. 9. Interconexión Memoria‐Procesador   

Una extensión natural del modelo Von Neumann es la llamada Red de Computadoras. 

En este esquema, cada nodo en la red es una computadora en sí misma, la cual puede  ser  considerablemente  compleja  y  operar  completamente  de  forma  autónoma 

Procesador   Memoria

Instrucción  

Datos

 

(21)

 

respecto  a  las  otras  computadoras  que  integran  la  red.  Además,  una  red  de  computadoras puede ser geográficamente distribuida.  

En  adición  a  esta  extensión  natural  del  modelo  Von  Neumann,  es  posible  tomar  un  enfoque más fundamental y diseñar nuevos modelos computacionales exclusivamente  para  el  procesamiento  paralelo.  El  número  de  instrucciones  leídas  y  datos  manipulados simultáneamente por el procesador forman la base para la clasificación  de arquitecturas que a continuación de detalla. 

 

2.1. Clasificación de las arquitecturas de computadoras   

Michael  Flynn  ha  clasificado  las  arquitecturas  de  computadoras  mediante  una  variedad  de  características  que  incluyen  el  número  de  procesadores,  número  de  programas  que  pueden  ser  ejecutados  por  dichos  procesadores  y  la  estructura  de  memoria.  La clasificación de Flynn es un buen método para definir la taxonomía de las  arquitecturas de computadoras aunque existen algunas otras taxonomías propuestas; 

tales  como  las  de  Dasgupta,  Hockney  ,  Skillicorn  y  Bell.  Treleaven,  Brownbridge  y  Hopking  [Roosta99],  sugieren  que  las  computadoras  convencionales  pueden  ser  analizadas bajo dos puntos de vista: 

• El mecanismo de control, el cual define el orden de ejecución 

• El mecanismo de datos, que define la forma en que los operandos son utilizados   

La clasificación de Flynn incluye las siguientes categorías: 

1. SISD   ‐ Convencional. 

2. SIMD  ‐ datos paralelos, vector computing. 

3. MISD  ‐ arrays sistólicos. 

4. MIMD – Muy general, múltiples enfoques. 

 

1. SISD (Single Instruction Stream, Single Data Stream) 

Las  computadoras  SISD  tienen  un  CPU  que  ejecutan  una  instrucción  a  la  vez  (single,  instruction stream) e instancía un item de datos a la vez (single data stream). La figura  10 muestra la estructura general de la arquitectura SISD9

 

      

9 Las figuras 10 a 15 de esta sección han sido tomadas de http://www.dgs.monash.edu.au/~rajkumar y traducidas al español.

(22)

 

 

  Fig. 10. Modelo de una Arquitectura SISD 

 

Todas  las  computadoras  SISD  utilizan  un  registro  simple  llamado  el  contador  del  programa, el cual lleva el conteo de la ejecución serial de las instrucciones. Como cada  instrucción es fetch‐eada desde la memoria, el contador del programa es actualizado  para direccionar a la siguiente instrucción ha ser fetch‐eada y ejecutada; lo que resulta  ser una orden serial de ejecución. 

 

La Arquitectura SISD

2. SIMD (Single Instruction Stream, Multiple Data Stream) 

Las máquinas SIMD tienen una unidad de control que ejecutan un flujo de instrucción  simple, pero tienen más de un elemento procesando. La unidad de control genera las  señales de control para todos los elementos que se están procesando, la cual ejecuta  la  misma  operación  en  diferentes  ítems  de  datos  (esto  es,  multiple  data  stream).  En  otras  palabras,  muchos  elementos  de  procesamiento  separados  son  invocados  mediante  una  simple  unidad  de  control.  Estas  computadoras  son  utilizadas  frecuentemente  para  problemas  que  tienen  un  alto  grado  de  paralelismo  de  grano  pequeño (small‐grain). Algunas computadoras SIMD popularmente comerciales son la  ILLIAC  IV,  la  DAP  o  la  CM‐2  [Roosta99].  Las  computadoras  SIMD  pueden  soportar  vectores  de  procesamiento,  los  cuales  pueden  ser  acompañados  por  vectores  de  elementos  para  procesado  individual  de  elementos  dentro  de  cálculos  concurrentes. 

La figura 11 representa una vista general de una arquitectura SIMD. 

 

Procesador

Ins tr uc cio ne

Datos de  Salida  Datos de 

Entrada

(23)

 

  Fig. 11. Modelo de una Arquitectura SIMD 

 

La arquitectura SIMD

Flujo de  Instrucción 

Flujo de  Salida de  D t A Flujo de 

Entrada de  Datos A

Procesador

A Flujo de 

Salida de  D t B Flujo de 

Entrada de  Datos B

Procesador

B Flujo de 

Salida de  D t C Procesador

Flujo de  Entrada de 

Datos C C

3. MISD (Multiple Instruction Stream, Single Data Stream) 

Las  máquinas  de  esta  categoría  pueden  ejecutar  varios  programas  distintos  con  el  mismo  item  de  datos.  Esto  implica  que  algunas  instrucciones  son  operadas  con  una  sola pieza de datos.  La arquitectura puede ser ilustrada en dos categorías: 

a) Una clase de máquinas que requieren de distintas unidades de procesamiento que  pueden  recibir  distintas  instrucciones  para  ser  ejecutadas  con  los  mismos  datos. 

Sin  embargo,  este  tipo  de  arquitecturas  es  más  un  ejercicio  intelectual  que  una  configuración práctica. 

b) Una  clase  de  máquinas  tales  que  el  flujo  de  datos  circula  sobre  una  serie  de  elementos  de  procesamiento.  Las  arquitecturas  pipeline  tales  como  los  arrays  sistólicos  entran  dentro  de  este  grupo  de  máquinas.    Las  arquitecturas  pipeline  realizan  un  vector  de  procesamiento  sobre  una  serie  de  etapas,  cada  una  de  las  cuales  lleva  a  cabo  una  función  particular  y  produce  un  resultado  inmediato.  La  razón por la que estas arquitecturas son agrupadas dentro de las máquinas MISD  es que los elementos de un vector pueden tener el mismo grupo de datos, y todas  las  etapas  del  pipeline  representen  múltiples  instrucciones  que  estén  siendo  aplicadas  al  vector.  La  figura  12  representa  la  estructura  general  de  una  arquitectura MISD. 

(24)

 

 

  Fig. 12. Modelo de una Arquitectura MISD 

  

La Arquitectura MISD

Flujo de  Instrucciones A

Flujo de  Instrucciones B

Flujo de  Instrucciones C  Procesador 

A  Flujo de 

Salida de  Datos Flujo de 

Entrada  de Datos

Procesador  B 

Procesador  C 

4. MIMD (Multiple Instruction Stream, Multiple Data Stream) 

Las  máquinas  MIMD  son  llamadas  multiprocesadores.  Estas  tienen  más  de  un  procesador  y  cada  uno  puede  ejecutar  un  programa  diferente  (multiple  instruction  stream)  con  múltiples  flujos  de  datos.  En  muchos  sistemas  MIMD,  cada  procesador  tiene acceso a una memoria global, la cual puede reducir el tiempo de comunicación  de los procesadores, tal como se ilustra en la figura 13.  

 

(25)

 

  Fig. 13. Modelo de memoria compartida en la arquitectura MIMD 

 

Además  cada  procesador  posee  una  memoria  privada  (ver  figura  14).  Muchas  de  las  arquitecturas MIMD son utilizadas para paralelismo de grano‐medio y grano‐largo. 

 

  Fig. 14. Modelo de memoria distribuida en la arquitectura MIMD 

Memoria Compartida en máquinas MIMD

Memoria Distribuida en máquinas MIMD

Procesador

Procesador

Procesador

Memoria  del  Sistema  A

Memoria  del  Sistema  B

Memoria  del  Sistema  C  

 

ME M OR 

S   I  A

ME M OR 

 I 

A

ME M OR 

 I 

A

 

Procesador  Procesador Procesador

A 

ME M OR  ME

M OR 

ME M OR 

 I 

 I   I 

A A A

Sistema   de   Memoria   Global

(26)

 

En  las  arquitecturas  paralelas  MIMD  actuales,  el  número  de  procesadores  es  más  pequeño que en los sistemas SIMD. Las computadoras MIMD son las más complejas,  pero  ofrecen  grandes  promesas  para  obtener  eficiencia  acompañada  del  procesamiento  concurrente.  Algunas  computadoras  MIMD  comerciales  son,  la  BBN  Butterfly,  la  serie  Alliant  FX,  la  serie  iPSC  de  Intel,  y  la  Ultracomputadora  de  la  universidad de New Cork [Roosta99]. La figura 15 representa la estructura general de  una arquitectura MIMD. 

 

  Fig. 15. Modelo de una arquitectura MIMD 

 

La Arquitectura MIMD

Flujo de   Flujo de  

Flujo de  

  2.2. Arquitecturas Paralelas 

 

En la sección anterior, la clasificación de Flynn divide a las arquitecturas paralelas en  dos  familias  importantes:  la  familia  de  las  computadoras  SIMD  y  la  familia  de  las  computadoras MIMD.  La figura 16 muestra una taxonomía que representa algunas de  las características de las arquitecturas paralelas. En esta sección se hablará entonces  de las arquitecturas paralelas SIMD y MIMD. 

         

Procesador A 

Procesador

Instrucciones  Instrucciones

Instrucciones

Flujo de   Flujo de  

Salida de Entrada de 

Datos A Flujo de   Datos A Flujo de  

Salida de Entrada de 

Datos B  Flujo de   Procesador

Datos B  Flujo de  

Salida de Entrada de 

Datos C  Datos C 

(27)

                             

Fig. 16. Taxonomía de las arquitecturas de procesamiento paralelo  Arquitecturas SIMD 

En una máquina SIMD, varios elementos procesados se supervisan por una unidad de  control.  Todas  las  unidades  de  procesamiento  reciben  la  misma  instrucción  desde  la  unidad  de  control,  pero  operan  con  diferentes  conjuntos  de  datos,  los  cuales  provienen de distintos flujos de datos (ver figura I.3). Las características principales de  este tipo de máquinas paralelas son: 

MIMD    SIMD 

Mybrid  MISD 

Multiprocesadores Multicomputadores Procesadores de Arrays

Procesadores de Vector Pipeline

Máquinas SIMD-MIMD

• Distribuyen el procesamiento sobre un larga cantidad de hardware 

• Operan concurrentemente con muchos elementos de datos diferentes 

• Realizan el mismo cálculo en todos los elementos de datos 

Así,  cada  unidad  de  procesamiento  ejecuta  la  misma  instrucción  al  mismo  tiempo,  y  los procesadores operan de manera síncrona. El potencial de speedup o aceleración de  las  máquinas  SIMD  es  proporcional  a  la  cantidad  de  hardware  disponible.  El  paralelismo hace que las máquinas SIMD desarrollen altas velocidades. 

 

El procesamiento de Arrays fue la primera forma de procesamiento paralelo estudiada  e implementada. De aquí se derivan dos tipos diferentes de máquinas: 

• Aquellas que realizan operaciones por bits, tales como las máquinas MPP y CM‐1 

• Aquellas que realizan operaciones por palabras, tales como la ILLIAC IV. 

   

(28)

 

Algunas  características  generales  de  las  computadoras  SIMD  comparándolas  con  las  computadoras MIMD son las siguientes: 

• Menos Hardware que las MIMD, debido a que utilizan sólo una unidad global de  control. 

• Menos  memoria  que  las  MIMD,  ya  que  sólo  se  necesita  una  copia  de  las  instrucciones colocada en la memoria del sistema. 

• Flujo  de  instrucciones  simples  y  sincronización  implícita  del  procesamiento  de  elementos,  lo  que  hace  que  una  aplicación  SIMD  sea  entendible,  fácil  de  programar y fácil de trazar. 

• Instrucciones de control de flujo y operaciones escalares que son comunes a todos  los  elementos  procesados  que  pueden  ser  ejecutados  en  la  unidad  de  control,  mientras los procesadores están ejecutando otras instrucciones. 

• Necesidad  de  mecanismos  de  sincronización  sobre  los  procesadores,  después  de  cada  ciclo  de  ejecución  de  una  instrucción.  En  contraste  con  las  primitivas  de  sincronización explícitas que se requieren en la arquitectura MIMD. 

• Menos costo ya que sólo se necesita un decodificador de instrucción simple en la  unidad  de  control,  en  contra  de  un  decodificador  en  cada  elemento  que  está  siendo procesado en una arquitectura MIMD. 

Ejemplos de computadoras SIMD incluyen la ILLIAC IV, MPP, DAP, CM‐2, MasPar MP‐1  y MasPar MP‐2 [Roosta99]. 

 

Arquitecturas MISD 

Un  procesador  Pipeline  es  un  procesador  MISD  que  trabaja  acorde  al  principio  del  funcionamiento  de  un  Pipe.  La  arquitectura  pipeline  es  la  forma  fundamental  de  ejecución paralela de un proceso y es una idea poderosa que puede probar de manera  significativa el rendimiento de una computadora SIMD. El principio Pipeline implica la  segmentación  o  partición  de  un  proceso  computacional.  Un  proceso  puede  ser  dividido  en  varios  segmentos  o  etapas  (stages).  El  procesamiento  serial  es  concerniente  con  la  ejecución  de  todos  los  stages  de  un  proceso  antes  de  iniciar  la  ejecución  del  primer  stage  del  siguiente  proceso.  Sin  embargo  en  el  procesamiento  serial,  un  proceso  finaliza  completamente  antes  de  iniciar  el  siguiente  proceso.  Un  procesador  puede  aumentar  su  velocidad  de  ejecución  utilizando  un  pipeline.  En  un  pipeline, mientras un stage está siendo ejecutado, otro stage está siendo cargado y la  entrada de un stage se corresponde con la salida del stage previo. La figura 17 muestra  los  principios  básicos  de  un  pipeline  para  un  proceso  que  se  constituye  de  cuatro  stages,  en  una  ejecución  serial  y  paralela.  Un  resultado  del  pipeline  es  que  el 

 

Referencias

Documento similar

Esta U.D.A. de Podología nace con la voluntad de dar respuesta a la necesidad de contribuir a la integración de conocimiento, actitudes y habilidades en la formación de

De la Salud de la Universidad de Málaga y comienza el primer curso de Grado en Podología, el cual ofrece una formación generalista y profesionalizadora que contempla

Colaboradores Teresa Castilla Mesa y Servicio de Orientación de Ciencias de la Educación Destinatarios Alumnos/as de todos los grados. Lugar Facultad de Ciencias de

Fuente de emisión secundaria que afecta a la estación: Combustión en sector residencial y comercial Distancia a la primera vía de tráfico: 3 metros (15 m de ancho)..

IN31 - Grado de satisfacción de los alumnos que participan en programas de movilidad (enviados) Intervalo Valor: -.. Rama: Ciencias Sociales

El contar con el financiamiento institucional a través de las cátedras ha significado para los grupos de profesores, el poder centrarse en estudios sobre áreas de interés

Dicha certificación, expedida por el Centro organizador de dichos estudios, deberá indicar, además de la relación de asignaturas cursadas y superadas, la

México, Colegio Nacional de Ciencias Políticas y Administración Pública; Universidad Juárez Autónoma de Tabasco; Benemérita Universidad Autónoma de Puebla; Universidad Nacional de