Global communication Desde el advenimiento de los ordenadores se ha desarrollado gran variedad de procesadores con distinto juego de instrucciones (Instruction Set Architecture). Su diseño arrancaba con la definición del juego y culminaba en la implementación de la microarquitectura que cumplía la especificación. Durante los años 80 el interés estuvo centrado en determinar cuáles constituían las características más recomendables de ese conjunto. Sin embargo, durante los 90, el foco se desplazó al desarrollo de técnicas innovadoras en la microarquitectura que fuesen aplicables a todos ellos.

El objetivo principal de cada nueva generación de procesadores ha sido el aumento de las prestaciones respecto a la generación anterior y, en los últimos tiempos, con la consideración adicional de reducir el consumo. Pero ¿cómo medir las prestaciones? Básicamente, en función del tiempo que lleva ejecutar un programa concreto. Haciendo una serie de manipulaciones muy sencillas podemos expresar las prestaciones en función de una serie de factores con un significado preciso.

microprocesadores graficoEl primer término del lado derecho de la igualdad expresa el número total de instrucciones que dinámicamente necesitan ejecutarse por el programa; el segundo, los ciclos de máquina que son consumidos en media por cada instrucción (CPI); y el tercero, el tiempo de ciclo o la inversa de la frecuencia de trabajo. Actuando sobre cada uno de los tres términos podemos aumentar las prestaciones, aunque desgraciadamente no son, en ocasiones, independientes, y la reducción de uno de ellos puede, potencialmente, aumentar la magnitud de los otros.

Una de las técnicas más sencillas para actuar sobre el segundo y tercer términos con un pequeño coste de hardware es el pipelining. El sistema o la unidad se tabica mediante buffers en múltiples etapas, de manera que el proceso se descompone en K subprocesos que se llevan a cabo en cada una de las K etapas. Para un sistema que opera sobre una tarea a la vez, el throughput es igual a 1/D, donde D es la latencia de la tarea o el retardo asociado a su ejecución por el sistema; introduciendo el pipeline se inicia una nueva tarea en cada una de las subunidades cuando la tarea previa la abandona, hecho que ocurre cada D/K unidades de tiempo. Los procesadores que haciendo uso de esta técnica consiguen un CPI igual a 1 se les denomina escalares y sus representantes son los RISC diseñados en la década de los 80. Este mismo parámetro se utiliza para definir los procesadores superescalares, como aquellos capaces de CPI menor que 1. El valor medio de su inverso, IPC (Instructions Per Cycle), constituye una buena medida de la efectividad de la microarquitectura.

El obstáculo principal de los pipeline processors, sean o no superescalares, son los pipeline hazards que requieren retener (stall) la ejecución de la subunidad afectada y las posteriores que contienen instrucciones situadas a continuación en el flujo del programa. Se identifican tres tipos:

  • Estructurales, cuando existe un recurso compartido que no puede dar servicio a todas las subunidades que en un momento determinado lo requieren.
  • De control, cuando un programa ejecuta una instrucción de salto que se bifurca a un destino diferente al estimado.
  • De datos, cuando instrucciones que exhiben dependencias entre sus datos acceden, o pretenden acceder a ellos, en orden diferente al establecido en el programa.

Los de este último tipo se clasifican según el orden de ocurrencia en el programa de las instrucciones i y j, con i ocurriendo antes que j, que debe preservarse en el pipeline. Las variedades son: Read After Write (RAW), cuando j trata de leer un operando antes de que i lo escriba, Write After Write (WAW), cuando j trata de escribir un operando antes que lo escriba i, y Write After Read (WAR), cuando j trata de escribir un operando antes de que lo lea i. Los dos últimos tipos son dependencias falsas, o de nombre, que se deben al número limitado de registros definidos en la arquitectura.

¿Cómo se gestionan estas dependencias que surgen por el pipelining, complicadas aún más en los procesadores superescalares con varias unidades operando simultáneamente? Pues introduciendo una serie de estructuras de control que se leen y actualizan conforme la instrucción avanza recorriendo las diferentes etapas que, para propósito de nuestra exposición, consideramos:

Fetch, Dispatch (IQ), Issue, {eXecute#1 II … II eXecute#N}, Writeback, Commit

El proceso es el siguiente: cada ciclo de reloj la unidad de control lee (fetch) una instrucción que pasa en el siguiente, si proviene de la I-caché, o varios después, si proviene de la RAM, a la etapa que va a continuación, Dispatch, en donde se realizan cuatro acciones.

(1)    Se selecciona un registro físico, no visible en el modelo de programación, de la tabla de registros libres (FL).

(2)    Se realiza la anotación de la asociación temporal del registro anterior con el registro de la arquitectura en la Rename Table (RAT), eliminando así las falsas dependencias (WAW y WAR), al asignar dinámicamente diferentes nombres a un mismo registro de la arquitectura en un intento de seguir el valor del resultado. No el nombre del registro, lo cual es posible porque el número de registros físicos es mayor que el de los definidos en la arquitectura.

(3)    Se reserva una entrada en el Reorder Buffer (ROB) que es una cola circular que mantiene el estado de los registros físicos asociados a las instrucciones en vuelo.

(4)    Se encola la instrucción en la Issue Queue (IQ), a la espera de que los operandos estén disponibles, momento en que avanza a la siguiente etapa (Issue) en el próximo ciclo de reloj, si la exploración del Scoreboard (SB) determina que la operación puede proceder en la unidad funcional correspondiente sin conflictos; de ser el caso, un ciclo después la operación comienza en la unidad funcional (eXecute#K), que se asocia en el SB con el registro físico que albergará el resultado, y donde se va actualizando el progreso cada ciclo de reloj. Al finalizar esta, uno o varios ciclos después, la unidad funcional actualiza el contenido del registro físico en la etapa denominada Writeback, en el mismo momento que se actualiza a “completado” el estado en la entrada asociada al registro en ROB y se pone a Writeback el estado de la operación en SB. Si la instrucción en Writeback es una escritura en memoria, se transfiere el contenido del registro de la arquitectura temporalmente a una estructura intermedia denominada Finished Store Buffer (FSB), cuyo objeto es mantener el dato por si alguna instrucción previa del programa, todavía en proceso en alguna unidad funcional, provocase una excepción (supuesto un modelo de excepciones precisas). La instrucción finaliza en la siguiente etapa del pipeline, commit, uno o varios ciclos después, cuando el puntero a la cabecera de ROB avanza a su entrada en la cola circular, se transfiere entonces el registro físico al de la arquitectura actualizando el estado de la máquina. Se libera la asociación entre ambos registros que mantiene RAT, se devuelve el registro físico al pool en FL y, en el caso de una escritura, y se transfiere el contenido de FSB a la memoria, ya sea la D-cache o la RAM.

La mayor parte de los procesadores que equipan los equipos TELDAT pertenecen a la familia PowerPC de Freescale. Por supuesto incorporan todas las técnicas descritas anteriormente y son superescalares; así, cuando una instrucción es enviada (dispatched) a una de las IQ (en nuestro ejemplo, la cola era centralizada y en esta arquitectura es distribuida, existiendo una cola por cada unidad) se le asigna un rename register (RR), equivalente al registro físico, al resultado de la operación y una entrada en la Completion Queue (CQ), que haría las funciones de ROB. Las instrucciones pasan de la IQ, a través de la reservation station (RS), a ejecutarse en la unidad funcional cuando sus operandos están disponibles. Finaliza la ejecución actualizando el RR y su estado en la CQ. Las instrucciones finalizadas se retiran de la CQ en el orden de programa y, un ciclo después, se trasladan los resultados desde los RR al banco de registros de la arquitectura.

En este artículo hemos mostrado, sin ánimo de entrar en justificaciones, las estructuras que aparecieron junto a los primeros RISC (allá por los años 80) y sus motivaciones. El concepto de pipelining, los hazards, reales y ficticios, la técnica de register renaming… Ideas que tienen su origen en el algoritmo que desarrolló Robert Tomasulo en IBM (en 1967), y que se implementaron por primera vez en la unidad de punto flotante del IBM System/360 Model 91, sentaron las bases de la ejecución fuera de orden (out-of-order execution) de los actuales procesadores.

Espero que lo sucinto del artículo, al menos, haya excitado la curiosidad del amable lector.


Sobre el autor

Manuel Sanchez
Manuel Sanchez
Manuel Sánchez González-Pola es Ingeniero de Telecomunicación en el departamento de I+D. Dentro de dicho departamento trabaja en el grupo de Hardware como responsable de proyectos.  

Comparte este post

Tweet about this on TwitterShare on LinkedInShare on Google+Email this to someone