En este artículo vamos a usar como ejemplo el
procesador MIPS64 que tiene como características el ser un procesador de tipo
RISC (ver artículo anterior) con cauce
segmentado de 5 etapas por instrucción de 1 ciclo de duración cada una, 64
registros de 64 bits cada uno, memoria cache tipo Harvard y 66 instrucciones
simples de longitud fija de 32 bits. Pero vayamos poco a poco antes de que
alguien se asuste.
Ahora que ya sabemos lo que es un procesador
tipo RISC (Reduced Instruction Set Computer) lo siguiente es comentar en qué
consiste segmentar el cauce de ejecución de las instrucciones de un procesador.
La segmentación (o pipelining) surge de la necesidad constante de aumentar la
velocidad de proceso de las computadoras, para ello nos vienen rápidamente a la
mente tres formas de hacerlo: mejorar el hardware para que sea más rápido,
explotar la concurrencia poniendo varios procesadores a trabajar en la misma
tarea o hacer lo mismo intentando que un procesador pueda hacer varias tareas a
la vez. Todas ellas tienen sus ventajas y sus inconvenientes que se suelen
traducir en costes de fabricación y/o de programación.
La primera solución es probablemente la más
cara y está limitada por el estado actual del arte, la segunda hoy en día es
muy común pero antes su precio era prohibitivo y además se puede usar junto con la tercera. Evidentemente la
tercera es la segmentación.
Se suele recurrir al ejemplo de la lavadora
para explicar cómo funciona, pero se pueden encontrar multitud de ejemplos hoy
en día que lo ilustran de igual manera. Hoy vamos a usar como modelo la
fabricación de un automóvil, en honor a Henry Ford que fue uno de los primeros
en aplicar las cadenas de montaje.
Fabricar un automóvil era tanto en 1913 como
hoy una tarea compleja, que requería un grupo experto de mecánicos trabajando
muchas horas. La idea básica era dividir esa tarea compleja es varias más
pequeñas y más sencillas, de manera que las pudieran realizar operarios menos
cualificados en menos tiempo. Vamos a suponer, de forma totalmente falsa, que
Henry Ford dividió su línea de producción en 5 pasos “sencillos”:
- Recoger las piezas del almacén.
- Leer las especificaciones del cliente y configurar las máquinas.
- Ensamblar todas las piezas en el chasis.
- Enviar el coche al concesionario para su venta.
- Escribir los datos de venta en los libros de cuentas.
Si tuviéramos una persona que sabe hacer todas
las tareas y tarda 1 día en cada una, podría construir el coche él sólo en 5
días, si contáramos con 5 de estas personas y empezaran a trabajar el mismo
día, venderían 5 coches cada 5 días pero necesitaríamos multiplicar por 5 los
recursos de la fábrica.
Lo siguiente es tener a una persona formada
en cada operación, de forma que tenemos un transportista de piezas, un operario
de las máquinas, un montador, un transportista de vehículos terminados y un
contable. Seguimos teniendo 5 operarios pero si les ponemos a trabajar sobre el
mismo coche sólo venderemos 1 cada 5 días y siempre habrá 4 esperando a que se
les dé trabajo.
Ahora bien, si encadenamos las operaciones de
manera que tengamos varios coches en fabricación a la vez podemos llegar a lo
siguiente:
Con este esquema llegamos a vender 1 coche en
5 días (como al principio), pero a partir del día 5 vendemos 1 coche al día,
teniendo una producción igual a la del ejemplo de 5 operarios expertos en todas
las tareas pero con unos costes mucho menores y vendiendo un coche diario en
vez de tener que esperar 5 días para vender 5 coches.
Si ahora dividiéramos cada tarea en dos
subtareas tendríamos que contratar 5 operarios más pero venderíamos dos coches
al día, etcétera, etcétera.
Como puede verse la segmentación no reduce el
tiempo de proceso sino que aumenta el rendimiento, racionaliza los recursos y
disminuye los costes. Este aumento del rendimiento depende directamente del
número de etapas de que se componga nuestro proceso.
Ahora traduciremos este ejemplo a la
arquitectura MIPS64, para ello las fases por las que pasa una instrucción son:
- Instruction Fetch (IF).
- Instruction Decode (ID).
- Execution (E).
- Memory Access (M).
- Write Back (WB).
Ahora se entiende lo rebuscado de las operaciones
de la analogía con los automóviles; el objetivo era que fueran similares a las
etapas del procesador.
Las 3 primeras son fáciles de entender y
corresponden con la carga de la instrucción a ejecutar, la segunda con la
decodificación de la instrucción y lectura de los registros de los operandos, y
la tercera es donde se hacen los cálculos. Para las otras dos etapas es
conveniente explicar muy por encima la jerarquía de memoria.
Cuanto más cerca está la memoria del
procesador, más rápida debe ser y menos tamaño puede tener. Conforme nos vamos
alejando del procesador tenemos: registros, cache, RAM y disco duro. Las dos
primeras están alojadas directamente en el procesador, la RAM está en la placa
base y el disco duro se engancha a la misma.
En el MIPS64 tenemos 64 registros de 64 bits
(32 de propósito general y 32 para operaciones en coma flotante) y una caché de
primer nivel (puede haber más niveles…) de 64 kilobytes para instrucciones y
otros 64 para datos.
Por tanto la etapa de Memory Access representa
una lectura o escritura en la memoria RAM y la de Write Back representa la
escritura en los registros de los resultados de las operaciones.
Las ventajas de la segmentación son muchas
pero como todo en la vida tienen un precio o unos inconvenientes. Los problemas
que hacen que la segmentación no funcione al mismo rendimiento que en la teoría
se llaman riesgos y puede haberlos de tres tipos: riesgos de datos, de control
y estructurales.
Riesgos
Estructurales:
en este tipo de riesgo uno de los problemas es que no todas las instrucciones
van a requerir hacer uso de todas las etapas pero pueden tener que esperar a
que alguno de los recursos quede libre, tardando al final lo mismo que la
instrucción más larga. Así mismo no todas las operaciones elementales tienen la
misma duración, por ejemplo una suma se realiza en menos tiempo que una
división.
Además, si queremos armonizar las etapas de
manera que todas avancen a la vez, sincronizadas con el reloj del procesador,
tenemos que hacer que todas las etapas duren lo mismo. Esta es la solución
ideal y, por definición, inalcanzable por lo que en la realidad diseñaremos el
procesador de manera que en un ciclo le dé tiempo a terminar a la etapa más
lenta. En el resto de etapas estaremos “perdiendo” tiempo desde que se termine
hasta que se pase a la siguiente.
Otra de las facetas de los riesgos
estructurales es cuando dos instrucciones distintas tienen que hacer uso del
mismo recurso:
Para resolver este problema se pueden
introducir paradas (“no op”: etapas que no realizan ningún trabajo) que
sincronizan el cauce a costa de hacerlo menos productivo. Otra opción, más
cara, es replicar los recursos que más van a utilizarse. Una tercera opción es
dividir las etapas en dos subetapas, en una de ellas se leen datos y en la otra
se escriben de manera que en la misma etapa se pueden hacer operaciones
concurrentes con el mismo registro.
Riesgos de Datos: este tipo de riesgos se
producen cuando entre las instrucciones existen dependencias de datos (tienen
operandos comunes). Hay tres tipos de dependencias de datos:
-
Verdadera dependencia o RAW (Read After Write).
-
Antidependencia
o WAR (Write After Read).
-
Dependencia de salida o WAW (Write After Write).
En MIPS no hay riesgos WAR ya que se produce
la emisión de instrucciones en orden, de manera que se leen los registros antes
de que se puedan modificar por otra orden posterior.
Los riesgos WAW pueden entenderse con el
siguiente esquema:
En la imagen siguiente puede verse un caso de
riesgo RAW; la instrucción de multiplicación requiere un dato guardado en el
registro 1 que es el resultado de una instrucción suma que aún no ha terminado.
Hay tres formas de solucionar esto:
introduciendo paradas que sincronicen el código, reordenando las instrucciones
para evitar esas dependencias o implementar la técnica del “data forwarding”.
La primera de estas técnicas ya se ha visto
para riesgos estructurales, mientras que la segunda también se puede aplicar
para estos y consiste en introducir otras instrucciones que no dependen de los
registros en conflicto hasta que se resuelva la parte WB de la primera
instrucción. Esta técnica forma parte de la llamada “planificación dinámica”.
El “data forwarding” o anticipación consiste
en dotar al hardware de una manera de redirigir el resultado de la etapa E de
la primera instrucción en conflicto, directamente a la entrada de la etapa E de la otra instrucción en conflicto. Esto
evita que se detenga el cauce pero hace que la etapa ID de la segunda
instrucción lea valores incorrectos.
Riesgos de
Control: este
tipo de datos se dan cuando en el cauce de instrucciones hay “saltos” fruto de
sentencias condicionales tipo if.
Normalmente el registro contador de programa (PC) se va actualizando con el
valor de la siguiente instrucción a ejecutar pero cuando tenemos sentencias
condicionales tenemos que esperar hasta que se resuelva la comparación para
saber qué instrucción va a ser la siguiente en ejecutarse.
Como siempre, podemos poner paradas en el
cauce hasta que se resuelva la indeterminación pero ya sabemos que con eso
estamos echando por tierra las ventajas de la segmentación.
Otras soluciones pasan por predecir el salto
o retardar la decisión (bifurcación retardada). Además de esto, existe una
mejora del cauce MIPS que intenta minimizar el efecto de estos riesgos
adelantando el cálculo de la dirección de salto a la etapa ID, de esta manera
sólo se retarda un ciclo la ejecución de la siguiente instrucción.
En la bifurcación retardada seguimos
ejecutando las siguientes instrucciones que hay en cola hasta que se resuelva
el salto, siempre y cuando estas instrucciones se deban ejecutar siempre
independientemente de que se tome o no el salto.
En la predicción de saltos lo que se hace es
asumir que siempre se va a tomar el salto (o lo contrario) y actuar en
consecuencia mientras no se sepa el resultado. Una vez que se sabe el resultado
del salto se sigue con la ejecución ya planeada o se desechan las instrucciones
que se habían alimentado. Analizando los costes de las dos opciones (siempre sí
o siempre no) se llega a la conclusión de que en MIPS lo que más cuenta trae es
actuar siempre como si nunca se fuera a tomar el salto. Esto se puede apreciar
en el esquema siguiente.
Bibliografía:
-
Patterson
and Hennessy: Computer Organization and Design. The Hardware/Software
Interface. Morgan Kaufmann Publishers. ISBN 1-55860-604-1
-
Apuntes de Arquitectura de Computadores y AGM de
varias facultades de Informática
No hay comentarios:
Publicar un comentario