Otra visión del concepto de hilo desde una perspectiva hardware es el multithreading [HWA93]. El concepto de multithreading o multihilado es una extensión de los conceptos de multitarea y multiproceso. El propósito es explotar el paralelismo de grano fino en modernos multiprocesadores construidos mediante procesadores de múltiples contextos o procesadores superescalares que generan múltiples instrucciones. Cada hilo emplea un contador de programa distinto. El mayor problema de la arquitectura es la resolución de los conflictos entre recursos. Los niveles de sofisticación, en asegurar la coherencia de los datos y preservar el orden de los eventos, se van incrementando progresivamente desde el modelo de programación simple, pasando por la multitarea, la multiprogramación, el multiproceso, hasta llegar al multihilado. Se necesitan desarrollar mecanismos especiales para gestión de memoria y protección con el fin de garantizar la corrección e integridad de los datos en las operaciones paralelas de los hilos. El multithreading o multihilado necesita que el procesador de la máquina esté diseñado para manejar múltiples contextos simultáneamente mediante un cambio de contexto base.
Un sistema masivamente paralelo multihilado (multithreaded MPP) se modela mediante una red de nodos de procesadores (P) y memorias (M). La memoria distribuida forma un espacio de direcciones global.
Para analizar el rendimiento de dicha red se emplea los siguientes parámetros:
1) Latencia L: Es la latencia de comunicación en un acceso a memoria remoto. Este valor incluye los retrasos de la red, las penalizaciones por fallos en la cache, y los retrasos causados por contenciones en divisiones de transacciones.
2) Número de hilos N: Es el número de hilos que pueden ser entremezclados en cada procesador. Un hilo está representado por un contexto que consta de un contador de programa, un conjunto de registros, y un vector de estado del contexto.
3) Sobrecarga del cambio de contexto C: Son los ciclos de máquina perdidos en la realización del cambio de contexto en un procesador. Este tiempo depende del mecanismo de intercambio y la cantidad de estados del procesador dedicados al mantenimiento de los hilos activos.
4) Intervalo entre intercambios R: Se refiere al número de ciclos entre intercambios disparados por referencia remota. El inverso p=1/R es el ratio de peticiones de accesos remotos. Refleja una combinación del comportamiento del programa y el diseño del sistema de memoria.
Una forma de incrementar la eficiencia es reducir el ratio de peticiones empleando memorias cache coherentes distribuidas. Otra forma es eliminar la espera del procesador mediante el multihilado.
El modelo de computaciones paralelas multihilo fue descrito por Bell(1992), y se presenta en la siguiente figura :
La computación comienza con un hilo secuencial (1), seguido por una planificación (2) donde los procesadores inician los hilos de la computación (3), a través de mensajes entre computadoras que actualizan las variables entre los nodos donde existe memoria distribuida (4), y finalmente mediante sincronización (5) antes del comienzo de la siguiente unidad de trabajo paralelo.
El periodo de sobrecarga de comunicación (3) inherente en las estructuras de memoria distribuida está normalmente distribuido en toda la computación y está solapado completamente. La sobrecarga del paso de mensajes debido a las llamadas receive y send en los multicomputadores puede reducirse mediante hardware especializado operando en paralelo con la computación.
El ancho de banda de la comunicación limita la granularidad, ya que se debe transferir cierta cantidad de datos a otros nodos para completar un grano de la computación. Las llamadas de paso de mensajes (4) y sincronización (5) son no productivas. Son necesarios mecanismos rápidos para reducir u ocultar estos retrasos. El mutithreading no es capaz de incrementar la velocidad en la ejecución de hilos simples, mientras que los modelos de consistencia débiles son capaces de hacerlo.
Los procesadores masivamente paralelos (MPP) operan de forma asíncrona en un entorno de red. La asincronía produce dos problemas de latencia fundamentales, las cargas remotas y la sincronización de las cargas :
1) Carga remota: Supongamos el siguiente ejemplo.
vA = rload pA vB = rload pB C = vA - vB
Las variables A y B se encuentran localizadas en
los nodos N2 y N3 respectivamente. Se necesitan en el nodo N1
para calcular la diferencia A-B en la variable C. El cálculo
básico necesita la ejecución de dos cargas remotas
(rload) y después la resta. Supongamos que pA y
pB son punteros a A y B respectivamente. Las dos cargas remotas
pueden realizarse en el mismo hilo o en dos hilos distintos. El
contexto de la computación en N1 se representa en la variable
CTXT, y puede tener un puntero de pila, un identificador de proceso,
etc.
2) Sincronización de las cargas: La desocupación debida a la sincronización se presenta a continuación:
- Nodo N1 calcula C=A-B - A y B son calculadas concurrentemente - El hilo en N1 debe ser notificado cuando A y B están listas.
Las variables A y B son calculadas mediante procesos
concurrentes, y no se sabe cuando estarán listas para ser
leídas por el nodo N1. Las señales ready1
y ready2 pueden llegar al nodo N1 de forma asíncrona.
Esta situación es típica en el problema del productor
consumidor. Puede resultar en una espera ocupada.
La clave es cómo evitar la desocupación en el nodo N1durante las operaciones de carga. La latencia debida a las cargas remotas es una propiedad de la arquitectura. La latencia debida a la sincronización de las cargas también depende de la planificación y del tiempo de cálculo de A y B que puede ser mucho mayor que la latencia de paso. Normalmente la latencia de sincronización es imprevisible, mientras que la latencia de carga remota sí los es.
La solución a los problemas de asincronía consiste en multiplexar varios hilos: cuando un hilo realiza una petición de carga remota, el procesador comienza a trabajar en otro hilo. Para ello, el coste de cambio de contexto de hilo debe ser mucho menor que la latencia de la carga remota, de lo contrario es preferible que el procesador espere la respuesta de la carga remota.
Cuanto mayor es la latencia entre nodos más hilos son necesarios para ocultarla eficientemente. Otro aspecto a tener en cuenta es que los mensajes deben llegar en orden. Así por ejemplo, supongamos que el hilo T1 realiza un carga remota; mientras llega el resultado podemos conmutar al hilo T2, que realiza otra carga remota. Entonces puede ocurrir que las respuestas no lleguen en el mismo orden, debido a que las peticiones pueden ser a nodos de distintas distancias, con distintos grados de congestión, nodos cuyas cargas se diferencian enormemente.
Una forma de abordar este problema es asociar a cada carga remota y cada respuesta un identificador del hilo apropiado, de tal forma que pueda ser reestablecido a la llegada de la respuesta. A estos identificadores se les llama continuaciones sobre mensajes. Se necesita un gran espacio de nombres de continuación para poder manejar un número adecuado de hilos a la espera de respuestas remotas.
El tamaño de este espacio de nombres de continuación soportado por el hardware difiere enormemente en los diseños existentes: desde 1 en la computadora Standford Dash, 4 en la MIT Alewife, 64 en la HEP, 1024 en la Tera, hasta todo el espacio de direcciones de memoria local en la Monsoon, la Hybrid Dataflow/VonNeumann, la MDP y la *T. Naturalmente si el soporte hardware para este espacio es pequeño, se puede virtualizar multiplexándolo por software, pero esto tiene una sobrecarga asociada.
Cada localización de memoria pertenece a un nodo. Los directorios son empleados para mantener listas de importación/exportación y estado cuando los datos son compartidos (en las lecturas, algunas caches pueden contener copias) o exclusivos (en las escrituras, una cache mantiene el valor actual).
Los directorios se multiplexan entre un pequeño número de contextos para cubrir los efectos de la carga de la cache. La MIT Alewife, la KSR-1, y la Stanford Dash implementan protocolos de coherencia basados en directorios.
La cache distribuida ofrece una solución al
problema de las cargas remotas pero no al problema de la sincronización
de cargas, en cambio el multihilo ofrece una solución para
las cargas remotas y posiblemente para la sincronización
de cargas. Normalmente, las dos aproximaciones se pueden combinar
para resolver ambos tipos de problemas de acceso remoto.