¿Cómo funciona el modificador de tipo de registro en diferentes arquitecturas de CPU?

Esta pregunta es para aclarar mi duda sobre esta clase de almacenamiento de registros.

  1. cuando una variable está calificada para el registro, el compilador coloca la variable en un registro de la CPU que no sea RAM para facilitar el acceso. entonces, ¿hay registros disponibles en una CPU para uso general (permite al usuario usarlo libremente)? (No voy a preguntar si significa algún SFR (no me gusta cambiar el significado de SFR)

    En una MCU PIC (tal vez aplicable solo a la línea base), los SFR y los registros de propósito general (que es la única RAM que creo) existen juntos. ¿Aquí no puedo pensar en ninguna optimización con un 'registro' o hay?

  2. ¿Alguien podría explicar cómo logra la optimización en términos de velocidad (de acceso) en otras arquitecturas?

  3. ¿O las preguntas anteriores se deben a muchos conceptos erróneos?

AFAIK "registrar" es solo una recomendación muy flexible para el compilador; no significa que la compilación tenga que poner la variable en un registro. La mayoría de las veces, el compilador sabe mejor dónde colocar la variable y utiliza registros siempre que sea posible. Muy a menudo la palabra clave no tiene efecto.
"utiliza registros cuando sea posible de todos modos" ¿podría explicar en qué registros y por qué no es posible?
No conozco los detalles internos de las MCU PIC pero, en general, los procesadores tienen un número limitado de registros que pueden usar para operaciones (por ejemplo, agregar). Si una función utiliza muchas variables locales, no todas ellas pueden almacenarse en registros y al menos algunas deben ubicarse en la RAM externa. El compilador tiene una estrategia sofisticada para determinar qué variables mantener en los registros (p. ej., las que se usan con más frecuencia) y cuáles no (p. ej., las que deben estar en la memoria porque se refieren a ellas mediante punteros).
@Curd, ¿por qué no pusiste ese comentario en Respuestas?
ok, copié el comentario también en una respuesta (no lo puse allí primero porque no estaba seguro de si respondía lo suficiente a la pregunta :-)

Respuestas (5)

Su pregunta es un poco confusa, pero tal vez esto ayude a aclararla.

Hay dos áreas a considerar:

  • si el núcleo de ejecución puede operar directamente en los elementos de la memoria
  • la velocidad de las operaciones en la memoria

Pequeños microcontroladores integrados

Estos pequeños microcontroladores no tienen RAM externa. Toda la RAM es interna, pero parte de ella se usa para cosas específicas como registros.

Por ejemplo, los PIC de Microchip que menciona tienen un registro "W". Esto es solo en la RAM normal como todo lo demás, pero las instrucciones con dos operandos generalmente requieren que uno de ellos esté en el registro W.

Esto simplifica enormemente el diseño del microcontrolador a nivel electrónico y mantiene bajos los costos/energía. También tiene otros beneficios como el tiempo predecible (en ciclos) para las instrucciones.

Esta es la razón por la que verá instrucciones que cargan W con un valor, lo operan y luego lo copian de W a otra parte de la memoria. El compilador usa el registro porque tiene que hacerlo.

Procesadores más grandes

Otros procesadores (CPU) como x86/64 tienen RAM externa, lo que es una gran diferencia. Fíjate ahora que un "registro" significa algo muy diferente porque tenemos diferentes tipos de memoria.

Externo a la CPU hay grandes cantidades de RAM, interno a la CPU hay varios bloques de memoria más pequeños. Algunos de estos son registros de almacenamiento que contienen una cantidad de datos, generalmente igual al ancho de datos de la arquitectura. Entonces, para un procesador Intel de 32 bits, los registros (como EAX, EBX, etc.) tienen 32 bits de ancho.

Estos procesadores tienen instrucciones más complicadas que a menudo pueden operar en registros o RAM externa. Los datos para una instrucción no siempre necesitan estar en un registro. Por lo tanto, ¿por qué nos molestaríamos? La respuesta es la velocidad. Cuando haya una opción, el compilador usará registros para reducir el tiempo de ejecución.

Estos procesadores complicados tienen diferentes tiempos de acceso para diferentes tipos de memoria. Los registros que están en la matriz de la CPU son de acceso muy rápido. Entonces, si tiene una variable que está en uso constante a lo largo de algún código, tiene sentido cargarla en un registro, operarla repetidamente y luego copiarla nuevamente a la RAM externa cuando termine.

No debí haber olvidado las diferencias entre un microprocesador y un microcontrolador
"Por ejemplo, los PIC de Microchip que mencionas tienen un registro "W". Esto es solo en la RAM normal como todo lo demás", creo que te perdiste un 'no'.
En realidad esto depende. En algunos dispositivos (verifiqué 16F) solo se puede acceder a través de instrucciones como MOVF x,W/MOVWF, pero en otros (18F25K22) también está asignado a una ubicación en la RAM a la que puede acceder. Sin embargo, en todos los dispositivos actúa como un registro acumulador de propósito general sin importar cómo se presente.

AFAIK "registrar" es solo una recomendación muy flexible para el compilador; no significa que el compilador tenga que poner la variable en un registro. La mayoría de las veces, el compilador sabe mejor dónde colocar la variable y utiliza registros siempre que sea posible. Muy a menudo la palabra clave no tiene efecto.

No conozco los detalles internos de las MCU PIC pero, en general, los procesadores tienen un número limitado de registros que pueden usar para operaciones (por ejemplo, agregar). Si una función utiliza muchas variables locales, no todas ellas pueden almacenarse en registros y al menos algunas deben ubicarse en la RAM externa. El compilador tiene una estrategia sofisticada para determinar qué variables mantener en los registros (p. ej., las que se usan con más frecuencia) y cuáles no (p. ej., las que deben estar en la memoria porque se refieren a ellas mediante punteros).

Dependería de qué compilador se use, pero tiene razón; para un procesador que no tiene nada más que registros, con el tiempo de acceso sin cambios sin importar qué registro (sin selección de banco), entonces la calificación no debería tener sentido.

Si se necesita la selección de un banco, entonces el compilador debe limitar la variable al mismo banco que está actualmente en uso en el momento de la ejecución.

Pero ¿por qué especular? Puede verificar esto comparando la salida de código ensamblador y máquina del compilador, con y sin una calificación de registro en un lugar interesante. Si no ve ninguna diferencia, sabe que la calificación no es necesaria actualmente. Sin embargo, no significa que no será útil más adelante.

(Puede usar programas de detección de diferencias, actuando sobre la salida del ensamblado. Exactamente qué programas usar depende de su preferencia. Yo usaría diff o meld en Linux, por ejemplo).

WinMerge y DiffMerge son medio decentes en Windows, Meld también funciona (decentemente) en Windows pero no es tan bueno como su contraparte de Linux.

Según el procesador, el acceso a los datos en los registros se puede realizar con una instrucción más corta que el acceso a los datos en la memoria. Eso solo puede acelerar la ejecución de esa instrucción, además de ahorrar memoria del programa. La ganancia de velocidad puede ser especialmente beneficiosa dentro de un bucle. Algunas operaciones solo pueden funcionar en un registro o entre dos registros. Tener una variable en un registro en ese momento es una necesidad. Si se puede mantener en el registro, se pueden guardar dos operaciones (cargar el registro desde la memoria y almacenar el nuevo valor nuevamente en la memoria). La mayoría de los compiladores son mejores y más minuciosos que la mayoría de los programadores para descubrir la mejor manera de beneficiarse usando el número limitado de registros para ese propósito. Examine la salida del ensamblador del mismo código sinregisterespecificadores, compilados con y sin optimización activada para ver qué hace y qué no asigna el optimizador a los registros.

Hoy en día, muchos compiladores tienen algoritmos de análisis de flujo de datos que pueden usar registros de manera más eficiente de lo que sería posible con un programador experto colocando cada variable en un registro o en la memoria, ya que los compiladores pueden decidir que las variables deben vivir en registros para algunas partes de su toda la vida y en la memoria de otras partes. Como tal, la función principal de la palabra clave de registro es indicar al compilador que debe prohibir cualquier intento de tomar la dirección de una variable en particular. Tomar la dirección de una variable generalmente implica que no debe mantenerse en un registro a través de las llamadas a funciones, pero para las variables cuya dirección no se toma, la palabra clave no registernecesita tener ningún efecto cuando el compilador decide cuándo mantener las variables en los registros.