En 32 bits, teníamos 8 registros de "propósito general". Con 64 bits, la cantidad se duplica, pero parece independiente del cambio de 64 bits en sí.
Ahora, si los registros son tan rápidos (sin acceso a la memoria), ¿por qué no hay más de ellos naturalmente? ¿No deberían los constructores de CPU trabajar con tantos registros como sea posible en la CPU? ¿Cuál es la restricción lógica de por qué solo tenemos la cantidad que tenemos?
88
Respuestas:
Hay muchas razones por las que no solo tiene una gran cantidad de registros:
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.
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.
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.
fuente
Nos hacemos tienen más de Ellos
Debido a que casi todas las instrucciones deben seleccionar 1, 2 o 3 registros arquitectónicamente visibles, expandir el número de ellos aumentaría el tamaño del código en varios bits en cada instrucción y reduciría la densidad del código. También aumenta la cantidad de contexto que debe guardarse como estado del hilo y guardarse parcialmente en el registro de activación de una función . Estas operaciones ocurren con frecuencia. Los enclavamientos de tuberías deben verificar un cuadro de indicadores para cada registro y esto tiene una complejidad cuadrática de tiempo y espacio. Y quizás la razón más importante es simplemente la compatibilidad con el conjunto de instrucciones ya definido.
Pero resulta que, gracias al cambio de nombre de los registros , realmente tenemos muchos registros disponibles y ni siquiera necesitamos guardarlos. La CPU en realidad tiene muchos conjuntos de registros y automáticamente cambia entre ellos a medida que se ejecuta su código. Hace esto simplemente para obtener más registros.
Ejemplo:
En una arquitectura que solo tiene r0-r7, el siguiente código puede ser reescrito automáticamente por la CPU como algo como:
En este caso, r10 es un registro oculto que se sustituye temporalmente por r1. La CPU puede decir que el valor de r1 nunca se vuelve a utilizar después del primer almacenamiento. Esto permite retrasar la primera carga (incluso un acierto de caché en el chip suele tardar varios ciclos) sin requerir el retraso de la segunda carga o de la segunda tienda.
fuente
Agregan registros todo el tiempo, pero a menudo están vinculados a instrucciones de propósito especial (por ejemplo, SIMD, SSE2, etc.) o requieren compilarse en una arquitectura de CPU específica, lo que reduce la portabilidad. Las instrucciones existentes a menudo funcionan en registros específicos y no podrían aprovechar otros registros si estuvieran disponibles. Conjunto de instrucciones heredado y todo.
fuente
Para agregar un poco de información interesante aquí, notará que tener 8 registros del mismo tamaño permite que los códigos de operación mantengan la coherencia con la notación hexadecimal. Por ejemplo la instrucción
push ax
es el código de operación 0x50 en x86 y sube a 0x57 para el último registro di. Luego, la instrucciónpop ax
comienza en 0x58 y sube hasta 0x5Fpop di
para completar la primera base-16. La consistencia hexadecimal se mantiene con 8 registros por tamaño.fuente