Hay muchas razones por las que no solo tiene una gran cantidad de registros:
- Están muy vinculados a la mayoría de las etapas de la canalización. Para empezar, debe realizar un seguimiento de su vida útil y reenviar los resultados a las etapas anteriores. La complejidad se vuelve intratable muy rápidamente y la cantidad de cables (literalmente) involucrados crece al mismo ritmo. Es caro en área, lo que en última instancia significa que es caro en potencia, precio y rendimiento después de cierto punto.
- Ocupa espacio de codificación de instrucciones. 16 registros ocupan 4 bits para origen y destino, y otros 4 si tiene instrucciones de 3 operandos (por ejemplo, ARM). Eso es una gran cantidad de espacio de codificación de conjuntos de instrucciones ocupado solo para especificar el registro. Esto eventualmente afecta la decodificación, el tamaño del código y nuevamente la complejidad.
- Hay mejores formas de lograr el mismo resultado ...
En estos días realmente tenemos muchos registros, simplemente no están programados explícitamente. Tenemos "registro de cambio de nombre". Si bien solo accede a un conjunto pequeño (8-32 registros), en realidad están respaldados por un conjunto mucho más grande (por ejemplo, 64-256). Luego, la CPU rastrea la visibilidad de cada registro y los asigna al conjunto renombrado. Por ejemplo, puede cargar, modificar y luego almacenar en un registro muchas veces seguidas y hacer que cada una de estas operaciones se realice de forma independiente dependiendo de las fallas de caché, etc.
ldr r0, [r4]
add r0, r0, #1
str r0, [r4]
ldr r0, [r5]
add r0, r0, #1
str r0, [r5]
Los núcleos Cortex A9 registran el cambio de nombre, por lo que la primera carga a "r0" en realidad va a un registro virtual renombrado, llamémoslo "v0". La carga, el incremento y el almacenamiento ocurren en "v0". Mientras tanto, también realizamos una carga / modificación / almacenamiento en r0 nuevamente, pero se le cambiará el nombre a "v1" porque esta es una secuencia completamente independiente que usa r0. Digamos que la carga del puntero en "r4" se detuvo debido a un error de caché. Está bien, no necesitamos esperar a que "r0" esté listo. Debido a que se le cambió el nombre, podemos ejecutar la siguiente secuencia con "v1" (también asignada a r0), y quizás eso sea un éxito de caché y acabamos de tener una gran ganancia de rendimiento.
ldr v0, [v2]
add v0, v0, #1
str v0, [v2]
ldr v1, [v3]
add v1, v1, #1
str v1, [v3]
Creo que x86 tiene una cantidad gigantesca de registros renombrados en estos días (estadio de béisbol 256). Eso significaría tener 8 bits multiplicado por 2 para cada instrucción solo para decir cuál es el origen y el destino. Aumentaría enormemente la cantidad de cables necesarios a través del núcleo y su tamaño. Así que hay un punto óptimo alrededor de 16-32 registros con el que se han conformado la mayoría de los diseñadores, y para los diseños de CPU fuera de orden, el cambio de nombre de registros es la forma de mitigarlo.
Editar : La importancia de la ejecución fuera de orden y el cambio de nombre del registro en esto. Una vez que tiene OOO, el número de registros no importa tanto, porque son simplemente "etiquetas temporales" y se les cambia el nombre al conjunto de registros virtuales mucho más grande. No desea que el número sea demasiado pequeño, porque se vuelve difícil escribir secuencias de código pequeñas. Este es un problema para x86-32, porque los 8 registros limitados significan que muchos temporales terminan pasando por la pila y el núcleo necesita lógica adicional para reenviar las lecturas / escrituras a la memoria. Si no tiene OOO, por lo general se refiere a un núcleo pequeño, en cuyo caso un conjunto de registros grande es un beneficio de bajo costo / rendimiento.
Por lo tanto, existe un punto óptimo natural para el tamaño del banco de registros que alcanza un máximo de aproximadamente 32 registros diseñados para la mayoría de las clases de CPU. x86-32 tiene 8 registros y definitivamente es demasiado pequeño. ARM fue con 16 registros y es un buen compromiso. 32 registros son un poco demasiado, si acaso, terminas sin necesitar los últimos 10 más o menos.
Nada de esto afecta a los registros adicionales que obtiene para SSE y otros coprocesadores de coma flotante vectorial. Esos tienen sentido como un conjunto adicional porque se ejecutan independientemente del núcleo entero y no aumentan la complejidad de la CPU de manera exponencial.