Siempre me he preguntado por qué los procesadores se detuvieron en 32 registros. Es, con mucho, la pieza más rápida de la máquina, ¿por qué no simplemente hacer procesadores más grandes con más registros? ¿No significaría eso menos ir a la RAM?
Siempre me he preguntado por qué los procesadores se detuvieron en 32 registros. Es, con mucho, la pieza más rápida de la máquina, ¿por qué no simplemente hacer procesadores más grandes con más registros? ¿No significaría eso menos ir a la RAM?
Respuestas:
Primero, no todas las arquitecturas de procesador se detuvieron en 32 registros. Casi todas las arquitecturas RISC que tienen 32 registros expuestos en el conjunto de instrucciones en realidad tienen 32 registros enteros y 32 registros de coma flotante más (es decir, 64). (El punto flotante "add" usa registros diferentes que los enteros "add".) La arquitectura SPARC tiene ventanas de registro. En SPARC solo puede acceder a 32 registros enteros a la vez, pero los registros actúan como una pila y puede empujar y abrir nuevos registros 16 a la vez. La arquitectura Itanium de HP / Intel tenía 128 registros enteros y 128 de coma flotante expuestos en el conjunto de instrucciones. Las GPU modernas de NVidia, AMD, Intel, ARM e Imagination Technologies, exponen un gran número de registros en sus archivos de registro. (Sé que esto es cierto para las arquitecturas NVidia e Intel, no estoy muy familiarizado con los conjuntos de instrucciones AMD, ARM e Imagination, pero creo que los archivos de registro también son grandes allí).
En segundo lugar, la mayoría de los microprocesadores modernos implementan el cambio de nombre de registros para eliminar la serialización innecesaria causada por la necesidad de reutilizar recursos, por lo que los archivos de registro físicos subyacentes pueden ser más grandes (96, 128 o 192 registros en algunas máquinas). Esto (y la programación dinámica) elimina algunos de los necesita que el compilador genere tantos nombres de registro únicos, al tiempo que proporciona un archivo de registro más grande al planificador.
Hay dos razones por las que puede ser difícil aumentar aún más el número de registros expuestos en el conjunto de instrucciones. Primero, debe poder especificar los identificadores de registro en cada instrucción. 32 registros requieren un especificador de registro de 5 bits, por lo que las instrucciones de 3 direcciones (comunes en las arquitecturas RISC) gastan 15 de los 32 bits de instrucción solo para especificar los registros. Si aumentara eso a 6 o 7 bits, tendría menos espacio para especificar códigos de operación y constantes. Las GPU e Itanium tienen instrucciones mucho más grandes. Las instrucciones más grandes tienen un costo: necesita usar más memoria de instrucciones, por lo que su comportamiento de caché de instrucciones es menos ideal.
Solo dos razones más para limitar el número de registros:
Una gran cantidad de código tiene muchos accesos a la memoria (el 30% es una cifra típica). Fuera de eso, típicamente alrededor de 2/3 son accesos de lectura y 1/3 son accesos de escritura. Esto no se debe a quedarse sin registros tanto como a acceder a matrices, acceder a variables de miembros de objetos, etc.
Esto TIENE que hacerse en la memoria (o caché de datos) debido a cómo se hace C / C ++ (todo lo que puede obtener un puntero debe tener una dirección para poder ser almacenado en la memoria). Si el compilador puede adivinar que no va a escribir en las variables de forma involuntaria usando trucos indirectos de puntero, los colocará en registros, y esto funciona muy bien para las variables de función, pero no para las variables de acceso global (generalmente, todo lo que sale de malloc ()) porque es esencialmente imposible adivinar cómo cambiará el estado global.
Debido a esto, no es común que el compilador pueda hacer algo con más de 16 registros de uso general de todos modos. Es por eso que todos los arquitectos populares tienen tantos (ARM tiene 16).
Los MIPS y otros RISC tienden a tener 32 porque no es muy difícil tener tantos registros; el costo es lo suficientemente bajo, por lo que es un poco "¿por qué no?". Más de 32 es en su mayoría inútil y tiene la desventaja de hacer que el archivo de registro sea más largo para acceder (cada duplicación en el número de registros potencialmente agrega una capa adicional de multiplexores que agrega un poco más de retraso ...). También hace que las instrucciones sean un poco más largas en promedio, lo que significa que cuando ejecuta el tipo de programas que dependen del ancho de banda de la memoria de instrucciones, ¡sus registros adicionales en realidad lo están ralentizando!
Si su CPU está en orden y no registra el cambio de nombre e intenta realizar muchas operaciones por ciclo (más de 3), entonces, en teoría, necesita más registros a medida que aumenta su número de operaciones por ciclo. ¡Es por eso que Itanium tiene tantos registros! Pero en la práctica, aparte del código numérico de coma flotante o SIMD (en el que Itanium era realmente bueno), la mayoría del código tendrá muchas lecturas / escrituras y saltos de memoria que hacen imposible este sueño de más de 3 operaciones por ciclo. (especialmente en software orientado al servidor como bases de datos, compiladores, ejecución de lenguaje de alto nivel como javascript, emulación, etc.). Esto es lo que hundió a Itanium.
¡Todo se reduce a la diferencia entre cálculo y ejecución!
¿Quién te dice que el procesador siempre tiene 32 registros? x86 tiene 8, ARM de 32 bits y x86_64 tiene 16, IA-64 tiene 128 y muchos otros números más. Puedes echar un vistazo aquí . Incluso MIPS, PPC o cualquier arquitectura que tenga 32 registros de propósito general en el conjunto de instrucciones, el número es mucho mayor que 32 ya que siempre hay registros de bandera (si los hay), registros de control ... sin incluir registros renombrados y registros de hardware
Todo tiene su precio. Cuanto mayor sea el número de registros, más trabajo tendrá al cambiar de tarea, más espacio necesitará en la codificación de instrucciones. Si tiene menos registros, no tiene que almacenar y restaurar mucho cuando llama y regresa de las funciones o cambia de tareas con la compensación de la falta de registros en algún código de cómputo extenso
Además, cuanto más grande sea el archivo de registro, más costoso y complejo será. SRAM es la RAM más rápida y costosa, por lo que solo se usa en la memoria caché de la CPU. Pero sigue siendo mucho más barato y ocupa menos área que un archivo de registro con la misma capacidad.
Por ejemplo, un procesador Intel típico tiene "oficialmente" 16 registros enteros y 16 vectores. Pero en realidad, hay muchos más: el procesador utiliza "cambio de nombre de registro". Si tiene una instrucción reg3 = reg1 + reg2, tendría un problema si otra instrucción que usara reg3 aún no hubiera terminado; no podría ejecutar la nueva instrucción en caso de que sobrescriba reg3 antes de que haya sido leída por la instrucción anterior.
Por lo tanto, hay aproximadamente 160 registros reales . Entonces, la instrucción simple anterior se cambia a "regX = reg1 + reg2, y recuerde que regX contiene reg3". Sin cambiar el nombre de los registros, la ejecución fuera de orden estaría absolutamente muerta en el agua.
No soy ingeniero eléctrico, pero creo que otra posibilidad por la razón de limitar el número de registros es el enrutamiento. Hay un número limitado de unidades aritméticas, y deben poder tomar la entrada de cada registro y la salida a cada registro. Esto es especialmente cierto cuando tiene programas canalizados que pueden ejecutar muchas instrucciones por ciclo.
Se me ocurrió la idea de esta respuesta al ver algunas de las charlas de Ivan Godard sobre la CPU Mill. Parte de la innovación de la CPU Mill es que no se puede enviar a registros arbitrarios: todas las salidas se insertan en una pila de registros o "correa", lo que reduce los problemas de enrutamiento, porque siempre se sabe a dónde irá la salida. Tenga en cuenta que todavía tienen el problema de enrutamiento para obtener los registros de entrada a las unidades aritméticas.
Consulte The Mill CPU Architecture - the Belt (2 de 9) para ver el enunciado del problema y la solución de Mill.