Renombre de registros

En arquitectura de computadores, se conoce como renombre de registros una técnica empleada para evitar la serialización innecesaria de las operaciones de los programas impuesta por la reutilización de los registros de procesador.

Definición del problema

editar

Los programas se componen de instrucciones que operan con valores, y éstas deben nombrar esos valores para distinguirlos de otros. Una instrucción típica sería sumar X e Y para almacenar el resultado en Z. En esta instrucción, X, Y y Z se refieren a lugares de almacenamiento.

Con el objetivo de tener una codificación compacta, la mayoría de los conjuntos de instrucciones tienen un pequeño grupo de localizaciones especiales que pueden ser nombradas directamente. Por ejemplo, la arquitectura del conjunto de instrucciones x86 tiene 8 registros enteros, la del x86-64 tiene 16, la mayoría de los RISC tienen 32 y la del IA-64 tiene 128. En procesadores más pequeños, los nombres de estos lugares se corresponden directamente con elementos del archivo de registros.

Diferentes instrucciones necesitan diferentes cantidades de tiempo. Por ejemplo, un procesador puede ejecutar cientos de instrucciones mientras se realiza una carga desde memoria principal. Las instrucciones más cortas ejecutadas mientras la carga está en proceso terminarán primero, de modo que las instrucciones estarían finalizando fuera del orden original del programa. La ejecución fuera de orden ha sido usada en la mayoría de los procesadores recientes de alto rendimiento para conseguir cierta ganancia de velocidad.

Tomemos el siguiente ejemplo de código ejecutándose en una CPU fuera de orden:

  1. Cargar en el registro 1 el dato de la posición de memoria 1024
  2. Sumar 2 al dato contenido en el registro 1
  3. Almacenar el dato del registro 1 en la posición de memoria 1032
  4. Cargar en el registro 1 el dato de la posición de memoria 2048
  5. Sumar 4 al dato contenido en el registro 1
  6. Almacenar el dato del registro 1 en la posición de memoria 2056

Las instrucciones 4, 5 y 6 son independientes de las 1, 2 y 3, pero el procesador no puede finalizar la 4 si no ha terminado la 3, ya que si no esta última escribiría un valor incorrecto.

Es posible eliminar esta restricción cambiando los nombres de algunos de los registros:

  1. Cargar en el registro 1 el dato de la posición de memoria 1024
  2. Sumar 2 al dato del registro 1
  3. Almacenar el dato del registro 1 en la posición de memoria 1032
  4. Cargar en el registro 2 el dato de la posición de memoria 2048
  5. Sumar 4 al dato del registro 2
  6. Almacenar el dato del registro 2 en la posición de memoria 2056

Ahora las instrucciones 4, 5 y 6 pueden ser ejecutadas en paralelo con las instrucciones 1, 2 y 3, de forma que el programa podrá ser ejecutado más rápidamente.

Cuando es posible, el compilador realiza este renombre. El compilador está limitado de varias maneras, principalmente por el número finito de nombres de registro en el conjunto de instrucciones. Muchos de los procesadores de alto rendimiento tienen más registros físicos de los que pueden ser nombrados directamente en el conjunto de instrucciones, así que se renombran registros de hardware para conseguir paralelismo adicional.

Riesgos y renombre

editar

Cuando más de una instrucción se refiere a una localización particular de un operando, ya sea para lectura o para escritura, ejecutar las mismas en un orden distinto del original puede llevar a tres tipos de problemas, también conocidos como riesgos:

  • Read-after-write (RAW)
Una lectura de un registro o posición de memoria debe devolver el valor almacenado ahí por la última escritura en el orden de programa y no otra. Esto se conoce como dependencia verdadera o dependencia de flujo, y requiere que las instrucciones se ejecuten en el orden del programa.
  • Write-after-write (WAW)
Sucesivas escrituras a un registro o posición de memoria concretos deben dejar en dicha localización el resultado de la última escritura. Esto puede ser resuelto anulando si es preciso cualquier escritura anterior. Estas dependencias son también conocidas como dependencias de salida.
  • Write-after-read (WAR)
Una lectura desde un registro o posición de memoria debe devolver el último valor escrito en esa localización antes de la ejecución de la instrucción en curso, y no un valor escrito por el programa tras la lectura. Este tipo es conocido como falsas dependencias o anti-dependencias, y se resuelven cuando es necesario mediante renombre.
En lugar de retrasar la escritura hasta que las lecturas se hayan completado, se mantienen dos copias de la localización, una con el valor antiguo y otra con el nuevo. Las lecturas que, en orden de programa, preceden a la escritura del nuevo valor, reciben el valor anterior, mientras que las posteriores recibirán el nuevo. De ese modo se rompe la falsa dependencia y se crean nuevas oportunidades de ejecución fuera de orden. Una vez que se han realizado todas las lecturas referidas al valor antiguo, este es descartado. Este es el concepto esencial existente detrás del renombre de registros.

Cualquier localización que sea leída y escrita puede ser renombrada. Aunque en su mayoría el renombre se aplica a los registros de propósito general y a los de punto flotante, la técnica también es aplicada a registros de marca o estado.

Las posiciones de memoria también pueden ser renombradas, tal y como ocurre en cierto modo en el procesador Transmeta Crusoe, si bien no es lo más común.

Si los programas se abstienen de reutilizar los registros de forma inmediata, en principio no sería necesario el renombre. Algunos conjuntos de instrucciones, tales como el del IA-64, permiten números de registros muy grandes específicamente por este motivo. De todas formas existen limitaciones en cuanto a este enfoque:

  • Al compilador le resulta muy difícil evitar la reutilización de registros sin que ello implique un aumento de la longitud del código. En los bucles, por ejemplo, las sucesivas iteraciones tendrían que usar registros diferentes, lo que requeriría replicar el código.
  • Un gran número de registros requiere mayor número de bits para direccionarlos, aumentando el tamaño del código.
  • Históricamente, muchos conjuntos de instrucciones han especificado números de registros pequeños y ya no puede cambiarse.

El aumento de tamaño del código es importante porque cuanto mayor es el código, antes se llena la caché y el procesador se bloquea esperando nuevas instrucciones.

Registros de arquitectura y registros físicos

editar

Los programas en lenguaje máquina especifican lecturas y escrituras en un limitado conjunto de registros marcados por la arquitectura del repertorio de instrucciones. Por ejemplo, el conjunto DEC Alpha especifica 32 registros enteros y 32 de punto flotante, ambos tipos de 64 bits. Estos son registros de arquitectura. Los programas escritos para procesadores que utilizan el conjunto de instrucciones Alpha realizarán operaciones de lectura y escritura referidas a esos 64 registros. Si un programador detiene el programa en un depurador de código, podrá observar el contenido de los 64 registros (y de algunos registros de estado) para determinar el progreso de la máquina.

Un caso particular de procesador que implementa este repertorio es el Alpha 21264, que tiene 80 registros enteros y 72 de punto flotante. En el chip del Alpha 21264 hay 80 localizaciones separadas que pueden almacenar los resultados de las operaciones enteras, y 72 para almacenar los resultados de las de punto flotante (De hecho, hay aún más localizaciones, pero a éstas no se les puede aplicar el renombre de registros).

Abajo se describen dos estilos de renombre de registros, diferenciados por el circuito que prepara los datos para la unidad de ejecución.

En todos los esquemas de renombre, la máquina convierte los registros de arquitectura utilizados en el flujo de instrucciones en etiquetas. Donde los registros de arquitectura pueden ser especificados con de 3 a 5 bits, las etiquetas suelen ser números de 6 a 8 bits. El archivo de renombre debe tener un puerto de lectura para cada entrada de cada instrucción renombrada en cada ciclo, y un puerto de escritura para cada salida de cada instrucción renombrada en cada ciclo. Puesto que el tamaño del archivo de registros generalmente crece de forma geométrica respecto del número de puertos, el archivo de renombre es normalmente físicamente grande y consume gran cantidad de energía.

En la técnica de archivo de registros indexado por etiquetas, hay un gran archivo de registros para los datos, que contiene un registro para cada etiqueta. Por ejemplo, si la máquina tiene 80 registros físicos utilizará etiquetas de 7 bits, quedando 48 valores de etiqueta sin utilizar.

En esta técnica, cuando una instrucción es enviada a la unidad de ejecución, las etiquetas de los registros fuente son enviadas al archivo físico de registros, donde los valores correspondientes a esas etiquetas son leídos y enviados a la unidad de ejecución.

En la técnica de estaciones de reserva, hay muchos pequeños archivos asociativos de registros, normalmente uno a la entrada de cada unidad de ejecución. Cada operando de cada instrucción que se encuentre en una cola de emisión tiene un lugar para su valor en uno de estos archivos de registros.

En esta técnica, cuando una instrucción es enviada a una unidad de ejecución, las entradas del archivo de registro correspondientes a la entrada de la cola de emisión son leídas y enviadas a la unidad de ejecución.

Referencias

editar